diff --git a/packages/trpc/src/services/stream.service.test.ts b/packages/trpc/src/services/stream.service.test.ts new file mode 100644 index 0000000..0233c71 --- /dev/null +++ b/packages/trpc/src/services/stream.service.test.ts @@ -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/ or octet-stream", () => { + expect(resolveContentType(null, "/m/a.avi")).toBe("video/avi"); + expect(resolveContentType(null, "/m/noext")).toBe("application/octet-stream"); + }); +}); diff --git a/packages/trpc/src/services/stream.service.ts b/packages/trpc/src/services/stream.service.ts index 6c7d879..3e5e9f2 100644 --- a/packages/trpc/src/services/stream.service.ts +++ b/packages/trpc/src/services/stream.service.ts @@ -8,6 +8,23 @@ export type RemoteFileStream = { body: ReadableStream | null; }; +const MIME_BY_EXT: Record = { + 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; /** @@ -54,7 +71,7 @@ export async function openRemoteFileStream( const abs = mount.rootPath.replace(/\/+$/, "") + item.path; 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("Cache-Control", "private, max-age=0");