Stream refreshed media for playback and view full

This commit is contained in:
Fisto
2026-09-16 13:18:55 +02:00
parent d3f9d20190
commit 612b2504dc
9 changed files with 207 additions and 18 deletions
+41 -3
View File
@@ -8,7 +8,8 @@
*/
import express, { type Request, type Response } from "express";
import { readFileSync } from "node:fs";
import { existsSync, readFileSync } from "node:fs";
import { Readable } from "node:stream";
import http from "node:http";
import https from "node:https";
import { dirname } from "node:path";
@@ -758,7 +759,7 @@ export function startWebUi(deps: WebUiDeps): WebUiHandle {
"/api/media/:scrapeId/:itemId/thumb",
wrap(async (req, res) => {
const file = deps.media.getMediaThumb(String(req.params.scrapeId), Number(req.params.itemId));
if (!file) throw new HttpError(404, "thumb not found");
if (!file || !existsSync(file.path)) throw new HttpError(404, "thumb not found");
res.type(file.contentType).sendFile(file.path);
})
);
@@ -767,11 +768,48 @@ export function startWebUi(deps: WebUiDeps): WebUiHandle {
"/api/media/:scrapeId/:itemId/file",
wrap(async (req, res) => {
const file = deps.media.getMediaFile(String(req.params.scrapeId), Number(req.params.itemId));
if (!file) throw new HttpError(404, "file not found");
if (!file || !existsSync(file.path)) throw new HttpError(404, "file not found");
res.type(file.contentType).sendFile(file.path);
})
);
// Playback and full-view source: local file when downloaded, otherwise a refreshed remote URL.
app.get(
"/api/media/:scrapeId/:itemId/stream",
wrap(async (req, res) => {
const source = await deps.media.resolveMediaSource(
String(req.params.scrapeId),
Number(req.params.itemId)
);
if (!source) throw new HttpError(404, "media source not found");
if (source.kind === "file") {
if (!existsSync(source.path)) throw new HttpError(404, "file not found");
res.type(source.contentType).sendFile(source.path);
return;
}
const upstream = await fetch(source.url, {
headers: req.headers.range ? { range: String(req.headers.range) } : {}
});
if (!upstream.ok && upstream.status !== 206) {
throw new HttpError(502, `upstream ${upstream.status}`);
}
res.status(upstream.status);
for (const header of ["content-type", "content-length", "accept-ranges", "content-range"]) {
const value = upstream.headers.get(header);
if (value) res.setHeader(header, value);
}
if (!upstream.body) {
res.end();
return;
}
Readable.fromWeb(upstream.body as any).pipe(res);
})
);
app.get(
"/api/media/storage",
wrap(async (_req, res) => {