fix(trpc): stream Content-Type fallback by extension
This commit is contained in:
parent
b4ac0b6281
commit
3af8ebead7
|
|
@ -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");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue