diff --git a/docs/en/social-media.md b/docs/en/social-media.md index 3cea3b9fc7..68a82fc366 100644 --- a/docs/en/social-media.md +++ b/docs/en/social-media.md @@ -490,6 +490,10 @@ YouTube provides official RSS feeds for channels, for instance +### Custom URL + + + ### Playlist diff --git a/docs/social-media.md b/docs/social-media.md index 7ca29bb588..ea9f9bb98d 100644 --- a/docs/social-media.md +++ b/docs/social-media.md @@ -867,6 +867,10 @@ YouTube 官方亦有提供频道 RSS,形如 +### 自定义网址 + + + ### 播放列表 diff --git a/lib/v2/youtube/custom.js b/lib/v2/youtube/custom.js new file mode 100644 index 0000000000..373f87cadc --- /dev/null +++ b/lib/v2/youtube/custom.js @@ -0,0 +1,45 @@ +const utils = require('./utils'); +const config = require('@/config').value; +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + if (!config.youtube || !config.youtube.key) { + throw 'YouTube RSS is disabled due to the lack of relevant config'; + } + const { username } = ctx.params; + const embed = !ctx.params.embed; + + const { data: response } = await got(`https://www.youtube.com/c/${username}`); + const $ = cheerio.load(response); + const ytInitialData = JSON.parse( + $('script') + .text() + .match(/ytInitialData = ({.*?});/)[1] + ); + const externalId = ytInitialData.metadata.channelMetadataRenderer.externalId; + const playlistId = (await utils.getChannelWithId(externalId, 'contentDetails', ctx.cache)).data.items[0].contentDetails.relatedPlaylists.uploads; + + const data = (await utils.getPlaylistItems(playlistId, 'snippet', ctx.cache)).data.items; + + ctx.state.data = { + title: `${username} - YouTube`, + link: `https://www.youtube.com/c/${username}`, + description: ytInitialData.metadata.channelMetadataRenderer.description, + item: data + .filter((d) => d.snippet.title !== 'Private video' && d.snippet.title !== 'Deleted video') + .map((item) => { + const snippet = item.snippet; + const videoId = snippet.resourceId.videoId; + const img = utils.getThumbnail(snippet.thumbnails); + return { + title: snippet.title, + description: utils.renderDescription(embed, videoId, img, utils.formatDescription(snippet.description)), + pubDate: parseDate(snippet.publishedAt), + link: `https://www.youtube.com/watch?v=${videoId}`, + author: snippet.videoOwnerChannelTitle, + }; + }), + }; +}; diff --git a/lib/v2/youtube/radar.js b/lib/v2/youtube/radar.js index 7a52893b64..cf2e38d83e 100644 --- a/lib/v2/youtube/radar.js +++ b/lib/v2/youtube/radar.js @@ -14,6 +14,12 @@ module.exports = { source: '/channel/:id', target: '/youtube/channel/:id', }, + { + title: '自定义网址', + docs: 'https://docs.rsshub.app/social-media.html#youtube', + source: '/c/:id', + target: '/youtube/c/:id', + }, { title: '播放列表', docs: 'https://docs.rsshub.app/social-media.html#youtube', diff --git a/lib/v2/youtube/router.js b/lib/v2/youtube/router.js index 54fc0634c7..8d6165cf45 100644 --- a/lib/v2/youtube/router.js +++ b/lib/v2/youtube/router.js @@ -1,4 +1,5 @@ module.exports = (router) => { + router.get('/c/:username/:embed?', require('./custom')); router.get('/channel/:id/:embed?', require('./channel')); router.get('/playlist/:id/:embed?', require('./playlist')); router.get('/subscriptions/:embed?', require('./subscriptions'));