diff --git a/web/app/src/api.ts b/web/app/src/api.ts index 2cc6e1d..7430701 100644 --- a/web/app/src/api.ts +++ b/web/app/src/api.ts @@ -149,11 +149,16 @@ export type Bibliothek = { extraVersionCount: number; }; export type Seite = { items: T[]; total: number }; -export type Lieferung = { - method: 'directFile' | 'remux'; reasons: string[]; - audioToEncode: number[]; subtitlesDropped: number[]; - packageState: string; progress: number; error: string | null; -}; + +/* **Herunterladen gibt es im Browser nicht mehr.** + * + * Hier standen `Lieferung`, `lieferung()` und `downloadAdresse()`. Die + * Detailseite bot damit einen Knopf an, der die Datei als MP4 herausgab — + * und das ist etwas anderes, als einen Strom zu zeigen: wer eine Datei + * bekommt, hat sie und kann sie weitergeben. Der Endpunkt bleibt, weil die + * Apps ihn für die Mitnahme aufs Gerät brauchen; die Weboberfläche bietet + * ihn nicht mehr an. */ + export type Scanstand = { state: string; filesFound: number | null; added: number | null; removed: number | null; unrecognised: number | null; probed: number | null; @@ -561,8 +566,6 @@ export const folgen = (serie: string, staffel?: number) => export const film = (id: string) => anfrage(`/movies/${id}`); export const serie = (id: string) => anfrage(`/series/${id}`); -export const lieferung = (datei: string) => anfrage(`/files/${datei}/delivery`); - /* ---------- Stand je Benutzer ---------- */ export const weiterschauen = (take = 20) => @@ -614,8 +617,6 @@ export const favorit = (id: string, wert: boolean) => method: 'PUT', body: JSON.stringify({ favourite: wert }), }); -export const downloadAdresse = (datei: string) => `/api/v1/files/${datei}/download`; - /* ---------- Strom ---------- */ /** Eine wählbare Spur, so wie sie im Menü steht. */ diff --git a/web/app/src/views/detail.ts b/web/app/src/views/detail.ts index c316a7a..e96bb30 100644 --- a/web/app/src/views/detail.ts +++ b/web/app/src/views/detail.ts @@ -1,7 +1,7 @@ import { ApiFehler, - film as filmLaden, serie as serieLaden, folgen, lieferung, downloadAdresse, + film as filmLaden, serie as serieLaden, folgen, vorschlaege, zuordnen, bildAdresse, istAdmin, gesehen, favorit, weglegen, - type Film, type Serie, type Folge, type Datei, type Lieferung, type Stand, + type Film, type Serie, type Folge, type Datei, type Stand, type Mitwirkender, type Spur, aehnliche, } from '../api'; import { @@ -313,10 +313,12 @@ export function detailseite(art: Art, id: string, zurueck: () => void): HTMLElem const tonwahl = tonspurwahl(datei); if (tonwahl) reihe.appendChild(tonwahl); - const laden = knopf('Herunterladen', 'btn'); - laden.prepend(icon('download')); - laden.onclick = () => void herunterladen(datei, laden, hinweisfeld); - reihe.appendChild(laden); + // **Kein „Herunterladen" im Browser.** + // + // Hier stand ein Knopf, der die Datei als MP4 herausgab. Das ist etwas + // anderes, als einen Strom zu zeigen: wer eine Datei bekommt, hat sie + // und kann sie weitergeben. Die Apps holen sich eine Mitnahme + // weiterhin über `/download` — dort ist sie an ein Gerät gebunden. } // **Runde Knöpfe ohne Beschriftung, wie bei Plex.** Gesehen, Favorit, @@ -743,41 +745,7 @@ export function detailseite(art: Art, id: string, zurueck: () => void): HTMLElem return block; } - /* ---------- Abspielen und Herunterladen ---------- */ - - /** Sorgt dafür, dass die Datei abholbereit ist — packt um, falls nötig. */ - /** Baut die Datei für den **Download** auf — dort ist eine fertige richtig. - * - * **Nicht mehr fürs Abspielen.** Dafür gibt es den Strom, der sofort - * anfängt; hier auf eine vollständige MP4 zu warten hiess bei einem - * 4K-Film: Minuten, in denen sichtbar nichts geschieht. */ - async function bereitstellen(datei: Datei, hinweisfeld: HTMLElement): Promise { - let auskunft: Lieferung; - try { auskunft = await lieferung(datei.id); } - catch (fehler) { hinweisfeld.textContent = meldung(fehler); return false; } - - if (auskunft.method === 'directFile') return true; - if (auskunft.packageState === 'ready') return true; - - // **Hier steht, was passieren wird, bevor es passiert.** - const gruende: string[] = []; - if (auskunft.audioToEncode.length) gruende.push(`${auskunft.audioToEncode.length} Tonspur(en) werden neu gerechnet`); - if (auskunft.subtitlesDropped.length) gruende.push(`${auskunft.subtitlesDropped.length} Bilduntertitel können nicht mit`); - hinweisfeld.textContent = `Wird in eine MP4 umgepackt${gruende.length ? ` — ${gruende.join(', ')}` : ''}…`; - - for (let i = 0; i < 600; i++) { - const antwort = await fetch(downloadAdresse(datei.id), { method: 'HEAD' }); - if (antwort.ok) { hinweisfeld.textContent = ''; return true; } - if (antwort.status !== 202) { hinweisfeld.textContent = `Umpacken gescheitert (${antwort.status}).`; return false; } - - const wie = await lieferung(datei.id); - if (wie.error) { hinweisfeld.textContent = wie.error; return false; } - hinweisfeld.textContent = `Wird umgepackt… ${Math.round(wie.progress * 100)} %`; - await new Promise(r => setTimeout(r, 1000)); - } - hinweisfeld.textContent = 'Das Umpacken dauert ungewöhnlich lange.'; - return false; - } + /* ---------- Abspielen ---------- */ /** Startet die Wiedergabe — ohne Vorlauf, ohne Warten. * @@ -796,21 +764,6 @@ export function detailseite(art: Art, id: string, zurueck: () => void): HTMLElem }); } - async function herunterladen( - datei: Datei, knopfDavon: HTMLButtonElement, hinweisfeld: HTMLElement, - ): Promise { - knopfDavon.disabled = true; - if (await bereitstellen(datei, hinweisfeld)) { - // Ein echter Link, kein Blob: der Browser lädt im Hintergrund und kann - // fortsetzen, statt den ganzen Film in den Speicher zu nehmen. - const a = document.createElement('a'); - a.href = downloadAdresse(datei.id); - a.download = ''; - a.click(); - } - knopfDavon.disabled = false; - } - /* ---------- Zuordnung korrigieren ---------- */ function zuordnungsbereich(titel: string, jahr: number | null): HTMLElement {