Files
netfelix-audio-fix/server/services/apple-compat.ts
Felix Förtsch 4057b692ba
All checks were successful
Build and Push Docker Image / build (push) Successful in 47s
audio: single EAC3 transcode target, prefer direct-play over lossless default
two simplifications to how we pick and transcode the one-per-language
audio track, motivated by seeing inconsistent DTS → FLAC vs DTS →
EAC3 outputs in the wild:

transcode target:
- drop the FLAC path entirely. every incompatible source now targets
  EAC3 regardless of container or lossless/lossy status
- FLAC for movie audio is bad value: ~2-3× the file size vs EAC3, no
  Atmos spatial metadata (TrueHD Atmos → FLAC silently loses Atmos),
  no AVR passthrough on Apple TV
- one target = no more container-conditional surprises

winner within a language group (betterAudio):
- new priority: highest channels → Apple-compatible → default → index
- old order put 'default' on top which forced a DTS-HD MA transcode
  even when an AC3 track at equal channels was right next to it.
  flipping means AC3 beats DTS-HD MA at the same channel count — pure
  copy instead of a lossless-then-re-encode round trip
- channel count still dominates, so 7.1 TrueHD still beats 5.1 AC3
  (and gets transcoded, which is the right call for real surround)

tests: new case for DTS-HD MA default + AC3 non-default at 5.1 → AC3
wins, job_type=copy. new case for 7.1 TrueHD beats 5.1 AC3 default.
every other existing test still holds.
2026-04-14 10:23:49 +02:00

61 lines
2.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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, etc.) transcodes to EAC3.
//
// Why EAC3 as the single target and not FLAC for lossless sources:
// - FLAC movie audio is ~2-3× the size of EAC3 at transparent quality
// - FLAC doesn't preserve Atmos spatial metadata (TrueHD Atmos → FLAC
// silently drops Atmos); EAC3 JOC can carry it
// - EAC3 passes through to AVRs over ARC/eARC; FLAC-to-AVR isn't a
// thing on Apple TV
// - One target per incompatible source = no container-conditional
// mapping surprises
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",
]);
export function isAppleCompatible(codec: string): boolean {
return APPLE_COMPATIBLE_AUDIO.has(codec.toLowerCase());
}
/**
* Maps (codec, profile, container) → target codec for transcoding.
* Returns null when the source is already Apple-compatible.
*
* profile and container are no longer consulted (kept in the signature
* for call-site stability and potential future per-source tuning).
*/
export function transcodeTarget(codec: string, _profile: string | null, _container: string | null): string | null {
if (isAppleCompatible(codec.toLowerCase())) return null;
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";
}