fix(route/fanbox): refactor to use playwright to load posts (#22681)
* fix(route/fanbox): refactor to use playwright to load posts * fix(route/fanbox) added missing await * fix(route/fanbox) closing context after page close * fix(route/fanbox) Update model to conform to new API response --------- Co-authored-by: Sryvkver <git@sryvkver.com>
This commit is contained in:
parent
abb7a21e28
commit
de93618f18
|
|
@ -1,12 +1,14 @@
|
|||
import type { Context } from 'hono';
|
||||
|
||||
import InvalidParameterError from '@/errors/types/invalid-parameter';
|
||||
import type { Data, Route } from '@/types';
|
||||
import type { Data, DataItem, Route } from '@/types';
|
||||
import ofetch from '@/utils/ofetch';
|
||||
import playwright from '@/utils/playwright';
|
||||
import { setCookies } from '@/utils/playwright-utils';
|
||||
import { isValidHost } from '@/utils/valid-host';
|
||||
|
||||
import type { PostListResponse, UserInfoResponse } from './types';
|
||||
import { getHeaders, parseItem } from './utils';
|
||||
import { getCookieString, getHeaders, parseItem } from './utils';
|
||||
|
||||
export const route: Route = {
|
||||
path: '/:creator',
|
||||
|
|
@ -24,6 +26,7 @@ export const route: Route = {
|
|||
optional: true,
|
||||
},
|
||||
],
|
||||
requirePuppeteer: true,
|
||||
nsfw: true,
|
||||
},
|
||||
};
|
||||
|
|
@ -53,7 +56,36 @@ async function handler(ctx: Context): Promise<Data> {
|
|||
}
|
||||
|
||||
const postListResponse = (await ofetch(`https://api.fanbox.cc/post.listCreator?creatorId=${creator}&limit=20&withPinned=true`, { headers: getHeaders() })) as PostListResponse;
|
||||
const items = await Promise.all(postListResponse.body.map((i) => parseItem(i)));
|
||||
|
||||
const context = await playwright();
|
||||
const page = await context.newPage();
|
||||
|
||||
const cookieString = getCookieString();
|
||||
if (cookieString) {
|
||||
await setCookies(page, cookieString, '.fanbox.cc');
|
||||
}
|
||||
|
||||
await page.route('**/*', (route) => {
|
||||
const request = route.request();
|
||||
|
||||
if (request.url().startsWith('https://api.fanbox.cc/post.info')) {
|
||||
route.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
request.resourceType() === 'document' ? route.continue() : route.abort();
|
||||
});
|
||||
await page.goto('https://www.fanbox.cc/', {
|
||||
waitUntil: 'domcontentloaded',
|
||||
});
|
||||
|
||||
let items: DataItem[];
|
||||
try {
|
||||
items = await Promise.all(postListResponse.body.map((i) => parseItem(page, i)));
|
||||
} finally {
|
||||
await page.close();
|
||||
await context.close();
|
||||
}
|
||||
|
||||
return {
|
||||
title,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ export interface PostListResponse {
|
|||
}
|
||||
|
||||
export interface PostDetailResponse {
|
||||
body: PostDetail;
|
||||
body: {
|
||||
post: PostDetail;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PostItem {
|
||||
|
|
|
|||
|
|
@ -5,18 +5,22 @@ import type { DataItem } from '@/types';
|
|||
import cache from '@/utils/cache';
|
||||
import ofetch from '@/utils/ofetch';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import type { Page } from '@/utils/playwright';
|
||||
|
||||
import type { ArticlePost, FilePost, ImagePost, PostDetailResponse, PostItem, TextPost, VideoPost } from './types';
|
||||
|
||||
export function getHeaders() {
|
||||
const sessionid = config.fanbox.session;
|
||||
const cookie = sessionid ? `FANBOXSESSID=${sessionid}` : '';
|
||||
return {
|
||||
origin: 'https://fanbox.cc',
|
||||
cookie,
|
||||
cookie: getCookieString(),
|
||||
};
|
||||
}
|
||||
|
||||
export function getCookieString() {
|
||||
const sessionid = config.fanbox.session;
|
||||
return sessionid ? `FANBOXSESSID=${sessionid}` : '';
|
||||
}
|
||||
|
||||
function embedUrlMap(urlEmbed: ArticlePost['body']['urlEmbedMap'][string]) {
|
||||
switch (urlEmbed.type) {
|
||||
case 'html':
|
||||
|
|
@ -131,7 +135,7 @@ async function parseArtile(body: ArticlePost['body']) {
|
|||
return ret.join('');
|
||||
}
|
||||
|
||||
async function parseDetail(i: PostDetailResponse['body']) {
|
||||
async function parseDetail(i: PostDetailResponse['body']['post']) {
|
||||
let ret = '';
|
||||
if (i.feeRequired !== 0) {
|
||||
ret += `Fee Required: <b>${i.feeRequired} JPY/month</b><hr>`;
|
||||
|
|
@ -167,12 +171,32 @@ async function parseDetail(i: PostDetailResponse['body']) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
export function parseItem(item: PostItem) {
|
||||
export function parseItem(page: Page, item: PostItem) {
|
||||
return cache.tryGet(`fanbox-${item.id}-${item.updatedDatetime}`, async () => {
|
||||
const postDetail = (await ofetch(`https://api.fanbox.cc/post.info?postId=${item.id}`, { headers: { ...getHeaders(), 'User-Agent': config.trueUA } })) as PostDetailResponse;
|
||||
const postDetail: PostDetailResponse = await page.evaluate(
|
||||
async ({ url }) => {
|
||||
const res = await fetch(url, {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-site',
|
||||
},
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP error! status: ${res.status}`);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
},
|
||||
{ url: `https://api.fanbox.cc/post.info?postId=${item.id}` }
|
||||
);
|
||||
|
||||
return {
|
||||
title: item.title || 'No title',
|
||||
description: await parseDetail(postDetail.body),
|
||||
description: await parseDetail(postDetail.body.post),
|
||||
pubDate: parseDate(item.updatedDatetime),
|
||||
link: `https://${item.creatorId}.fanbox.cc/posts/${item.id}`,
|
||||
category: item.tags,
|
||||
|
|
|
|||
Loading…
Reference in New Issue