Files
gerbeur/src/utils/youtube.ts

59 lines
1.7 KiB
TypeScript

import { API_URL } from "../config/api.ts";
/** Mirrors one entry of GET /api/youtube/playlist. */
export interface YouTubeTrack {
videoId: string;
title: string;
/** Seconds. */
duration?: number;
}
/**
* The playlist an embed URL plays, if any.
*
* Covers both embed shapes the provider produces: `/embed/videoseries?list=…`
* for a bare playlist, and `/embed/<id>?list=…` for a video opened in one.
*/
export function embedListId(embedUrl: string): string | null {
try {
return new URL(embedUrl).searchParams.get("list");
} catch {
return null;
}
}
/**
* Add the parameters the IFrame Player API needs to talk to the embed.
*
* `enablejsapi` has to be in the src the iframe was created with — it cannot be
* turned on afterwards — so it is added for every playlist embed, whether or
* not the API script ends up loading.
*/
export function withJsApi(url: string): string {
try {
const u = new URL(url);
u.searchParams.set("enablejsapi", "1");
u.searchParams.set("origin", globalThis.location.origin);
return u.toString();
} catch {
return url;
}
}
/** Titles and lengths for a playlist. Throws; callers fall back to no list. */
export async function fetchPlaylistTracks(
listId: string,
): Promise<YouTubeTrack[]> {
const res = await fetch(
`${API_URL}/api/youtube/playlist?list=${encodeURIComponent(listId)}`,
);
if (!res.ok) throw new Error(`playlist fetch failed: ${res.status}`);
const body = await res.json() as {
success: boolean;
data?: { tracks?: YouTubeTrack[] };
};
const tracks = body.data?.tracks;
if (!body.success || !tracks?.length) throw new Error("no tracks");
return tracks;
}