import './Series.css'; import html from './Series.html'; import { getItem, getSeriesEpisodes, getSeriesSeasons } from '../../api/embyApi'; import { createParty } from '../../api/party'; import { navigate } from '../../router'; import type { RouteParams } from '../../router'; import { escapeHtml } from '../../tiles'; import { LOADING_HINT } from '../../loading'; import type { ItemDto, SeasonDto } from '../../types'; export async function renderSeries(root: HTMLElement, params: RouteParams): Promise { const seriesId = params.id as string; root.innerHTML = LOADING_HINT; let series: ItemDto; let seasons: SeasonDto[]; try { [series, seasons] = await Promise.all([getItem(seriesId), getSeriesSeasons(seriesId)]); } catch { root.innerHTML = '

Impossible de charger cette série.

'; return; } root.innerHTML = html; root.querySelector('#back')!.addEventListener('click', () => history.back()); (root.querySelector('#seriesPoster') as HTMLImageElement).src = series.imageUrl; root.querySelector('#seriesTitle')!.innerHTML = series.year ? `${escapeHtml(series.name)} (${series.year})` : escapeHtml(series.name); if (series.overview) { const overviewEl = root.querySelector('#seriesOverview')!; overviewEl.textContent = series.overview; overviewEl.classList.remove('hide'); } (root.querySelector('#embyLink') as HTMLAnchorElement).href = series.embyUrl; if (!seasons.length) { root.querySelector('#episodeList')!.innerHTML = '

Aucune saison trouvée.

'; return; } const tabsEl = root.querySelector('#seasonTabs')!; tabsEl.innerHTML = seasons .map((s, i) => ``) .join(''); tabsEl.querySelectorAll('.tab').forEach((tab) => { tab.addEventListener('click', () => { if (tab.classList.contains('active')) return; tabsEl.querySelectorAll('.tab').forEach((t) => t.classList.remove('active')); tab.classList.add('active'); loadEpisodes(root, seriesId, tab.dataset.seasonId as string); }); }); loadEpisodes(root, seriesId, seasons[0]!.id); } async function loadEpisodes(root: HTMLElement, seriesId: string, seasonId: string): Promise { const listEl = root.querySelector('#episodeList')!; listEl.innerHTML = LOADING_HINT; let episodes: ItemDto[]; try { episodes = await getSeriesEpisodes(seriesId, seasonId); } catch { listEl.innerHTML = '

Impossible de charger les épisodes.

'; return; } if (!episodes.length) { listEl.innerHTML = '

Aucun épisode dans cette saison.

'; return; } // Starting a party from any episode here queues the rest of the series // from that point on server-side (see CatalogService/index.ts) - the // host can then use "Épisode suivant" in the player instead of // restarting per episode. listEl.innerHTML = episodes.map((ep) => `
${ep.year ? ep.year + ' · ' : ''}${escapeHtml(ep.name)}

${escapeHtml(ep.overview || '')}

`).join(''); listEl.querySelectorAll('.startEpisodeBtn').forEach((btn) => { btn.addEventListener('click', async () => { btn.disabled = true; btn.textContent = 'Démarrage...'; try { const party = await createParty(btn.dataset.itemId as string); navigate(`/party/${party.partyId}`); } catch (err) { alert((err as Error).message); btn.disabled = false; btn.textContent = 'Lancer une watch party'; } }); }); }