package playback import ( "strings" "github.com/Silo-Server/silo-server/internal/lang" "github.com/Silo-Server/silo-server/internal/models" "github.com/Silo-Server/silo-server/internal/userstore" ) // OriginalLanguageSentinel is the value stored in audio language preference // columns to mean "use the media item's original language." It is resolved // to a concrete language code in the playback handler before reaching // SelectAudioTrack. const OriginalLanguageSentinel = "original" // AudioTrackPreference holds a per-series audio track preference. type AudioTrackPreference struct { AudioTrackIndex int AudioLanguage string TrackSignature *userstore.AudioTrackSignature } func langMatch(a, b string) bool { if a == "" || b == "" { return false } return lang.Canonical(a) == lang.Canonical(b) } // SelectAudioTrack determines which audio track to use based on preferences. // // Priority: // 1. Series preference exact track signature // 2. Series preference index (if track exists at that index with matching language) // 3. Series preference language (first track matching that language) // 4. Profile preferred language (first track matching) // 5. File's default track (first track with Default: true) // 6. First track (index 0) func SelectAudioTrack(tracks []models.AudioTrack, preferredLang string, seriesPref *AudioTrackPreference) int { if len(tracks) == 0 { return 0 } // 1. Series preference: try exact signature match first. if seriesPref != nil { if idx := findExactAudioTrack(tracks, seriesPref.TrackSignature); idx >= 0 { return idx } // 2. Series preference: try exact index+language match. if seriesPref.AudioTrackIndex >= 0 && seriesPref.AudioTrackIndex < len(tracks) { if langMatch(tracks[seriesPref.AudioTrackIndex].Language, seriesPref.AudioLanguage) { return seriesPref.AudioTrackIndex } } // 3. Series preference: fall back to language match. if seriesPref.AudioLanguage != "" { for i, t := range tracks { if langMatch(t.Language, seriesPref.AudioLanguage) { return i } } } } // 4. Profile language preference. if preferredLang != "" { for i, t := range tracks { if langMatch(t.Language, preferredLang) { return i } } } // 5. File's default track. for i, t := range tracks { if t.Default { return i } } // 6. First track. return 0 } // BrowserSupportsAudioCodec returns true if the given audio codec can be // played natively by web browsers without transcoding. func BrowserSupportsAudioCodec(codec string) bool { switch strings.ToLower(codec) { case "aac", "mp3", "opus", "vorbis", "flac": return true default: return false } }