Files
netfelix-audio-fix/server/services/__tests__/probe.test.ts
T
felixfoertsch 748145a372
Build and Push Docker Image / build (push) Successful in 3m57s
detect dirty container title and comment, rewrite to canonical form
Track format.tags.title and format.tags.comment on media_items via a new
containerTitle() helper producing "Name (Year)" for movies and
"Series (Year) - S01E02 - Title" for episodes. Analyzer and
recomputePlanAfterToggle now flag non-canonical container title and
non-empty comment as non-noop ("Fix container title", "Clear comment"),
and verifyDesiredState checks them post-ffmpeg. buildStreamFlags writes
the canonical title and clears comment on every run.

Existing libraries need a rescan to populate the new columns.
2026-04-24 21:45:39 +02:00

257 lines
7.0 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { parseProbeOutput } from "../probe";
describe("parseProbeOutput — format metadata", () => {
test("parses size, duration, and container from format", () => {
const json = JSON.stringify({
format: {
size: "1500000000",
duration: "7200.000000",
format_name: "matroska,webm",
},
streams: [],
});
const result = parseProbeOutput(json);
expect(result.fileSize).toBe(1500000000);
expect(result.durationSeconds).toBe(7200.0);
expect(result.container).toBe("matroska");
});
test("returns null for missing format fields", () => {
const json = JSON.stringify({ format: {}, streams: [] });
const result = parseProbeOutput(json);
expect(result.fileSize).toBeNull();
expect(result.durationSeconds).toBeNull();
expect(result.container).toBeNull();
expect(result.containerTitle).toBeNull();
expect(result.containerComment).toBeNull();
});
test("parses container-level title and comment tags", () => {
const json = JSON.stringify({
format: {
format_name: "matroska",
tags: { title: "101 Dalmatians (1961)", comment: "rarbg" },
},
streams: [],
});
const result = parseProbeOutput(json);
expect(result.containerTitle).toBe("101 Dalmatians (1961)");
expect(result.containerComment).toBe("rarbg");
});
test("accepts uppercase TITLE and COMMENT container tags", () => {
const json = JSON.stringify({
format: { format_name: "matroska", tags: { TITLE: "Movie", COMMENT: "ads" } },
streams: [],
});
const result = parseProbeOutput(json);
expect(result.containerTitle).toBe("Movie");
expect(result.containerComment).toBe("ads");
});
test("takes first part of comma-separated format_name", () => {
const json = JSON.stringify({
format: { format_name: "mov,mp4,m4a,3gp,3g2,mj2" },
streams: [],
});
const result = parseProbeOutput(json);
expect(result.container).toBe("mov");
});
});
describe("parseProbeOutput — video stream", () => {
test("parses a video stream correctly", () => {
const json = JSON.stringify({
format: {},
streams: [
{
index: 0,
codec_type: "video",
codec_name: "h264",
profile: "High",
width: 1920,
height: 1080,
disposition: { default: 1, forced: 0, hearing_impaired: 0 },
tags: { language: "eng", title: "Main Video" },
},
],
});
const result = parseProbeOutput(json);
expect(result.streams).toHaveLength(1);
const s = result.streams[0];
expect(s.streamIndex).toBe(0);
expect(s.type).toBe("Video");
expect(s.codec).toBe("h264");
expect(s.profile).toBe("High");
expect(s.language).toBe("eng");
expect(s.title).toBe("Main Video");
expect(s.isDefault).toBe(1);
expect(s.isForced).toBe(0);
expect(s.isHearingImpaired).toBe(0);
expect(s.channels).toBeNull();
expect(s.channelLayout).toBeNull();
expect(s.bitRate).toBeNull();
expect(s.sampleRate).toBeNull();
expect(s.bitDepth).toBeNull();
expect(s.width).toBe(1920);
expect(s.height).toBe(1080);
});
});
describe("parseProbeOutput — audio stream", () => {
test("parses audio stream with full metadata", () => {
const json = JSON.stringify({
format: {},
streams: [
{
index: 1,
codec_type: "audio",
codec_name: "eac3",
profile: "DDP",
channels: 6,
channel_layout: "5.1(side)",
bit_rate: "640000",
sample_rate: "48000",
bits_per_raw_sample: "24",
disposition: { default: 1, forced: 0, hearing_impaired: 0 },
tags: { language: "eng", title: "English Surround" },
},
],
});
const result = parseProbeOutput(json);
expect(result.streams).toHaveLength(1);
const s = result.streams[0];
expect(s.streamIndex).toBe(1);
expect(s.type).toBe("Audio");
expect(s.codec).toBe("eac3");
expect(s.profile).toBe("DDP");
expect(s.language).toBe("eng");
expect(s.title).toBe("English Surround");
expect(s.channels).toBe(6);
expect(s.channelLayout).toBe("5.1(side)");
expect(s.bitRate).toBe(640000);
expect(s.sampleRate).toBe(48000);
expect(s.bitDepth).toBe(24);
expect(s.width).toBeNull();
expect(s.height).toBeNull();
expect(s.isDefault).toBe(1);
expect(s.isForced).toBe(0);
expect(s.isHearingImpaired).toBe(0);
});
test("accepts uppercase LANGUAGE and TITLE tags", () => {
const json = JSON.stringify({
format: {},
streams: [
{
index: 2,
codec_type: "audio",
codec_name: "aac",
disposition: { default: 0, forced: 0, hearing_impaired: 0 },
tags: { LANGUAGE: "deu", TITLE: "Deutsch" },
},
],
});
const result = parseProbeOutput(json);
const s = result.streams[0];
expect(s.language).toBe("deu");
expect(s.title).toBe("Deutsch");
});
test("handles null/missing optional audio fields", () => {
const json = JSON.stringify({
format: {},
streams: [
{
index: 0,
codec_type: "audio",
codec_name: "aac",
disposition: { default: 0, forced: 0, hearing_impaired: 0 },
},
],
});
const result = parseProbeOutput(json);
const s = result.streams[0];
expect(s.channels).toBeNull();
expect(s.channelLayout).toBeNull();
expect(s.bitRate).toBeNull();
expect(s.sampleRate).toBeNull();
expect(s.bitDepth).toBeNull();
expect(s.language).toBeNull();
expect(s.title).toBeNull();
});
});
describe("parseProbeOutput — subtitle stream", () => {
test("parses subtitle stream with hearing_impaired flag", () => {
const json = JSON.stringify({
format: {},
streams: [
{
index: 3,
codec_type: "subtitle",
codec_name: "subrip",
disposition: { default: 0, forced: 0, hearing_impaired: 1 },
tags: { language: "eng", title: "SDH" },
},
],
});
const result = parseProbeOutput(json);
expect(result.streams).toHaveLength(1);
const s = result.streams[0];
expect(s.streamIndex).toBe(3);
expect(s.type).toBe("Subtitle");
expect(s.codec).toBe("subrip");
expect(s.language).toBe("eng");
expect(s.title).toBe("SDH");
expect(s.isDefault).toBe(0);
expect(s.isForced).toBe(0);
expect(s.isHearingImpaired).toBe(1);
});
test("parses forced subtitle stream", () => {
const json = JSON.stringify({
format: {},
streams: [
{
index: 4,
codec_type: "subtitle",
codec_name: "hdmv_pgs_subtitle",
disposition: { default: 0, forced: 1, hearing_impaired: 0 },
tags: { language: "deu" },
},
],
});
const result = parseProbeOutput(json);
const s = result.streams[0];
expect(s.type).toBe("Subtitle");
expect(s.isForced).toBe(1);
expect(s.language).toBe("deu");
expect(s.title).toBeNull();
});
});
describe("parseProbeOutput — codec_type mapping", () => {
test("maps data → Data and attachment → EmbeddedImage", () => {
const json = JSON.stringify({
format: {},
streams: [
{
index: 0,
codec_type: "data",
disposition: { default: 0, forced: 0, hearing_impaired: 0 },
},
{
index: 1,
codec_type: "attachment",
disposition: { default: 0, forced: 0, hearing_impaired: 0 },
},
],
});
const result = parseProbeOutput(json);
expect(result.streams[0].type).toBe("Data");
expect(result.streams[1].type).toBe("EmbeddedImage");
});
});