From 4db02b2638cd839788fdd5fedc45b240564b2e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toni=20F=C3=B6rster?= Date: Mon, 20 May 2024 02:49:32 +0200 Subject: [PATCH] Improved Captions handling New options for captions in `Settings-Player`: - Always show captions - Default language User can now select whether they want to show captions automatically when the video starts, and select the language. Captions selector now shows proper name -> `English (en)` instead of only `en` --- Model/Player/Backends/MPVBackend.swift | 17 ++-- Model/Player/Backends/MPVClient.swift | 8 ++ Shared/Defaults.swift | 2 + Shared/LanguageCodes.swift | 107 +++++++++++++++++++++++++ Shared/Player/PlaybackSettings.swift | 7 +- Shared/Settings/PlayerSettings.swift | 28 ++++++- Yattee.xcodeproj/project.pbxproj | 8 ++ 7 files changed, 168 insertions(+), 9 deletions(-) create mode 100644 Shared/LanguageCodes.swift diff --git a/Model/Player/Backends/MPVBackend.swift b/Model/Player/Backends/MPVBackend.swift index 01a9adb7..a77806c6 100644 --- a/Model/Player/Backends/MPVBackend.swift +++ b/Model/Player/Backends/MPVBackend.swift @@ -217,9 +217,15 @@ final class MPVBackend: PlayerBackend { #endif var captions: Captions? - if let captionsLanguageCode = Defaults[.captionsLanguageCode] { - captions = video.captions.first { $0.code == captionsLanguageCode } ?? - video.captions.first { $0.code.contains(captionsLanguageCode) } + + if Defaults[.captionsAutoShow] == true { + let captionsLanguageCode = Defaults[.captionsDefaultLanguageCode] + if !captionsLanguageCode.isEmpty { + captions = video.captions.first { $0.code == captionsLanguageCode } ?? + video.captions.first { $0.code.contains(captionsLanguageCode) } + } + } else { + captions = nil } let updateCurrentStream = { @@ -254,9 +260,8 @@ final class MPVBackend: PlayerBackend { self.startClientUpdates() - // Captions should only be displayed when selected by the user, - // not when the video starts. So, we remove them. - self.client?.removeSubs() + if Defaults[.captionsAutoShow] { self.client?.setSubToAuto() } else { self.client?.setSubToNo() } + PlayerModel.shared.captions = self.captions if !preservingTime, !upgrading, diff --git a/Model/Player/Backends/MPVClient.swift b/Model/Player/Backends/MPVClient.swift index 75f9128c..11066345 100644 --- a/Model/Player/Backends/MPVClient.swift +++ b/Model/Player/Backends/MPVClient.swift @@ -405,6 +405,14 @@ final class MPVClient: ObservableObject { setString("video", "no") } + func setSubToAuto() { + setString("sub", "auto") + } + + func setSubToNo() { + setString("sub", "no") + } + var tracksCount: Int { Int(getString("track-list/count") ?? "-1") ?? -1 } diff --git a/Shared/Defaults.swift b/Shared/Defaults.swift index c2cc9d23..0529ad27 100644 --- a/Shared/Defaults.swift +++ b/Shared/Defaults.swift @@ -301,7 +301,9 @@ extension Defaults.Keys { static let lastPlayed = Key("lastPlayed") static let activeBackend = Key("activeBackend", default: .mpv) + static let captionsAutoShow = Key("captionsAutoShow", default: false) static let captionsLanguageCode = Key("captionsLanguageCode") + static let captionsDefaultLanguageCode = Key("captionsDefaultLanguageCode", default: LanguageCodes.English.rawValue) static let lastUsedPlaylistID = Key("lastPlaylistID") static let lastAccountIsPublic = Key("lastAccountIsPublic", default: false) diff --git a/Shared/LanguageCodes.swift b/Shared/LanguageCodes.swift new file mode 100644 index 00000000..2a271995 --- /dev/null +++ b/Shared/LanguageCodes.swift @@ -0,0 +1,107 @@ +enum LanguageCodes: String, CaseIterable { + case Afrikaans = "af" + case Arabic = "ar" + case Azerbaijani = "az" + case Bengali = "bn" + case Catalan = "ca" + case Czech = "cs" + case Welsh = "cy" + case Danish = "da" + case German = "de" + case Greek = "el" + case English = "en" + case Spanish = "es" + case Persian = "fa" + case Finnish = "fi" + case Filipino = "fil" + case French = "fr" + case Irish = "ga" + case Hebrew = "he" + case Hindi = "hi" + case Hungarian = "hu" + case Indonesian = "id" + case Italian = "it" + case Japanese = "ja" + case Javanese = "jv" + case Korean = "ko" + case Lithuanian = "lt" + case Malay = "ms" + case Maltese = "mt" + case Dutch = "nl" + case Norwegian = "no" + case Polish = "pl" + case Portuguese = "pt" + case Romanian = "ro" + case Russian = "ru" + case Slovak = "sk" + case Slovene = "sl" + case Swedish = "sv" + case Swahili = "sw" + case Thai = "th" + case Tagalog = "tl" + case Turkish = "tr" + case Ukrainian = "uk" + case Urdu = "ur" + case Uzbek = "uz" + case Vietnamese = "vi" + case Xhosa = "xh" + case Chinese = "zh" + case Zulu = "zu" + + var description: String { + switch self { + case .Afrikaans: return "Afrikaans" + case .Arabic: return "Arabic" + case .Azerbaijani: return "Azerbaijani" + case .Bengali: return "Bengali" + case .Catalan: return "Catalan" + case .Czech: return "Czech" + case .Welsh: return "Welsh" + case .Danish: return "Danish" + case .German: return "German" + case .Greek: return "Greek" + case .English: return "English" + case .Spanish: return "Spanish" + case .Persian: return "Persian" + case .Finnish: return "Finnish" + case .Filipino: return "Filipino" + case .French: return "French" + case .Irish: return "Irish" + case .Hebrew: return "Hebrew" + case .Hindi: return "Hindi" + case .Hungarian: return "Hungarian" + case .Indonesian: return "Indonesian" + case .Italian: return "Italian" + case .Japanese: return "Japanese" + case .Javanese: return "Javanese" + case .Korean: return "Korean" + case .Lithuanian: return "Lithuanian" + case .Malay: return "Malay" + case .Maltese: return "Maltese" + case .Dutch: return "Dutch" + case .Norwegian: return "Norwegian" + case .Polish: return "Polish" + case .Portuguese: return "Portuguese" + case .Romanian: return "Romanian" + case .Russian: return "Russian" + case .Slovak: return "Slovak" + case .Slovene: return "Slovene" + case .Swedish: return "Swedish" + case .Swahili: return "Swahili" + case .Thai: return "Thai" + case .Tagalog: return "Tagalog" + case .Turkish: return "Turkish" + case .Ukrainian: return "Ukrainian" + case .Urdu: return "Urdu" + case .Uzbek: return "Uzbek" + case .Vietnamese: return "Vietnamese" + case .Xhosa: return "Xhosa" + case .Chinese: return "Chinese" + case .Zulu: return "Zulu" + } + } + + static func languageName(for code: String) -> String { + return LanguageCodes(rawValue: code)?.description ?? "Unknown" + } +} diff --git a/Shared/Player/PlaybackSettings.swift b/Shared/Player/PlaybackSettings.swift index 66638b11..765314f8 100644 --- a/Shared/Player/PlaybackSettings.swift +++ b/Shared/Player/PlaybackSettings.swift @@ -1,3 +1,4 @@ +import Combine import Defaults import SwiftUI @@ -393,8 +394,10 @@ struct PlaybackSettings: View { } label: { HStack(spacing: 4) { Image(systemName: "text.bubble") - if let captions = player.captions { - Text(captions.code) + if let captions = player.captions, + let language = LanguageCodes(rawValue: captions.code) + { + Text("\(language.description.capitalized) (\(language.rawValue))") .foregroundColor(.accentColor) } } diff --git a/Shared/Settings/PlayerSettings.swift b/Shared/Settings/PlayerSettings.swift index eaa890f9..9c5e6c9f 100644 --- a/Shared/Settings/PlayerSettings.swift +++ b/Shared/Settings/PlayerSettings.swift @@ -30,12 +30,16 @@ struct PlayerSettings: View { @Default(.enableReturnYouTubeDislike) private var enableReturnYouTubeDislike + @Default(.showRelated) private var showRelated @Default(.showInspector) private var showInspector + @Default(.showChapters) private var showChapters @Default(.showChapterThumbnails) private var showThumbnails @Default(.showChapterThumbnailsOnlyWhenDifferent) private var showThumbnailsOnlyWhenDifferent @Default(.expandChapters) private var expandChapters - @Default(.showRelated) private var showRelated + + @Default(.captionsAutoShow) private var captionsAutoShow + @Default(.captionsDefaultLanguageCode) private var captionsDefaultLanguageCode @ObservedObject private var accounts = AccountsModel.shared @@ -93,7 +97,14 @@ struct PlayerSettings: View { inspectorVisibilityPicker #endif } + #endif + Section(header: SettingsHeader(text: "Captions".localized())) { + showCaptionsAutoShowToggle + captionDefaultLanguagePicker + } + + #if !os(tvOS) Section(header: SettingsHeader(text: "Chapters".localized())) { showChaptersToggle showThumbnailsToggle @@ -290,6 +301,21 @@ struct PlayerSettings: View { #endif } + private var showCaptionsAutoShowToggle: some View { + Toggle("Always show captions", isOn: $captionsAutoShow) + } + + private var captionDefaultLanguagePicker: some View { + Picker("Default language", selection: $captionsDefaultLanguageCode) { + ForEach(LanguageCodes.allCases, id: \.self) { language in + Text("\(language.description.capitalized) (\(language.rawValue))").tag(language.rawValue) + } + } + #if os(macOS) + .labelsHidden() + #endif + } + private var showChaptersToggle: some View { Toggle("Show chapters", isOn: $showChapters) } diff --git a/Yattee.xcodeproj/project.pbxproj b/Yattee.xcodeproj/project.pbxproj index 5691e4bb..dfcdc1d1 100644 --- a/Yattee.xcodeproj/project.pbxproj +++ b/Yattee.xcodeproj/project.pbxproj @@ -1076,6 +1076,9 @@ E258F38A2BF61BD2005B8C28 /* URLTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = E258F3892BF61BD2005B8C28 /* URLTester.swift */; }; E258F38B2BF61BD2005B8C28 /* URLTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = E258F3892BF61BD2005B8C28 /* URLTester.swift */; }; E258F38C2BF61BD2005B8C28 /* URLTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = E258F3892BF61BD2005B8C28 /* URLTester.swift */; }; + E27568B92BFAAC2000BDF0AF /* LanguageCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E27568B82BFAAC2000BDF0AF /* LanguageCodes.swift */; }; + E27568BA2BFAAC2000BDF0AF /* LanguageCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E27568B82BFAAC2000BDF0AF /* LanguageCodes.swift */; }; + E27568BB2BFAAC2000BDF0AF /* LanguageCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E27568B82BFAAC2000BDF0AF /* LanguageCodes.swift */; }; FA97174C2A494700001FF53D /* MPVKit in Frameworks */ = {isa = PBXBuildFile; productRef = FA97174B2A494700001FF53D /* MPVKit */; }; /* End PBXBuildFile section */ @@ -1547,6 +1550,7 @@ 3DA101AF287C30F50027D920 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = ""; }; E25028AF2BF790F5002CB9FC /* HTTPStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPStatus.swift; sourceTree = ""; }; E258F3892BF61BD2005B8C28 /* URLTester.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLTester.swift; sourceTree = ""; }; + E27568B82BFAAC2000BDF0AF /* LanguageCodes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageCodes.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -2295,6 +2299,7 @@ 37D2E0D328B67EFC00F64D52 /* Delay.swift */, 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */, E25028AF2BF790F5002CB9FC /* HTTPStatus.swift */, + E27568B82BFAAC2000BDF0AF /* LanguageCodes.swift */, 375B537828DF6CBB004C1D19 /* Localizable.strings */, 3729037D2739E47400EA99F6 /* MenuCommands.swift */, 37B7958F2771DAE0001CF27B /* OpenURLHandler.swift */, @@ -3225,6 +3230,7 @@ 37C3A24D272360470087A57A /* ChannelPlaylist+Fixtures.swift in Sources */, 37484C2D26FC844700287258 /* InstanceSettings.swift in Sources */, 37DD9DA32785BBC900539416 /* NoCommentsView.swift in Sources */, + E27568B92BFAAC2000BDF0AF /* LanguageCodes.swift in Sources */, 377A20A92693C9A2002842B8 /* TypedContentAccessors.swift in Sources */, 37484C3126FCB8F900287258 /* AccountValidator.swift in Sources */, 377ABC48286E5887009C986F /* Sequence+Unique.swift in Sources */, @@ -3405,6 +3411,7 @@ 371F2F1B269B43D300E4A7AB /* NavigationModel.swift in Sources */, 3756C2AB2861151C00E4B059 /* NetworkStateModel.swift in Sources */, 375EC95A289EEB8200751258 /* QualityProfileForm.swift in Sources */, + E27568BA2BFAAC2000BDF0AF /* LanguageCodes.swift in Sources */, 37001564271B1F250049C794 /* AccountsModel.swift in Sources */, 378FFBC528660172009E3FBE /* URLParser.swift in Sources */, 3761ABFE26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */, @@ -4000,6 +4007,7 @@ 37FB28432721B22200A57617 /* ContentItem.swift in Sources */, 37A7D6EF2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift in Sources */, 37D2E0D228B67DBC00F64D52 /* AnimationCompletionObserverModifier.swift in Sources */, + E27568BB2BFAAC2000BDF0AF /* LanguageCodes.swift in Sources */, 37A7D6EB2B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift in Sources */, 37AAF2A226741C97007FC770 /* FeedView.swift in Sources */, 37484C1B26FC837400287258 /* PlayerSettings.swift in Sources */,