add apple compatibility service: codec checks, transcode target mapping
This commit is contained in:
72
server/services/apple-compat.ts
Normal file
72
server/services/apple-compat.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
// 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',
|
||||
]);
|
||||
|
||||
// Lossless source codecs — get FLAC in MKV, EAC3 in MP4
|
||||
const LOSSLESS_CODECS = new Set([
|
||||
'dts', // DTS-HD MA reports as 'dts' with profile
|
||||
'truehd',
|
||||
]);
|
||||
|
||||
// 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';
|
||||
}
|
||||
Reference in New Issue
Block a user