Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
bcb6933
fix(media-server): check every remote URL against a private-address p…
07rjain Oct 23, 2026
163102d
chore: merge current recording verification work from main
07rjain Oct 23, 2026
c4e645d
fix(media-server): pin remote connections and complete safe media sta…
07rjain Oct 23, 2026
fdb9c6a
test(media-server): enforce one deadline across recording staging and…
07rjain Oct 23, 2026
7b49d10
test(media-server): preserve the spawn overload type in the deadline …
07rjain Oct 23, 2026
d80a37b
fix(media-server): try checked fallback addresses for media reads
07rjain Oct 23, 2026
5ab2e54
Merge remote-tracking branch 'origin/main' into split/media-url-policy
07rjain Oct 23, 2026
6933d95
chore(media-server): merge latest main into URL policy fixes
07rjain Oct 24, 2026
d4aee16
fix(media-server): align IPv6 restrictions with the S3 endpoint policy
07rjain Oct 24, 2026
c8166f9
fix(media-server): bound audio staging and retry refused connections
07rjain Oct 26, 2026
cbea23c
fix(media-server): include test configuration in Docker images
07rjain Oct 26, 2026
d6ded5b
Merge remote-tracking branch 'origin/main' into split/media-url-policy
07rjain Oct 26, 2026
d43279c
test(media-server): align merged video tests with staged resources
07rjain Oct 26, 2026
13b33fa
fix(media-server): secure upstream verification and Loom downloads
07rjain Oct 26, 2026
7c1d0b5
chore: merge main and resolve media URL policy conflicts
07rjain Nov 3, 2026
8733953
chore: merge main and preserve checked media downloads
07rjain Nov 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/media-server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ COPY apps/media-server/package.json apps/media-server/bun.lock ./
RUN bun install --frozen-lockfile --production

COPY apps/media-server/src ./src
COPY apps/media-server/capcodec.md ./capcodec.md
COPY apps/media-server/capcodec.md apps/media-server/bunfig.toml ./

RUN bun test src/__tests__/lib/audio-quality-policy.test.ts src/__tests__/lib/audio-quality.integration.test.ts \
&& bun test src/__tests__/lib/audio-quality-formats.integration.test.ts src/__tests__/lib/audio-quality-benchmark.test.ts \
Expand Down
2 changes: 1 addition & 1 deletion apps/media-server/Dockerfile.standalone
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production

COPY src ./src
COPY capcodec.md ./capcodec.md
COPY capcodec.md bunfig.toml ./

