diff --git a/assets/radar-rules.js b/assets/radar-rules.js index 8208ed7a1..ebc8bb4bd 100644 --- a/assets/radar-rules.js +++ b/assets/radar-rules.js @@ -982,12 +982,26 @@ _name: '网易', ds: [ { - title: '网易大神', + title: '大神', docs: 'https://docs.rsshub.app/game.html#wang-yi-da-shen', source: '/user/:id', target: '/netease/ds/:id', }, ], + open: [ + { + title: '公开课 - 精品课程', + docs: 'https://docs.rsshub.app/study.html#wang-yi-gong-kai-ke', + source: '/', + target: '/open163/vip', + }, + { + title: '公开课 - 最新课程', + docs: 'https://docs.rsshub.app/study.html#wang-yi-gong-kai-ke', + source: '/', + target: '/open163/latest', + }, + ], }, 'suzhou.gov.cn': { _name: '苏州市政府', @@ -1063,4 +1077,34 @@ }, ], }, + 'ddrk.me': { + _name: '低端影视', + www: [ + { + title: '首页', + docs: 'https://docs.rsshub.app/multimedia.html#di-duan-ying-shi', + source: '/', + target: '/ddrk/index', + }, + { + title: '标签', + docs: 'https://docs.rsshub.app/multimedia.html#di-duan-ying-shi', + source: '/tag/:tag', + target: '/ddrk/tag/:tag', + }, + { + title: '分类', + docs: 'https://docs.rsshub.app/multimedia.html#di-duan-ying-shi', + source: ['/category/:category', '/category/:uplevel/:category'], + target: '/ddrk/category/:category', + }, + { + title: '影视剧集更新', + docs: 'https://docs.rsshub.app/multimedia.html#di-duan-ying-shi', + source: ['/:name', '/:name/:season'], + target: (params) => `/ddrk/update/${params.name}${params.season ? '/' + params.season : ''}`, + verification: (params) => params.name !== 'category' && params.name !== 'tag' && params.name !== 'ddrklogin' && params.name !== 'about' && params.name !== 'deleted', + }, + ], + }, }); diff --git a/docs/en/multimedia.md b/docs/en/multimedia.md index 6965f36fd..f3acad1ad 100644 --- a/docs/en/multimedia.md +++ b/docs/en/multimedia.md @@ -30,13 +30,25 @@ Official RSS: https://eztv.io/ezrss.xml ## JavLibrary -### Best rated videos +### Videos - + +|New Comments|New Release|New Entries|Most Wanted|Best Rated| +|-----|------|------|-----|------| +|update|newrelease|newentries|mostwanted|bestrated| + -### User posts +### Stars - + + +### Users + + +|User wanted|User watched|User owned|User posts| +|-----|------|------|-----| +|userwanted|userwatched|userowned|userposts| + ## Nyaa diff --git a/docs/multimedia.md b/docs/multimedia.md index 19f3e6bf0..b07fe448e 100644 --- a/docs/multimedia.md +++ b/docs/multimedia.md @@ -120,13 +120,25 @@ pageClass: routes ## JavLibrary -### 评价最高的影片 +### 影片 - + +|新话题|新发行|新加入|最想要|高评价| +|-----|------|------|-----|------| +|update|newrelease|newentries|mostwanted|bestrated| + -### 用户文章 +### 影星 - + + +### 用户 + + +|想要的|看过的|拥有的|发表的文章| +|-----|------|------|-----| +|userwanted|userwatched|userowned|userposts| + ## Last.fm @@ -283,9 +295,21 @@ pageClass: routes ## 低端影视 -### 影视剧集 +### 影视剧集更新 - + + +### 首页 + + + +### 分类 + + + +### 标签 + + ## 电影首发站 diff --git a/docs/study.md b/docs/study.md index eea08479a..761a6126a 100644 --- a/docs/study.md +++ b/docs/study.md @@ -103,6 +103,16 @@ pageClass: routes +## 网易公开课 + +### 精品课程 + + + +### 最新课程 + + + ## 下厨房 ### 用户作品 diff --git a/lib/router.js b/lib/router.js index 05f3a8e12..93462e4bd 100644 --- a/lib/router.js +++ b/lib/router.js @@ -2113,7 +2113,10 @@ router.get('/yahoo-news/:region/:category?', require('./routes/yahoo-news/index' router.get('/baijing', require('./routes/baijing')); // 低端影视 -router.get('/ddrk/:name/:season?', require('./routes/ddrk/index')); +router.get('/ddrk/update/:name/:season?', require('./routes/ddrk/index')); +router.get('/ddrk/tag/:tag', require('./routes/ddrk/list')); +router.get('/ddrk/category/:category', require('./routes/ddrk/list')); +router.get('/ddrk/index', require('./routes/ddrk/list')); // 公主链接日服公告 router.get('/pcr/news', require('./routes/pcr/news')); @@ -2185,8 +2188,9 @@ router.get('/letterboxd/user/followingdiary/:username', require('./routes/letter router.get('/netease/ds/:id', require('./routes/netease/ds')); // javlibrary -router.get('/javlibrary/bestrated', require('./routes/javlibrary/bestrated')); -router.get('/javlibrary/userposts/:uid', require('./routes/javlibrary/userposts')); +router.get('/javlibrary/users/:uid/:utype', require('./routes/javlibrary/users')); +router.get('/javlibrary/videos/:vtype', require('./routes/javlibrary/videos')); +router.get('/javlibrary/stars/:sid', require('./routes/javlibrary/stars')); // Last.FM router.get('/lastfm/recent/:user', require('./routes/lastfm/recent')); @@ -2197,4 +2201,8 @@ router.get('/lastfm/top/:country?', require('./routes/lastfm/top')); router.get('/piapro/user/:pid', require('./routes/piapro/user')); router.get('/piapro/public/:type/:tag?/:category?', require('./routes/piapro/public')); +// 网易公开课 +router.get('/open163/vip', require('./routes/netease/open/vip')); +router.get('/open163/latest', require('./routes/netease/open/latest')); + module.exports = router; diff --git a/lib/routes/bbc/index.js b/lib/routes/bbc/index.js index bfec803c7..734a042c2 100644 --- a/lib/routes/bbc/index.js +++ b/lib/routes/bbc/index.js @@ -48,7 +48,7 @@ module.exports = async (ctx) => { let description; - if (response.request.gotOptions.path.startsWith('/news/av')) { + if (response.request.options.url.pathname.startsWith('/news/av')) { description = utils.ProcessAVFeed($, item.link); } else { description = utils.ProcessFeed($, item.link); diff --git a/lib/routes/bilibili/bangumi.js b/lib/routes/bilibili/bangumi.js index 4c8ff8446..f40cd10fc 100644 --- a/lib/routes/bilibili/bangumi.js +++ b/lib/routes/bilibili/bangumi.js @@ -16,7 +16,7 @@ module.exports = async (ctx) => { const { data } = await got.get(`https://api.bilibili.com/pgc/web/season/section?season_id=${seasonid}`); ctx.state.data = { - title: mediaData.mediaInfo.chn_name, + title: mediaData.mediaInfo.title, link: `https://www.bilibili.com/bangumi/media/md${mediaData.mediaInfo.media_id}/`, image: mediaData.mediaInfo.cover, description: mediaData.mediaInfo.evaluate, diff --git a/lib/routes/ddrk/list.js b/lib/routes/ddrk/list.js new file mode 100644 index 000000000..b8dce5aa3 --- /dev/null +++ b/lib/routes/ddrk/list.js @@ -0,0 +1,37 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const md5 = require('@/utils/md5'); + +module.exports = async (ctx) => { + const url = ctx.params.tag ? `https://ddrk.me/tag/${encodeURIComponent(ctx.params.tag)}/` : ctx.params.category ? `https://ddrk.me/category/${ctx.params.category}/` : 'https://ddrk.me/'; + + const response = await got(url); + const $ = cheerio.load(response.data); + + const title = $('title').html(); + const list = $('.post-box-list .post-box-container').toArray(); + + ctx.state.data = { + title: title, + link: url, + item: list.map((single, index) => { + const item = $(single); + const name = item.find('.post-box-title').text(); + const poster = item + .find('.post-box-image') + .attr('style') + .match(/url\((.*?)\)/)[1]; + const text = item.find('.post-box-text p').html(); + const time = new Date(); + time.setMinutes(time.getMinutes() - index); + return { + title: name, + author: '低端影视', + description: `${text ? text : ''}`, + link: item.find('.post-box-title a').attr('href'), + pubDate: time, + guid: md5(name), + }; + }), + }; +}; diff --git a/lib/routes/huya/live.js b/lib/routes/huya/live.js index 34ef0aa83..38eb4080f 100644 --- a/lib/routes/huya/live.js +++ b/lib/routes/huya/live.js @@ -28,5 +28,6 @@ module.exports = async (ctx) => { title: `${$('.host-name').text()}的虎牙直播`, link: url, item: item, + allowEmpty: true, }; }; diff --git a/lib/routes/javlibrary/stars.js b/lib/routes/javlibrary/stars.js new file mode 100644 index 000000000..34c581fa0 --- /dev/null +++ b/lib/routes/javlibrary/stars.js @@ -0,0 +1,9 @@ +const template = require('./utils'); + +module.exports = async (ctx) => { + const sid = ctx.params.sid; + ctx.state.data = { + link: `http://www.javlibrary.com/cn/vl_star.php?s=${sid}`, + }; + await template(ctx); +}; diff --git a/lib/routes/javlibrary/userposts.js b/lib/routes/javlibrary/userposts.js index df9ddd5fe..06a00c4c6 100644 --- a/lib/routes/javlibrary/userposts.js +++ b/lib/routes/javlibrary/userposts.js @@ -1,14 +1,13 @@ const cheerio = require('cheerio'); const dateUtil = require('@/utils/date'); const cloudscraper = require('cloudscraper'); -const API = 'http://www.javlibrary.com/cn/userposts.php?u='; module.exports = async (ctx) => { const uid = ctx.params.uid; - const link = API + uid; + const link = `http://www.javlibrary.com/cn/userposts.php?u=${uid}`; const response = await cloudscraper.get(link); const $ = cheerio.load(response); - const list = $('div#rightcolumn table.comment'); + const list = $('#video_comments > table'); ctx.state.data = { title: `Javlibrary - ${uid} 发表的文章`, @@ -19,15 +18,17 @@ module.exports = async (ctx) => { .map((index, item) => { item = $(item); // 文章内容只能抓取到 td.t textarea,若含有图片则替换[img]image-link[/img] => - let comments = item.find('td.t textarea').text(); + let comments = item.find('textarea').text(); comments = comments.replace(new RegExp('\\[img\\]', 'g'), ''); return { - title: item.find('td > strong').text(), - link: item.find('td > strong > a').attr('href'), - description: `${item.find('table.videoinfo').html()}
-

