fix(trpc): stream Content-Type fallback by extension

This commit is contained in:
noelorin 2026-09-25 12:15:50 +08:00
parent b4ac0b6281
commit 3af8ebead7
2 changed files with 36 additions and 1 deletions

View File

@ -0,0 +1,18 @@
import { describe, expect, it } from "vitest";
import { resolveContentType } from "./stream.service.js";
describe("resolveContentType", () => {
it("keeps existing mime", () => {
expect(resolveContentType("video/mp4", "a.mkv")).toBe("video/mp4");
});
it("maps common video extensions", () => {
expect(resolveContentType(null, "/m/a.mp4")).toBe("video/mp4");
expect(resolveContentType("", "/m/a.m4v")).toBe("video/mp4");
expect(resolveContentType(undefined, "/m/a.mkv")).toBe("video/x-matroska");
expect(resolveContentType(null, "/m/a.webm")).toBe("video/webm");
});
it("falls back to video/<ext> or octet-stream", () => {
expect(resolveContentType(null, "/m/a.avi")).toBe("video/avi");
expect(resolveContentType(null, "/m/noext")).toBe("application/octet-stream");
});
});

View File

@ -8,6 +8,23 @@ export type RemoteFileStream = {
body: ReadableStream<Uint8Array> | null; body: ReadableStream<Uint8Array> | null;
}; };
const MIME_BY_EXT: Record<string, string> = {
mp4: "video/mp4",
m4v: "video/mp4",
mkv: "video/x-matroska",
webm: "video/webm",
};
/** scan/ensurePath 不写 mime;按扩展名回落,保证播放器拿到可识别的 Content-Type */
export function resolveContentType(mime: string | null | undefined, path: string): string {
if (mime) return mime;
const name = path.split("/").pop() ?? path;
const dot = name.lastIndexOf(".");
const ext = dot >= 0 ? name.slice(dot + 1).toLowerCase() : "";
if (!ext) return "application/octet-stream";
return MIME_BY_EXT[ext] ?? `video/${ext}`;
}
type ParsedRange = { start: number; end: number } | "unsatisfiable" | null; type ParsedRange = { start: number; end: number } | "unsatisfiable" | null;
/** /**
@ -54,7 +71,7 @@ export async function openRemoteFileStream(
const abs = mount.rootPath.replace(/\/+$/, "") + item.path; const abs = mount.rootPath.replace(/\/+$/, "") + item.path;
const headers = new Headers(); const headers = new Headers();
headers.set("Content-Type", item.mime || "application/octet-stream"); headers.set("Content-Type", resolveContentType(item.mime, item.path));
headers.set("Accept-Ranges", "bytes"); headers.set("Accept-Ranges", "bytes");
headers.set("Cache-Control", "private, max-age=0"); headers.set("Cache-Control", "private, max-age=0");