UNPKG

rsshub

Version:
229 lines (194 loc) 8.95 kB
import { Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import path from 'node:path'; import md5 from '@/utils/md5'; export const route: Route = { path: '/:id?', name: '分类', url: '4ksj.com', maintainers: ['nczitzk'], handler, example: '/4ksj/4k-uhd-1', parameters: { id: '分类 id,默认为最新4K电影' }, description: `::: tip 若订阅 [最新 4K 电影](https://www.4ksj.com/4k-uhd-1.html),网址为 \`https://www.4ksj.com/4k-uhd-1.html\`。截取 \`https://www.4ksj.com/\` 到末尾 \`.html\` 的部分 \`4k-uhd-1\` 作为参数,此时路由为 [\`/4ksj/4k-uhd-1\`](https://rsshub.app/4ksj/4k-uhd-1)。 若订阅子分类 [Dolby Vision 动作 4K 电影](https://www.4ksj.com/4k-uhd-s7-display-3-dytypes-1-1.html),网址为 \`https://www.4ksj.com/4k-uhd-s7-display-3-dytypes-1-1.html\`。截取 \`https://www.4ksj.com/forum-\` 到末尾 \`.html\` 的部分 \`4kdianying-s7-dianyingbiaozhun-3-dytypes-9-1\` 作为参数,此时路由为 [\`/4ksj/4k-uhd-s7-display-3-dytypes-1-1\`](https://rsshub.app/4ksj/4k-uhd-s7-display-3-dytypes-1-1)。 :::`, categories: ['multimedia'], }; function stringtoHex(acSTR) { let val = ''; for (let i = 0; i <= acSTR.length - 1; i++) { const str = acSTR.charAt(i); const code = str.codePointAt(); val += code; } return val; } async function handler(ctx) { const { id = '4k-uhd-1' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25; const rootUrl = 'https://www.4ksj.com'; const currentUrl = new URL(`${id}.html`, rootUrl).href; const response = await ofetch(currentUrl, { responseType: 'arrayBuffer', }); const decoder = new TextDecoder('gbk'); const $ = load(decoder.decode(response)); const language = 'zh'; const image = $('div.nexlogo img').prop('src'); let items = $('div.nex_cmo_piv a') .slice(0, limit) .toArray() .map((item) => { item = $(item); return { link: new URL(item.prop('href'), rootUrl).href, }; }); const getCookie = () => cache.tryGet('4ksj:cookie', async () => { const response = await ofetch(items[0].link); const $ = load(response); const scriptPath = $('script').attr('src')!; const scriptUrl = new URL(scriptPath, rootUrl).href; const scriptResponse = await ofetch(scriptUrl); const key = scriptResponse.match(/{var key="(.*?)"/)?.[1]; const value = scriptResponse.match(/",value="(.*?)"/)?.[1]; const getPath = scriptResponse.match(/\.get\("(.*?&key=)"/)?.[1]; if (!key || !value || !getPath) { throw new Error('Failed to get cookie'); } const cookieResponse = await ofetch.raw(`${rootUrl}${getPath}${key}&value=${md5(stringtoHex(value))}`); return cookieResponse.headers .getSetCookie() .map((c) => c.split(';')[0]) .join('; '); }); const cookie = await getCookie(); items = await Promise.all( items.map((item) => cache.tryGet(item.link, async () => { const detailResponse = await ofetch(item.link, { responseType: 'arrayBuffer', headers: { Cookie: cookie as string, }, }); const $$ = load(decoder.decode(detailResponse)); $$('div.nex_drama_intros em').first().remove(); $$('strong font').each((_, el) => { el = $$(el); el.parent().remove(); }); const title = $$('div.nex_drama_Top h5').text(); const description = $$('div.nex_drama_intros').html(); const picture = $$('div.nex_drama_pic') .html() .match(/background:url\((.*?)\)/)?.[1] ?? ''; const details = $$('li.nex_drama_Detail_li, li.nex_drama_Detail_lis dd') .toArray() .map((li) => { li = $$(li); const key = li.find('em').text().replaceAll(/:|\s/g, ''); const value = li.find('span').length === 0 ? li.contents().last().text().trim() : li.find('span').text().trim(); return { [key]: value }; }); const mergedDetails = Object.assign({}, ...details); const links = $$('td.t_f ignore_js_op').length === 0 ? $$('td.t_f strong') .toArray() .map((l) => { l = $$(l); const title = l.contents().first().text(); const link = l.next().prop('href') ?? l.nextUntil('a').next().prop('href'); item.enclosure_url = item.enclosure_url ?? link; item.enclosure_type = item.enclosure_type ?? 'application/x-bittorrent'; item.enclosure_title = item.enclosure_title ?? title; return { title, tags: l .contents() .last() .text() .match(/【(.*?)】/g), link, }; }) : $$('div.newfujian') .toArray() .map((l) => { l = $$(l); return { title: l.find('p.filename').prop('title') || l.find('p.filename').text(), tags: l .find('div.fileaq') .text() .match(/【(.*?)】/g), link: l.find('div.down_2 a').prop('href'), }; }); const pubDateEl = $$('table.boxtable em').first(); const pubDate = pubDateEl.find('span[title]').length === 0 ? pubDateEl .first() .text() .replace(/发表于\s/, '') : pubDateEl.find('span[title]').prop('title'); item.title = title; item.description = art(path.join(__dirname, 'templates/description.art'), { images: picture ? [ { src: picture, alt: title, }, ] : undefined, title, keys: Object.keys(mergedDetails), details: mergedDetails, description, info: $$('div.nex_drama_sums').html(), links, }); item.pubDate = timezone(parseDate(pubDate, 'YYYY-M-D HH:mm:ss'), +8); item.category = Object.values(mergedDetails) .flatMap((c) => c.split(/\s/)) .filter(Boolean); item.author = mergedDetails['导演']; item.content = { html: description, text: $$('div.nex_drama_intros').text(), }; item.image = picture; item.banner = picture; item.language = language; return item; }) ) ); return { title: `4k世界 - ${ $('#fontsearch ul.cl li.a') .toArray() .map((a) => $(a).text()) .join('+') || '不限' }`, description: $('meta[name="description"]').prop('content'), link: currentUrl, item: items, allowEmpty: true, image, author: $('meta[name="application-name"]').prop('content'), language, }; }