Files
watchparty/server/utils/youtube.ts
T
2026-06-21 03:53:06 +03:00

128 lines
3.3 KiB
TypeScript

import config from "../config.ts";
import {
PT_HOURS_REGEX,
PT_MINUTES_REGEX,
PT_SECONDS_REGEX,
YOUTUBE_VIDEO_ID_REGEX,
} from "./regex.ts";
import { youtube, youtube_v3 } from "@googleapis/youtube";
let Youtube = config.YOUTUBE_API_KEY
? youtube({
version: "v3",
auth: config.YOUTUBE_API_KEY,
})
: null;
export const mapYoutubeSearchResult = (
video: youtube_v3.Schema$SearchResult,
): PlaylistVideo => {
return {
channel: video.snippet?.channelTitle ?? "",
url: "https://www.youtube.com/watch?v=" + video?.id?.videoId,
name: video.snippet?.title ?? "",
img: video.snippet?.thumbnails?.default?.url ?? "",
duration: 0,
type: "youtube",
};
};
export const mapYoutubeListResult = (
video: youtube_v3.Schema$Video,
): PlaylistVideo => {
const videoId = video.id;
return {
url: "https://www.youtube.com/watch?v=" + videoId,
name: video.snippet?.title ?? "",
img: video.snippet?.thumbnails?.default?.url ?? "",
channel: video.snippet?.channelTitle ?? "",
duration: getVideoDuration(video.contentDetails?.duration ?? ""),
type: "youtube",
};
};
export const mapYoutubePlaylistResult = (
item: youtube_v3.Schema$PlaylistItem,
): PlaylistVideo => {
return {
url: "https://www.youtube.com/watch?v=" + item.snippet?.resourceId?.videoId,
name: item.snippet?.title ?? "",
img: item.snippet?.thumbnails?.default?.url ?? "",
channel: item.snippet?.channelTitle ?? "",
duration: 0,
// duration: getVideoDuration(video.contentDetails?.duration ?? ''),
type: "youtube",
};
};
export const searchYoutube = async (
query: string,
): Promise<PlaylistVideo[]> => {
const response = await Youtube?.search.list({
part: ["snippet"],
type: ["video"],
maxResults: 25,
q: query,
});
return response?.data?.items?.map(mapYoutubeSearchResult) ?? [];
};
export const youtubePlaylist = async (
playlistId: string,
): Promise<PlaylistVideo[]> => {
const response = await Youtube?.playlistItems.list({
part: ["snippet"],
playlistId,
maxResults: 100,
});
return response?.data?.items?.map(mapYoutubePlaylistResult) ?? [];
};
export const getYoutubeVideoID = (url: string) => {
const idParts = YOUTUBE_VIDEO_ID_REGEX.exec(url);
if (!idParts) {
return;
}
const id = idParts[1];
if (!id) {
return;
}
return id;
};
export const fetchYoutubeVideo = async (
id: string,
): Promise<PlaylistVideo | null> => {
const response = await Youtube?.videos.list({
part: ["snippet", "contentDetails"],
id: [id],
});
const top = response?.data?.items?.[0];
return top ? mapYoutubeListResult(top) : null;
};
export const getVideoDuration = (string: string): number => {
if (!string) {
return 0;
}
const hoursParts = PT_HOURS_REGEX.exec(string);
const minutesParts = PT_MINUTES_REGEX.exec(string);
const secondsParts = PT_SECONDS_REGEX.exec(string);
const hours = hoursParts ? parseInt(hoursParts[1]) : 0;
const minutes = minutesParts ? parseInt(minutesParts[1]) : 0;
const seconds = secondsParts ? parseInt(secondsParts[1]) : 0;
const totalSeconds = seconds + minutes * 60 + hours * 60 * 60;
return totalSeconds;
};
export const isYouTube = (input: string) => {
return (
input.startsWith("https://www.youtube.com/") ||
input.startsWith("https://youtu.be/")
);
};