resume offset and live tv moved to experimental

This commit is contained in:
Ethan Pippin 2022-01-05 13:38:15 -07:00
parent c310b71963
commit 6a7c4c0a5b
11 changed files with 113 additions and 25 deletions

View File

@ -23,7 +23,7 @@ struct MediaPlayButtonRowView: View {
MediaViewActionButton(icon: "play.fill", scrollView: $wrappedScrollView) MediaViewActionButton(icon: "play.fill", scrollView: $wrappedScrollView)
} }
Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString()) left" : L10n.play) Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString() ?? "") left" : L10n.play)
.font(.caption) .font(.caption)
} }
VStack { VStack {

View File

@ -7,6 +7,7 @@
* Copyright 2021 Aiden Vigue & Jellyfin Contributors * Copyright 2021 Aiden Vigue & Jellyfin Contributors
*/ */
import Defaults
import Foundation import Foundation
import SwiftUI import SwiftUI
@ -14,6 +15,8 @@ struct LibraryListView: View {
@EnvironmentObject var mainCoordinator: MainCoordinator.Router @EnvironmentObject var mainCoordinator: MainCoordinator.Router
@EnvironmentObject var libraryListRouter: LibraryListCoordinator.Router @EnvironmentObject var libraryListRouter: LibraryListCoordinator.Router
@StateObject var viewModel = LibraryListViewModel() @StateObject var viewModel = LibraryListViewModel()
@Default(.Experimental.liveTVAlphaEnabled) var liveTVAlphaEnabled
var body: some View { var body: some View {
ScrollView { ScrollView {
@ -23,32 +26,55 @@ struct LibraryListView: View {
if library.collectionType ?? "" == "movies" || library.collectionType ?? "" == "tvshows" || library.collectionType ?? "" == "music" { if library.collectionType ?? "" == "movies" || library.collectionType ?? "" == "tvshows" || library.collectionType ?? "" == "music" {
EmptyView() EmptyView()
} else { } else {
Button() { if library.collectionType == "livetv" {
if library.collectionType == "livetv" { if liveTVAlphaEnabled {
self.mainCoordinator.root(\.liveTV) Button() {
} else { self.mainCoordinator.root(\.liveTV)
}
label: {
ZStack {
HStack {
Spacer()
VStack {
Text(library.name ?? "")
.foregroundColor(.white)
.font(.title2)
.fontWeight(.semibold)
}
Spacer()
}.padding(32)
}
.frame(minWidth: 100, maxWidth: .infinity)
.frame(height: 100)
}
.cornerRadius(10)
.shadow(radius: 5)
.padding(.bottom, 5)
}
} else {
Button() {
self.libraryListRouter.route(to: \.library, (viewModel: LibraryViewModel(), title: library.name ?? "")) self.libraryListRouter.route(to: \.library, (viewModel: LibraryViewModel(), title: library.name ?? ""))
} }
} label: {
label: { ZStack {
ZStack { HStack {
HStack { Spacer()
Spacer() VStack {
VStack { Text(library.name ?? "")
Text(library.name ?? "") .foregroundColor(.white)
.foregroundColor(.white) .font(.title2)
.font(.title2) .fontWeight(.semibold)
.fontWeight(.semibold) }
} Spacer()
Spacer() }.padding(32)
}.padding(32) }
.frame(minWidth: 100, maxWidth: .infinity)
.frame(height: 100)
} }
.frame(minWidth: 100, maxWidth: .infinity) .cornerRadius(10)
.frame(height: 100) .shadow(radius: 5)
.padding(.bottom, 5)
} }
.cornerRadius(10)
.shadow(radius: 5)
.padding(.bottom, 5)
} }
} }
} else { } else {

View File

@ -13,6 +13,7 @@ import SwiftUI
struct ExperimentalSettingsView: View { struct ExperimentalSettingsView: View {
@Default(.Experimental.syncSubtitleStateWithAdjacent) var syncSubtitleStateWithAdjacent @Default(.Experimental.syncSubtitleStateWithAdjacent) var syncSubtitleStateWithAdjacent
@Default(.Experimental.liveTVAlphaEnabled) var liveTVAlphaEnabled
var body: some View { var body: some View {
Form { Form {
@ -20,6 +21,8 @@ struct ExperimentalSettingsView: View {
Toggle("Sync Subtitles with Adjacent Episodes", isOn: $syncSubtitleStateWithAdjacent) Toggle("Sync Subtitles with Adjacent Episodes", isOn: $syncSubtitleStateWithAdjacent)
Toggle("Live TV (Alpha)", isOn: $liveTVAlphaEnabled)
} header: { } header: {
Text("Experimental") Text("Experimental")
} }

View File

@ -23,6 +23,7 @@ struct SettingsView: View {
@Default(.tvOSEpisodeItemCinematicView) var tvOSEpisodeItemCinematicView @Default(.tvOSEpisodeItemCinematicView) var tvOSEpisodeItemCinematicView
@Default(.tvOSMovieItemCinematicView) var tvOSMovieItemCinematicView @Default(.tvOSMovieItemCinematicView) var tvOSMovieItemCinematicView
@Default(.showPosterLabels) var showPosterLabels @Default(.showPosterLabels) var showPosterLabels
@Default(.resumeOffset) var resumeOffset
var body: some View { var body: some View {
GeometryReader { reader in GeometryReader { reader in
@ -80,6 +81,8 @@ struct SettingsView: View {
} }
} }
Toggle("Resume 5 Second Offset", isOn: $resumeOffset)
Toggle("Press Down for Menu", isOn: $downActionShowsMenu) Toggle("Press Down for Menu", isOn: $downActionShowsMenu)
Toggle("Confirm Close", isOn: $confirmClose) Toggle("Confirm Close", isOn: $confirmClose)

View File

@ -418,7 +418,15 @@ extension VLCPlayerViewController {
let startPercentage = newViewModel.item.userData?.playedPercentage ?? 0 let startPercentage = newViewModel.item.userData?.playedPercentage ?? 0
if startPercentage > 0 { if startPercentage > 0 {
newViewModel.sliderPercentage = startPercentage / 100 if viewModel.resumeOffset {
let videoDurationSeconds = Double(viewModel.item.runTimeTicks! / 10_000_000)
var startSeconds = round((startPercentage / 100) * videoDurationSeconds)
startSeconds = startSeconds.subtract(5, floor: 0)
let newStartPercentage = startSeconds / videoDurationSeconds
newViewModel.sliderPercentage = newStartPercentage
} else {
newViewModel.sliderPercentage = startPercentage / 100
}
} }
viewModel = newViewModel viewModel = newViewModel

View File

@ -395,6 +395,9 @@
E1D4BF8C2719F39F00A11E64 /* AppAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1D4BF802719D22800A11E64 /* AppAppearance.swift */; }; E1D4BF8C2719F39F00A11E64 /* AppAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1D4BF802719D22800A11E64 /* AppAppearance.swift */; };
E1D4BF8D2719F3A300A11E64 /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; }; E1D4BF8D2719F3A300A11E64 /* VideoPlayerJumpLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */; };
E1D4BF8F271A079A00A11E64 /* BasicAppSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1D4BF8E271A079A00A11E64 /* BasicAppSettingsView.swift */; }; E1D4BF8F271A079A00A11E64 /* BasicAppSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1D4BF8E271A079A00A11E64 /* BasicAppSettingsView.swift */; };
E1E00A35278628A40022235B /* DoubleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E00A34278628A40022235B /* DoubleExtensions.swift */; };
E1E00A36278628A40022235B /* DoubleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E00A34278628A40022235B /* DoubleExtensions.swift */; };
E1E00A37278628A40022235B /* DoubleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E00A34278628A40022235B /* DoubleExtensions.swift */; };
E1E48CC9271E6D410021A2F9 /* RefreshHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E48CC8271E6D410021A2F9 /* RefreshHelper.swift */; }; E1E48CC9271E6D410021A2F9 /* RefreshHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E48CC8271E6D410021A2F9 /* RefreshHelper.swift */; };
E1E5D5372783A52C00692DFE /* CinematicEpisodeItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D5362783A52C00692DFE /* CinematicEpisodeItemView.swift */; }; E1E5D5372783A52C00692DFE /* CinematicEpisodeItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D5362783A52C00692DFE /* CinematicEpisodeItemView.swift */; };
E1E5D5392783A56B00692DFE /* EpisodesRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D5382783A56B00692DFE /* EpisodesRowView.swift */; }; E1E5D5392783A56B00692DFE /* EpisodesRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E5D5382783A56B00692DFE /* EpisodesRowView.swift */; };
@ -695,6 +698,7 @@
E1D4BF862719D27100A11E64 /* Bitrates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bitrates.swift; sourceTree = "<group>"; }; E1D4BF862719D27100A11E64 /* Bitrates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bitrates.swift; sourceTree = "<group>"; };
E1D4BF892719D3D000A11E64 /* BasicAppSettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicAppSettingsCoordinator.swift; sourceTree = "<group>"; }; E1D4BF892719D3D000A11E64 /* BasicAppSettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicAppSettingsCoordinator.swift; sourceTree = "<group>"; };
E1D4BF8E271A079A00A11E64 /* BasicAppSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicAppSettingsView.swift; sourceTree = "<group>"; }; E1D4BF8E271A079A00A11E64 /* BasicAppSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicAppSettingsView.swift; sourceTree = "<group>"; };
E1E00A34278628A40022235B /* DoubleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleExtensions.swift; sourceTree = "<group>"; };
E1E48CC8271E6D410021A2F9 /* RefreshHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshHelper.swift; sourceTree = "<group>"; }; E1E48CC8271E6D410021A2F9 /* RefreshHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshHelper.swift; sourceTree = "<group>"; };
E1E5D5362783A52C00692DFE /* CinematicEpisodeItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicEpisodeItemView.swift; sourceTree = "<group>"; }; E1E5D5362783A52C00692DFE /* CinematicEpisodeItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CinematicEpisodeItemView.swift; sourceTree = "<group>"; };
E1E5D5382783A56B00692DFE /* EpisodesRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodesRowView.swift; sourceTree = "<group>"; }; E1E5D5382783A56B00692DFE /* EpisodesRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodesRowView.swift; sourceTree = "<group>"; };
@ -1179,6 +1183,7 @@
E10EAA4E277BBCC4000269ED /* CGSizeExtensions.swift */, E10EAA4E277BBCC4000269ED /* CGSizeExtensions.swift */,
6267B3D526710B8900A7371D /* CollectionExtensions.swift */, 6267B3D526710B8900A7371D /* CollectionExtensions.swift */,
E173DA5126D04AAF00CC4EB7 /* ColorExtension.swift */, E173DA5126D04AAF00CC4EB7 /* ColorExtension.swift */,
E1E00A34278628A40022235B /* DoubleExtensions.swift */,
6267B3D92671138200A7371D /* ImageExtensions.swift */, 6267B3D92671138200A7371D /* ImageExtensions.swift */,
E1AD105226D96D5F003E4A08 /* JellyfinAPIExtensions */, E1AD105226D96D5F003E4A08 /* JellyfinAPIExtensions */,
621338922660107500A81A2A /* StringExtensions.swift */, 621338922660107500A81A2A /* StringExtensions.swift */,
@ -2060,6 +2065,7 @@
53272537268C1DBB0035FBF1 /* SeasonItemView.swift in Sources */, 53272537268C1DBB0035FBF1 /* SeasonItemView.swift in Sources */,
09389CC526814E4500AE350E /* DeviceProfileBuilder.swift in Sources */, 09389CC526814E4500AE350E /* DeviceProfileBuilder.swift in Sources */,
E1C812D2277AE50A00918266 /* URLComponentsExtensions.swift in Sources */, E1C812D2277AE50A00918266 /* URLComponentsExtensions.swift in Sources */,
E1E00A36278628A40022235B /* DoubleExtensions.swift in Sources */,
E1FA2F7427818A8800B4C270 /* SmallMenuOverlay.swift in Sources */, E1FA2F7427818A8800B4C270 /* SmallMenuOverlay.swift in Sources */,
E193D53C27193F9500900D82 /* UserListCoordinator.swift in Sources */, E193D53C27193F9500900D82 /* UserListCoordinator.swift in Sources */,
E13DD3C927164B1E009D4DAF /* UIDeviceExtensions.swift in Sources */, E13DD3C927164B1E009D4DAF /* UIDeviceExtensions.swift in Sources */,
@ -2231,6 +2237,7 @@
6220D0BA26D6092100B8E046 /* FilterCoordinator.swift in Sources */, 6220D0BA26D6092100B8E046 /* FilterCoordinator.swift in Sources */,
E1E5D54C2783E27200692DFE /* ExperimentalSettingsView.swift in Sources */, E1E5D54C2783E27200692DFE /* ExperimentalSettingsView.swift in Sources */,
6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */, 6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */,
E1E00A35278628A40022235B /* DoubleExtensions.swift in Sources */,
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */, 62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */,
5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */, 5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */,
E1D4BF8A2719D3D000A11E64 /* BasicAppSettingsCoordinator.swift in Sources */, E1D4BF8A2719D3D000A11E64 /* BasicAppSettingsCoordinator.swift in Sources */,
@ -2259,6 +2266,7 @@
E13DD3CB27164BA8009D4DAF /* UIDeviceExtensions.swift in Sources */, E13DD3CB27164BA8009D4DAF /* UIDeviceExtensions.swift in Sources */,
6264E88A27384A6F0081A12A /* NetworkError.swift in Sources */, 6264E88A27384A6F0081A12A /* NetworkError.swift in Sources */,
E19169D0272514760085832A /* HTTPScheme.swift in Sources */, E19169D0272514760085832A /* HTTPScheme.swift in Sources */,
E1E00A37278628A40022235B /* DoubleExtensions.swift in Sources */,
6267B3D726710B9700A7371D /* CollectionExtensions.swift in Sources */, 6267B3D726710B9700A7371D /* CollectionExtensions.swift in Sources */,
628B953C2670D2430091AF3B /* StringExtensions.swift in Sources */, 628B953C2670D2430091AF3B /* StringExtensions.swift in Sources */,
6267B3DB2671139400A7371D /* ImageExtensions.swift in Sources */, 6267B3DB2671139400A7371D /* ImageExtensions.swift in Sources */,

