UNPKG

rsshub

Version:
80 lines (79 loc) 3.93 kB
import { n as parseRelativeDate } from "./parse-date-Cr0wQjV_.mjs"; import { t as cache_default } from "./cache-BkqOokyU.mjs"; import { h as renderYoutube, i as getSrtAttachmentBatch, p as getVideoUrl } from "./google-Dsvx580L.mjs"; import { Innertube, YTNodes } from "youtubei.js"; //#region lib/routes/youtube/api/youtubei.ts let innertubePromise; const getInnertube = () => { if (!innertubePromise) innertubePromise = Innertube.create({ fetch: (input, init) => { const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url; return fetch(url, { method: input?.method, ...init }); } }); return innertubePromise; }; const isLockupVideo = (video) => video instanceof YTNodes.LockupView; const lockupViewToItem = (video, embed) => { const videoId = video.content_id; const thumbnail = video.content_image?.is(YTNodes.ThumbnailView) ? video.content_image : void 0; const img = `https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg`; const metadataRows = video.metadata?.metadata?.metadata_rows ?? []; const publishedText = metadataRows.flatMap((row) => row.metadata_parts ?? []).map((part) => part.text?.text).findLast((text) => text?.endsWith("ago")); const durationText = thumbnail?.overlays?.filter((overlay) => overlay.is(YTNodes.ThumbnailBottomOverlayView)).flatMap((overlay) => overlay.badges ?? []).find((badge) => /^\d+(?::\d+)+$/.test(badge.text))?.text; return { title: video.metadata?.title?.text || `YouTube Video ${videoId}`, description: renderYoutube(embed, videoId, img, ""), link: `https://www.youtube.com/watch?v=${videoId}`, author: metadataRows.length > 1 ? metadataRows[0].metadata_parts?.[0]?.text?.text : void 0, image: img, pubDate: publishedText ? parseRelativeDate(publishedText) : void 0, attachments: [{ url: getVideoUrl(videoId), mime_type: "text/html", duration_in_seconds: durationText ? durationText.split(":").reduce((acc, part) => acc * 60 + Number(part), 0) : void 0 }] }; }; const getChannelIdByUsername = (username) => cache_default.tryGet(`youtube:getChannelIdByUsername:${username}`, async () => { return (await (await getInnertube()).resolveURL(`https://www.youtube.com/${username}`)).payload.browseId; }); const getDataByUsername = async ({ username, embed, filterShorts, isJsonFeed }) => { const channelId = await getChannelIdByUsername(username); return getDataByChannelId({ channelId, embed, filterShorts, isJsonFeed }); }; const getDataByChannelId = async ({ channelId, embed, isJsonFeed }) => { const channel = await (await getInnertube()).getChannel(channelId); const lockupVideos = (await channel.getVideos()).videos.filter((video) => isLockupVideo(video)); const videoSubtitles = isJsonFeed ? await getSrtAttachmentBatch(lockupVideos.map((video) => video.content_id)) : {}; return { title: `${channel.metadata.title || channelId} - YouTube`, link: `https://www.youtube.com/channel/${channelId}`, image: channel.metadata.avatar?.[0].url, description: channel.metadata.description, item: lockupVideos.map((video) => { const item = lockupViewToItem(video, embed); item.attachments?.push(...isJsonFeed ? videoSubtitles[video.content_id] || [] : []); return item; }) }; }; const getDataByPlaylistId = async ({ playlistId, embed }) => { const playlist = await (await getInnertube()).getPlaylist(playlistId); const videos = await playlist.videos; return { title: `${playlist.info.title || playlistId} by ${playlist.info.author.name} - YouTube`, link: `https://www.youtube.com/playlist?list=${playlistId}`, image: playlist.info.thumbnails?.[0].url, description: playlist.info.description || `${playlist.info.title} by ${playlist.info.author.name}`, item: videos.filter((video) => isLockupVideo(video)).map((video) => lockupViewToItem(video, embed)) }; }; //#endregion export { getDataByPlaylistId as n, getDataByUsername as r, getDataByChannelId as t };