diff --git a/static/player/episodes/metadata.ts b/static/player/episodes/metadata.ts new file mode 100644 index 00000000..6129afea --- /dev/null +++ b/static/player/episodes/metadata.ts @@ -0,0 +1,31 @@ +import { state } from "../state"; + +/** Fetches episode metadata from API and updates episode-card titles. */ +export const setupEpisodeMetadata = (): void => { + const { episodeList } = state.elements; + if (!episodeList) { + return; + } + + fetch(`/api/watch/episodes/${state.episode.malID}/metadata`) + .then((res) => res.json()) + .then((data: Array<{ mal_id: number; title?: string }>) => { + data.forEach((item) => { + const card = episodeList.querySelector(`[data-episode-id="${item.mal_id}"]`); + if (!card) { + return; + } + + if (item.title) { + const titleEl = card.querySelector("[data-episode-title]"); + if (titleEl) { + titleEl.textContent = item.title; + } + } + }); + }) + .catch((error) => { + window.showToast?.({ message: "Failed to load episode metadata." }); + console.error("failed to load episode metadata:", error); + }); +}; diff --git a/static/player/episodes/thumbnails.ts b/static/player/episodes/thumbnails.ts deleted file mode 100644 index 24c57f7f..00000000 --- a/static/player/episodes/thumbnails.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { state } from "../state"; - -/** - * Fetches episode thumbnails and titles from API. Injects images into episode cards, replaces - * placeholder. - */ -export const setupThumbnails = (): void => { - const { episodeList } = state.elements; - if (!episodeList) { - return; - } - - fetch(`/api/watch/thumbnails/${state.episode.malID}`) - .then((res) => res.json()) - .then((data: Array<{ mal_id: number; url: string; title?: string }>) => { - data.forEach((item) => { - const card = episodeList.querySelector(`[data-episode-id="${item.mal_id}"]`); - if (!card) { - return; - } - - // inject thumbnail image - if (item.url) { - const imgContainer = card.querySelector(".relative.aspect-video"); - if (imgContainer) { - let img = imgContainer.querySelector("img"); - if (!img) { - // replace placeholder with actual image - img = document.createElement("img"); - img.className = - "h-full w-full object-cover transition-transform group-hover:scale-105"; - img.loading = "lazy"; - imgContainer - .querySelector(".flex.h-full.w-full.items-center.justify-center") - ?.remove(); - imgContainer.prepend(img); - } - img.src = item.url; - img.alt = item.title ?? `Episode ${item.mal_id}`; - } - } - - // inject title text - if (item.title) { - const titleEl = card.querySelector("[data-episode-title]"); - if (titleEl) { - titleEl.textContent = item.title; - } - } - }); - }) - .catch((error) => { - window.showToast?.({ message: "Failed to load episode thumbnails." }); - console.error("failed to load episode thumbnails:", error); - }); -}; diff --git a/static/player/main.ts b/static/player/main.ts index 94dfd894..579f052a 100644 --- a/static/player/main.ts +++ b/static/player/main.ts @@ -1,8 +1,8 @@ import { onHtmxLoad, onReady } from "../utils"; import { setupControls, showControls } from "./controls"; import { formatTime } from "./controls"; +import { setupEpisodeMetadata } from "./episodes/metadata"; import { goToNextEpisode } from "./episodes/nav"; -import { setupThumbnails } from "./episodes/thumbnails"; import { setupAutoplayButton, updateEpisodeHighlight, switchEpisodeRange } from "./episodes/ui"; import { setupKeyboard } from "./keyboard"; import { @@ -462,7 +462,7 @@ const initPlayer = async (): Promise => { switchEpisodeRange(Math.floor((Number.parseInt(state.episode.current, 10) - 1) / 100)); } - setupThumbnails(); + setupEpisodeMetadata(); window.setTimeout(() => { if (!signal.aborted) { hydrateAlternateMode(signal).catch((error) => {