mirror of
https://github.com/yattee/yattee.git
synced 2026-08-06 07:11:28 +00:00
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:
@@ -99,6 +99,9 @@ final class MPVBackend: PlayerBackend {
|
||||
private var currentCaption: Caption?
|
||||
private var pendingAutoplay: Bool = false
|
||||
|
||||
/// Tracks reported by mpv for the loaded file (embedded and external).
|
||||
private(set) var currentTracks: [MPVTrack] = []
|
||||
|
||||
// Retry mechanism: 4 attempts with increasing timeouts and delays
|
||||
// Attempt 1: 3s timeout, 1s delay | Attempt 2: 3s timeout, 3s delay
|
||||
// Attempt 3: 10s timeout, 5s delay | Attempt 4: 10s timeout, fail
|
||||
@@ -481,6 +484,10 @@ final class MPVBackend: PlayerBackend {
|
||||
currentCaption = nil
|
||||
mpvClient?.removeAllSubtitlesAsync()
|
||||
|
||||
// Clear stale track info; the observer repopulates it once the new file loads
|
||||
currentTracks = []
|
||||
delegate?.backend(self, didUpdateTracks: [])
|
||||
|
||||
// Reset first-frame tracking for new content
|
||||
renderView?.resetFirstFrameTracking()
|
||||
applyStreamFrameRateHint(stream)
|
||||
@@ -910,6 +917,22 @@ final class MPVBackend: PlayerBackend {
|
||||
currentCaption
|
||||
}
|
||||
|
||||
// MARK: - Embedded Tracks
|
||||
|
||||
/// Select an embedded audio track by mpv track id (live, no reload).
|
||||
func selectEmbeddedAudioTrack(_ trackID: Int) {
|
||||
mpvClient?.setAudioTrack(trackID)
|
||||
}
|
||||
|
||||
/// Select an embedded subtitle track by mpv track id (nil = off).
|
||||
/// Deliberately no `sub-remove`: external captions stay loaded so the user
|
||||
/// can switch back without a re-download. Selection is no longer
|
||||
/// represented by a Caption, so `currentCaption` is cleared.
|
||||
func selectEmbeddedSubtitleTrack(_ trackID: Int?) {
|
||||
currentCaption = nil
|
||||
mpvClient?.setSubtitleTrack(trackID)
|
||||
}
|
||||
|
||||
/// Update subtitle appearance settings on the active MPV instance.
|
||||
/// Call this after changing subtitle settings to apply them immediately without restarting playback.
|
||||
func updateSubtitleSettings() {
|
||||
@@ -1573,6 +1596,14 @@ extension MPVBackend: MPVClientDelegate {
|
||||
// Cache state is used for buffer display on seek bar - no action needed here
|
||||
}
|
||||
|
||||
nonisolated func mpvClient(_ client: MPVClient, didUpdateTrackList tracks: [MPVTrack]) {
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self else { return }
|
||||
self.currentTracks = tracks
|
||||
self.delegate?.backend(self, didUpdateTracks: tracks)
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func mpvClientDidEndFile(_ client: MPVClient, reason: MPVEndFileReason, errorCode: Int32, errorString: String?) {
|
||||
Task { @MainActor [weak self] in
|
||||
self?.handleEndFile(reason: reason, errorCode: errorCode, errorString: errorString)
|
||||
|
||||
Reference in New Issue
Block a user