ENV PORT=3456
EXPOSE 3456
Expand Down
2 changes: 2 additions & 0 deletions apps/media-server/bunfig.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[test]
preload = ["./src/__tests__/setup.ts"]
4 changes: 3 additions & 1 deletion apps/media-server/src/__tests__/lib/job-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,10 @@ describe("durable recording workers", () => {
const sending = sendWebhook(job);
updateJob(job.jobId, { phase: "complete", progress: 100 });
expect(sendWebhook(job)).toBe(sending);
// The URL policy check runs before fetch, so wait for the request to start.
while (!release) await Bun.sleep(1);
const started = performance.now();
release?.(new Response(null, { status: 503 }));
release(new Response(null, { status: 503 }));
await sending;
expect(performance.now() - started).toBeLessThan(400);
expect(maximumInFlight).toBe(1);
Expand Down
78 changes: 67 additions & 11 deletions apps/media-server/src/__tests__/lib/media-audio.integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterAll, beforeAll, describe, expect, spyOn, test } from "bun:test";
import { mkdtemp, rm } from "node:fs/promises";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import {
Expand All @@ -24,8 +24,15 @@ const FIXTURES_DIR = join(import.meta.dir, "..", "fixtures");
const TEST_VIDEO_WITH_AUDIO = `file://${join(FIXTURES_DIR, "test-with-audio.mp4")}`;
const TEST_VIDEO_NO_AUDIO = `file://${join(FIXTURES_DIR, "test-no-audio.mp4")}`;

async function createHlsFixture(): Promise<string> {
async function createHlsFixture(encrypted = false): Promise<string> {
const dirPath = await mkdtemp(join(tmpdir(), "cap-audio-hls-"));
if (encrypted) {
await writeFile(join(dirPath, "key.bin"), Buffer.alloc(16, 7));
await writeFile(
join(dirPath, "key.info"),
`key.bin\n${join(dirPath, "key.bin")}\n`,
);
}
const proc = Bun.spawn({
cmd: [
"ffmpeg",
Expand All @@ -41,6 +48,7 @@ async function createHlsFixture(): Promise<string> {
"0.25",
"-hls_list_size",
"0",
...(encrypted ? ["-hls_key_info_file", join(dirPath, "key.info")] : []),
"-hls_segment_filename",
join(dirPath, "segment-%03d.ts"),
join(dirPath, "manifest.m3u8"),
Expand Down Expand Up @@ -212,7 +220,7 @@ describe("mediaAudio integration tests", () => {
try {
await expect(
checkHasAudioTrack(`${server.url}video.mp4`),
).rejects.toThrow("FFprobe exited");
).rejects.toThrow("Failed to fetch media resource: 503");
await waitForAudioOperations(beforeCount);
} finally {
await server.stop(true);
Expand All @@ -236,7 +244,9 @@ describe("mediaAudio integration tests", () => {
describe("extractAudioStream", () => {
test("preserves queued audio when the reader pauses before EOF", async () => {
const expected = await extractAudio(TEST_VIDEO_WITH_AUDIO);
const { stream, cleanup } = extractAudioStream(TEST_VIDEO_WITH_AUDIO);
const { stream, cleanup } = await extractAudioStream(
TEST_VIDEO_WITH_AUDIO,
);
const reader = stream.getReader();
const chunks: Uint8Array[] = [];
try {
Expand All @@ -256,7 +266,9 @@ describe("mediaAudio integration tests", () => {
test("drains all encoded bytes when the consumer waits for FFmpeg to exit", async () => {
const expected = await extractAudio(TEST_VIDEO_WITH_AUDIO);
const registration = spyOn(subprocess, "registerSubprocess");
const { stream, cleanup } = extractAudioStream(TEST_VIDEO_WITH_AUDIO);
const { stream, cleanup } = await extractAudioStream(
TEST_VIDEO_WITH_AUDIO,
);
try {
const proc = registration.mock.calls[0][0];
expect(await proc.exited).toBe(0);
Expand All @@ -271,7 +283,7 @@ describe("mediaAudio integration tests", () => {

test("errors the stream when FFmpeg cannot extract audio", async () => {
const beforeCount = getActiveProcessCount();
const { stream, cleanup } = extractAudioStream(TEST_VIDEO_NO_AUDIO);
const { stream, cleanup } = await extractAudioStream(TEST_VIDEO_NO_AUDIO);
try {
await expect(readStream(stream)).rejects.toThrow("FFmpeg exited");
await waitForAudioOperations(beforeCount);
Expand All @@ -283,9 +295,12 @@ describe("mediaAudio integration tests", () => {
test("errors the stream and terminates FFmpeg at the deadline", async () => {
const beforeCount = getActiveProcessCount();
const registration = spyOn(subprocess, "registerSubprocess");
const { stream, cleanup } = extractAudioStream(TEST_VIDEO_WITH_AUDIO, {
timeoutMs: 1,
});
const { stream, cleanup } = await extractAudioStream(
TEST_VIDEO_WITH_AUDIO,
{
timeoutMs: 1,
},
);
try {
await expect(readStream(stream)).rejects.toThrow("timed out");
const proc = registration.mock.calls[0][0];
Expand All @@ -299,21 +314,62 @@ describe("mediaAudio integration tests", () => {

test("streams real mp3 data and cleans up its operation", async () => {
const beforeCount = getActiveProcessCount();
const { stream } = extractAudioStream(TEST_VIDEO_WITH_AUDIO);
const { stream } = await extractAudioStream(TEST_VIDEO_WITH_AUDIO);

const audioData = await readStream(stream);

expectMp3(audioData);
await waitForAudioOperations(beforeCount);
});

test("releases the operation when a remote download fails", async () => {
const beforeCount = getActiveProcessCount();
const server = Bun.serve({
port: 0,
fetch: () => new Response("Missing", { status: 404 }),
});

try {
await expect(
extractAudioStream(`${server.url}clip.mp4`),
).rejects.toThrow("Failed to fetch media resource: 404");
await waitForAudioOperations(beforeCount);
} finally {
await server.stop(true);
}
});

test("cleans up when a real stream is cancelled", async () => {
const beforeCount = getActiveProcessCount();
const { stream } = extractAudioStream(TEST_VIDEO_WITH_AUDIO);
const { stream } = await extractAudioStream(TEST_VIDEO_WITH_AUDIO);

await stream.cancel();

await waitForAudioOperations(beforeCount);
});
});
});

describe("encrypted remote HLS audio", () => {
test("extracts MP3 audio after staging AES-128 keys and segments locally", async () => {
const directory = await createHlsFixture(true);
const server = Bun.serve({
port: 0,
fetch(request) {
return new Response(
Bun.file(join(directory, new URL(request.url).pathname)),
);
},
});
try {
const url = new URL("manifest.m3u8", server.url).toString();
expect(await checkHasAudioTrack(url)).toBe(true);
expectMp3(await extractAudio(url));
const { stream } = await extractAudioStream(url);
expectMp3(await readStream(stream));
} finally {
server.stop(true);
await rm(directory, { recursive: true, force: true });
}
});
});
Loading
Loading