From 3339e8cb1fbcb2d50024c10f9a09bef31aacdf87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toni=20F=C3=B6rster?= Date: Sat, 14 Sep 2024 10:26:51 +0200 Subject: [PATCH] improved subtitle handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fix subtitle disabling not working - make subtitle adding/removing async - make subtitle menu non blocking Signed-off-by: Toni Förster --- Model/Player/Backends/MPVBackend.swift | 34 ++++++++++++++------ Model/Player/Backends/MPVClient.swift | 26 +++++++-------- Shared/Player/Controls/ControlsOverlay.swift | 33 ++++++++++++++++--- 3 files changed, 65 insertions(+), 28 deletions(-) diff --git a/Model/Player/Backends/MPVBackend.swift b/Model/Player/Backends/MPVBackend.swift index bb758436..203adaa2 100644 --- a/Model/Player/Backends/MPVBackend.swift +++ b/Model/Player/Backends/MPVBackend.swift @@ -23,15 +23,14 @@ final class MPVBackend: PlayerBackend { var stream: Stream? var video: Video? - var captions: Captions? { didSet { - guard let captions else { - if client?.areSubtitlesAdded == true { - client?.removeSubs() + var captions: Captions? { + didSet { + Task { + await handleCaptionsChange() } - return } - addSubTrack(captions.url) - }} + } + var currentTime: CMTime? var loadedVideo = false @@ -617,10 +616,14 @@ final class MPVBackend: PlayerBackend { } func addSubTrack(_ url: URL) { - if client?.areSubtitlesAdded == true { - client?.removeSubs() + Task { + if let areSubtitlesAdded = client?.areSubtitlesAdded { + if await areSubtitlesAdded() { + await client?.removeSubs() + } + } + await client?.addSubTrack(url) } - client?.addSubTrack(url) } func setVideoToAuto() { @@ -684,6 +687,17 @@ final class MPVBackend: PlayerBackend { } } + private func handleCaptionsChange() async { + guard let captions else { + if let isSubtitlesAdded = client?.areSubtitlesAdded, await isSubtitlesAdded() { + await client?.removeSubs() + } + return + } + + addSubTrack(captions.url) + } + private func handlePropertyChange(_ name: String, _ property: mpv_event_property) { switch name { case "pause": diff --git a/Model/Player/Backends/MPVClient.swift b/Model/Player/Backends/MPVClient.swift index b257a4f7..8279878b 100644 --- a/Model/Player/Backends/MPVClient.swift +++ b/Model/Player/Backends/MPVClient.swift @@ -349,21 +349,15 @@ final class MPVClient: ObservableObject { return Int(fps.rounded()) } - var areSubtitlesAdded: Bool { + func areSubtitlesAdded() async -> Bool { guard !mpv.isNil else { return false } - // Retrieve the number of tracks - let trackCount = getInt("track-list/count") + let trackCount = await Task(operation: { getInt("track-list/count") }).value guard trackCount > 0 else { return false } for index in 0 ..< trackCount { - // Get the type of each track - if let trackType = getString("track-list/\(index)/type"), trackType == "sub" { - // Check if the subtitle track is currently selected - let selected = getInt("track-list/\(index)/selected") - if selected == 1 { - return true - } + if let trackType = await Task(operation: { getString("track-list/\(index)/type") }).value, trackType == "sub" { + return true } } return false @@ -539,12 +533,16 @@ final class MPVClient: ObservableObject { command("video-add", args: [url.absoluteString]) } - func addSubTrack(_ url: URL) { - command("sub-add", args: [url.absoluteString]) + func addSubTrack(_ url: URL) async { + await Task { + command("sub-add", args: [url.absoluteString]) + }.value } - func removeSubs() { - command("sub-remove") + func removeSubs() async { + await Task { + command("sub-remove") + }.value } func setVideoToAuto() { diff --git a/Shared/Player/Controls/ControlsOverlay.swift b/Shared/Player/Controls/ControlsOverlay.swift index 398520db..5ba0f816 100644 --- a/Shared/Player/Controls/ControlsOverlay.swift +++ b/Shared/Player/Controls/ControlsOverlay.swift @@ -5,6 +5,8 @@ struct ControlsOverlay: View { @ObservedObject private var player = PlayerModel.shared private var model = PlayerControlsModel.shared + @State private var availableCaptions: [Captions] = [] + @State private var isLoadingCaptions = true @State private var contentSize: CGSize = .zero @Default(.showMPVPlaybackStats) private var showMPVPlaybackStats @@ -335,7 +337,6 @@ struct ControlsOverlay: View { Image(systemName: "text.bubble") if let captions = captionsBinding.wrappedValue, let language = LanguageCodes(rawValue: captions.code) - { Text("\(language.description.capitalized) (\(language.rawValue))") .foregroundColor(.accentColor) @@ -380,17 +381,16 @@ struct ControlsOverlay: View { .contextMenu { Button("Disabled") { captionsBinding.wrappedValue = nil } - ForEach(player.currentVideo?.captions ?? []) { caption in + ForEach(availableCaptions) { caption in Button(caption.description) { captionsBinding.wrappedValue = caption } } Button("Cancel", role: .cancel) {} } - #endif } @ViewBuilder private var captionsPicker: some View { - let captions = player.currentVideo?.captions ?? [] + let captions = availableCaptions Picker("Captions", selection: captionsBinding) { if captions.isEmpty { Text("Not available").tag(Captions?.none) @@ -402,6 +402,31 @@ struct ControlsOverlay: View { } } .disabled(captions.isEmpty) + .onAppear { + loadCaptions() + } + } + + private func loadCaptions() { + isLoadingCaptions = true + + // Fetch captions asynchronously + Task { + let fetchedCaptions = await fetchCaptions() + await MainActor.run { + // Update state on the main thread + self.availableCaptions = fetchedCaptions + self.isLoadingCaptions = false + } + } + } + + private func fetchCaptions() async -> [Captions] { + // Access currentVideo from the main actor context + await MainActor.run { + // Safely access the main actor-isolated currentVideo property + player.currentVideo?.captions ?? [] + } } private var captionsBinding: Binding {