Skip to content

Commit

Permalink
feat(picnob): cache user metadata & video playback in img_multi (DIYg…
Browse files Browse the repository at this point in the history
  • Loading branch information
TonyRL authored Nov 29, 2024
1 parent c106c5f commit bc89b3d
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 37 deletions.
11 changes: 9 additions & 2 deletions lib/routes/picnob/templates/desc.art
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
{{ if item.type === 'video' }}
<video poster="{{ item.pic }}" controls>
<video poster="{{ item.pic }}" controls preload="metadata">
<source src="{{ item.video }}" type="video/mp4">
</video>
{{ else if item.type === 'img_multi' }}
{{ each item.images i }}
<img src="{{ i.url }}">
{{ if i.isVideo }}
<video poster="{{ i.url }}" controls preload="metadata">
<source src="{{ i.ori }}" type="video/mp4">
</video>
{{ else }}
<img src="{{ i.url }}">
{{ /if }}
<br>
{{ /each }}
{{ else if item.type === 'img_sig' }}
<img src="{{ item.pic }}">
Expand Down
87 changes: 52 additions & 35 deletions lib/routes/picnob/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);

import cache from '@/utils/cache';
import got from '@/utils/got';
import ofetch from '@/utils/ofetch';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { puppeteerGet } from './utils';
import puppeteer from '@/utils/puppeteer';
import sanitizeHtml from 'sanitize-html';

export const route: Route = {
path: '/user/:id',
Expand All @@ -26,7 +27,7 @@ export const route: Route = {
},
radar: [
{
source: ['picnob.com/profile/:id/*'],
source: ['piokok.com/profile/:id/*'],
target: '/user/:id',
},
],
Expand All @@ -37,61 +38,77 @@ export const route: Route = {
};

async function handler(ctx) {
// NOTE: 'picnob' is still available, but all requests to 'picnob' will be redirected to 'pixwox' eventually
const baseUrl = 'https://www.pixwox.com';
// NOTE: 'picnob' is still available, but all requests to 'picnob' will be redirected to 'piokok' eventually
const baseUrl = 'https://www.piokok.com';
const id = ctx.req.param('id');
const url = `${baseUrl}/profile/${id}/`;

const browser = await puppeteer();
// TODO: can't bypass cloudflare 403 error without puppeteer
let html;
let usePuppeteer = false;
try {
const { data } = await got(url, {
headers: {
accept: 'text/html',
referer: 'https://www.google.com/',
},
});
html = data;
} catch (error: any) {
if (error.message.includes('403')) {
html = await puppeteerGet(url, browser);
usePuppeteer = true;
const profile = (await cache.tryGet(`picnob:user:${id}`, async () => {
let html;
let usePuppeteer = false;
try {
const data = await ofetch(url, {
headers: {
accept: 'text/html',
referer: 'https://www.google.com/',
},
});
html = data;
} catch (error: any) {
if (error.message.includes('403')) {
html = await puppeteerGet(url, browser);
usePuppeteer = true;
}
}
}
const $ = load(html);
const profileName = $('h1.fullname').text();
const userId = $('input[name=userid]').attr('value');
const $ = load(html);
const name = $('h1.fullname').text();
const userId = $('input[name=userid]').attr('value');

return {
name,
userId,
description: $('.info .sum').text(),
image: $('.ava .pic img').attr('src'),
usePuppeteer,
};
})) as {
name: string;
userId: string;
description: string;
image: string;
usePuppeteer: boolean;
};

let posts;
if (usePuppeteer) {
const data = await puppeteerGet(`${baseUrl}/api/posts?userid=${userId}`, browser);
if (profile.usePuppeteer) {
const data = await puppeteerGet(`${baseUrl}/api/posts?userid=${profile.userId}`, browser);
posts = data.posts;
} else {
const { data } = await got(`${baseUrl}/api/posts`, {
const data = await ofetch(`${baseUrl}/api/posts`, {
headers: {
accept: 'application/json',
},
searchParams: {
userid: userId,
query: {
userid: profile.userId,
},
});
posts = data.posts;
}

const list = await Promise.all(
posts.items.map(async (item) => {
const { shortcode, sum, type, time } = item;
const { shortcode, sum, sum_pure, type, time } = item;

const link = `${baseUrl}/post/${shortcode}/`;
if (type === 'img_multi') {
item.images = await cache.tryGet(link, async () => {
let html;
if (usePuppeteer) {
if (profile.usePuppeteer) {
html = await puppeteerGet(link, browser);
} else {
const { data } = await got(link);
const data = await ofetch(link);
html = data;
}
const $ = load(html);
Expand All @@ -104,6 +121,7 @@ async function handler(ctx) {
return {
ori: a.attr('href'),
url: a.find('img').attr('data-src'),
isVideo: !!a.find('.icon_play').length,
};
})
),
Expand All @@ -112,8 +130,7 @@ async function handler(ctx) {
}

return {
// sum_pure lacks linebreaks/spaces between lines
title: load(sum, null, false).text().replaceAll('\n', ' '),
title: sanitizeHtml(sum.split('\n')[0], { allowedTags: [], allowedAttributes: {} }) || sum_pure,
description: art(path.join(__dirname, 'templates/desc.art'), {
item: {
...item,
Expand All @@ -129,10 +146,10 @@ async function handler(ctx) {
await browser.close();

return {
title: `${profileName} (@${id}) - Picnob`,
description: $('.info .sum').text(),
title: `${profile.name} (@${id}) - Picnob`,
description: profile.description,
link: url,
image: $('.ava .pic img').attr('src'),
image: profile.image,
item: list,
};
}

0 comments on commit bc89b3d

Please sign in to comment.