${comments}

`, - pubDate: dateUtil(item.find('td.date')), - guid: item.find('td > strong > a').attr('href'), + title: item.find('tbody > tr:nth-child(1) > td:nth-child(2)').text(), + link: item.find('tbody > tr:nth-child(1) > td:nth-child(2) > a').attr('href'), + description: ` + + + +
${item.find('tbody > tr:nth-child(1) > td:nth-child(2)').html()}
${comments}
`, + pubDate: dateUtil(item.find('tbody > tr:nth-child(3) > td.date')), }; }) .get(), diff --git a/lib/routes/javlibrary/users.js b/lib/routes/javlibrary/users.js new file mode 100644 index 000000000..6328a4a12 --- /dev/null +++ b/lib/routes/javlibrary/users.js @@ -0,0 +1,16 @@ +const template = require('./utils'); +const userposts = require('./userposts'); +module.exports = async (ctx) => { + const utype = ctx.params.utype; + const uid = ctx.params.uid; + + // userposts 页面结构不同,单独写路由 + if (utype === 'userposts') { + await userposts(ctx); + } else { + ctx.state.data = { + link: `http://www.javlibrary.com/cn/${utype}.php?u=${uid}`, + }; + await template(ctx); + } +}; diff --git a/lib/routes/javlibrary/bestrated.js b/lib/routes/javlibrary/utils.js similarity index 84% rename from lib/routes/javlibrary/bestrated.js rename to lib/routes/javlibrary/utils.js index 48b1fa08a..9bbb32c59 100644 --- a/lib/routes/javlibrary/bestrated.js +++ b/lib/routes/javlibrary/utils.js @@ -1,9 +1,10 @@ +const url = require('url'); const cheerio = require('cheerio'); const cloudscraper = require('cloudscraper'); -const url = require('url'); -module.exports = async (ctx) => { - const link = 'http://www.javlibrary.com/cn/vl_bestrated.php'; +// 通过传入不同的ctx.state.data.link 返回javlibrary 不同link的rss +module.exports = async function template(ctx) { + const link = ctx.state.data.link; const response = await cloudscraper.get(link); const $ = cheerio.load(response); const list = $('.videothumblist .video').get(); @@ -31,7 +32,7 @@ module.exports = async (ctx) => { ); ctx.state.data = { - title: `Javlibrary - 评价最高的影片`, + title: `Javlibrary - ${$('.boxtitle').text()}`, link: link, item: items, }; diff --git a/lib/routes/javlibrary/videos.js b/lib/routes/javlibrary/videos.js new file mode 100644 index 000000000..fa8c1b374 --- /dev/null +++ b/lib/routes/javlibrary/videos.js @@ -0,0 +1,9 @@ +const template = require('./utils'); + +module.exports = async (ctx) => { + const vtype = ctx.params.vtype; + ctx.state.data = { + link: `http://www.javlibrary.com/cn/vl_${vtype}.php`, + }; + await template(ctx); +}; diff --git a/lib/routes/netease/open/latest.js b/lib/routes/netease/open/latest.js new file mode 100644 index 000000000..4e52b01d0 --- /dev/null +++ b/lib/routes/netease/open/latest.js @@ -0,0 +1,68 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); + +module.exports = async (ctx) => { + const list_response = await got.get('https://c.open.163.com/open/getLatestMovies.do?callback='); + + const list = JSON.parse(list_response.data.match(/\((.*?)\)/)[1]) || []; + + const parseContent = (htmlString) => { + const $ = cheerio.load(htmlString); + + const author = $('.m-courseinfo__side ul > li:nth-child(2)'); + + const images = $('img'); + for (let k = 0; k < images.length; k++) { + const testRealURL = $(images[k]) + .attr('src') + .match(/\?url=([^&]+)/); + if (testRealURL) { + $(images[k]).replaceWith(``); + } + } + + const content = $('.m-courseinfo__side'); + $('.i-container__title').remove(); + + return { + author: author.text().trim(), + description: content.html(), + pubDate: new Date(), + }; + }; + + const out = await Promise.all( + list.map(async (item) => { + const link = item.url; + + const cache = await ctx.cache.get(link); + if (cache) { + return Promise.resolve(JSON.parse(cache)); + } + + const rssitem = { + title: item.title, + link: link, + }; + + try { + const response = await got.get(link); + const result = parseContent(response.data); + + rssitem.author = result.author; + rssitem.description = result.description; + rssitem.pubDate = result.pubDate; + } catch (err) { + return Promise.resolve(''); + } + ctx.cache.set(link, JSON.stringify(rssitem)); + return Promise.resolve(rssitem); + }) + ); + + ctx.state.data = { + title: '网易公开课 - 最新课程', + link: 'https://open.163.com/', + item: out.filter((item) => item !== ''), + }; +}; diff --git a/lib/routes/netease/open/vip.js b/lib/routes/netease/open/vip.js new file mode 100644 index 000000000..738ae4ed1 --- /dev/null +++ b/lib/routes/netease/open/vip.js @@ -0,0 +1,65 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); + +module.exports = async (ctx) => { + const url = 'https://open.163.com/'; + + const list_response = await got.get(url); + const $ = cheerio.load(list_response.data); + + const list = $('.tabcon > div:nth-child(1) ul.list li').toArray(); + + const parseContent = (htmlString) => { + const $ = cheerio.load(htmlString); + + const author = $('.CourseCommonDetail_info > div:nth-child(4)'); + const content = $('.CourseCommonDetail_contentDesc'); + + return { + author: author.text().trim(), + description: content.html(), + pubDate: new Date(), + }; + }; + + const out = await Promise.all( + list.map(async (item) => { + const $ = cheerio.load(item); + const title = $('.ltxt') + .text() + .trim(); + const link = $('a.item') + .attr('href') + .split('?')[0]; + + const cache = await ctx.cache.get(link); + if (cache) { + return Promise.resolve(JSON.parse(cache)); + } + + const rssitem = { + title: title, + link: link, + }; + + try { + const response = await got.get(link); + const result = parseContent(response.data); + + rssitem.author = result.author; + rssitem.description = result.description; + rssitem.pubDate = result.pubDate; + } catch (err) { + return Promise.resolve(''); + } + ctx.cache.set(link, JSON.stringify(rssitem)); + return Promise.resolve(rssitem); + }) + ); + + ctx.state.data = { + title: '网易公开课 - 精品课程', + link: url, + item: out.filter((item) => item !== ''), + }; +}; diff --git a/package.json b/package.json index 1a4c4264b..74e8e3ec2 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "yorkie": "2.0.0" }, "dependencies": { - "@koa/router": "8.0.6", + "@koa/router": "8.0.7", "@postlight/mercury-parser": "2.2.0", "@sentry/node": "5.12.3", "aes-js": "3.1.2", @@ -75,7 +75,7 @@ "fanfou-sdk": "4.1.0", "git-rev-sync": "2.0.0", "googleapis": "47.0.0", - "got": "10.4.0", + "got": "10.5.0", "he": "1.2.0", "iconv-lite": "0.5.1", "jsdom": "16.1.0", @@ -92,7 +92,7 @@ "module-alias": "2.2.2", "pidusage": "2.0.17", "plist": "3.0.1", - "puppeteer": "2.1.0", + "puppeteer": "2.1.1", "query-string": "6.10.1", "redis": "2.8.0", "require-all": "3.0.0", diff --git a/yarn.lock b/yarn.lock index eb70d1390..53e83122f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1009,10 +1009,10 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" -"@koa/router@8.0.6": - version "8.0.6" - resolved "https://registry.yarnpkg.com/@koa/router/-/router-8.0.6.tgz#bd3f76e7f61450b47691ed944ce8e052b861139d" - integrity sha512-b4BDdDks0hvLYlr20REnWpo1FmV9SVh4/GS3yvK6VQ8/zPOIYcA9x2kSJpRbLMx40nRuIV1GXkYsRT8u42jh4g== +"@koa/router@8.0.7": + version "8.0.7" + resolved "https://registry.yarnpkg.com/@koa/router/-/router-8.0.7.tgz#0c6b747eae85b012ae09ef9d2d7267b2e877c156" + integrity sha512-oqPJ5aiR8nbRh2PSYo7leNdiJsmICKOU2i2Da8P00Ldm0uBYJE989i3nkj0mbaepwJmOWo7FUpAzdaS5tvZe9w== dependencies: debug "^4.1.1" http-errors "^1.7.3" @@ -2759,10 +2759,10 @@ cache-loader@^3.0.0: neo-async "^2.6.1" schema-utils "^1.0.0" -cacheable-lookup@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-1.0.0.tgz#ae877296b7b43b720e4a4554c47ec85e74d0932a" - integrity sha512-Te7MkBWBEPUdLjFWLoIu61osWKjrvBdBrSxEso6T9iGLTDPhcA2PI6J++lF/Hmqi5HtZmkKN3q/C7gwa+U/EUg== +cacheable-lookup@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-2.0.0.tgz#33b1e56f17507f5cf9bb46075112d65473fb7713" + integrity sha512-s2piO6LvA7xnL1AR03wuEdSx3BZT3tIJpZ56/lcJwzO/6DTJZlTs7X3lrvPxk6d1PlDe6PrVe2TjlUIZNFglAQ== dependencies: keyv "^4.0.0" @@ -5429,15 +5429,15 @@ googleapis@47.0.0: google-auth-library "^5.6.1" googleapis-common "^3.2.0" -got@10.4.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/got/-/got-10.4.0.tgz#2bd869d3d965716a43bee89d6fc3d16b565109c4" - integrity sha512-yHxq0LgdLFmJcrl27wEOIvZaHbgtn1DYpYUUX/kovLZoQ8q+QwJH+i9zkldDxGUawu1cUsgNMp8Xxm5yKT2UyQ== +got@10.5.0: + version "10.5.0" + resolved "https://registry.yarnpkg.com/got/-/got-10.5.0.tgz#8335299ca0b69b8564971837a455c87ace4779a4" + integrity sha512-khBPRcs/TspRdC7O7bEJdlIaEHhzhKr5kosYrDbKoLto+2goepOodis6JsVGBAIHo7ZNbarflf+/kH4ySJFrsA== dependencies: "@sindresorhus/is" "^1.0.0" "@szmarczak/http-timer" "^4.0.0" "@types/cacheable-request" "^6.0.1" - cacheable-lookup "^1.0.0" + cacheable-lookup "^2.0.0" cacheable-request "^7.0.1" decompress-response "^5.0.0" duplexer3 "^0.1.4" @@ -7831,12 +7831,7 @@ mime@1.6.0, mime@^1.3.4, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.0.3, mime@^2.4.2: - version "2.4.3" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.3.tgz#229687331e86f68924e6cb59e1cdd937f18275fe" - integrity sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw== - -mime@^2.2.0: +mime@^2.0.3, mime@^2.2.0, mime@^2.4.2: version "2.4.4" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== @@ -9551,10 +9546,10 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-2.1.0.tgz#c10cabc5525f57c6766eed4f3006b6e10afcafc1" - integrity sha512-PC4oKMtwAElo8YtS/cYnk2/dew/3TonsGKKzjpFLWwkhBCteFsOZCVOXTt2QlP6w53mH0YsJE+fPLPzOW+DCug== +puppeteer@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-2.1.1.tgz#ccde47c2a688f131883b50f2d697bd25189da27e" + integrity sha512-LWzaDVQkk1EPiuYeTOj+CZRIjda4k2s5w4MK4xoH2+kgWV/SDlkYHmxatDdtYrciHUKSXTsGgPgPP8ILVdBsxg== dependencies: "@types/mime-types" "^2.1.0" debug "^4.1.0"