diff --git a/docs/en/multimedia.md b/docs/en/multimedia.md index 55a47c977..28593c95d 100644 --- a/docs/en/multimedia.md +++ b/docs/en/multimedia.md @@ -523,6 +523,46 @@ Refer to [Pornhub F.A.Qs](https://help.pornhub.com/hc/en-us/articles/36004432703 +## The Movie Database + +::: tip Tips +Refer to for the language parameter in the route. +::: + +### Collection + + + +### Trending + + + +### TV Show Seasons + + + +### TV Show Episodes + + + +### Sheet + + + +When `mediaType` is `tv`, `sheet` should be: + +| Airing Today | On TV | Top Rated | +| ------------ | ---------- | --------- | +| airing-today | on-the-air | top-rated | + +When `mediaType` is `movie`, `sheet` should be: + +| Now Playing | Upcoming | Top Rated | +| ----------- | -------- | --------- | +| now-playing | upcoming | top-rated | + + + ## Trakt.tv ### User Collection diff --git a/docs/multimedia.md b/docs/multimedia.md index b3d9283c2..b08884c59 100644 --- a/docs/multimedia.md +++ b/docs/multimedia.md @@ -1179,6 +1179,46 @@ JavDB 有多个备用域名,本路由默认使用永久域名 +## The Movie Database + +::: tip 提示 +路由中的语言参数可参考 。 +::: + +### 系列 + + + +### 热门 + + + +### 剧集系列 + + + +### 剧集章节 + + + +### 片单 + + + +当 `mediaType` 为 `tv` 时,`sheet` 的值如下: + +| 今日播出 | 电视播出中 | 高分 | +| ------------ | ---------- | --------- | +| airing-today | on-the-air | top-rated | + +当 `mediaType` 为 `movie` 时,`sheet` 的值如下: + +| 正在上映 | 即将上映 | 高分 | +| ----------- | -------- | --------- | +| now-playing | upcoming | top-rated | + + + ## Trakt.tv ### 用户收藏 diff --git a/lib/v2/themoviedb/api-key.js b/lib/v2/themoviedb/api-key.js new file mode 100644 index 000000000..dc7885cf4 --- /dev/null +++ b/lib/v2/themoviedb/api-key.js @@ -0,0 +1,23 @@ +// https://github.com/rickylawson/freekeys/blob/master/index.js +const KEYS = [ + 'fb7bb23f03b6994dafc674c074d01761', + 'e55425032d3d0f371fc776f302e7c09b', + '8301a21598f8b45668d5711a814f01f6', + '8cf43ad9c085135b9479ad5cf6bbcbda', + 'da63548086e399ffc910fbc08526df05', + '13e53ff644a8bd4ba37b3e1044ad24f3', + '269890f657dddf4635473cf4cf456576', + 'a2f888b27315e62e471b2d587048f32e', + '8476a7ab80ad76f0936744df0430e67c', + '5622cafbfe8f8cfe358a29c53e19bba0', + 'ae4bd1b6fce2a5648671bfc171d15ba4', + '257654f35e3dff105574f97fb4b97035', + '2f4038e83265214a0dcd6ec2eb3276f5', + '9e43f45f94705cc8e1d5a0400d19a7b7', + 'af6887753365e14160254ac7f4345dd2', + '06f10fc8741a672af455421c239a1ffc', + 'fb7bb23f03b6994dafc674c074d01761', + '09ad8ace66eec34302943272db0e8d2c', +]; + +module.exports = () => KEYS[Math.floor(Math.random() * KEYS.length)]; diff --git a/lib/v2/themoviedb/collection.js b/lib/v2/themoviedb/collection.js new file mode 100644 index 000000000..20ea2e61b --- /dev/null +++ b/lib/v2/themoviedb/collection.js @@ -0,0 +1,21 @@ +const got = require('@/utils/got'); +const apiKey = require('./api-key'); +const { handleMovieItem } = require('./utils'); + +module.exports = async (ctx) => { + const { id, lang } = ctx.params; + const { data } = await got(`https://api.themoviedb.org/3/collection/${id}`, { + searchParams: { + language: lang, + api_key: apiKey(), + }, + }); + + ctx.state.data = { + title: `${data.name} — TMDB`, + description: data.overview.trim(), + image: `https://image.tmdb.org/t/p/original${data.poster_path}`, + link: `https://www.themoviedb.org/collection/${data.id}`, + item: data.parts.map((item) => handleMovieItem(item, lang)), + }; +}; diff --git a/lib/v2/themoviedb/episodes.js b/lib/v2/themoviedb/episodes.js new file mode 100644 index 000000000..e1c21927b --- /dev/null +++ b/lib/v2/themoviedb/episodes.js @@ -0,0 +1,27 @@ +const got = require('@/utils/got'); +const { parseDate } = require('@/utils/parse-date'); +const apiKey = require('./api-key'); +const { handleDescription } = require('./utils'); + +module.exports = async (ctx) => { + const { id, seasonNumber, lang } = ctx.params; + const searchParams = { + language: lang, + api_key: apiKey(), + }; + const { data: tvShowDetails } = await got(`https://api.themoviedb.org/3/tv/${id}`, { searchParams }); + const { data: seasonDetails } = await got(`https://api.themoviedb.org/3/tv/${id}/season/${seasonNumber}`, { searchParams }); + + ctx.state.data = { + title: `${tvShowDetails.name} ${seasonDetails.name} — TMDB`, + description: seasonDetails.overview.trim(), + image: `https://image.tmdb.org/t/p/original${seasonDetails.poster_path}`, + link: `https://www.themoviedb.org/tv/${tvShowDetails.id}/season/${seasonDetails.season_number}`, + item: seasonDetails.episodes.reverse().map((item) => ({ + title: `${item.episode_number} ${item.name}`, + link: `https://www.themoviedb.org/tv/${tvShowDetails.id}/season/${item.season_number}/episode/${item.episode_number}`, + description: handleDescription(item), + pubDate: parseDate(item.air_date), + })), + }; +}; diff --git a/lib/v2/themoviedb/maintainer.js b/lib/v2/themoviedb/maintainer.js new file mode 100644 index 000000000..5d2559b0d --- /dev/null +++ b/lib/v2/themoviedb/maintainer.js @@ -0,0 +1,7 @@ +module.exports = { + '/collection/:id/:lang?': ['x2cf'], + '/trending/:mediaType/:timeWindow/:lang?': ['x2cf'], + '/tv/:id/seasons/:lang?': ['x2cf'], + '/tv/:id/seasons/:seasonNumber/episodes/:lang?': ['x2cf'], + '/:mediaType/:sheet/:lang?': ['x2cf'], +}; diff --git a/lib/v2/themoviedb/radar.js b/lib/v2/themoviedb/radar.js new file mode 100644 index 000000000..374e2a3ea --- /dev/null +++ b/lib/v2/themoviedb/radar.js @@ -0,0 +1,73 @@ +module.exports = { + 'themoviedb.org': { + _name: 'The Movie Database', + '.': [ + { + title: 'Collection', + docs: 'https://docs.rsshub.app/multimedia.html#the-movie-database', + source: ['/collection/:name'], + target: (params) => `/themoviedb/collection/${params.name.split('-')[0]}`, + }, + { + title: 'Popular Movies', + docs: 'https://docs.rsshub.app/multimedia.html#the-movie-database', + source: ['/movie'], + target: '/themoviedb/trending/movie/week', + }, + { + title: 'Popular TV Shows', + docs: 'https://docs.rsshub.app/multimedia.html#the-movie-database', + source: ['/tv'], + target: '/themoviedb/trending/tv/week', + }, + { + title: 'TV Show Seasons', + docs: 'https://docs.rsshub.app/multimedia.html#the-movie-database', + source: ['/tv/:id/seasons', '/tv/:id'], + target: (params) => `/themoviedb/tv/${params.id.split('-')[0]}/seasons`, + }, + { + title: 'TV Show Episodes', + docs: 'https://docs.rsshub.app/multimedia.html#the-movie-database', + source: ['/tv/:id/season/:seasonNumber'], + target: (params) => `/themoviedb/tv/${params.id.split('-')[0]}/seasons/${params.seasonNumber}/episodes`, + }, + { + title: 'TV Shows Airing Today', + docs: 'https://docs.rsshub.app/multimedia.html#the-movie-database', + source: ['/tv/airing-today'], + target: '/themoviedb/tv/airing-today', + }, + { + title: 'Currently Airing TV Shows', + docs: 'https://docs.rsshub.app/multimedia.html#the-movie-database', + source: ['/tv/on-the-air'], + target: '/themoviedb/tv/on-the-air', + }, + { + title: 'Top Rated TV Shows', + docs: 'https://docs.rsshub.app/multimedia.html#the-movie-database', + source: ['/tv/top-rated'], + target: '/themoviedb/tv/top-rated', + }, + { + title: 'Now Playing Movies', + docs: 'https://docs.rsshub.app/multimedia.html#the-movie-database', + source: ['/movie/now-playing'], + target: '/themoviedb/movie/now-playing', + }, + { + title: 'Upcoming Movies', + docs: 'https://docs.rsshub.app/multimedia.html#the-movie-database', + source: ['/movie/upcoming'], + target: '/themoviedb/movie/upcoming', + }, + { + title: 'Top Rated Movies', + docs: 'https://docs.rsshub.app/multimedia.html#the-movie-database', + source: ['/movie/top-rated'], + target: '/themoviedb/movie/top-rated', + }, + ], + }, +}; diff --git a/lib/v2/themoviedb/router.js b/lib/v2/themoviedb/router.js new file mode 100644 index 000000000..5868dd9ec --- /dev/null +++ b/lib/v2/themoviedb/router.js @@ -0,0 +1,7 @@ +module.exports = (router) => { + router.get('/collection/:id/:lang?', require('./collection')); + router.get('/trending/:mediaType/:timeWindow/:lang?', require('./trending')); + router.get('/tv/:id/seasons/:lang?', require('./seasons')); + router.get('/tv/:id/seasons/:seasonNumber/episodes/:lang?', require('./episodes')); + router.get('/:mediaType/:sheet/:lang?', require('./sheet')); +}; diff --git a/lib/v2/themoviedb/seasons.js b/lib/v2/themoviedb/seasons.js new file mode 100644 index 000000000..9ed0093f1 --- /dev/null +++ b/lib/v2/themoviedb/seasons.js @@ -0,0 +1,31 @@ +const got = require('@/utils/got'); +const { parseDate } = require('@/utils/parse-date'); +const apiKey = require('./api-key'); +const { handleDescription } = require('./utils'); + +module.exports = async (ctx) => { + const { id, lang } = ctx.params; + const { data } = await got(`https://api.themoviedb.org/3/tv/${id}`, { + searchParams: { + language: lang, + api_key: apiKey(), + }, + }); + + ctx.state.data = { + title: `${data.name} - Seasons — TMDB`, + description: data.overview.trim(), + image: `https://image.tmdb.org/t/p/original${data.poster_path}`, + link: `https://www.themoviedb.org/tv/${data.id}/seasons`, + item: data.seasons.map((item) => { + item.vote_average = data.vote_average; + item.vote_count = data.vote_count; + return { + title: item.name, + link: `https://www.themoviedb.org/tv/${data.id}/season/${item.season_number}`, + description: handleDescription(item), + pubDate: parseDate(item.air_date), + }; + }), + }; +}; diff --git a/lib/v2/themoviedb/sheet.js b/lib/v2/themoviedb/sheet.js new file mode 100644 index 000000000..66a93cda3 --- /dev/null +++ b/lib/v2/themoviedb/sheet.js @@ -0,0 +1,45 @@ +const got = require('@/utils/got'); +const apiKey = require('./api-key'); +const { MEDIA_TYPE_TO_ITEM_HANDLE } = require('./utils'); + +const API_PATH = { + movie: { + 'now-playing': 'movie/now_playing', + upcoming: 'movie/upcoming', + 'top-rated': 'movie/top_rated', + }, + tv: { + 'airing-today': 'tv/airing_today', + 'on-the-air': 'tv/on_the_air', + 'top-rated': 'tv/top_rated', + }, +}; + +const TITLE = { + movie: { + 'now-playing': 'Now Playing Movies', + upcoming: 'Upcoming Movies', + 'top-rated': 'Top Rated Movies', + }, + tv: { + 'airing-today': 'TV Shows Airing Today', + 'on-the-air': 'Currently Airing TV Shows', + 'top-rated': 'Top Rated TV Shows', + }, +}; + +module.exports = async (ctx) => { + const { mediaType, sheet, lang } = ctx.params; + const { data } = await got(`https://api.themoviedb.org/3/${API_PATH[mediaType][sheet]}`, { + searchParams: { + language: lang, + api_key: apiKey(), + }, + }); + + ctx.state.data = { + title: `${TITLE[mediaType][sheet]} — TMDB`, + link: `https://www.themoviedb.org/${mediaType}/${sheet}`, + item: data.results.map((item) => MEDIA_TYPE_TO_ITEM_HANDLE[mediaType](item, lang)), + }; +}; diff --git a/lib/v2/themoviedb/templates/description.art b/lib/v2/themoviedb/templates/description.art new file mode 100644 index 000000000..daf746176 --- /dev/null +++ b/lib/v2/themoviedb/templates/description.art @@ -0,0 +1,6 @@ + +

+ User Score: {{vote_average}}
+ Vote Count: {{vote_count}}
+

+

{{description}}

diff --git a/lib/v2/themoviedb/trending.js b/lib/v2/themoviedb/trending.js new file mode 100644 index 000000000..c5bed3780 --- /dev/null +++ b/lib/v2/themoviedb/trending.js @@ -0,0 +1,24 @@ +const got = require('@/utils/got'); +const apiKey = require('./api-key'); +const { MEDIA_TYPE_TO_ITEM_HANDLE } = require('./utils'); + +const MEDIA_TYPE_TO_TITLE = { + tv: 'TV Shows', + movie: 'Movies', +}; + +module.exports = async (ctx) => { + const { mediaType, timeWindow, lang } = ctx.params; + const { data } = await got(`https://api.themoviedb.org/3/trending/${mediaType}/${timeWindow}`, { + searchParams: { + language: lang, + api_key: apiKey(), + }, + }); + + ctx.state.data = { + title: `Popular ${MEDIA_TYPE_TO_TITLE[mediaType]} — TMDB`, + link: `https://www.themoviedb.org/${mediaType}`, + item: data.results.map((item) => MEDIA_TYPE_TO_ITEM_HANDLE[mediaType](item, lang)), + }; +}; diff --git a/lib/v2/themoviedb/utils.js b/lib/v2/themoviedb/utils.js new file mode 100644 index 000000000..13cf3dcc2 --- /dev/null +++ b/lib/v2/themoviedb/utils.js @@ -0,0 +1,32 @@ +const path = require('path'); +const { art } = require('@/utils/render'); +const { parseDate } = require('@/utils/parse-date'); + +const handleDescription = (item) => + art(path.join(__dirname, 'templates/description.art'), { + poster: `https://image.tmdb.org/t/p/original${item.poster_path ?? item.still_path}`, + description: item.overview.trim(), + vote_average: item.vote_average, + vote_count: item.vote_count, + }); + +const handleMovieItem = (item, lang) => ({ + title: lang ? item.title : item.original_title, + link: `https://www.themoviedb.org/movie/${item.id}`, + description: handleDescription(item), + pubDate: parseDate(item.release_date), +}); + +const handleTVShowItem = (item, lang) => ({ + title: lang ? item.name : item.original_name, + link: `https://www.themoviedb.org/tv/${item.id}`, + description: handleDescription(item), + pubDate: parseDate(item.first_air_date), +}); + +const MEDIA_TYPE_TO_ITEM_HANDLE = { + tv: handleTVShowItem, + movie: handleMovieItem, +}; + +module.exports = { handleDescription, handleMovieItem, handleTVShowItem, MEDIA_TYPE_TO_ITEM_HANDLE };