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'));