UNPKG

rsshub

Version:
80 lines (74 loc) 3.21 kB
import { Route } from '@/types'; import cache from '@/utils/cache'; import utils from './utils'; import { config } from '@/config'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import ConfigNotFoundError from '@/errors/types/config-not-found'; export const route: Route = { path: '/c/:username/:embed?', categories: ['social-media'], example: '/youtube/c/YouTubeCreators', parameters: { username: 'YouTube custom URL', embed: 'Default to embed the video, set to any value to disable embedding' }, features: { requireConfig: [ { name: 'YOUTUBE_KEY', description: ' YouTube API Key, support multiple keys, split them with `,`, [API Key application](https://console.developers.google.com/)', }, ], requirePuppeteer: false, antiCrawler: false, supportBT: false, supportPodcast: false, supportScihub: false, }, radar: [ { source: ['www.youtube.com/c/:id'], target: '/c/:id', }, ], name: 'Custom URL', maintainers: ['TonyRL'], handler, }; async function handler(ctx) { if (!config.youtube || !config.youtube.key) { throw new ConfigNotFoundError('YouTube RSS is disabled due to the lack of <a href="https://docs.rsshub.app/deploy/config#route-specific-configurations">relevant config</a>'); } const username = ctx.req.param('username'); const embed = !ctx.req.param('embed'); const { data: response } = await got(`https://www.youtube.com/c/${username}`); const $ = load(response); const ytInitialData = JSON.parse( $('script') .text() .match(/ytInitialData = ({.*?});/)?.[1] || '{}' ); const externalId = ytInitialData.metadata.channelMetadataRenderer.externalId; const playlistId = (await utils.getChannelWithId(externalId, 'contentDetails', cache)).data.items[0].contentDetails.relatedPlaylists.uploads; const data = (await utils.getPlaylistItems(playlistId, 'snippet', cache)).data.items; return { title: `${username} - YouTube`, link: `https://www.youtube.com/c/${username}`, description: ytInitialData.metadata.channelMetadataRenderer.description, image: ytInitialData.metadata.channelMetadataRenderer.avatar?.thumbnails?.[0]?.url, 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, image: img.url, }; }), }; }