Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(route/picuki): update title parsing logic #17131

Merged
merged 2 commits into from
Oct 16, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 50 additions & 39 deletions lib/routes/picuki/profile.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Route } from '@/types';
import { DataItem, Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);

Expand All @@ -16,20 +16,29 @@ function deVideo(media) {
let media_deVideo = '';
$('img,video').each((_, medium) => {
const tag = medium.name;
medium = $(medium);
const poster = medium.attr('poster');
const mediumElement = $(medium);
const poster = mediumElement.attr('poster');
// 如果有 poster 属性,表明它是视频,将它替换为它的 poster;如果不是,就原样返回
media_deVideo += poster ? `<img src='${poster}' alt='video poster'>` : tag === 'img' ? medium.toString() : '';
media_deVideo += (() => {
if (poster) {
return `<img src='${poster}' alt='video poster'>`;
} else if (tag === 'img') {
return mediumElement.toString();
} else {
return '';
}
})();
});
return media_deVideo;
}

export const route: Route = {
path: '/profile/:id/:functionalFlag?',
path: '/profile/:id/:type?/:functionalFlag?',
categories: ['social-media'],
example: '/picuki/profile/stefaniejoosten',
parameters: {
id: 'Instagram user id',
type: 'Type of profile page (profile or tagged)',
functionalFlag: `functional flag, see the table below
| functionalFlag | Video embedding | Fetching Instagram Stories |
| -------------- | --------------------------------------- | -------------------------- |
Expand All @@ -51,9 +60,13 @@ export const route: Route = {
source: ['www.picuki.com/profile/:id'],
target: '/profile/:id',
},
{
source: ['www.picuki.com/profile-tagged/:id'],
target: '/profile/:id/tagged',
},
],
name: 'User Profile - Picuki',
maintainers: ['hoilc', 'Rongronggg9', 'devinmugen'],
maintainers: ['hoilc', 'Rongronggg9', 'devinmugen', 'NekoAria'],
handler,
description: `
:::warning
Expand All @@ -67,61 +80,58 @@ async function handler(ctx) {
const browser = await puppeteer();

const id = ctx.req.param('id');
const displayVideo = ctx.req.param('functionalFlag') !== '0';
const includeStories = ctx.req.param('functionalFlag') === '10';
const type = ctx.req.param('type') ?? 'profile';
const functionalFlag = ctx.req.param('functionalFlag') ?? '1';
const displayVideo = functionalFlag !== '0';
const includeStories = functionalFlag === '10';

const profileUrl = `https://www.picuki.com/profile/${id}`;
const profileUrl = `https://www.picuki.com/${type === 'tagged' ? 'profile-tagged' : 'profile'}/${id}`;

const data = await cache.tryGet(
`picuki-${id}-profile-${includeStories}`,
async () => {
const _r = await puppeteerGet(profileUrl, browser, includeStories);
return _r;
},
config.cache.routeExpire,
false
);
const key = `picuki-${id}-${type}-${includeStories}`;
const data = await cache.tryGet(key, () => puppeteerGet(profileUrl, browser, includeStories), config.cache.routeExpire, false);
const $ = load(data);

const profileName = $('.profile-name-bottom').text();
const profileTitle = type === 'tagged' ? `${profileName} (@${id}) tagged posts - Picuki` : `${profileName} (@${id}) public posts - Picuki`;
const profileImg = $('.profile-avatar > img').attr('src');
const profileDescription = $('.profile-description').text();

const list = $('ul.box-photos [data-s="media"]').get();
const list = $('ul.box-photos [data-s="media"]').toArray();

let items: DataItem[] = [];

let items = [];
let description: string;

if (includeStories) {
const stories_wrapper = $('.stories_wrapper');
if (stories_wrapper.length) {
const storyItems = $(stories_wrapper)
.find('.item')
.get()
.toArray()
.map((item) => {
const $item = $(item);
let title = $item.find('.stories_count');
title = title.length ? title.text() : '';
const pubDate = title ? chrono.parseDate(title) : new Date();
const titleElement = $item.find('.stories_count');
const title = titleElement.length ? titleElement.text() : '';
const pubDate = chrono.parseDate(title) || new Date();
const postBox = $item.find('.launchLightbox');
const poster = postBox.attr('data-video-poster');
const href = postBox.attr('href');
const type = postBox.attr('data-post-type'); // video / image
const origin = postBox.attr('data-origin');
const storiesBackground = $item.find('.stories_background');
const storiesBackgroundUrl = storiesBackground && storiesBackground.css('background-image').match(/url\('?(.*)'?\)/);
const storiesBackgroundUrlSrc = storiesBackgroundUrl && storiesBackgroundUrl[1];
let description;
const storiesBackgroundUrl = storiesBackground?.css('background-image')?.match(/url\('?(.*)'?\)/);
const storiesBackgroundUrlSrc = storiesBackgroundUrl?.[1];
if (type === 'video') {
description = art(path.join(__dirname, 'templates/video.art'), {
videoSrcs: [href, origin].filter(Boolean),
videoPoster: poster || storiesBackgroundUrlSrc || '',
videoPoster: poster ?? storiesBackgroundUrlSrc ?? '',
});
} else if (type === 'image') {
description = `<img src="${href || poster || storiesBackgroundUrlSrc || ''}" alt="Instagram Story">`;
description = `<img src="${href ?? poster ?? storiesBackgroundUrlSrc ?? ''}" alt="Instagram Story">`;
}

return {
title: 'Instagram Story' + (pubDate ? '' : `: ${title}`),
title: title ? `Instagram Story: ${title}` : 'Instagram Story',
author: `@${id}`,
description,
link: href,
Expand Down Expand Up @@ -149,17 +159,18 @@ async function handler(ctx) {
$('.single-photo img').each((_, item) => {
html += $(item).toString();
});
$('.single-photo video').each((_, item) => {
item = $(item);
$('.single-photo video').each((_, element) => {
const item = $(element);
let videoSrc = item.attr('src');
if (videoSrc === undefined) {
videoSrc = item.children().attr('src');
}
const videoPoster = item.attr('poster');
let origin = item.parent().attr('onclick');
if (origin) {
origin = origin.match(/window\.open\('([^']*)'/);
origin = origin && origin[1];
const regex = /window\.open\('([^']*)'/;
const match = regex.exec(origin);
origin = match?.[1];
}
html += art(path.join(__dirname, 'templates/video.art'), {
videoSrcs: [videoSrc, origin].filter(Boolean),
Expand All @@ -172,15 +183,15 @@ async function handler(ctx) {
items = [
...items,
...(await Promise.all(
list.map(async (post) => {
post = $(post);
list.map(async (item) => {
const post = $(item);

const postLink = post.find('.photo > a').attr('href');
const postTime = post.find('.time');
const pubDate = postTime ? chrono.parseDate(postTime.text()) : new Date();
const pubDate = postTime?.text() ? (chrono.parseDate(postTime.text()) ?? new Date()) : new Date();
const media_displayVideo = await getMedia(postLink);
const postText = post
.find('.photo-description')
.find('.photo-action-description')
.text()
.trim()
.replaceAll(/[^\S\n]+/g, ' ')
Expand All @@ -206,7 +217,7 @@ async function handler(ctx) {
await browser.close();

return {
title: `${profileName} (@${id}) - Picuki`,
title: profileTitle,
link: profileUrl,
image: profileImg,
description: profileDescription,
Expand Down