Add embedded audio/subtitle track selection for multi-track files

Files with multiple embedded tracks (e.g. MKV from media sources or
downloads) previously exposed none of them: mpv's track-list was never
read, aid was never set, and sid was only ever no/auto. The quality
selector showed no Audio or Subtitles tab for such files.

MPVClient now observes track-list/aid/sid and delivers a coalesced,
leniently decoded [MPVTrack] snapshot; selection state is derived from
the reported "selected" flags. Embedded tracks are switched live via
aid/sid (no reload), surfaced in the existing Audio/Subtitles sections
alongside external captions on iOS, tvOS and macOS. External sub-add/
audio-add tracks are filtered out to avoid double-listing. User picks
are sticky across same-video reloads (quality switch, audio mode,
retries) and preferred audio/subtitle languages auto-select a matching
embedded track once per load, with external captions taking precedence.
This commit is contained in:
Arkadiusz Fal
2026-07-29 22:14:46 +02:00
parent 150ebf42a4
commit 67ed8c322b
17 changed files with 841 additions and 11 deletions

View File

@@ -91,6 +91,15 @@ struct PlayerControlsView: View {
var onCaptionSelected: ((Caption?) -> Void)? = nil
/// Callback when stream is selected (for quality selector integration)
var onStreamSelected: ((Stream, Stream?) -> Void)? = nil
/// Embedded (in-container) tracks reported by mpv for the loaded file
var embeddedAudioTracks: [MPVTrack] = []
var embeddedSubtitleTracks: [MPVTrack] = []
var currentEmbeddedAudioTrackID: Int? = nil
var currentEmbeddedSubtitleTrackID: Int? = nil
/// Callback when an embedded audio track is selected
var onEmbeddedAudioTrackSelected: ((Int) -> Void)? = nil
/// Callback when an embedded subtitle track is selected (nil = off)
var onEmbeddedSubtitleTrackSelected: ((Int?) -> Void)? = nil
/// Current panscan value (0.0 = fit, 1.0 = fill)
var panscanValue: Double = 0.0
@@ -285,9 +294,14 @@ struct PlayerControlsView: View {
currentCaption: currentCaption,
initialTab: .subtitles,
showTabPicker: false,
embeddedSubtitleTracks: embeddedSubtitleTracks,
currentEmbeddedSubtitleTrackID: currentEmbeddedSubtitleTrackID,
onStreamSelected: { _, _ in },
onCaptionSelected: { caption in
onCaptionSelected?(caption)
},
onEmbeddedSubtitleTrackSelected: { trackID in
onEmbeddedSubtitleTrackSelected?(trackID)
}
)
}
@@ -317,10 +331,15 @@ struct PlayerControlsView: View {
currentAudioStream: currentAudioStream,
initialTab: .audio,
showTabPicker: false,
embeddedAudioTracks: embeddedAudioTracks,
currentEmbeddedAudioTrackID: currentEmbeddedAudioTrackID,
onStreamSelected: { stream, audioStream in
onStreamSelected?(stream, audioStream)
},
onCaptionSelected: { _ in }
onCaptionSelected: { _ in },
onEmbeddedAudioTrackSelected: { trackID in
onEmbeddedAudioTrackSelected?(trackID)
}
)
}
@@ -379,6 +398,10 @@ struct PlayerControlsView: View {
availableStreams: availableStreams,
currentStream: currentStream,
currentAudioStream: currentAudioStream,
embeddedAudioTracks: embeddedAudioTracks,
embeddedSubtitleTracks: embeddedSubtitleTracks,
currentEmbeddedAudioTrackID: currentEmbeddedAudioTrackID,
currentEmbeddedSubtitleTrackID: currentEmbeddedSubtitleTrackID,
panscanValue: panscanValue,
isPanscanAllowed: isPanscanAllowed,
isAutoPlayNextEnabled: appEnvironment?.settingsManager.queueAutoPlayNext ?? true,