View File

@ -27,6 +27,7 @@ struct SettingsView: View {
@Default(.jumpGesturesEnabled) var jumpGesturesEnabled @Default(.jumpGesturesEnabled) var jumpGesturesEnabled
@Default(.showPosterLabels) var showPosterLabels @Default(.showPosterLabels) var showPosterLabels
@Default(.showCastAndCrew) var showCastAndCrew @Default(.showCastAndCrew) var showCastAndCrew
@Default(.resumeOffset) var resumeOffset
var body: some View { var body: some View {
Form { Form {
@ -91,6 +92,8 @@ struct SettingsView: View {
Toggle("Jump Gestures Enabled", isOn: $jumpGesturesEnabled) Toggle("Jump Gestures Enabled", isOn: $jumpGesturesEnabled)
Toggle("Resume 5 Second Offset", isOn: $resumeOffset)
Button { Button {
settingsRouter.route(to: \.overlaySettings) settingsRouter.route(to: \.overlaySettings)
} label: { } label: {

View File

@ -316,7 +316,15 @@ extension VLCPlayerViewController {
let startPercentage = newViewModel.item.userData?.playedPercentage ?? 0 let startPercentage = newViewModel.item.userData?.playedPercentage ?? 0
if startPercentage > 0 { if startPercentage > 0 {
newViewModel.sliderPercentage = startPercentage / 100 if viewModel.resumeOffset {
let videoDurationSeconds = Double(viewModel.item.runTimeTicks! / 10_000_000)
var startSeconds = round((startPercentage / 100) * videoDurationSeconds)
startSeconds = startSeconds.subtract(5, floor: 0)
let newStartPercentage = startSeconds / videoDurationSeconds
newViewModel.sliderPercentage = newStartPercentage
} else {
newViewModel.sliderPercentage = startPercentage / 100
}
} }
viewModel = newViewModel viewModel = newViewModel
@ -522,6 +530,7 @@ extension VLCPlayerViewController: VLCMediaPlayerDelegate {
didSelectSubtitleStream(index: viewModel.selectedSubtitleStreamIndex) didSelectSubtitleStream(index: viewModel.selectedSubtitleStreamIndex)
} }
// If needing to fix audio stream during playback
if vlcMediaPlayer.currentAudioTrackIndex != viewModel.selectedAudioStreamIndex { if vlcMediaPlayer.currentAudioTrackIndex != viewModel.selectedAudioStreamIndex {
didSelectAudioStream(index: viewModel.selectedAudioStreamIndex) didSelectAudioStream(index: viewModel.selectedAudioStreamIndex)
} }

View File

@ -0,0 +1,23 @@
//
/*
* 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 Foundation
extension Double {
func subtract(_ other: Double, floor: Double) -> Double {
var v = self - other
if v < floor {
v += abs(floor - v)
}
return v
}
}

View File

@ -48,6 +48,7 @@ extension Defaults.Keys {
static let videoPlayerJumpForward = Key<VideoPlayerJumpLength>("videoPlayerJumpForward", default: .fifteen, suite: SwiftfinStore.Defaults.generalSuite) static let videoPlayerJumpForward = Key<VideoPlayerJumpLength>("videoPlayerJumpForward", default: .fifteen, suite: SwiftfinStore.Defaults.generalSuite)
static let videoPlayerJumpBackward = Key<VideoPlayerJumpLength>("videoPlayerJumpBackward", default: .fifteen, suite: SwiftfinStore.Defaults.generalSuite) static let videoPlayerJumpBackward = Key<VideoPlayerJumpLength>("videoPlayerJumpBackward", default: .fifteen, suite: SwiftfinStore.Defaults.generalSuite)
static let autoplayEnabled = Key<Bool>("autoPlayNextItem", default: true, suite: SwiftfinStore.Defaults.generalSuite) static let autoplayEnabled = Key<Bool>("autoPlayNextItem", default: true, suite: SwiftfinStore.Defaults.generalSuite)
static let resumeOffset = Key<Bool>("resumeOffset", default: false, suite: SwiftfinStore.Defaults.generalSuite)
// Should show video player items // Should show video player items
static let shouldShowPlayPreviousItem = Key<Bool>("shouldShowPreviousItem", default: true, suite: SwiftfinStore.Defaults.generalSuite) static let shouldShowPlayPreviousItem = Key<Bool>("shouldShowPreviousItem", default: true, suite: SwiftfinStore.Defaults.generalSuite)
@ -60,6 +61,7 @@ extension Defaults.Keys {
// Experimental settings // Experimental settings
struct Experimental { struct Experimental {
static let syncSubtitleStateWithAdjacent = Key<Bool>("experimental.syncSubtitleState", default: false, suite: SwiftfinStore.Defaults.generalSuite) static let syncSubtitleStateWithAdjacent = Key<Bool>("experimental.syncSubtitleState", default: false, suite: SwiftfinStore.Defaults.generalSuite)
static let liveTVAlphaEnabled = Key<Bool>("liveTVAlphaEnabled", default: false, suite: SwiftfinStore.Defaults.generalSuite)
} }
// tvos specific // tvos specific

View File

@ -88,6 +88,7 @@ final class VideoPlayerViewModel: ViewModel {
let subtitleStreams: [MediaStream] let subtitleStreams: [MediaStream]
let overlayType: OverlayType let overlayType: OverlayType
let jumpGesturesEnabled: Bool let jumpGesturesEnabled: Bool
let resumeOffset: Bool
// MARK: Experimental // MARK: Experimental
let syncSubtitleStateWithAdjacent: Bool let syncSubtitleStateWithAdjacent: Bool
@ -162,6 +163,8 @@ final class VideoPlayerViewModel: ViewModel {
self.jumpGesturesEnabled = Defaults[.jumpGesturesEnabled] self.jumpGesturesEnabled = Defaults[.jumpGesturesEnabled]
self.shouldShowJumpButtonsInOverlayMenu = Defaults[.shouldShowJumpButtonsInOverlayMenu] self.shouldShowJumpButtonsInOverlayMenu = Defaults[.shouldShowJumpButtonsInOverlayMenu]
self.resumeOffset = Defaults[.resumeOffset]
self.syncSubtitleStateWithAdjacent = Defaults[.Experimental.syncSubtitleStateWithAdjacent] self.syncSubtitleStateWithAdjacent = Defaults[.Experimental.syncSubtitleStateWithAdjacent]
self.confirmClose = Defaults[.confirmClose] self.confirmClose = Defaults[.confirmClose]