diff --git a/src/media/store.header-ext.test.ts b/src/media/store.header-ext.test.ts new file mode 100644 index 000000000..d24f46c2e --- /dev/null +++ b/src/media/store.header-ext.test.ts @@ -0,0 +1,38 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; + +const realOs = await vi.importActual("node:os"); +const HOME = path.join(realOs.tmpdir(), "clawdis-home-header-ext-test"); + +vi.mock("node:os", () => ({ + default: { homedir: () => HOME, tmpdir: () => realOs.tmpdir() }, + homedir: () => HOME, + tmpdir: () => realOs.tmpdir(), +})); + +vi.mock("./mime.js", async () => { + const actual = await vi.importActual("./mime.js"); + return { + ...actual, + detectMime: vi.fn(async () => "audio/opus"), + }; +}); + +const store = await import("./store.js"); + +describe("media store header extensions", () => { + beforeAll(async () => { + await fs.rm(HOME, { recursive: true, force: true }); + }); + + afterAll(async () => { + await fs.rm(HOME, { recursive: true, force: true }); + }); + + it("prefers header mime extension when sniffed mime lacks mapping", async () => { + const buf = Buffer.from("fake-audio"); + const saved = await store.saveMediaBuffer(buf, "audio/ogg; codecs=opus"); + expect(path.extname(saved.path)).toBe(".ogg"); + }); +}); diff --git a/src/media/store.ts b/src/media/store.ts index 67bf0ddce..4bd99cca7 100644 --- a/src/media/store.ts +++ b/src/media/store.ts @@ -169,8 +169,11 @@ export async function saveMediaBuffer( const dir = path.join(MEDIA_DIR, subdir); await fs.mkdir(dir, { recursive: true }); const baseId = crypto.randomUUID(); + const headerExt = extensionForMime( + contentType?.split(";")[0]?.trim() ?? undefined, + ); const mime = await detectMime({ buffer, headerMime: contentType }); - const ext = extensionForMime(mime); + const ext = headerExt ?? extensionForMime(mime); const id = ext ? `${baseId}${ext}` : baseId; const dest = path.join(dir, id); await fs.writeFile(dest, buffer);