Skip to content

Commit

Permalink
feat(route): add 电影天堂 (#18200)
Browse files Browse the repository at this point in the history
  • Loading branch information
nczitzk authored Jan 26, 2025
1 parent f193dfb commit a8bf6e6
Show file tree
Hide file tree
Showing 2 changed files with 300 additions and 57 deletions.
355 changes: 299 additions & 56 deletions lib/routes/dytt/index.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,318 @@
import type { Route } from '@/types';
import { type Data, type DataItem, type Route, ViewType } from '@/types';

import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import iconv from 'iconv-lite';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';

import { type CheerioAPI, type Cheerio, type Element, load } from 'cheerio';
import { type Context } from 'hono';

const domain: string = 'www.dydytt.net';
const baseUrl: string = `https://${domain}`;

export const handler = async (ctx: Context): Promise<Data> => {
const { category = 'gndy/dyzz' } = ctx.req.param();
const limit: number = Number.parseInt(ctx.req.query('limit') ?? '25', 10);

const targetUrl: string = new URL(`html/${category.replace(/^html\//, '')}`, baseUrl).href;

const response = await ofetch(targetUrl, {
responseType: 'arrayBuffer',
});
const $: CheerioAPI = load(iconv.decode(Buffer.from(response), 'gb2312'));
const language = $('html').attr('lang') ?? 'zh-CN';

let items: DataItem[] = [];

items = $('div.co_content8 ul table')
.slice(0, limit)
.toArray()
.map((el): Element => {
const $el: Cheerio<Element> = $(el);

const $aEl: Cheerio<Element> = $el.find('a.ulink');

const title: string = $aEl.text();
const description: string = $el.find('td').last().text();
const pubDateStr: string | undefined = $el.find('font').last().text().split(//).pop();
const linkUrl: string | undefined = $aEl.attr('href');
const upDatedStr: string | undefined = pubDateStr;

const processedItem: DataItem = {
title,
description,
pubDate: pubDateStr ? timezone(parseDate(pubDateStr), +8) : undefined,
link: linkUrl ? new URL(linkUrl, baseUrl).href : undefined,
doi: $el.find('meta[name="citation_doi"]').attr('content'),
content: {
html: description,
text: description,
},
updated: upDatedStr ? timezone(parseDate(upDatedStr), +8) : undefined,
language,
};

return processedItem;
});

items = (
await Promise.all(
items.map((item) => {
if (!item.link) {
return item;
}

return cache.tryGet(item.link, async (): Promise<DataItem> => {
const detailResponse = await ofetch(item.link, {
responseType: 'arrayBuffer',
});
const $$: CheerioAPI = load(iconv.decode(Buffer.from(detailResponse), 'gb2312'));

const title: string = $$('div.title_all h1 font').text();

const $descriptionEl: Cheerio<Element> = $$('div#Zoom span').first();
const childEls = $descriptionEl.contents().toArray();
const centerIdx = childEls.findIndex((node) => node.type === 'tag' && node.name === 'center');
const description: string = (centerIdx === -1 ? childEls : childEls.slice(0, centerIdx)).map((node) => $.html(node)).join('');

const pubDateStr: string | undefined = item.pubDate ? undefined : $descriptionEl.prev().text().split(//).pop();
const image: string | undefined = $descriptionEl.find('img').first().attr('src');
const upDatedStr: string | undefined = pubDateStr;

let processedItem: DataItem = {
title,
description,
pubDate: pubDateStr ? parseDate(pubDateStr) : item.pubDate,
content: {
html: description,
text: description,
},
image,
banner: image,
updated: upDatedStr ? parseDate(upDatedStr) : item.updated,
language,
};

const $enclosureEl: Cheerio<Element> = $descriptionEl.find('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,
enclosure_length: undefined,
itunes_duration: undefined,
itunes_item_image: image,
};
}

return {
...item,
...processedItem,
};
});
})
)
).filter((_): _ is DataItem => true);

const title: string = $('title').text();

return {
title,
description: $('META[name=description]').attr('content'),
link: targetUrl,
item: items,
allowEmpty: true,
image: new URL('images/logo.gif', baseUrl).href,
author: title.split(/_/).pop(),
language,
id: targetUrl,
};
};

export const route: Route = {
path: '/',
path: '/:category{.+}?',
name: '分类',
url: domain,
maintainers: ['junfengP', 'nczitzk'],
handler,
example: '/dytt/gndy/dyzz',
parameters: {
category: {
description: '分类,默认为 `gndy/dyzz`,即最新影片,可在对应分类页 URL 中找到',
options: [
{
label: '最新影片',
value: 'gndy/dyzz',
},
{
label: '经典影片',
value: 'gndy',
},
{
label: '国内电影',
value: 'gndy/china',
},
{
label: '欧美电影',
value: 'gndy/oumei',
},
{
label: '其它电影',
value: 'gndy/rihan',
},
{
label: '华语电视',
value: 'tv/hytv',
},
{
label: '欧美电视',
value: 'tv/oumeitv',
},
{
label: '最新综艺',
value: 'zongyi2013',
},
{
label: '旧版综艺',
value: '2009zongyi',
},
{
label: '动漫资源',
value: 'dongman',
},
{
label: '旧版游戏',
value: 'game',
},
{
label: '游戏下载',
value: 'newgame',
},
{
label: '日韩剧集专区',
value: 'tv/rihantv',
},
],
},
},
description: `:::tip
若订阅 [最新影片](${baseUrl}/html/gndy/dyzz),网址为 \`${baseUrl}/html/gndy/dyzz\`,请截取 \`${baseUrl}/html/\` 到末尾的部分 \`gndy/dyzz\` 作为 \`category\` 参数填入,此时目标路由为 [\`/dytt/gndy/dyzz\`](https://rsshub.app/dytt/gndy/dyzz)。
:::
<details>
<summary>更多分类</summary>
| 分类 | ID |
| ----------------------------------------------------- | ------------------------------------------------ |
| [最新影片](${baseUrl}/html/gndy/dyzz/index.html) | [gndy/dyzz](https://rsshub.app/dytt/gndy/dyzz) |
| [经典影片](${baseUrl}/html/gndy/index.html) | [gndy](https://rsshub.app/dytt/gndy) |
| [国内电影](${baseUrl}/html/gndy/china/index.html) | [gndy/china](https://rsshub.app/dytt/gndy/china) |
| [欧美电影](${baseUrl}/html/gndy/oumei/index.html) | [gndy/oumei](https://rsshub.app/dytt/gndy/oumei) |
| [其它电影](${baseUrl}/html/gndy/rihan/index.html) | [gndy/rihan](https://rsshub.app/dytt/gndy/rihan) |
| [华语电视](${baseUrl}/html/tv/hytv/index.html) | [tv/hytv](https://rsshub.app/dytt/tv/hytv) |
| [欧美电视](${baseUrl}/html/tv/oumeitv/index.html) | [tv/oumeitv](https://rsshub.app/dytt/tv/oumeitv) |
| [最新综艺](${baseUrl}/html/zongyi2013/index.html) | [zongyi2013](https://rsshub.app/dytt/zongyi2013) |
| [旧版综艺](${baseUrl}/html/2009zongyi/index.html) | [2009zongyi](https://rsshub.app/dytt/2009zongyi) |
| [动漫资源](${baseUrl}/html/dongman/index.html) | [dongman](https://rsshub.app/dytt/dongman) |
| [旧版游戏](${baseUrl}/html/game/index.html) | [game](https://rsshub.app/dytt/game) |
| [游戏下载](${baseUrl}/html/newgame/index.html) | [newgame](https://rsshub.app/dytt/newgame) |
| [日韩剧集专区](${baseUrl}/html/tv/rihantv/index.html) | [tv/rihantv](https://rsshub.app/dytt/tv/rihantv) |
</details>
`,
categories: ['multimedia'],
example: '/dytt',
parameters: {},
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportRadar: true,
supportBT: true,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['ygdy8.net/index.html'],
source: ['${domain}/index.htm', `${domain}/html/:category`],
target: (params) => {
const category: string = params.category;

return `/dytt/html${category ? `/${category}` : ''}`;
},
},
{
title: '最新影片',
source: [`${baseUrl}/html/gndy/dyzz/index.html`],
target: '/gndy/dyzz',
},
{
title: '经典影片',
source: [`${baseUrl}/html/gndy/index.html`],
target: '/gndy',
},
{
title: '国内电影',
source: [`${baseUrl}/html/gndy/china/index.html`],
target: '/gndy/china',
},
{
title: '欧美电影',
source: [`${baseUrl}/html/gndy/oumei/index.html`],
target: '/gndy/oumei',
},
{
title: '其它电影',
source: [`${baseUrl}/html/gndy/rihan/index.html`],
target: '/gndy/rihan',
},
{
title: '华语电视',
source: [`${baseUrl}/html/tv/hytv/index.html`],
target: '/tv/hytv',
},
{
title: '欧美电视',
source: [`${baseUrl}/html/tv/oumeitv/index.html`],
target: '/tv/oumeitv',
},
{
title: '最新综艺',
source: [`${baseUrl}/html/zongyi2013/index.html`],
target: '/zongyi2013',
},
{
title: '旧版综艺',
source: [`${baseUrl}/html/2009zongyi/index.html`],
target: '/2009zongyi',
},
{
title: '动漫资源',
source: [`${baseUrl}/html/dongman/index.html`],
target: '/dongman',
},
{
title: '旧版游戏',
source: [`${baseUrl}/html/game/index.html`],
target: '/game',
},
{
title: '游戏下载',
source: [`${baseUrl}/html/newgame/index.html`],
target: '/newgame',
},
{
title: '日韩剧集专区',
source: [`${baseUrl}/html/tv/rihantv/index.html`],
target: '/tv/rihantv',
},
],
name: '最新电影',
maintainers: ['junfengP'],
handler,
view: ViewType.Articles,
};

