diff --git a/docker-compose.yml b/docker-compose.yml index 33f9172b1a..3f11873660 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,7 +12,6 @@ services: CACHE_TYPE: redis REDIS_URL: 'redis://redis:6379/' PUPPETEER_WS_ENDPOINT: 'ws://browserless:3000' # marked - PUPPETEER_REAL_BROWSER_SERVICE: 'http://real-browser:3000' # marked healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:1200/healthz'] interval: 30s @@ -22,17 +21,6 @@ services: - redis - browserless # marked - real-browser: - image: ghcr.io/hyoban/puppeteer-real-browser-hono - restart: always - ports: - - '3001:3000' - healthcheck: - test: ['CMD', 'curl', '-f', 'http://localhost:3000'] - interval: 30s - timeout: 10s - retries: 3 - browserless: # marked image: browserless/chrome # marked restart: always # marked diff --git a/lib/routes/comic-walker/manga.ts b/lib/routes/comic-walker/manga.ts index 4ee72b67ab..77bdf53afc 100644 --- a/lib/routes/comic-walker/manga.ts +++ b/lib/routes/comic-walker/manga.ts @@ -25,7 +25,7 @@ export const route: Route = { target: '/manga/:id', }, ], - name: 'カドコミ(Kadocomi)漫画详情', + name: '漫画详情', maintainers: ['xiaobailoves'], handler: async (ctx) => { diff --git a/lib/routes/picnob.info/user.ts b/lib/routes/picnob.info/user.ts index 4160f53ca4..f8ac278722 100644 --- a/lib/routes/picnob.info/user.ts +++ b/lib/routes/picnob.info/user.ts @@ -6,6 +6,7 @@ import { ViewType } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import wait from '@/utils/wait'; import type { Post, Profile, Pull, Status, Story } from './types'; @@ -105,7 +106,7 @@ async function handler(ctx) { } if (attempt < 9) { // eslint-disable-next-line no-await-in-loop - await new Promise((resolve) => setTimeout(resolve, 3000)); + await wait(3000); } } diff --git a/lib/routes/picnob/user.ts b/lib/routes/picnob/user.ts index e76aa0ab1b..14a388a42f 100644 --- a/lib/routes/picnob/user.ts +++ b/lib/routes/picnob/user.ts @@ -1,51 +1,51 @@ -import { load } from 'cheerio'; -import type { ConnectResult, Options } from 'puppeteer-real-browser'; -import { connect } from 'puppeteer-real-browser'; +// import { load } from 'cheerio'; +// import type { ConnectResult, Options } from 'puppeteer-real-browser'; +// import { connect } from 'puppeteer-real-browser'; -import { config } from '@/config'; +// import { config } from '@/config'; import type { Route } from '@/types'; import { ViewType } from '@/types'; -import cache from '@/utils/cache'; -import { parseRelativeDate } from '@/utils/parse-date'; +// import cache from '@/utils/cache'; +// import { parseRelativeDate } from '@/utils/parse-date'; -const realBrowserOption: Options = { - args: ['--start-maximized'], - turnstile: true, - headless: false, - // disableXvfb: true, - // ignoreAllFlags:true, - customConfig: { - chromePath: config.chromiumExecutablePath, - }, - connectOption: { - defaultViewport: null, - }, - plugins: [], -}; +// const realBrowserOption: Options = { +// args: ['--start-maximized'], +// turnstile: true, +// headless: false, +// // disableXvfb: true, +// // ignoreAllFlags:true, +// customConfig: { +// chromePath: config.chromiumExecutablePath, +// }, +// connectOption: { +// defaultViewport: null, +// }, +// plugins: [], +// }; -async function getPageWithRealBrowser(url: string, selector: string, conn: ConnectResult | null) { - try { - if (conn) { - const page = conn.page; - await page.goto(url, { timeout: 30000 }); - let verify: boolean | null = null; - const startDate = Date.now(); - while (!verify && Date.now() - startDate < 30000) { - // eslint-disable-next-line no-await-in-loop, no-restricted-syntax - verify = await page.evaluate((sel) => (document.querySelector(sel) ? true : null), selector).catch(() => null); - // eslint-disable-next-line no-await-in-loop - await new Promise((r) => setTimeout(r, 1000)); - } - return await page.content(); - } else { - const res = await fetch(`${config.puppeteerRealBrowserService}?url=${encodeURIComponent(url)}&selector=${encodeURIComponent(selector)}`); - const json = await res.json(); - return (json.data?.at(0) || '') as string; - } - } catch { - return ''; - } -} +// async function getPageWithRealBrowser(url: string, selector: string, conn: ConnectResult | null) { +// try { +// if (conn) { +// const page = conn.page; +// await page.goto(url, { timeout: 30000 }); +// let verify: boolean | null = null; +// const startDate = Date.now(); +// while (!verify && Date.now() - startDate < 30000) { +// // eslint-disable-next-line no-await-in-loop, no-restricted-syntax +// verify = await page.evaluate((sel) => (document.querySelector(sel) ? true : null), selector).catch(() => null); +// // eslint-disable-next-line no-await-in-loop +// await new Promise((r) => setTimeout(r, 1000)); +// } +// return await page.content(); +// } else { +// const res = await fetch(`${config.puppeteerRealBrowserService}?url=${encodeURIComponent(url)}&selector=${encodeURIComponent(selector)}`); +// const json = await res.json(); +// return (json.data?.at(0) || '') as string; +// } +// } catch { +// return ''; +// } +// } export const route: Route = { path: '/user/:id/:type?', @@ -57,8 +57,8 @@ export const route: Route = { }, features: { requireConfig: false, - requirePuppeteer: true, - antiCrawler: true, + requirePuppeteer: false, + antiCrawler: false, supportBT: false, supportPodcast: false, supportScihub: false, @@ -79,104 +79,108 @@ export const route: Route = { view: ViewType.Pictures, }; -async function handler(ctx) { - if (!config.puppeteerRealBrowserService && !config.chromiumExecutablePath) { - throw new Error('PUPPETEER_REAL_BROWSER_SERVICE or CHROMIUM_EXECUTABLE_PATH is required to use this route.'); - } - - // NOTE: 'picnob' is still available, but all requests to 'picnob' will be redirected to 'pixnoy' eventually - const baseUrl = 'https://www.pixnoy.com'; +function handler(ctx) { const id = ctx.req.param('id'); - const type = ctx.req.param('type') ?? 'profile'; - const profileUrl = `${baseUrl}/profile/${id}/${type === 'tagged' ? 'tagged/' : ''}`; + return ctx.set('redirect', `/picnob.info/user/${id}`); - let conn: ConnectResult | null = null; + // // Original puppeteer-real-browser implementation (deprecated) + // if (!config.puppeteerRealBrowserService && !config.chromiumExecutablePath) { + // throw new Error('PUPPETEER_REAL_BROWSER_SERVICE or CHROMIUM_EXECUTABLE_PATH is required to use this route.'); + // } - if (!config.puppeteerRealBrowserService) { - conn = await connect(realBrowserOption); + // // NOTE: 'picnob' is still available, but all requests to 'picnob' will be redirected to 'picnob.info' 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/' : ''}`; - setTimeout(async () => { - if (conn) { - await conn.browser.close(); - } - }, 60000); - } + // let conn: ConnectResult | null = null; - const html = await getPageWithRealBrowser(profileUrl, '.post_box', conn); - if (!html) { - if (conn) { - await conn.browser.close(); - conn = null; - } - throw new Error('Failed to fetch user profile page. User may not exist or there are no posts available.'); - } + // if (!config.puppeteerRealBrowserService) { + // conn = await connect(realBrowserOption); - const $ = load(html); + // setTimeout(async () => { + // if (conn) { + // await conn.browser.close(); + // } + // }, 60000); + // } - const list = $('.post_box') - .toArray() - .map((item) => { - const $item = $(item); - const coverLink = $item.find('.cover_link').attr('href'); - const shortcode = coverLink?.split('/')?.[2]; - const image = $item.find('.cover .cover_link img'); - const title = image.attr('alt') || ''; + // const html = await getPageWithRealBrowser(profileUrl, '.post_box', conn); + // if (!html) { + // if (conn) { + // await conn.browser.close(); + // conn = null; + // } + // throw new Error('Failed to fetch user profile page. User may not exist or there are no posts available.'); + // } - return { - title, - description: `
${title}`, - link: `${baseUrl}${coverLink}`, - guid: shortcode, - pubDate: parseRelativeDate($item.find('.time .txt').text()), - }; - }); + // const $ = load(html); - const jobs = list.map((item) => cache.tryGet(`picnob:user:${id}:${item.guid}:html`, async () => await getPageWithRealBrowser(item.link, '.view', conn))); + // const list = $('.post_box') + // .toArray() + // .map((item) => { + // const $item = $(item); + // const coverLink = $item.find('.cover_link').attr('href'); + // const shortcode = coverLink?.split('/')?.[2]; + // const image = $item.find('.cover .cover_link img'); + // const title = image.attr('alt') || ''; - let htmlList: string[] = []; - if (conn) { - try { - for (const job of jobs) { - // eslint-disable-next-line no-await-in-loop - const html = await job; - htmlList.push(html); - } - } finally { - await conn.browser.close(); - conn = null; - } - } else { - htmlList = await Promise.all(jobs); - } + // return { + // title, + // description: `
${title}`, + // link: `${baseUrl}${coverLink}`, + // guid: shortcode, + // pubDate: parseRelativeDate($item.find('.time .txt').text()), + // }; + // }); - const newDescription = htmlList.map((html) => { - if (!html) { - return ''; - } - const $ = load(html); - if ($('.video_img').length > 0) { - return `
${$('.sum_full').text()}`; - } else { - let description = ''; - for (const pic of $('.pic img').toArray()) { - const dataSrc = $(pic).attr('data-src'); - if (dataSrc) { - description += `
`; - } - } - description += $('.sum_full').text(); - return description; - } - }); + // const jobs = list.map((item) => cache.tryGet(`picnob:user:${id}:${item.guid}:html`, async () => await getPageWithRealBrowser(item.link, '.view', conn))); - 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, - })), - }; + // let htmlList: string[] = []; + // if (conn) { + // try { + // for (const job of jobs) { + // // eslint-disable-next-line no-await-in-loop + // const html = await job; + // htmlList.push(html); + // } + // } finally { + // await conn.browser.close(); + // conn = null; + // } + // } else { + // htmlList = await Promise.all(jobs); + // } + + // const newDescription = htmlList.map((html) => { + // if (!html) { + // return ''; + // } + // const $ = load(html); + // if ($('.video_img').length > 0) { + // return `
${$('.sum_full').text()}`; + // } else { + // let description = ''; + // for (const pic of $('.pic img').toArray()) { + // const dataSrc = $(pic).attr('data-src'); + // if (dataSrc) { + // description += `
`; + // } + // } + // description += $('.sum_full').text(); + // return description; + // } + // }); + + // 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, + // })), + // }; }