const got = require('@/utils/got');
const cheerio = require('cheerio');
const chrono = require('chrono-node');
const { art } = require('@/utils/render');
const path = require('path');
const config = require('@/config').value;
module.exports = async (ctx) => {
const id = ctx.params.id;
const displayVideo = ctx.params.functionalFlag !== '0';
const includeStories = ctx.params.functionalFlag === '10';
const profileUrl = `https://www.picuki.com/profile/${id}`;
const data = await ctx.cache.tryGet(
profileUrl,
async () => {
const _r = await got(profileUrl);
return _r.data;
},
config.cache.routeExpire,
false
);
const $ = cheerio.load(data);
const profileName = $('.profile-name-bottom').text();
const profileImg = $('.profile-avatar > img').attr('src');
const profileDescription = $('.profile-description').text();
const list = $('ul.box-photos [data-s="media"]').get();
async function getMedia(url) {
const getPost = async () => {
const response = await got(url, {
headers: {
Referer: profileUrl,
},
});
return response.data;
};
let data = await ctx.cache.tryGet(url, getPost);
if (Object.prototype.toString.call(data) === '[object Object]') {
// oops, it's a json, maybe it's an old cache from the old version of this route!
data = await getPost();
// re-cache it
await ctx.cache.set(url, data);
}
const $ = cheerio.load(data);
// Instagram 允许最多 10 条图片/视频任意混合于一条 post,picuki 在所有情况下都会将它(们)置于 .single-photo 内
let html = '';
$('.single-photo img,video').each((_, item) => {
html += $(item).toString(); // 可能是视频,也可能是图片,格式都比较友好,这里直接添加
});
return html;
}
function deVideo(media) {
const $ = cheerio.load(media);
let media_deVideo = '';
$('img,video').each((_, medium) => {
const tag = medium.name;
medium = $(medium);
const poster = medium.attr('poster');
// 如果有 poster 属性,表明它是视频,将它替换为它的 poster;如果不是,就原样返回
media_deVideo += poster ? `
` : tag === 'img' ? medium.toString() : '';
});
return media_deVideo;
}
let items = await Promise.all(
list.map(async (post) => {
post = $(post);
const postLink = post.find('.photo > a').attr('href');
const postTime = post.find('.time');
const pubDate = postTime ? chrono.parseDate(postTime.text()) : new Date();
const media_displayVideo = await getMedia(postLink);
const postText = post
.find('.photo-description')
.text()
.trim()
.replace(/[^\S\n]+/g, ' ')
.replace(/((?<=\n|^)[^\S\n])|([^\S\n](?=\n|$))/g, '');
const title = postText.replace(/\n/g, ' ') || 'Untitled';
const description = art(path.join(__dirname, 'templates/post.art'), {
media: displayVideo ? media_displayVideo : deVideo(media_displayVideo),
desc: postText,
locationLink: post.find('.photo-location .icon-globe-alt a'),
});
return {
title,
author: `@${id}`,
description,
link: postLink,
pubDate,
};
})
);
const getStories = async (queryId) => {
const apiUrl = `https://www.picuki.com/app/controllers/ajax.php`;
const key = `${apiUrl}?query=${queryId}&type=story`;
const data = await ctx.cache.tryGet(key, async () => {
const response = await got.post(apiUrl, {
form: {
query: queryId,
type: 'story',
},
headers: {
Referer: profileUrl,
Origin: 'https://www.picuki.com',
'X-Requested-With': 'XMLHttpRequest',
},
});
return response.data;
});
if (data && data.stories_container) {
const $ = cheerio.load(data.stories_container);
const storyItems = $('.item')
.get()
.map((item) => {
const $item = $(item);
const postTime = $item.find('.stories_count');
const pubDate = postTime.length ? chrono.parseDate(postTime.text()) : 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 storiesBackground = $item.find('.stories_background');
const storiesBackgroundUrl = storiesBackground && storiesBackground.css('background-image').match(/url\('?(.*)'?\)/);
const storiesBackgroundUrlSrc = storiesBackgroundUrl && storiesBackgroundUrl[1];
let description;
if (type === 'video') {
description = `