async function loadContent(link: string) {
const response = await got.get(link, {
responseType: 'buffer',
});
const data = iconv.decode(response.data, 'gb2312');
const $ = load(data);
return $('div#Zoom').html() || '';
}

async function handler() {
const baseURL = 'https://www.ygdy8.net/html/gndy/dyzz/index.html';
const response = await got.get(baseURL, {
responseType: 'buffer',
});
const data = iconv.decode(response.data, 'gb2312');

const $ = load(data);
const list = $('.co_content8 table tr b a').toArray();

const items = await Promise.all(
list.map(async (item) => {
const link = $(item);
const itemUrl = 'https://www.ygdy8.net' + link.attr('href');

return await cache.tryGet(itemUrl, async () => {
const description = await loadContent(itemUrl);
return {
enclosure_url: description.match(/magnet:.*?(?=">)/) || '',
enclosure_type: 'application/x-bittorrent',
title: link.text(),
description,
pubDate: parseDate($(item).find('font').text()),
link: itemUrl,
};
});
})
);

return {
title: '电影天堂/阳光电影',
link: baseURL,
description: '电影天堂RSS',
item: items,
};
}
2 changes: 1 addition & 1 deletion lib/routes/dytt/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import type { Namespace } from '@/types';

export const namespace: Namespace = {
name: '电影天堂',
url: 'www.ygdy8.net',
url: 'www.dydytt.net',
lang: 'zh-CN',
};

0 comments on commit a8bf6e6

Please sign in to comment.