diff --git a/JellyfinPlayer.xcodeproj/project.pbxproj b/JellyfinPlayer.xcodeproj/project.pbxproj index de8e65bf..87aeb1c6 100644 --- a/JellyfinPlayer.xcodeproj/project.pbxproj +++ b/JellyfinPlayer.xcodeproj/project.pbxproj @@ -383,6 +383,7 @@ E1E5D5442783BB5100692DFE /* ItemDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D5432783BB5100692DFE /* ItemDetailsView.swift */; }; E1E5D5462783C28100692DFE /* CinematicItemAboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D5452783C28100692DFE /* CinematicItemAboutView.swift */; }; E1E5D5492783CDD700692DFE /* OverlaySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D5472783CCF900692DFE /* OverlaySettingsView.swift */; }; + E1E5D54C2783E27200692DFE /* ExperimentalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D54B2783E27200692DFE /* ExperimentalSettingsView.swift */; }; E1F0204E26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; }; E1F0204F26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; }; E1FA2F7427818A8800B4C270 /* SmallMenuOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FA2F7327818A8800B4C270 /* SmallMenuOverlay.swift */; }; @@ -666,6 +667,7 @@ E1E5D5432783BB5100692DFE /* ItemDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailsView.swift; sourceTree = ""; }; E1E5D5452783C28100692DFE /* CinematicItemAboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicItemAboutView.swift; sourceTree = ""; }; E1E5D5472783CCF900692DFE /* OverlaySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlaySettingsView.swift; sourceTree = ""; }; + E1E5D54B2783E27200692DFE /* ExperimentalSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExperimentalSettingsView.swift; sourceTree = ""; }; E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerJumpLength.swift; sourceTree = ""; }; E1FA2F7327818A8800B4C270 /* SmallMenuOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmallMenuOverlay.swift; sourceTree = ""; }; E1FCD08726C35A0D007C8DCF /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; }; @@ -1295,10 +1297,9 @@ 53DF641D263D9C0600A7CD1A /* LibraryView.swift */, 53892771263C8C6F0035E14B /* LoadingView.swift */, 5389276F263C25230035E14B /* NextUpView.swift */, - E1E5D5472783CCF900692DFE /* OverlaySettingsView.swift */, + E1E5D54A2783E26100692DFE /* SettingsView */, E173DA4F26D048D600CC4EB7 /* ServerDetailView.swift */, E13DD3E427177D15009D4DAF /* ServerListView.swift */, - 539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */, E13DD3FB2717EAE8009D4DAF /* UserListView.swift */, E13DD3F4271793BB009D4DAF /* UserSignInView.swift */, E193D5452719418B00900D82 /* VideoPlayer */, @@ -1449,6 +1450,16 @@ path = CinematicItemView; sourceTree = ""; }; + E1E5D54A2783E26100692DFE /* SettingsView */ = { + isa = PBXGroup; + children = ( + E1E5D54B2783E27200692DFE /* ExperimentalSettingsView.swift */, + E1E5D5472783CCF900692DFE /* OverlaySettingsView.swift */, + 539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */, + ); + path = SettingsView; + sourceTree = ""; + }; E1FCD08E26C466F3007C8DCF /* Errors */ = { isa = PBXGroup; children = ( @@ -2131,6 +2142,7 @@ C4BE0769271FC164003F4AD1 /* TVLibrariesView.swift in Sources */, E1267D3E271A1F46003C492E /* PreferenceUIHostingController.swift in Sources */, 6220D0BA26D6092100B8E046 /* FilterCoordinator.swift in Sources */, + E1E5D54C2783E27200692DFE /* ExperimentalSettingsView.swift in Sources */, 6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */, 62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */, 5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */, diff --git a/JellyfinPlayer/Views/SettingsView/ExperimentalSettingsView.swift b/JellyfinPlayer/Views/SettingsView/ExperimentalSettingsView.swift new file mode 100644 index 00000000..179b1889 --- /dev/null +++ b/JellyfinPlayer/Views/SettingsView/ExperimentalSettingsView.swift @@ -0,0 +1,28 @@ +// + /* + * SwiftFin is subject to the terms of the Mozilla Public + * License, v2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * Copyright 2021 Aiden Vigue & Jellyfin Contributors + */ + +import Defaults +import SwiftUI + +struct ExperimentalSettingsView: View { + + @Default(.Experimental.syncSubtitleStateWithAdjacent) var syncSubtitleStateWithAdjacent + + var body: some View { + Form { + Section { + + Toggle("Sync Subtitles with Adjacent Episodes", isOn: $syncSubtitleStateWithAdjacent) + + } header: { + Text("Experimental") + } + } + } +} diff --git a/JellyfinPlayer/Views/OverlaySettingsView.swift b/JellyfinPlayer/Views/SettingsView/OverlaySettingsView.swift similarity index 100% rename from JellyfinPlayer/Views/OverlaySettingsView.swift rename to JellyfinPlayer/Views/SettingsView/OverlaySettingsView.swift diff --git a/JellyfinPlayer/Views/SettingsView.swift b/JellyfinPlayer/Views/SettingsView/SettingsView.swift similarity index 94% rename from JellyfinPlayer/Views/SettingsView.swift rename to JellyfinPlayer/Views/SettingsView/SettingsView.swift index 3bb528fd..0cbe663d 100644 --- a/JellyfinPlayer/Views/SettingsView.swift +++ b/JellyfinPlayer/Views/SettingsView/SettingsView.swift @@ -97,7 +97,17 @@ struct SettingsView: View { .foregroundColor(.primary) Spacer() Text(overlayType.label) - + Image(systemName: "chevron.right") + } + } + + Button { + settingsRouter.route(to: \.experimentalSettings) + } label: { + HStack { + Text("Experimental") + .foregroundColor(.primary) + Spacer() Image(systemName: "chevron.right") } } diff --git a/Shared/Coordinators/SettingsCoordinator.swift b/Shared/Coordinators/SettingsCoordinator.swift index 0b7da98e..6d91d12a 100644 --- a/Shared/Coordinators/SettingsCoordinator.swift +++ b/Shared/Coordinators/SettingsCoordinator.swift @@ -18,6 +18,7 @@ final class SettingsCoordinator: NavigationCoordinatable { @Root var start = makeStart @Route(.push) var serverDetail = makeServerDetail @Route(.push) var overlaySettings = makeOverlaySettings + @Route(.push) var experimentalSettings = makeExperimentalSettings @ViewBuilder func makeServerDetail() -> some View { let viewModel = ServerDetailViewModel(server: SessionManager.main.currentLogin.server) @@ -27,6 +28,10 @@ final class SettingsCoordinator: NavigationCoordinatable { @ViewBuilder func makeOverlaySettings() -> some View { OverlaySettingsView() } + + @ViewBuilder func makeExperimentalSettings() -> some View { + ExperimentalSettingsView() + } @ViewBuilder func makeStart() -> some View { let viewModel = SettingsViewModel(server: SessionManager.main.currentLogin.server, user: SessionManager.main.currentLogin.user) diff --git a/Shared/ViewModels/VideoPlayerViewModel.swift b/Shared/ViewModels/VideoPlayerViewModel.swift index f810ce62..dc2ee91c 100644 --- a/Shared/ViewModels/VideoPlayerViewModel.swift +++ b/Shared/ViewModels/VideoPlayerViewModel.swift @@ -29,9 +29,23 @@ final class VideoPlayerViewModel: ViewModel { @Published var leftLabelText: String = "--:--" @Published var rightLabelText: String = "--:--" @Published var playbackSpeed: PlaybackSpeed = .one - @Published var subtitlesEnabled: Bool + @Published var subtitlesEnabled: Bool { + didSet { + if syncSubtitleStateWithAdjacent { + previousItemVideoPlayerViewModel?.matchSubtitlesEnabled(with: self) + nextItemVideoPlayerViewModel?.matchSubtitlesEnabled(with: self) + } + } + } @Published var selectedAudioStreamIndex: Int - @Published var selectedSubtitleStreamIndex: Int + @Published var selectedSubtitleStreamIndex: Int { + didSet { + if syncSubtitleStateWithAdjacent { + previousItemVideoPlayerViewModel?.matchSubtitleStream(with: self) + nextItemVideoPlayerViewModel?.matchSubtitleStream(with: self) + } + } + } @Published var previousItemVideoPlayerViewModel: VideoPlayerViewModel? @Published var nextItemVideoPlayerViewModel: VideoPlayerViewModel? @Published var jumpBackwardLength: VideoPlayerJumpLength { @@ -75,6 +89,9 @@ final class VideoPlayerViewModel: ViewModel { let overlayType: OverlayType let jumpGesturesEnabled: Bool + // MARK: Experimental + let syncSubtitleStateWithAdjacent: Bool + // Full response kept for convenience let response: PlaybackInfoResponse @@ -137,6 +154,8 @@ final class VideoPlayerViewModel: ViewModel { self.jumpGesturesEnabled = Defaults[.jumpGesturesEnabled] self.shouldShowJumpButtonsInOverlayMenu = Defaults[.shouldShowJumpButtonsInOverlayMenu] + self.syncSubtitleStateWithAdjacent = Defaults[.Experimental.syncSubtitleStateWithAdjacent] + super.init() self.sliderPercentage = (item.userData?.playedPercentage ?? 0) / 100