From 0c3ca1bb11825ff9d02681c89aa7731a997ec2e3 Mon Sep 17 00:00:00 2001 From: jerry1119 Date: Mon, 9 May 2022 01:00:29 +0800 Subject: [PATCH] fix(route): fix pianyuan, add pianyuan/search (#9721) * fix route pianyuan, add route pianyuan/search * update doc * changes for reviews * Update lib/routes/pianyuan/utils.js Co-authored-by: Rongrong * changes for reviews * refactor: migrate to v2 Co-authored-by: Rongrong --- assets/radar-rules.js | 1 - docs/install/README.md | 4 ++ docs/multimedia.md | 12 ++++- lib/config.js | 3 ++ lib/radar-rules.js | 10 ---- lib/router.js | 5 +- lib/routes/pianyuan/app.js | 49 ------------------- lib/v2/pianyuan/app.js | 34 ++++++++++++++ lib/v2/pianyuan/maintainer.js | 4 ++ lib/v2/pianyuan/radar.js | 13 ++++++ lib/v2/pianyuan/router.js | 4 ++ lib/v2/pianyuan/search.js | 45 ++++++++++++++++++ lib/v2/pianyuan/utils.js | 88 +++++++++++++++++++++++++++++++++++ 13 files changed, 209 insertions(+), 63 deletions(-) delete mode 100644 lib/routes/pianyuan/app.js create mode 100644 lib/v2/pianyuan/app.js create mode 100644 lib/v2/pianyuan/maintainer.js create mode 100644 lib/v2/pianyuan/radar.js create mode 100644 lib/v2/pianyuan/router.js create mode 100644 lib/v2/pianyuan/search.js create mode 100644 lib/v2/pianyuan/utils.js diff --git a/assets/radar-rules.js b/assets/radar-rules.js index 356936bcc8..6a9867f1be 100644 --- a/assets/radar-rules.js +++ b/assets/radar-rules.js @@ -220,7 +220,6 @@ 'tw.manhuagui.com': { _name: '漫画柜台湾', www: [{ title: '漫画更新', docs: 'https://docs.rsshub.app/anime.html#kan-man-hua-tai-wan', source: '/comic/:id/', target: '/twmanhuagui/comic/:id' }] }, 'copymanga.com': { _name: '拷贝漫画', www: [{ title: '漫画更新', docs: 'https://docs.rsshub.app/anime.html#kao-bei-man-hua', source: '/comic/:id/', target: '/manhuagui/comic/:id/5' }] }, 'pgyer.com': { _name: '蒲公英应用分发', www: [{ title: 'app更新', docs: 'https://docs.rsshub.app/program-update.html#pu-gong-ying-ying-yong-fen-fa', source: '/:app', target: '/pgyer/:app' }] }, - 'pianyuan.la': { _name: '片源网', '.': [{ title: '电影和剧集', docs: 'https://docs.rsshub.app/multimedia.html#pian-yuan', source: '/' }] }, 'baidu.com': { _name: '贴吧', tieba: [ diff --git a/docs/install/README.md b/docs/install/README.md index f5ff73e073..eb55906288 100644 --- a/docs/install/README.md +++ b/docs/install/README.md @@ -664,6 +664,10 @@ RSSHub 支持使用访问密钥 / 码,白名单和黑名单三种方式进行 - `NHENTAI_USERNAME`: nhentai 用户名或邮箱 - `NHENTAI_PASSWORD`: nhentai 密码 +- pianyuan 全部路由: [注册地址](https://pianyuan.org) + + - `PIANYUAN_COOKIE`: 对应 cookie 中的 `py_loginauth`, 例: PIANYUAN_COOKIE='py_loginauth=xxxxxxxxxx' + - pixiv 全部路由:[注册地址](https://accounts.pixiv.net/signup) - `PIXIV_REFRESHTOKEN`: Pixiv Refresh Token, 请参考 [此文](https://gist.github.com/ZipFile/c9ebedb224406f4f11845ab700124362) 获取,或自行对客户端抓包获取 diff --git a/docs/multimedia.md b/docs/multimedia.md index 06f6b6dbb2..4ce932dd25 100644 --- a/docs/multimedia.md +++ b/docs/multimedia.md @@ -1262,7 +1262,17 @@ JavDB 有多个备用域名,本路由默认使用永久域名 + + +| 电影 | 剧集 | +| -- | -- | +| mv | tv | + +### 搜索 + + + +搜索路由模仿 jackett 的搜索 api, 以提供给 nastools 使用,填写在 nastools 配置 indexer 中 ## 飘花电影网 diff --git a/lib/config.js b/lib/config.js index 42bcde8b22..c6f9985189 100644 --- a/lib/config.js +++ b/lib/config.js @@ -198,6 +198,9 @@ const calculateValue = () => { username: envs.NHENTAI_USERNAME, password: envs.NHENTAI_PASSWORD, }, + pianyuan: { + cookie: envs.PIANYUAN_COOKIE, + }, pixiv: { refreshToken: envs.PIXIV_REFRESHTOKEN, bypassCdn: envs.PIXIV_BYPASS_CDN !== '0' && envs.PIXIV_BYPASS_CDN !== 'false', diff --git a/lib/radar-rules.js b/lib/radar-rules.js index 48f76f489a..8075b44deb 100644 --- a/lib/radar-rules.js +++ b/lib/radar-rules.js @@ -466,16 +466,6 @@ module.exports = { }, ], }, - 'pianyuan.la': { - _name: '片源网', - '.': [ - { - title: '电影和剧集', - docs: 'https://docs.rsshub.app/multimedia.html#pian-yuan', - source: '/', - }, - ], - }, 'baidu.com': { _name: '贴吧', tieba: [ diff --git a/lib/router.js b/lib/router.js index 666116a001..70d418b84b 100644 --- a/lib/router.js +++ b/lib/router.js @@ -2271,8 +2271,9 @@ router.get('/chocolatey/software/:name?', lazyloadRouteHandler('./routes/chocola // Nyaa router.get('/nyaa/search/:query?', lazyloadRouteHandler('./routes/nyaa/search')); -// 片源网 -router.get('/pianyuan/:media?', lazyloadRouteHandler('./routes/pianyuan/app')); +// 片源网 migrated to v2 +// router.get('/pianyuan/index/:media?', lazyloadRouteHandler('./routes/pianyuan/app')); +// router.get('/pianyuan/indexers/pianyuan/results/search/api', lazyloadRouteHandler('./routes/pianyuan/search')); // 巴哈姆特 router.get('/bahamut/creation/:author/:category?', lazyloadRouteHandler('./routes/bahamut/creation')); diff --git a/lib/routes/pianyuan/app.js b/lib/routes/pianyuan/app.js deleted file mode 100644 index f5c9874b41..0000000000 --- a/lib/routes/pianyuan/app.js +++ /dev/null @@ -1,49 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); - -module.exports = async (ctx) => { - const media = ctx.params.media || -1; - const link_base = 'http://pianyuan.tv/'; - let description = '电影和剧集'; - let link = link_base; - if (media !== -1) { - link = link_base + `?cat=${media}`; - if (media === 'mv') { - description = '电影'; - } else if (media === 'tv') { - description = '剧集'; - } else { - link = link_base; - } - } - - const response = await got.get(link); - const data = response.data; - const $ = cheerio.load(data); - - const items = []; - $('#main-container > div > div.col-md-10 > table > tbody > tr').each((i, item) => { - const link = $(item).find('td.dt.prel.nobr > a').attr('href'); - const text = $(item).find('td.dt.prel.nobr > a').text(); - const description = $(item) - .find('td.dt.prel.nobr') - .text() - .replace(/^\s+|\s+$/g, ''); - const size = $(item).find('td:nth-child(2)').text(); - - const description_simple = description.substr(0, description.indexOf('主演')).replace(text, ''); - - items.push({ - title: `[${size}] ${description_simple}`, - description, - link: link_base + link, - }); - }); - - ctx.state.data = { - title: `片源网`, - description, - link: link_base, - item: items, - }; -}; diff --git a/lib/v2/pianyuan/app.js b/lib/v2/pianyuan/app.js new file mode 100644 index 0000000000..9c68f4f2c9 --- /dev/null +++ b/lib/v2/pianyuan/app.js @@ -0,0 +1,34 @@ +const cheerio = require('cheerio'); +const utils = require('./utils'); + +module.exports = async (ctx) => { + const media = ctx.params.media ?? -1; + const link_base = 'https://pianyuan.org/'; + let description = '电影和剧集'; + let link = link_base; + if (media !== -1) { + link = link_base + `?cat=${media}`; + if (media === 'mv') { + description = '电影'; + } else if (media === 'tv') { + description = '剧集'; + } else { + link = link_base; + } + } + + const response = await utils.request(link, ctx.cache); + const $ = cheerio.load(response.data); + const detailLinks = $('#main-container > div > div.col-md-10 > table > tbody > tr') + .get() + .map((tr) => $(tr).find('td.dt.prel.nobr > a').attr('href')); + detailLinks.shift(); + const items = await utils.ProcessFeed(detailLinks, ctx.cache); + + ctx.state.data = { + title: '片源网', + description, + link: link_base, + item: items, + }; +}; diff --git a/lib/v2/pianyuan/maintainer.js b/lib/v2/pianyuan/maintainer.js new file mode 100644 index 0000000000..3acdaf8dfb --- /dev/null +++ b/lib/v2/pianyuan/maintainer.js @@ -0,0 +1,4 @@ +module.exports = { + '/index/:media?': ['greatcodeeer', 'jerry1119'], + '/indexers/pianyuan/results/search/api': ['jerry1119'], +}; diff --git a/lib/v2/pianyuan/radar.js b/lib/v2/pianyuan/radar.js new file mode 100644 index 0000000000..d0b016e747 --- /dev/null +++ b/lib/v2/pianyuan/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'pianyuan.org': { + _name: '片源网', + '.': [ + { + title: '电影和剧集', + docs: 'https://docs.rsshub.app/multimedia.html#pian-yuan', + source: '/', + target: '/pianyuan/index', + }, + ], + }, +}; diff --git a/lib/v2/pianyuan/router.js b/lib/v2/pianyuan/router.js new file mode 100644 index 0000000000..cc32cc2708 --- /dev/null +++ b/lib/v2/pianyuan/router.js @@ -0,0 +1,4 @@ +module.exports = (router) => { + router.get('/index/:media?', require('./app')); + router.get('/indexers/pianyuan/results/search/api', require('./search')); +}; diff --git a/lib/v2/pianyuan/search.js b/lib/v2/pianyuan/search.js new file mode 100644 index 0000000000..fba635dff0 --- /dev/null +++ b/lib/v2/pianyuan/search.js @@ -0,0 +1,45 @@ +const cheerio = require('cheerio'); +const utils = require('./utils'); + +module.exports = async (ctx) => { + const link_base = 'https://pianyuan.org/'; + const description = '搜索'; + // 适配jackett 搜索api, eg: https://rsshub.app/pianyuan/indexers/pianyuan/results/search/api?t=test&q=halo + const searchKey = ctx.originalUrl.split('&q=')[1]; + const link = link_base + `search?q=${searchKey}`; + + const response = await utils.request(link, ctx.cache); + const $ = cheerio.load(response.data); + // 只获取第一页的搜索结果 + const searchLinks = $('.nomt > a') + .get() + .map((a) => $(a).attr('href')); + if (searchLinks.length === 0) { + throw 'pianyuan 搜索失败'; + } + + const detailLinks = []; + + await Promise.all( + searchLinks.map(async (e) => { + const link = new URL(e, link_base); + const single = await ctx.cache.tryGet(link, async () => { + const res = await utils.request(link, ctx.cache); + const content = cheerio.load(res.data); + content('.ico.ico_bt') + .get() + .map((a) => detailLinks.push($(a).attr('href'))); + }); + return single; + }) + ); + + const items = await utils.ProcessFeed(detailLinks, ctx.cache); + + ctx.state.data = { + title: '片源网', + description, + link: link_base, + item: items, + }; +}; diff --git a/lib/v2/pianyuan/utils.js b/lib/v2/pianyuan/utils.js new file mode 100644 index 0000000000..0eaaf9ce50 --- /dev/null +++ b/lib/v2/pianyuan/utils.js @@ -0,0 +1,88 @@ +const cheerio = require('cheerio'); +const got = require('@/utils/got'); +const config = require('@/config').value; +const security_key = 'pianyuan-security_session_verify'; +const PHPSESSID_key = 'pianyuan-PHPSESSID'; +const loginauth_key = 'pianyuan-py_loginauth'; + +const ProcessFeed = async (list, cache) => { + const link_base = 'https://pianyuan.org'; + + const items = await Promise.all( + list.map(async (e) => { + const link = new URL(e, link_base); + const single = await cache.tryGet(link, async () => { + const response = await request(link, cache); + const content = cheerio.load(response.data); + const magnet = content('.btn-primary').attr('href'); + const torrent_name = content('body > div.jumbotron.masthead > div > div > div.col-sm-10.col-md-10.col-lg-10.text-left > h1').text(); + const name = content('body > div.jumbotron.masthead > div > div > div.col-sm-10.col-md-10.col-lg-10.text-left > h2 > a').text(); + const size = content('#main-container > div > div.col-sm-10.col-md-8.col-lg-8 > div > ul > li:nth-child(2)').text(); + let length; + if (size.includes('MB')) { + length = size.replace('MB', '') * 1024; + } else if (size.includes('GB')) { + length = size.replace('GB', '') * 1024 * 1024; + } + // const description =''; + + return { + title: `${torrent_name} [${name}] [${size}]`, + size: length, + enclosure_url: magnet, + enclosure_type: 'application/x-bittorrent', + enclosure_length: length, + }; + }); + return single; + }) + ); + + return items; +}; + +async function getCookie(cache) { + const security_session_verify = await cache.get(security_key); + const PHPSESSID = await cache.get(PHPSESSID_key); + let py_loginauth = await cache.get(loginauth_key); + if (!py_loginauth) { + if (!config.pianyuan || !config.pianyuan.cookie) { + throw 'pianyuan is disabled due to the lack of relevant config'; + } + py_loginauth = config.pianyuan.cookie; + } + return `${py_loginauth};${security_session_verify};${PHPSESSID};`; +} + +async function request(link, cache) { + const cookie = await getCookie(cache); + const response = await got({ + method: 'get', + url: link, + headers: { + Cookie: cookie, + }, + }); + // set cookie + const set_cookie = response.headers['set-cookie']; + if (set_cookie) { + for (const e of set_cookie) { + if (e.indexOf('security_session_verify') === 0) { + cache.set(security_key, e.split(';')[0]); + } else if (e.indexOf('PHPSESSID') === 0) { + cache.set(PHPSESSID_key, e.split(';')[0]); + } else if (e.indexOf('py_loginauth') === 0) { + cache.set(loginauth_key, e.split(';')[0]); + } + } + } + if (response.data.includes('会员登录后才能访问')) { + throw 'pianyuan Cookie已失效'; + } + return response; +} + +module.exports = { + ProcessFeed, + request, +};