fix(player): change the audio track, and load subtitles at all
Two faults, both present since v0.0.1, both found and confirmed on a device. Audio track (DR-258). Jellyfin builds a transcode around one AudioStreamIndex, so the alternate tracks are not in the stream that arrives — but the native path only ever called setAudioTrack(n), which indexes ExoPlayer's audio track *groups*. On Android that is the common case, since any source whose default audio codec the device cannot decode is transcoded: logcat showed ExoPlayer holding `Audio tracks: 1` while the menu listed every track in the file, so each selection warned `Invalid audio track index` and was dropped, leaving the default track playing with nothing in the UI saying so. determine_audio_track_switch_strategy now decides by whether the stream in front of the engine carries the track at all — a direct play still selects in place, a transcode is re-negotiated at the chosen index and resumed. Where it resumes is the player's answer rather than the UI's: the native path has no <video> element to read, so it sends no position, and defaulting that to zero re-opened the film at the beginning (caught on device before it shipped). Subtitles (DR-259). The URL was missing its `Stream.` route segment, so every sideloaded subtitle 404ed; since media3 1.5 a sideloaded text track only becomes a track group once its file is parsed, so 42 failed fetches left ExoPlayer with no text tracks and selection warned `available: 0`. Verified against a live server: the built URL answers 404, the corrected one 200. The tests that should have caught this asserted the shape of a mock helper that restated the format string instead of the URL the app requests — so the new test drives the repository itself, and failed red on the old URL.
This commit is contained in:
+28
-9
@@ -145,18 +145,37 @@ async playerSetAudioTrack(streamIndex: number) : Promise<PlayerStatus> {
|
||||
return await TAURI_INVOKE("player_set_audio_track", { streamIndex });
|
||||
},
|
||||
/**
|
||||
* Switch audio track - handles both HTML5 (stream reload) and native (direct switch)
|
||||
* Switch audio track.
|
||||
* Note: Frontend should handle saving series preferences after this command succeeds
|
||||
*
|
||||
* The split is the requirement: an HTML5 `<video>` element cannot be told to
|
||||
* change audio track, so the stream is re-opened at the chosen
|
||||
* `AudioStreamIndex` and the frontend seeks the reloaded element back to
|
||||
* `position`; a native backend (ExoPlayer) switches in place by track-group
|
||||
* index. libmpv implements neither — it is the audio-only backend here and
|
||||
* leaves `PlayerBackend::set_audio_track` at its `not_implemented()` default,
|
||||
* which is why IR-019 is met by these two paths rather than by MPV.
|
||||
* 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`]:
|
||||
*
|
||||
* TRACES: UR-021 | IR-019, DR-024
|
||||
* - 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
|
||||
*/
|
||||
async playerSwitchAudioTrack(repositoryHandle: string, streamIndex: number, arrayIndex: number, useHtml5: boolean, currentPosition: number | null, mediaSourceId: string | null) : Promise<AudioTrackSwitchResponse> {
|
||||
return await TAURI_INVOKE("player_switch_audio_track", { repositoryHandle, streamIndex, arrayIndex, useHtml5, currentPosition, mediaSourceId });
|
||||
|
||||
Reference in New Issue
Block a user