rsshub
Version:
Make RSS Great Again!
80 lines (79 loc) • 3.93 kB
JavaScript
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 };