rsshub
Version:
Make RSS Great Again!
119 lines (110 loc) • 4.28 kB
text/typescript
import { Route, ViewType } from '@/types';
import ofetch from '@/utils/ofetch';
import { load } from 'cheerio';
import { parseRelativeDate } from '@/utils/parse-date';
import { puppeteerGet } from './utils';
import sanitizeHtml from 'sanitize-html';
import cache from '@/utils/cache';
export const route: Route = {
path: '/user/:id/:type?',
categories: ['social-media'],
example: '/picnob/user/xlisa_olivex',
parameters: {
id: 'Instagram id',
type: 'Type of profile page (profile or tagged)',
},
features: {
requireConfig: false,
requirePuppeteer: true,
antiCrawler: true,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['www.pixnoy.com/profile/:id'],
target: '/user/:id',
},
{
source: ['www.pixnoy.com/profile/:id/tagged'],
target: '/user/:id/tagged',
},
],
name: 'User Profile - Picnob',
maintainers: ['TonyRL', 'micheal-death', 'AiraNadih', 'DIYgod'],
handler,
view: ViewType.Pictures,
};
async function handler(ctx) {
// NOTE: 'picnob' is still available, but all requests to 'picnob' will be redirected to 'pixnoy' eventually
const baseUrl = 'https://www.pixnoy.com';
const id = ctx.req.param('id');
const type = ctx.req.param('type') ?? 'profile';
const profileUrl = `${baseUrl}/profile/${id}/${type === 'tagged' ? 'tagged/' : ''}`;
// TODO: can't bypass cloudflare 403 error without puppeteer
let html;
let usePuppeteer = false;
try {
const data = await ofetch(profileUrl);
html = data;
} catch {
html = await puppeteerGet(profileUrl);
usePuppeteer = true;
}
const $ = load(html);
const list = $('.post_box')
.toArray()
.map((item) => {
const $item = $(item);
const sum = $item.find('.sum').text();
const coverLink = $item.find('.cover_link').attr('href');
const shortcode = coverLink?.split('/')?.[2];
return {
title: sanitizeHtml(sum.split('\n')[0], { allowedTags: [], allowedAttributes: {} }),
description: `<img src="${$item.find('.preview_w img').attr('data-src')}" /><br />${sum.replaceAll('\n', '<br>')}`,
link: `${baseUrl}${coverLink}`,
guid: shortcode,
pubDate: parseRelativeDate($item.find('.time .txt').text()),
};
});
const newDescription = await Promise.all(
list.map((item) =>
cache.tryGet(`picnob:user:${id}:${item.guid}`, async () => {
try {
const html = usePuppeteer
? await puppeteerGet(item.link)
: await ofetch(item.link, {
headers: {
'user-agent': 'PostmanRuntime/7.44.0',
},
});
const $ = load(html);
if ($('.video_img').length > 0) {
return `<video src="${$('.video_img a').attr('href')}" poster="${$('.video_img img').attr('data-src')}"></video><br />${$('.sum_full').text()}`;
} else {
let description = '';
for (const slide of $('.swiper-slide').toArray()) {
const $slide = $(slide);
description += `<img src="${$slide.find('.pic img').attr('data-src')}" /><br />`;
}
description += $('.sum_full').text();
return description;
}
} catch {
return '';
}
})
)
);
return {
title: `${$('h1.fullname').text()} (@${id}) ${type === 'tagged' ? 'tagged' : 'public'} posts - Picnob`,
description: $('.info .sum').text(),
link: profileUrl,
image: $('.ava .pic img').attr('src'),
item: list.map((item, index) => ({
...item,
description: newDescription[index] || item.description,
})),
};
}