diff --git a/assets/radar-rules.js b/assets/radar-rules.js index 4eceeeb24f841c..64ebc195579189 100644 --- a/assets/radar-rules.js +++ b/assets/radar-rules.js @@ -27,21 +27,6 @@ { title: '仓库 Contributors', docs: 'https://docs.rsshub.app/programming.html#github', source: ['/:user/:repo/graphs/contributors', '/:user/:repo'], target: '/github/contributors/:user/:repo' }, ], }, - 'ximalaya.com': { - _name: '喜马拉雅', - '.': [ - { - title: '专辑', - docs: 'https://docs.rsshub.app/multimedia.html#xi-ma-la-ya', - source: '/:type/:id', - target: (params) => { - if (parseInt(params.id) + '' === params.id) { - return '/ximalaya/:type/:id/'; - } - }, - }, - ], - }, 'algocasts.io': { _name: 'AlgoCasts', '.': [{ title: '视频更新', docs: 'https://docs.rsshub.app/programming.html#algocasts', source: '/episodes', target: '/algocasts' }] }, 'soulapp.cn': { _name: 'Soul', '.': [{ title: '瞬间更新', docs: 'https://docs.rsshub.app/social-media.html#soul' }] }, 'anime1.me': { diff --git a/lib/radar-rules.js b/lib/radar-rules.js index a6108a586e36de..be8d25579424c0 100644 --- a/lib/radar-rules.js +++ b/lib/radar-rules.js @@ -1,19 +1,4 @@ module.exports = { - 'ximalaya.com': { - _name: '喜马拉雅', - '.': [ - { - title: '专辑', - docs: 'https://docs.rsshub.app/multimedia.html#xi-ma-la-ya', - source: '/:type/:id', - target: (params) => { - if (parseInt(params.id) + '' === params.id) { - return '/ximalaya/:type/:id/'; - } - }, - }, - ], - }, 'algocasts.io': { _name: 'AlgoCasts', '.': [ diff --git a/lib/router.js b/lib/router.js index 11524bf9612abc..1e91db8ab63b09 100644 --- a/lib/router.js +++ b/lib/router.js @@ -246,8 +246,8 @@ router.get('/yande.re/post/popular_recent/:period', lazyloadRouteHandler('./rout // router.get('/3dm/news', lazyloadRouteHandler('./routes/3dm/news_center')); // 喜马拉雅 -router.get('/ximalaya/:type/:id/:all?', lazyloadRouteHandler('./routes/ximalaya/album')); -router.get('/ximalaya/:type/:id/:all/:shownote?', lazyloadRouteHandler('./routes/ximalaya/album')); +// router.get('/ximalaya/:type/:id/:all?', lazyloadRouteHandler('./routes/ximalaya/album')); +// router.get('/ximalaya/:type/:id/:all/:shownote?', lazyloadRouteHandler('./routes/ximalaya/album')); // EZTV router.get('/eztv/torrents/:imdb_id', lazyloadRouteHandler('./routes/eztv/imdb')); diff --git a/lib/routes/ximalaya/album.js b/lib/v2/ximalaya/album.js similarity index 82% rename from lib/routes/ximalaya/album.js rename to lib/v2/ximalaya/album.js index 066ab3e9c7bb27..197d2d11440b73 100644 --- a/lib/routes/ximalaya/album.js +++ b/lib/v2/ximalaya/album.js @@ -3,6 +3,7 @@ const cheerio = require('cheerio'); const { getUrl, getRandom16 } = require('./utils'); const baseUrl = 'https://www.ximalaya.com'; const config = require('@/config').value; +const { parseDate } = require('@/utils/parse-date'); // Find category from: https://help.apple.com/itc/podcasts_connect/?lang=en#/itc9267a2f12 const categoryDict = { @@ -46,8 +47,8 @@ async function parseAlbumData(category, album_id) { // 解析网页版的 HTML 获取该专辑的信息 // 这里的 {category} 并不重要,系统会准确的返回该 id 对应专辑的信息 // 现在 {category} 必须要精确到大的类别 - // https://www.ximalaya.com/{category}/{album_id}/ - const response = await got(`https://www.ximalaya.com/${category}/${album_id}/`); + // https://www.ximalaya.com/{category}/{album_id} + const response = await got(`${baseUrl}/${category}/${album_id}`); const data = response.body; const $ = cheerio.load(data); const stateElement = $('script') @@ -87,25 +88,27 @@ module.exports = async (ctx) => { const albumData = await parseAlbumData(type, id); - const isPaid = albumData.store.AlbumDetailPage.albumPageMainInfo.isPaid; + const { albumPageMainInfo, currentCategory, anchorInfo } = albumData?.store?.AlbumDetailPage || {}; - const authorInfo = albumData.store.AlbumDetailPage.anchorInfo; // 作者数据 + const isPaid = albumPageMainInfo.isPaid; + + const authorInfo = anchorInfo; // 作者数据 const author = authorInfo.nickName; - const albumInfo = albumData.store.AlbumDetailPage.albumPageMainInfo; // 专辑数据 + const albumInfo = albumPageMainInfo; // 专辑数据 const albumTitle = albumInfo.albumTitle; // 专辑标题 const albumCover = 'http:' + albumInfo.cover.split('!')[0]; const albumIntro = albumInfo.detailRichIntro; // 专辑介绍 - const categoryInfo = albumData.store.AlbumDetailPage.currentCategory; - const albumCategory = categoryInfo.categoryTitle; // 专辑分类名字 - const albumUrl = '/' + categoryInfo.categoryPinyin + '/' + id + '/'; + const categoryInfo = currentCategory; + const albumCategory = categoryInfo?.categoryTitle || albumPageMainInfo.categoryTitle; // 专辑分类名字 + const albumUrl = '/' + (categoryInfo?.categoryPinyin || type) + '/' + id + '/'; // 没有 categoryPinyin 这个字段了,用 type 兼容一下 // sort 为 1 时是降序 // const isAsc = albumData.store.AlbumDetailTrackList.sort === 0; // 喜马拉雅的 API 的 query 参数 isAsc=0 时才是升序,不写就是降序。 const trackInfoApi = `http://mobile.ximalaya.com/mobile/v1/album/track/?albumId=${id}&pageSize=${pageSize}&pageId=`; - const trackInfoResponse = await got.get(trackInfoApi + '1'); + const trackInfoResponse = await got(trackInfoApi + '1'); const maxPageId = trackInfoResponse.data.data.maxPageId; // 最大页数 let playList = trackInfoResponse.data.data.list; @@ -114,22 +117,22 @@ module.exports = async (ctx) => { const promises = []; for (let i = 2; i <= maxPageId; i++) { // string + number -> string - promises.push(got.get(trackInfoApi + i)); + promises.push(got(trackInfoApi + i)); } const responses = await Promise.all(promises); - for (let j = 0; j < responses.length; j++) { - playList = playList.concat(responses[j].data.data.list); + for (const j of responses) { + playList = playList.concat(j.data.data.list); } } await Promise.all( playList.map(async (item) => { const link = baseUrl + albumUrl + item.trackId; - item.desc = await ctx.cache.tryGet(shouldShowNote.toString() + 'trackRichInfo' + link, async () => { + item.desc = await ctx.cache.tryGet('ximalaya:' + shouldShowNote.toString() + 'trackRichInfo' + link, async () => { let _desc; if (shouldShowNote) { const trackRichInfoApi = `https://mobile.ximalaya.com/mobile-track/richIntro?trackId=${item.trackId}`; - const trackRichInfoResponse = await got.get(trackRichInfoApi); + const trackRichInfoResponse = await got(trackRichInfoApi); _desc = trackRichInfoResponse.data.richIntro; } if (!_desc) { @@ -146,10 +149,8 @@ module.exports = async (ctx) => { await Promise.all( playList.map(async (item) => { const trackPayInfoApi = `https://mpay.ximalaya.com/mobile/track/pay/${item.trackId}/?device=pc`; - const data = await ctx.cache.tryGet('trackPayInfo' + trackPayInfoApi, async () => { - const trackPayInfoResponse = await got({ - method: 'get', - url: trackPayInfoApi, + const data = await ctx.cache.tryGet('ximalaya:trackPayInfo' + trackPayInfoApi, async () => { + const trackPayInfoResponse = await got(trackPayInfoApi, { headers: { 'user-agent': 'ting_6.7.9(GM1900,Android29)', cookie: `1&_device=android&${randomToken}&6.7.9;1&_token=${token}`, @@ -178,7 +179,7 @@ module.exports = async (ctx) => { const trackId = item.trackId; const itunesItemImage = item.coverLarge ? item.coverLarge : albumCover; const link = baseUrl + albumUrl + trackId; - const pubDate = new Date(item.createdAt).toUTCString(); + const pubDate = parseDate(item.createdAt, 'x'); const duration = item.duration; // 时间长度:单位(秒) const enclosureUrl = item.playPathAacv224 || item.playPathAacv164; diff --git a/lib/v2/ximalaya/maintainer.js b/lib/v2/ximalaya/maintainer.js new file mode 100644 index 00000000000000..8f461073a46694 --- /dev/null +++ b/lib/v2/ximalaya/maintainer.js @@ -0,0 +1,4 @@ +module.exports = { + '/:type/:id/:all?': ['lengthmin', 'jjeejj', 'prnake'], + '/:type/:id/:all/:shownote?': ['lengthmin', 'jjeejj', 'prnake'], +}; diff --git a/lib/v2/ximalaya/radar.js b/lib/v2/ximalaya/radar.js new file mode 100644 index 00000000000000..567dfafc61783f --- /dev/null +++ b/lib/v2/ximalaya/radar.js @@ -0,0 +1,17 @@ +module.exports = { + 'ximalaya.com': { + _name: '喜马拉雅', + '.': [ + { + title: '专辑', + docs: 'https://docs.rsshub.app/multimedia.html#xi-ma-la-ya', + source: '/:type/:id', + target: (params) => { + if (parseInt(params.id) + '' === params.id) { + return '/ximalaya/:type/:id'; + } + }, + }, + ], + }, +}; diff --git a/lib/v2/ximalaya/router.js b/lib/v2/ximalaya/router.js new file mode 100644 index 00000000000000..dfda7356b60644 --- /dev/null +++ b/lib/v2/ximalaya/router.js @@ -0,0 +1,4 @@ +module.exports = (router) => { + router.get('/:type/:id/:all?', require('./album')); + router.get('/:type/:id/:all/:shownote?', require('./album')); +}; diff --git a/lib/routes/ximalaya/utils.js b/lib/v2/ximalaya/utils.js similarity index 89% rename from lib/routes/ximalaya/utils.js rename to lib/v2/ximalaya/utils.js index dc599fe256aac9..1800cfa3b27158 100644 --- a/lib/routes/ximalaya/utils.js +++ b/lib/v2/ximalaya/utils.js @@ -78,11 +78,11 @@ const getParams = (ep) => { let a3 = 0; o = 0; - for (let u = 0; u < a2.length; u++) { + for (const u of a2) { a3 = (a3 + 1) % 256; o = (o + r1[a3]) % 256; [r1[a3], r1[o]] = [r1[o], r1[a3]]; - i += String.fromCharCode(a2[u] ^ r1[(r1[a3] + r1[o]) % 256]); + i += String.fromCharCode(u ^ r1[(r1[a3] + r1[o]) % 256]); } i = i.split('-'); return { @@ -112,10 +112,11 @@ const getPath = (seed, fileId) => { const getUrl = (r) => { const params = getParams(r.ep); - const paramsArray = []; params.duration = r.duration; - Object.keys(params).forEach((key) => params[key] && paramsArray.push(`${key}=${params[key]}`)); - const url = 'https://audiopay.cos.xmcdn.com/download/' + r.apiVersion + '/' + getPath(r.seed, r.fileId) + '?' + paramsArray.join('&'); + const paramsArray = Object.keys(params) + .filter((key) => params[key]) + .map((key) => `${key}=${params[key]}`); + const url = `https://audiopay.cos.xmcdn.com/download/${r.apiVersion}/${getPath(r.seed, r.fileId)}?${paramsArray.join('&')}`; return url; };