2021-09-25 08:18:22 +00:00
import Defaults
import SwiftUI
2022-01-06 15:02:53 +00:00
struct PlayerSettings : View {
2021-11-03 23:40:01 +00:00
@ Default ( . instances ) private var instances
@ Default ( . playerInstanceID ) private var playerInstanceID
2022-01-06 15:02:53 +00:00
2021-11-03 23:00:17 +00:00
@ Default ( . playerSidebar ) private var playerSidebar
2022-08-28 17:18:49 +00:00
@ Default ( . playerControlsLayout ) private var playerControlsLayout
@ Default ( . fullScreenPlayerControlsLayout ) private var fullScreenPlayerControlsLayout
@ Default ( . horizontalPlayerGestureEnabled ) private var horizontalPlayerGestureEnabled
@ Default ( . seekGestureSpeed ) private var seekGestureSpeed
2022-08-29 12:07:27 +00:00
@ Default ( . seekGestureSensitivity ) private var seekGestureSensitivity
2021-11-03 23:14:09 +00:00
@ Default ( . showKeywords ) private var showKeywords
2021-12-19 17:17:04 +00:00
@ Default ( . pauseOnHidingPlayer ) private var pauseOnHidingPlayer
2022-01-02 19:43:30 +00:00
#if os ( iOS )
2022-11-13 11:49:39 +00:00
@ Default ( . honorSystemOrientationLock ) private var honorSystemOrientationLock
2022-01-02 19:43:30 +00:00
@ Default ( . enterFullscreenInLandscape ) private var enterFullscreenInLandscape
2022-08-07 11:48:50 +00:00
@ Default ( . rotateToPortraitOnExitFullScreen ) private var rotateToPortraitOnExitFullScreen
2022-01-02 19:43:30 +00:00
#endif
2021-12-19 17:17:04 +00:00
@ Default ( . closePiPOnNavigation ) private var closePiPOnNavigation
@ Default ( . closePiPOnOpeningPlayer ) private var closePiPOnOpeningPlayer
2022-08-26 20:17:21 +00:00
@ Default ( . closePlayerOnOpeningPiP ) private var closePlayerOnOpeningPiP
2021-12-19 17:17:04 +00:00
#if ! os ( macOS )
2022-06-30 09:46:20 +00:00
@ Default ( . pauseOnEnteringBackground ) private var pauseOnEnteringBackground
2021-12-19 17:17:04 +00:00
@ Default ( . closePiPAndOpenPlayerOnEnteringForeground ) private var closePiPAndOpenPlayerOnEnteringForeground
#endif
2021-11-03 23:00:17 +00:00
2022-03-20 20:31:19 +00:00
@ Default ( . enableReturnYouTubeDislike ) private var enableReturnYouTubeDislike
2022-07-11 16:10:51 +00:00
@ Default ( . systemControlsCommands ) private var systemControlsCommands
2022-12-18 18:39:03 +00:00
@ Default ( . openWatchNextOnClose ) private var openWatchNextOnClose
@ Default ( . openWatchNextOnFinishedWatching ) private var openWatchNextOnFinishedWatching
@ Default ( . openWatchNextOnFinishedWatchingDelay ) private var openWatchNextOnFinishedWatchingDelay
2022-12-19 11:08:27 +00:00
@ Default ( . buttonBackwardSeekDuration ) private var buttonBackwardSeekDuration
@ Default ( . buttonForwardSeekDuration ) private var buttonForwardSeekDuration
@ Default ( . gestureBackwardSeekDuration ) private var gestureBackwardSeekDuration
@ Default ( . gestureForwardSeekDuration ) private var gestureForwardSeekDuration
@ Default ( . systemControlsSeekDuration ) private var systemControlsSeekDuration
2022-12-19 10:29:18 +00:00
@ Default ( . actionButtonShareEnabled ) private var actionButtonShareEnabled
@ Default ( . actionButtonSubscribeEnabled ) private var actionButtonSubscribeEnabled
@ Default ( . actionButtonNextEnabled ) private var actionButtonNextEnabled
@ Default ( . actionButtonCloseEnabled ) private var actionButtonCloseEnabled
@ Default ( . actionButtonAddToPlaylistEnabled ) private var actionButtonAddToPlaylistEnabled
@ Default ( . actionButtonSettingsEnabled ) private var actionButtonSettingsEnabled
@ Default ( . actionButtonHideEnabled ) private var actionButtonHideEnabled
@ Default ( . actionButtonNextQueueCountEnabled ) private var actionButtonNextQueueCountEnabled
2022-12-19 11:08:27 +00:00
2022-11-24 20:36:05 +00:00
@ ObservedObject private var accounts = AccountsModel . shared
private var player = PlayerModel . shared
2022-03-20 20:31:19 +00:00
2021-11-03 23:00:17 +00:00
#if os ( iOS )
private var idiom : UIUserInterfaceIdiom {
UIDevice . current . userInterfaceIdiom
}
#endif
2021-09-25 08:18:22 +00:00
var body : some View {
2021-11-04 22:01:27 +00:00
Group {
2022-01-06 15:02:53 +00:00
#if os ( macOS )
sections
2021-12-19 17:17:04 +00:00
2022-01-06 15:02:53 +00:00
Spacer ( )
2021-11-04 22:01:27 +00:00
#else
2022-01-06 15:02:53 +00:00
List {
sections
2021-11-04 22:01:27 +00:00
}
2022-01-06 15:02:53 +00:00
#endif
}
#if os ( tvOS )
. frame ( maxWidth : 1000 )
#elseif os ( iOS )
. listStyle ( . insetGrouped )
#endif
. navigationTitle ( " Player " )
}
2021-11-03 23:40:01 +00:00
2022-01-06 15:02:53 +00:00
private var sections : some View {
Group {
2022-09-04 15:28:30 +00:00
Section ( header : SettingsHeader ( text : " Playback " . localized ( ) ) ) {
2022-11-11 19:34:20 +00:00
if ! accounts . isEmpty {
sourcePicker
}
2022-01-06 15:02:53 +00:00
pauseOnHidingPlayerToggle
2022-06-30 09:46:20 +00:00
#if ! os ( macOS )
pauseOnEnteringBackgroundToogle
#endif
2022-07-11 16:10:51 +00:00
systemControlsCommandsPicker
2022-01-06 15:02:53 +00:00
}
2021-11-03 23:14:09 +00:00
2022-12-19 11:08:27 +00:00
Section ( header : SettingsHeader ( text : " Seeking " ) , footer : seekingGestureSection ) {
seekingSection
}
2022-12-19 10:29:18 +00:00
#if ! os ( tvOS )
Section ( header : SettingsHeader ( text : " Actions Buttons " ) ) {
actionButtonToggles
}
actionButtonNextQueueCountEnabledToggle
#endif
2022-12-18 18:39:03 +00:00
Section ( header : SettingsHeader ( text : " Watch Next " ) ) {
openWatchNextOnFinishedWatchingToggle
openWatchNextOnFinishedWatchingDelayTextField
openWatchNextOnCloseToggle
}
2022-08-29 12:07:27 +00:00
#if ! os ( tvOS )
2022-09-04 15:28:30 +00:00
Section ( header : SettingsHeader ( text : " Controls " . localized ( ) ) , footer : controlsLayoutFooter ) {
2022-08-28 17:18:49 +00:00
horizontalPlayerGestureEnabledToggle
2022-09-04 15:28:30 +00:00
SettingsHeader ( text : " Seek gesture sensitivity " . localized ( ) , secondary : true )
2022-08-29 12:07:27 +00:00
seekGestureSensitivityPicker
2022-09-04 15:28:30 +00:00
SettingsHeader ( text : " Seek gesture speed " . localized ( ) , secondary : true )
2022-08-28 17:18:49 +00:00
seekGestureSpeedPicker
2022-09-04 15:28:30 +00:00
SettingsHeader ( text : " Regular size " . localized ( ) , secondary : true )
2022-08-28 17:18:49 +00:00
playerControlsLayoutPicker
2022-09-04 15:28:30 +00:00
SettingsHeader ( text : " Fullscreen size " . localized ( ) , secondary : true )
2022-08-29 12:07:27 +00:00
fullScreenPlayerControlsLayoutPicker
}
#endif
2022-08-28 17:18:49 +00:00
2022-11-18 22:04:49 +00:00
let interface = Section ( header : SettingsHeader ( text : " Interface " . localized ( ) ) ) {
2022-01-06 15:02:53 +00:00
#if os ( iOS )
if idiom = = . pad {
2021-11-04 22:01:27 +00:00
sidebarPicker
}
#endif
2022-01-06 15:02:53 +00:00
#if os ( macOS )
sidebarPicker
#endif
2022-11-11 19:34:20 +00:00
if ! accounts . isEmpty {
keywordsToggle
2022-10-26 11:11:35 +00:00
returnYouTubeDislikeToggle
}
2022-01-06 15:02:53 +00:00
}
2021-12-19 17:17:04 +00:00
2022-11-18 22:04:49 +00:00
#if os ( tvOS )
if ! accounts . isEmpty {
interface
}
#elseif os ( macOS )
interface
#elseif os ( iOS )
if idiom = = . pad || ! accounts . isEmpty {
interface
}
#endif
2022-01-06 15:02:53 +00:00
#if os ( iOS )
2022-09-04 15:28:30 +00:00
Section ( header : SettingsHeader ( text : " Orientation " . localized ( ) ) ) {
2022-01-06 15:02:53 +00:00
if idiom = = . pad {
enterFullscreenInLandscapeToggle
}
2022-08-07 11:48:50 +00:00
rotateToPortraitOnExitFullScreenToggle
2022-11-13 11:49:39 +00:00
honorSystemOrientationLockToggle
2021-12-19 17:17:04 +00:00
}
2021-11-04 22:01:27 +00:00
#endif
2022-08-07 11:48:50 +00:00
2022-09-04 15:28:30 +00:00
Section ( header : SettingsHeader ( text : " Picture in Picture " . localized ( ) ) ) {
2022-08-07 11:48:50 +00:00
closePiPOnNavigationToggle
closePiPOnOpeningPlayerToggle
2022-08-26 20:17:21 +00:00
closePlayerOnOpeningPiPToggle
2022-08-07 11:48:50 +00:00
#if ! os ( macOS )
closePiPAndOpenPlayerOnEnteringForegroundToggle
#endif
}
2022-01-06 15:02:53 +00:00
}
2021-11-03 23:14:09 +00:00
}
2022-11-13 22:36:46 +00:00
private var videoDetailsHeaderPadding : Double {
#if os ( macOS )
5.0
#else
0.0
#endif
}
2021-11-04 22:01:27 +00:00
private var sourcePicker : some View {
Picker ( " Source " , selection : $ playerInstanceID ) {
2022-09-04 15:28:30 +00:00
Text ( " Instance of current account " ) . tag ( String ? . none )
2021-11-03 23:40:01 +00:00
2021-11-04 22:01:27 +00:00
ForEach ( instances ) { instance in
2021-12-04 19:35:41 +00:00
Text ( instance . description ) . tag ( Optional ( instance . id ) )
2021-11-03 23:40:01 +00:00
}
}
2022-08-06 14:28:05 +00:00
. modifier ( SettingsPickerModifier ( ) )
2022-07-11 16:10:51 +00:00
}
private var systemControlsCommandsPicker : some View {
2022-07-11 18:01:27 +00:00
func labelText ( _ label : String ) -> String {
#if os ( macOS )
2022-09-04 15:28:30 +00:00
String ( format : " System controls show buttons for %@ " . localized ( ) , label )
2022-07-11 18:01:27 +00:00
#else
label
#endif
}
return Picker ( " System controls buttons " , selection : $ systemControlsCommands ) {
2022-09-04 15:28:30 +00:00
Text ( labelText ( " 10 seconds forwards/backwards " . localized ( ) ) ) . tag ( SystemControlsCommands . seek )
Text ( labelText ( " Restart/Play next " . localized ( ) ) ) . tag ( SystemControlsCommands . restartAndAdvanceToNext )
2022-07-11 16:10:51 +00:00
}
. onChange ( of : systemControlsCommands ) { _ in
player . updateRemoteCommandCenter ( )
}
2022-08-06 14:28:05 +00:00
. modifier ( SettingsPickerModifier ( ) )
2021-11-03 23:40:01 +00:00
}
2022-12-18 18:39:03 +00:00
private var openWatchNextOnCloseToggle : some View {
Toggle ( " Open after manual close of video " , isOn : $ openWatchNextOnClose )
}
private var openWatchNextOnFinishedWatchingToggle : some View {
Toggle ( " Open after watching video " , isOn : $ openWatchNextOnFinishedWatching )
}
private var openWatchNextOnFinishedWatchingDelayTextField : some View {
HStack {
Text ( " Autoplay delay " )
. frame ( minWidth : 140 , alignment : . leading )
#if ! os ( iOS )
Spacer ( )
#endif
TextField ( " Delay " , text : $ openWatchNextOnFinishedWatchingDelay )
#if ! os ( iOS )
. frame ( maxWidth : 100 , alignment : . trailing )
#endif
. labelsHidden ( )
#if ! os ( macOS )
. keyboardType ( . numberPad )
#endif
}
. multilineTextAlignment ( . trailing )
}
2022-12-19 11:08:27 +00:00
@ ViewBuilder private var seekingSection : some View {
seekingDurationSetting ( " System controls " , $ systemControlsSeekDuration )
. foregroundColor ( systemControlsCommands = = . restartAndAdvanceToNext ? . secondary : . primary )
. disabled ( systemControlsCommands = = . restartAndAdvanceToNext )
seekingDurationSetting ( " Controls button: backwards " , $ buttonBackwardSeekDuration )
seekingDurationSetting ( " Controls button: forwards " , $ buttonForwardSeekDuration )
seekingDurationSetting ( " Gesture: backwards " , $ gestureBackwardSeekDuration )
seekingDurationSetting ( " Gesture: fowards " , $ gestureForwardSeekDuration )
}
private var seekingGestureSection : some View {
#if os ( iOS )
Text ( " Gesture settings control skipping interval for double tap gesture on left/right side of the player. Changing system controls settings requires restart. " )
#elseif os ( macOS )
Text ( " Gesture settings control skipping interval for double click on left/right side of the player. Changing system controls settings requires restart. " )
#else
Text ( " Gesture settings control skipping interval for remote arrow buttons (for 2nd generation Siri Remote or newer). Changing system controls settings requires restart. " )
#endif
}
private func seekingDurationSetting ( _ name : String , _ value : Binding < String > ) -> some View {
HStack {
Text ( name )
. frame ( minWidth : 140 , alignment : . leading )
Spacer ( )
TextField ( " Duration " , text : value )
. frame ( maxWidth : 50 , alignment : . trailing )
. multilineTextAlignment ( . trailing )
. labelsHidden ( )
#if ! os ( macOS )
. keyboardType ( . numberPad )
#endif
}
}
2022-12-19 10:29:18 +00:00
@ ViewBuilder private var actionButtonToggles : some View {
actionButtonToggle ( " Share " , $ actionButtonShareEnabled )
actionButtonToggle ( " Add to Playlist " , $ actionButtonAddToPlaylistEnabled )
actionButtonToggle ( " Subscribe/Unsubscribe " , $ actionButtonSubscribeEnabled )
actionButtonToggle ( " Settings " , $ actionButtonSettingsEnabled )
actionButtonToggle ( " Watch Next " , $ actionButtonNextEnabled )
actionButtonToggle ( " Hide player " , $ actionButtonHideEnabled )
actionButtonToggle ( " Close video " , $ actionButtonCloseEnabled )
}
private func actionButtonToggle ( _ name : String , _ value : Binding < Bool > ) -> some View {
Toggle ( name , isOn : value )
}
var actionButtonNextQueueCountEnabledToggle : some View {
Toggle ( " Show queue items count in Watch Next button label " , isOn : $ actionButtonNextQueueCountEnabled )
}
2021-11-03 23:14:09 +00:00
private var sidebarPicker : some View {
Picker ( " Sidebar " , selection : $ playerSidebar ) {
#if os ( macOS )
2022-01-06 15:02:53 +00:00
Text ( " Show sidebar " ) . tag ( PlayerSidebarSetting . always )
2021-11-03 23:14:09 +00:00
#endif
2021-11-03 23:00:17 +00:00
#if os ( iOS )
2021-11-03 23:14:09 +00:00
Text ( " Show sidebar when space permits " ) . tag ( PlayerSidebarSetting . whenFits )
2021-09-25 08:18:22 +00:00
#endif
2021-11-03 23:14:09 +00:00
2022-01-06 15:02:53 +00:00
Text ( " Hide sidebar " ) . tag ( PlayerSidebarSetting . never )
2021-09-25 08:18:22 +00:00
}
2022-08-06 14:28:05 +00:00
. modifier ( SettingsPickerModifier ( ) )
2021-09-25 08:18:22 +00:00
}
2021-11-04 22:01:27 +00:00
2022-08-28 17:18:49 +00:00
private var horizontalPlayerGestureEnabledToggle : some View {
Toggle ( " Seek with horizontal swipe on video " , isOn : $ horizontalPlayerGestureEnabled )
}
private var seekGestureSpeedPicker : some View {
2022-08-29 12:07:27 +00:00
Picker ( " Seek gesture speed " , selection : $ seekGestureSpeed ) {
2022-08-28 17:18:49 +00:00
ForEach ( [ 1 , 0.75 , 0.66 , 0.5 , 0.33 , 0.25 , 0.1 ] , id : \ . self ) { value in
Text ( String ( format : " %.0f%% " , value * 100 ) ) . tag ( value )
}
}
. disabled ( ! horizontalPlayerGestureEnabled )
. modifier ( SettingsPickerModifier ( ) )
}
2022-08-29 12:07:27 +00:00
private var seekGestureSensitivityPicker : some View {
Picker ( " Seek gesture sensitivity " , selection : $ seekGestureSensitivity ) {
Text ( " Highest " ) . tag ( 1.0 )
Text ( " High " ) . tag ( 10.0 )
Text ( " Normal " ) . tag ( 30.0 )
Text ( " Low " ) . tag ( 50.0 )
Text ( " Lowest " ) . tag ( 100.0 )
}
. disabled ( ! horizontalPlayerGestureEnabled )
. modifier ( SettingsPickerModifier ( ) )
}
2022-08-28 17:18:49 +00:00
@ ViewBuilder private var controlsLayoutFooter : some View {
#if os ( iOS )
2022-08-29 12:41:21 +00:00
Text ( " Large layout is not suitable for all devices and using it may cause controls not to fit on the screen. " )
2022-08-28 17:18:49 +00:00
#endif
}
private var playerControlsLayoutPicker : some View {
Picker ( " Regular Size " , selection : $ playerControlsLayout ) {
2022-08-29 12:41:21 +00:00
ForEach ( PlayerControlsLayout . allCases . filter ( \ . available ) , id : \ . self ) { layout in
2022-08-28 17:18:49 +00:00
Text ( layout . description ) . tag ( layout . rawValue )
}
}
. modifier ( SettingsPickerModifier ( ) )
}
private var fullScreenPlayerControlsLayoutPicker : some View {
2022-09-27 13:22:40 +00:00
Picker ( " Fullscreen size " , selection : $ fullScreenPlayerControlsLayout ) {
2022-08-29 12:41:21 +00:00
ForEach ( PlayerControlsLayout . allCases . filter ( \ . available ) , id : \ . self ) { layout in
2022-08-28 17:18:49 +00:00
Text ( layout . description ) . tag ( layout . rawValue )
}
}
. modifier ( SettingsPickerModifier ( ) )
}
2021-11-04 22:01:27 +00:00
private var keywordsToggle : some View {
2022-01-06 15:02:53 +00:00
Toggle ( " Show keywords " , isOn : $ showKeywords )
2021-11-04 22:01:27 +00:00
}
2021-12-17 20:01:05 +00:00
2022-03-20 20:31:19 +00:00
private var returnYouTubeDislikeToggle : some View {
Toggle ( " Enable Return YouTube Dislike " , isOn : $ enableReturnYouTubeDislike )
}
2021-12-19 17:17:04 +00:00
private var pauseOnHidingPlayerToggle : some View {
Toggle ( " Pause when player is closed " , isOn : $ pauseOnHidingPlayer )
}
2022-06-30 09:46:20 +00:00
#if ! os ( macOS )
private var pauseOnEnteringBackgroundToogle : some View {
Toggle ( " Pause when entering background " , isOn : $ pauseOnEnteringBackground )
}
#endif
2022-01-02 19:43:30 +00:00
#if os ( iOS )
2022-11-13 11:49:39 +00:00
private var honorSystemOrientationLockToggle : some View {
Toggle ( " Honor orientation lock " , isOn : $ honorSystemOrientationLock )
. disabled ( ! enterFullscreenInLandscape )
}
2022-01-02 19:43:30 +00:00
private var enterFullscreenInLandscapeToggle : some View {
Toggle ( " Enter fullscreen in landscape " , isOn : $ enterFullscreenInLandscape )
}
2022-08-07 11:48:50 +00:00
private var rotateToPortraitOnExitFullScreenToggle : some View {
Toggle ( " Rotate to portrait when exiting fullscreen " , isOn : $ rotateToPortraitOnExitFullScreen )
}
2022-01-02 19:43:30 +00:00
#endif
2021-12-19 17:17:04 +00:00
private var closePiPOnNavigationToggle : some View {
Toggle ( " Close PiP when starting playing other video " , isOn : $ closePiPOnNavigation )
}
private var closePiPOnOpeningPlayerToggle : some View {
Toggle ( " Close PiP when player is opened " , isOn : $ closePiPOnOpeningPlayer )
}
2022-08-26 20:17:21 +00:00
private var closePlayerOnOpeningPiPToggle : some View {
Toggle ( " Close player when starting PiP " , isOn : $ closePlayerOnOpeningPiP )
}
2021-12-19 17:17:04 +00:00
#if ! os ( macOS )
private var closePiPAndOpenPlayerOnEnteringForegroundToggle : some View {
Toggle ( " Close PiP and open player when application enters foreground " , isOn : $ closePiPAndOpenPlayerOnEnteringForeground )
}
#endif
2021-11-04 22:01:27 +00:00
}
2022-08-07 11:15:27 +00:00
struct PlayerSettings_Previews : PreviewProvider {
2021-11-04 22:01:27 +00:00
static var previews : some View {
2022-06-17 10:27:01 +00:00
VStack ( alignment : . leading ) {
PlayerSettings ( )
}
2022-11-13 22:36:46 +00:00
. frame ( minHeight : 800 )
2022-06-17 10:27:01 +00:00
. injectFixtureEnvironmentObjects ( )
2021-11-04 22:01:27 +00:00
}
2021-09-25 08:18:22 +00:00
}