Skip to main content

player_switch_audio_track

Function player_switch_audio_track 

Source
pub async fn player_switch_audio_track(
    player: State<'_, PlayerStateWrapper>,
    repository_manager: State<'_, RepositoryManagerWrapper>,
    repository_handle: String,
    stream_index: i32,
    array_index: i32,
    use_html5: bool,
    current_position: Option<f64>,
    media_source_id: Option<String>,
) -> Result<AudioTrackSwitchResponse, String>
Expand description

Switch audio track. Note: Frontend should handle saving series preferences after this command succeeds

What decides the route is whether the stream in front of the engine carries the requested track at all — see determine_audio_track_switch_strategy:

  • An HTML5 <video> element has no track-selection API, so the stream is always re-opened at the chosen AudioStreamIndex and the frontend seeks the reloaded element back to position.
  • A native backend playing a direct play holds the source file with every track in it, so ExoPlayer selects in place by track-group index.
  • A native backend playing a transcode does not. Jellyfin builds a transcode around one AudioStreamIndex, so the alternate tracks are not in the stream; the switch has to re-open it, which this command does itself and resumes at current_position.

That last case is a bug fix, and it was the common case on Android: any source whose default audio codec the device cannot decode is transcoded, so ExoPlayer saw Audio tracks: 1 while the menu listed every track in the file. The old code called setAudioTrack(n) regardless, which indexes ExoPlayer’s audio track groups, found nothing at n, warned Invalid audio track index and dropped the request — the default track just kept playing, with nothing in the UI saying so.

libmpv implements neither selection nor reload here — it is the audio-only backend and leaves PlayerBackend::set_audio_track at its not_implemented() default, which is why IR-019 is met by these paths rather than by MPV.

TRACES: UR-021, UR-005 | IR-019, DR-024, DR-258