Files
netfelix-audio-fix/server/services/apple-compat.ts
Felix Förtsch 874f04b7a5 wire scheduler into queue, add retry, dev-reset cleanup, biome 2.4 migrate
- execute: actually call isInScheduleWindow/waitForWindow/sleepBetweenJobs in runSequential (they were dead code); emit queue_status SSE events (running/paused/sleeping/idle) so the pipeline's existing QueueStatus listener lights up
- review: POST /:id/retry resets an errored plan to approved, wipes old done/error jobs, rebuilds command from current decisions, queues fresh job
- scan: dev-mode DELETE now also wipes jobs + subtitle_files (previously orphaned after every dev reset)
- biome: migrate config to 2.4 schema, autoformat 68 files (strings + indentation), relax opinionated a11y/hooks-deps/index-key rules that don't fit this codebase
- routeTree.gen.ts regenerated after /nodes removal
2026-04-13 07:41:19 +02:00

70 lines
2.1 KiB
TypeScript

// Codec sets and transcode target mapping for Apple device compatibility.
// Apple natively decodes: AAC, AC3, EAC3, ALAC, FLAC, MP3, PCM, Opus
// Everything else (DTS family, TrueHD family) needs transcoding.
const APPLE_COMPATIBLE_AUDIO = new Set([
"aac",
"ac3",
"eac3",
"alac",
"flac",
"mp3",
"pcm_s16le",
"pcm_s24le",
"pcm_s32le",
"pcm_f32le",
"pcm_s16be",
"pcm_s24be",
"pcm_s32be",
"pcm_f64le",
"opus",
]);
// Codec strings Jellyfin may report for DTS variants
const DTS_CODECS = new Set(["dts", "dca"]);
const TRUEHD_CODECS = new Set(["truehd"]);
export function isAppleCompatible(codec: string): boolean {
return APPLE_COMPATIBLE_AUDIO.has(codec.toLowerCase());
}
/** Maps (codec, profile, container) → target codec for transcoding. */
export function transcodeTarget(codec: string, profile: string | null, container: string | null): string | null {
const c = codec.toLowerCase();
const isMkv = !container || container.toLowerCase() === "mkv" || container.toLowerCase() === "matroska";
if (isAppleCompatible(c)) return null; // no transcode needed
// DTS-HD MA and DTS:X are lossless → FLAC in MKV, EAC3 in MP4
if (DTS_CODECS.has(c)) {
const p = (profile ?? "").toLowerCase();
const isLossless = p.includes("ma") || p.includes("hd ma") || p.includes("x");
if (isLossless) return isMkv ? "flac" : "eac3";
// Lossy DTS variants → EAC3
return "eac3";
}
// TrueHD (including Atmos) → FLAC in MKV, EAC3 in MP4
if (TRUEHD_CODECS.has(c)) {
return isMkv ? "flac" : "eac3";
}
// Any other incompatible codec → EAC3 as safe fallback
return "eac3";
}
/** Determine overall Apple compatibility for a set of kept audio streams. */
export function computeAppleCompat(
keptAudioCodecs: string[],
container: string | null,
): "direct_play" | "remux" | "audio_transcode" {
const hasIncompatible = keptAudioCodecs.some((c) => !isAppleCompatible(c));
if (hasIncompatible) return "audio_transcode";
const isMkv = !container || container.toLowerCase() === "mkv" || container.toLowerCase() === "matroska";
if (isMkv) return "remux";
return "direct_play";
}