From 9e48d6993a9ac895e3826fb79e3e4d3b6bff3fba Mon Sep 17 00:00:00 2001 From: Ethan Shen <42264778+nczitzk@users.noreply.github.com> Date: Wed, 20 Aug 2025 23:21:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(route):=20add=206v=E7=94=B5=E5=BD=B1=20(#1?= =?UTF-8?q?9867)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(route): add 6v电影 * fix typo * fix: move to 6v123 --- lib/routes/6v123/index.ts | 488 ++++++++++++++++++++++++++++++++++ lib/routes/6v123/namespace.ts | 1 + 2 files changed, 489 insertions(+) create mode 100644 lib/routes/6v123/index.ts diff --git a/lib/routes/6v123/index.ts b/lib/routes/6v123/index.ts new file mode 100644 index 0000000000..1398c8f10b --- /dev/null +++ b/lib/routes/6v123/index.ts @@ -0,0 +1,488 @@ +import { type Data, type DataItem, type Route, ViewType } from '@/types'; + +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +import { type CheerioAPI, type Cheerio, load } from 'cheerio'; +import type { Element } from 'domhandler'; +import { type Context } from 'hono'; +import iconv from 'iconv-lite'; + +export const handler = async (ctx: Context): Promise => { + const { category = 'dy' } = ctx.req.param(); + const limit: number = Number.parseInt(ctx.req.query('limit') ?? '25', 10); + + const encoding: string = 'gb2312'; + + const baseUrl: string = 'https://www.hao6v.me'; + const targetUrl: string = new URL(category.startsWith('gvod') ? `${category}.html` : category, baseUrl).href; + + const response = await ofetch(targetUrl, { + responseType: 'arrayBuffer', + }); + const $: CheerioAPI = load(iconv.decode(Buffer.from(response), encoding)); + const language = $('html').attr('lang') ?? 'zh'; + + let items: DataItem[] = []; + + items = $('ul.list li') + .slice(0, limit) + .toArray() + .map((el): Element => { + const $el: Cheerio = $(el); + + const title: string = $el.find('a').text(); + const pubDateStr: string | undefined = $el + .find('span') + .text() + .replaceAll(/(\[|\])/g, ''); + const linkUrl: string | undefined = $el.find('a').attr('href'); + const guid: string = `${linkUrl}#${title}`; + const upDatedStr: string | undefined = pubDateStr; + + const processedItem: DataItem = { + title, + pubDate: pubDateStr ? parseDate(pubDateStr, ['MM-DD', 'YYYY-MM-DD']) : undefined, + link: linkUrl ? new URL(linkUrl, baseUrl).href : undefined, + guid, + id: guid, + updated: upDatedStr ? parseDate(upDatedStr, ['MM-DD', 'YYYY-MM-DD']) : undefined, + language, + }; + + return processedItem; + }); + + items = await Promise.all( + items.map((item) => { + if (!item.link) { + return item; + } + + return cache.tryGet(item.link, async (): Promise => { + const detailResponse = await ofetch(item.link, { + responseType: 'arrayBuffer', + }); + const $$: CheerioAPI = load(iconv.decode(Buffer.from(detailResponse), encoding)); + + $$('div#endText div.fl').remove(); + $$('div#endText div.fr').remove(); + $$('div#endText div.cr').remove(); + + $$('div#endText div.tps').remove(); + $$('div#endText div.downtps').remove(); + + const title: string = $$('h1').text(); + const description: string | undefined = $$('div#endText').html() ?? undefined; + const pubDateStr: string | undefined = item.link?.match(/\/(\d{4}-\d{2}-\d{2})\/\d+\.html/)?.[1]; + const categoryEls: Element[] = $$('div#endText p a').toArray(); + const categories: string[] = [...new Set(categoryEls.map((el) => $$(el).text()?.trim()).filter(Boolean))]; + const image: string | undefined = $$('div#endText p img').attr('src'); + const upDatedStr: string | undefined = pubDateStr; + + let processedItem: DataItem = { + title, + description, + pubDate: pubDateStr ? parseDate(pubDateStr) : item.pubDate, + category: categories, + content: { + html: description, + text: description, + }, + image, + banner: image, + updated: upDatedStr ? parseDate(upDatedStr) : item.updated, + language, + }; + + const $enclosureEl: Cheerio = $$('td a[href^="magnet"]').last(); + const enclosureUrl: string | undefined = $enclosureEl.attr('href'); + + if (enclosureUrl) { + const enclosureType: string = 'application/x-bittorrent'; + const enclosureTitle: string = $enclosureEl.text(); + + processedItem = { + ...processedItem, + enclosure_url: enclosureUrl, + enclosure_type: enclosureType, + enclosure_title: enclosureTitle || title, + }; + } + + return { + ...item, + ...processedItem, + }; + }); + }) + ); + + return { + title: `${$('title').text().split(/,/).pop()} - ${$('div.t a').last().text()}`, + description: $('meta[name="description"]').attr('content'), + link: targetUrl, + item: items, + allowEmpty: true, + image: new URL('images/logo.gif', baseUrl).href, + language, + id: targetUrl, + }; +}; + +export const route: Route = { + path: '/:category{.+}?', + name: '分类', + url: 'www.hao6v.me', + maintainers: ['nczitzk'], + handler, + example: '/6v123/dy', + parameters: { + category: { + description: '分类,默认为 `dy`,即最新电影,可在对应分类页 URL 中找到', + options: [ + { + label: '最新电影', + value: 'dy', + }, + { + label: '国语配音电影', + value: 'gydy', + }, + { + label: '动漫新番', + value: 'zydy', + }, + { + label: '经典高清', + value: 'gq', + }, + { + label: '动画电影', + value: 'jddy', + }, + { + label: '3D 电影', + value: '3D', + }, + { + label: '真人秀', + value: 'shoujidianyingmp4', + }, + { + label: '国剧', + value: 'dlz', + }, + { + label: '日韩剧', + value: 'rj', + }, + { + label: '欧美剧', + value: 'mj', + }, + { + label: '综艺节目', + value: 'zy', + }, + { + label: '港台电影', + value: 's/gangtaidianying', + }, + { + label: '日韩电影', + value: 's/jingdiandianying', + }, + { + label: '喜剧', + value: 's/xiju', + }, + { + label: '动作', + value: 's/dongzuo', + }, + { + label: '爱情', + value: 's/aiqing', + }, + { + label: '科幻', + value: 's/kehuan', + }, + { + label: '奇幻', + value: 's/qihuan', + }, + { + label: '神秘', + value: 's/shenmi', + }, + { + label: '幻想', + value: 's/huanxiang', + }, + { + label: '恐怖', + value: 's/kongbu', + }, + { + label: '战争', + value: 's/zhanzheng', + }, + { + label: '冒险', + value: 's/maoxian', + }, + { + label: '惊悚', + value: 's/jingsong', + }, + { + label: '剧情', + value: 's/juqingpian', + }, + { + label: '传记', + value: 's/zhuanji', + }, + { + label: '历史', + value: 's/lishi', + }, + { + label: '纪录', + value: 's/jilu', + }, + { + label: '印度电影', + value: 's/yindudianying', + }, + { + label: '国产电影', + value: 's/guochandianying', + }, + { + label: '欧洲电影', + value: 's/xijudianying', + }, + ], + }, + }, + description: `:::tip +订阅 [最新电影](https://www.hao6v.me/dy/),其源网址为 \`https://www.hao6v.me/dy/\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/6v123/dy\`](https://rsshub.app/6v123/dy)。 + +::: + +
+ 更多分类 + +| 分类 | ID | +| ---------------------------------------------------- | ----------------------------------------------------------------- | +| [最新电影](https://www.hao6v.me/dy/) | [dy](https://rsshub.app/6v123/dy) | +| [国语配音电影](https://www.hao6v.me/gydy/) | [gydy](https://rsshub.app/6v123/gydy) | +| [动漫新番](https://www.hao6v.me/zydy/) | [zydy](https://rsshub.app/6v123/zydy) | +| [经典高清](https://www.hao6v.me/gq/) | [gq](https://rsshub.app/6v123/gq) | +| [动画电影](https://www.hao6v.me/jddy/) | [jddy](https://rsshub.app/6v123/jddy) | +| [3D 电影](https://www.hao6v.me/3D/) | [3D](https://rsshub.app/6v123/3D) | +| [真人秀](https://www.hao6v.me/shoujidianyingmp4/) | [shoujidianyingmp4](https://rsshub.app/6v123/shoujidianyingmp4) | +| [国剧](https://www.hao6v.me/dlz/) | [dlz](https://rsshub.app/6v123/dlz) | +| [日韩剧](https://www.hao6v.me/rj/) | [rj](https://rsshub.app/6v123/rj) | +| [欧美剧](https://www.hao6v.me/mj/) | [mj](https://rsshub.app/6v123/mj) | +| [综艺节目](https://www.hao6v.me/zy/) | [zy](https://rsshub.app/6v123/zy) | +| [港台电影](https://www.hao6v.me/s/gangtaidianying/) | [s/gangtaidianying](https://rsshub.app/6v123/s/gangtaidianying) | +| [日韩电影](https://www.hao6v.me/s/jingdiandianying/) | [s/jingdiandianying](https://rsshub.app/6v123/s/jingdiandianying) | +| [喜剧](https://www.hao6v.me/s/xiju/) | [s/xiju](https://rsshub.app/6v123/s/xiju) | +| [动作](https://www.hao6v.me/s/dongzuo/) | [s/dongzuo](https://rsshub.app/6v123/s/dongzuo) | +| [爱情](https://www.hao6v.me/s/aiqing/) | [s/aiqing](https://rsshub.app/6v123/s/aiqing) | +| [科幻](https://www.hao6v.me/s/kehuan/) | [s/kehuan](https://rsshub.app/6v123/s/kehuan) | +| [奇幻](https://www.hao6v.me/s/qihuan/) | [s/qihuan](https://rsshub.app/6v123/s/qihuan) | +| [神秘](https://www.hao6v.me/s/shenmi/) | [s/shenmi](https://rsshub.app/6v123/s/shenmi) | +| [幻想](https://www.hao6v.me/s/huanxiang/) | [s/huanxiang](https://rsshub.app/6v123/s/huanxiang) | +| [恐怖](https://www.hao6v.me/s/kongbu/) | [s/kongbu](https://rsshub.app/6v123/s/kongbu) | +| [战争](https://www.hao6v.me/s/zhanzheng/) | [s/zhanzheng](https://rsshub.app/6v123/s/zhanzheng) | +| [冒险](https://www.hao6v.me/s/maoxian/) | [s/maoxian](https://rsshub.app/6v123/s/maoxian) | +| [惊悚](https://www.hao6v.me/s/jingsong/) | [s/jingsong](https://rsshub.app/6v123/s/jingsong) | +| [剧情](https://www.hao6v.me/s/juqingpian/) | [s/juqingpian](https://rsshub.app/6v123/s/juqingpian) | +| [传记](https://www.hao6v.me/s/zhuanji/) | [s/zhuanji](https://rsshub.app/6v123/s/zhuanji) | +| [历史](https://www.hao6v.me/s/lishi/) | [s/lishi](https://rsshub.app/6v123/s/lishi) | +| [纪录](https://www.hao6v.me/s/jilu/) | [s/jilu](https://rsshub.app/6v123/s/jilu) | +| [印度电影](https://www.hao6v.me/s/yindudianying/) | [s/yindudianying](https://rsshub.app/6v123/s/yindudianying) | +| [国产电影](https://www.hao6v.me/s/guochandianying/) | [s/guochandianying](https://rsshub.app/6v123/s/guochandianying) | +| [欧洲电影](https://www.hao6v.me/s/xijudianying/) | [s/xijudianying](https://rsshub.app/6v123/s/xijudianying) | + +
+`, + categories: ['multimedia'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['www.hao6v.me/:category'], + target: '/:category', + }, + { + title: '最新电影', + source: ['www.hao6v.me/dy/'], + target: '/dy', + }, + { + title: '国语配音电影', + source: ['www.hao6v.me/gydy/'], + target: '/gydy', + }, + { + title: '动漫新番', + source: ['www.hao6v.me/zydy/'], + target: '/zydy', + }, + { + title: '经典高清', + source: ['www.hao6v.me/gq/'], + target: '/gq', + }, + { + title: '动画电影', + source: ['www.hao6v.me/jddy/'], + target: '/jddy', + }, + { + title: '3D电影', + source: ['www.hao6v.me/3D/'], + target: '/3D', + }, + { + title: '真人秀', + source: ['www.hao6v.me/shoujidianyingmp4/'], + target: '/shoujidianyingmp4', + }, + { + title: '国剧', + source: ['www.hao6v.me/dlz/'], + target: '/dlz', + }, + { + title: '日韩剧', + source: ['www.hao6v.me/rj/'], + target: '/rj', + }, + { + title: '欧美剧', + source: ['www.hao6v.me/mj/'], + target: '/mj', + }, + { + title: '综艺节目', + source: ['www.hao6v.me/zy/'], + target: '/zy', + }, + { + title: '港台电影', + source: ['www.hao6v.me/s/gangtaidianying/'], + target: '/s/gangtaidianying', + }, + { + title: '日韩电影', + source: ['www.hao6v.me/s/jingdiandianying/'], + target: '/s/jingdiandianying', + }, + { + title: '喜剧', + source: ['www.hao6v.me/s/xiju/'], + target: '/s/xiju', + }, + { + title: '动作', + source: ['www.hao6v.me/s/dongzuo/'], + target: '/s/dongzuo', + }, + { + title: '爱情', + source: ['www.hao6v.me/s/aiqing/'], + target: '/s/aiqing', + }, + { + title: '科幻', + source: ['www.hao6v.me/s/kehuan/'], + target: '/s/kehuan', + }, + { + title: '奇幻', + source: ['www.hao6v.me/s/qihuan/'], + target: '/s/qihuan', + }, + { + title: '神秘', + source: ['www.hao6v.me/s/shenmi/'], + target: '/s/shenmi', + }, + { + title: '幻想', + source: ['www.hao6v.me/s/huanxiang/'], + target: '/s/huanxiang', + }, + { + title: '恐怖', + source: ['www.hao6v.me/s/kongbu/'], + target: '/s/kongbu', + }, + { + title: '战争', + source: ['www.hao6v.me/s/zhanzheng/'], + target: '/s/zhanzheng', + }, + { + title: '冒险', + source: ['www.hao6v.me/s/maoxian/'], + target: '/s/maoxian', + }, + { + title: '惊悚', + source: ['www.hao6v.me/s/jingsong/'], + target: '/s/jingsong', + }, + { + title: '剧情', + source: ['www.hao6v.me/s/juqingpian/'], + target: '/s/juqingpian', + }, + { + title: '传记', + source: ['www.hao6v.me/s/zhuanji/'], + target: '/s/zhuanji', + }, + { + title: '历史', + source: ['www.hao6v.me/s/lishi/'], + target: '/s/lishi', + }, + { + title: '纪录', + source: ['www.hao6v.me/s/jilu/'], + target: '/s/jilu', + }, + { + title: '印度电影', + source: ['www.hao6v.me/s/yindudianying/'], + target: '/s/yindudianying', + }, + { + title: '国产电影', + source: ['www.hao6v.me/s/guochandianying/'], + target: '/s/guochandianying', + }, + { + title: '欧洲电影', + source: ['www.hao6v.me/s/xijudianying/'], + target: '/s/xijudianying', + }, + ], + view: ViewType.Articles, +}; diff --git a/lib/routes/6v123/namespace.ts b/lib/routes/6v123/namespace.ts index 9155405002..80d157fef6 100644 --- a/lib/routes/6v123/namespace.ts +++ b/lib/routes/6v123/namespace.ts @@ -3,5 +3,6 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: '6v 电影', url: 'hao6v.cc', + categories: ['multimedia'], lang: 'zh-CN', };