mirror of
				https://github.com/yattee/yattee.git
				synced 2025-11-03 22:22:02 +00:00 
			
		
		
		
	Add setting for controls center buttons
This commit is contained in:
		@@ -82,7 +82,7 @@ final class PlayerModel: ObservableObject {
 | 
			
		||||
    @Published var restoredSegments = [Segment]()
 | 
			
		||||
 | 
			
		||||
    @Published var musicMode = false
 | 
			
		||||
    @Published var playbackMode = PlaybackMode.queue { didSet { handlePlaybackModeChange() }}
 | 
			
		||||
    @Published var playbackMode = PlaybackMode.queue { didSet { handlePlaybackModeChange() } }
 | 
			
		||||
    @Published var autoplayItem: PlayerQueueItem?
 | 
			
		||||
    @Published var autoplayItemSource: Video?
 | 
			
		||||
    @Published var advancing = false
 | 
			
		||||
@@ -158,6 +158,7 @@ final class PlayerModel: ObservableObject {
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    var onPresentPlayer: (() -> Void)?
 | 
			
		||||
    private var remoteCommandCenterConfigured = false
 | 
			
		||||
 | 
			
		||||
    init(
 | 
			
		||||
        accounts: AccountsModel = AccountsModel(),
 | 
			
		||||
@@ -537,6 +538,7 @@ final class PlayerModel: ObservableObject {
 | 
			
		||||
    func handleQueueChange() {
 | 
			
		||||
        Defaults[.queue] = queue
 | 
			
		||||
 | 
			
		||||
        updateRemoteCommandCenter()
 | 
			
		||||
        controls.objectWillChange.send()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -561,6 +563,8 @@ final class PlayerModel: ObservableObject {
 | 
			
		||||
    func handlePlaybackModeChange() {
 | 
			
		||||
        Defaults[.playbackMode] = playbackMode
 | 
			
		||||
 | 
			
		||||
        updateRemoteCommandCenter()
 | 
			
		||||
 | 
			
		||||
        guard playbackMode == .related else {
 | 
			
		||||
            autoplayItem = nil
 | 
			
		||||
            return
 | 
			
		||||
@@ -580,11 +584,94 @@ final class PlayerModel: ObservableObject {
 | 
			
		||||
            self.accounts.api.loadDetails(item, completionHandler: { newItem in
 | 
			
		||||
                guard newItem.videoID == self.autoplayItem?.videoID else { return }
 | 
			
		||||
                self.autoplayItem = newItem
 | 
			
		||||
                self.updateRemoteCommandCenter()
 | 
			
		||||
                self.controls.objectWillChange.send()
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func updateRemoteCommandCenter() {
 | 
			
		||||
        let skipForwardCommand = MPRemoteCommandCenter.shared().skipForwardCommand
 | 
			
		||||
        let skipBackwardCommand = MPRemoteCommandCenter.shared().skipBackwardCommand
 | 
			
		||||
        let previousTrackCommand = MPRemoteCommandCenter.shared().previousTrackCommand
 | 
			
		||||
        let nextTrackCommand = MPRemoteCommandCenter.shared().nextTrackCommand
 | 
			
		||||
 | 
			
		||||
        if !remoteCommandCenterConfigured {
 | 
			
		||||
            remoteCommandCenterConfigured = true
 | 
			
		||||
 | 
			
		||||
            #if !os(macOS)
 | 
			
		||||
                try? AVAudioSession.sharedInstance().setCategory(
 | 
			
		||||
                    .playback,
 | 
			
		||||
                    mode: .moviePlayback
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                UIApplication.shared.beginReceivingRemoteControlEvents()
 | 
			
		||||
            #endif
 | 
			
		||||
 | 
			
		||||
            let preferredIntervals = [NSNumber(10)]
 | 
			
		||||
 | 
			
		||||
            skipForwardCommand.preferredIntervals = preferredIntervals
 | 
			
		||||
            skipBackwardCommand.preferredIntervals = preferredIntervals
 | 
			
		||||
 | 
			
		||||
            skipForwardCommand.addTarget { [weak self] _ in
 | 
			
		||||
                self?.backend.seek(relative: .secondsInDefaultTimescale(10))
 | 
			
		||||
                return .success
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            skipBackwardCommand.addTarget { [weak self] _ in
 | 
			
		||||
                self?.backend.seek(relative: .secondsInDefaultTimescale(-10))
 | 
			
		||||
                return .success
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            previousTrackCommand.addTarget { [weak self] _ in
 | 
			
		||||
                self?.backend.seek(to: .zero)
 | 
			
		||||
                return .success
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            nextTrackCommand.addTarget { [weak self] _ in
 | 
			
		||||
                self?.advanceToNextItem()
 | 
			
		||||
                return .success
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            MPRemoteCommandCenter.shared().playCommand.addTarget { [weak self] _ in
 | 
			
		||||
                self?.play()
 | 
			
		||||
                return .success
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            MPRemoteCommandCenter.shared().pauseCommand.addTarget { [weak self] _ in
 | 
			
		||||
                self?.pause()
 | 
			
		||||
                return .success
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            MPRemoteCommandCenter.shared().togglePlayPauseCommand.addTarget { [weak self] _ in
 | 
			
		||||
                self?.togglePlay()
 | 
			
		||||
                return .success
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            MPRemoteCommandCenter.shared().changePlaybackPositionCommand.addTarget { [weak self] remoteEvent in
 | 
			
		||||
                guard let event = remoteEvent as? MPChangePlaybackPositionCommandEvent else { return .commandFailed }
 | 
			
		||||
 | 
			
		||||
                self?.backend.seek(to: event.positionTime)
 | 
			
		||||
 | 
			
		||||
                return .success
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch Defaults[.systemControlsCommands] {
 | 
			
		||||
        case .seek:
 | 
			
		||||
            previousTrackCommand.isEnabled = false
 | 
			
		||||
            nextTrackCommand.isEnabled = false
 | 
			
		||||
            skipForwardCommand.isEnabled = true
 | 
			
		||||
            skipBackwardCommand.isEnabled = true
 | 
			
		||||
 | 
			
		||||
        case .restartAndAdvanceToNext:
 | 
			
		||||
            skipForwardCommand.isEnabled = false
 | 
			
		||||
            skipBackwardCommand.isEnabled = false
 | 
			
		||||
            previousTrackCommand.isEnabled = true
 | 
			
		||||
            nextTrackCommand.isEnabled = isAdvanceToNextItemAvailable
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func resetAutoplay() {
 | 
			
		||||
        autoplayItem = nil
 | 
			
		||||
        autoplayItemSource = nil
 | 
			
		||||
 
 | 
			
		||||
@@ -99,6 +99,7 @@ extension Defaults.Keys {
 | 
			
		||||
 | 
			
		||||
    static let playerDetailsPageButtonLabelStyle = Key<PlayerDetailsPageButtonLabelStyle>("playerDetailsPageButtonLabelStyle", default: defaultForPlayerDetailsPageButtonLabelStyle)
 | 
			
		||||
 | 
			
		||||
    static let systemControlsCommands = Key<SystemControlsCommands>("systemControlsCommands", default: .restartAndAdvanceToNext)
 | 
			
		||||
    static let mpvCacheSecs = Key<String>("mpvCacheSecs", default: "20")
 | 
			
		||||
    static let mpvCachePauseWait = Key<String>("mpvCachePauseWait", default: "2")
 | 
			
		||||
    static let mpvEnableLogging = Key<Bool>("mpvEnableLogging", default: false)
 | 
			
		||||
@@ -225,3 +226,7 @@ enum PlayerDetailsPageButtonLabelStyle: String, CaseIterable, Defaults.Serializa
 | 
			
		||||
enum ThumbnailsQuality: String, CaseIterable, Defaults.Serializable {
 | 
			
		||||
    case highest, medium, low
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum SystemControlsCommands: String, CaseIterable, Defaults.Serializable {
 | 
			
		||||
    case seek, restartAndAdvanceToNext
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,9 @@ struct PlayerSettings: View {
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    @Default(.enableReturnYouTubeDislike) private var enableReturnYouTubeDislike
 | 
			
		||||
    @Default(.systemControlsCommands) private var systemControlsCommands
 | 
			
		||||
 | 
			
		||||
    @EnvironmentObject<PlayerModel> private var player
 | 
			
		||||
 | 
			
		||||
    #if os(iOS)
 | 
			
		||||
        private var idiom: UIUserInterfaceIdiom {
 | 
			
		||||
@@ -60,6 +63,7 @@ struct PlayerSettings: View {
 | 
			
		||||
                    pauseOnEnteringBackgroundToogle
 | 
			
		||||
                #endif
 | 
			
		||||
                closeLastItemOnPlaybackEndToggle
 | 
			
		||||
                systemControlsCommandsPicker
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Section(header: SettingsHeader(text: "Interface")) {
 | 
			
		||||
@@ -113,6 +117,22 @@ struct PlayerSettings: View {
 | 
			
		||||
        #endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var systemControlsCommandsPicker: some View {
 | 
			
		||||
        Picker("System controls buttons", selection: $systemControlsCommands) {
 | 
			
		||||
            Text("10 seconds forwards/backwards").tag(SystemControlsCommands.seek)
 | 
			
		||||
            Text("Restart/Play next").tag(SystemControlsCommands.restartAndAdvanceToNext)
 | 
			
		||||
        }
 | 
			
		||||
        .onChange(of: systemControlsCommands) { _ in
 | 
			
		||||
            player.updateRemoteCommandCenter()
 | 
			
		||||
        }
 | 
			
		||||
        .labelsHidden()
 | 
			
		||||
        #if os(iOS)
 | 
			
		||||
            .pickerStyle(.automatic)
 | 
			
		||||
        #elseif os(tvOS)
 | 
			
		||||
            .pickerStyle(.inline)
 | 
			
		||||
        #endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var qualityPicker: some View {
 | 
			
		||||
        Picker("Quality", selection: $quality) {
 | 
			
		||||
            ForEach(ResolutionSetting.allCases, id: \.self) { resolution in
 | 
			
		||||
 
 | 
			
		||||
@@ -168,10 +168,6 @@ struct YatteeApp: App {
 | 
			
		||||
        SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared)
 | 
			
		||||
        SDWebImageManager.defaultImageCache = PINCache(name: "stream.yattee.app")
 | 
			
		||||
 | 
			
		||||
        #if !os(macOS)
 | 
			
		||||
            configureNowPlayingInfoCenter()
 | 
			
		||||
        #endif
 | 
			
		||||
 | 
			
		||||
        #if os(iOS)
 | 
			
		||||
            if Defaults[.lockPortraitWhenBrowsing] {
 | 
			
		||||
                Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
 | 
			
		||||
@@ -236,56 +232,8 @@ struct YatteeApp: App {
 | 
			
		||||
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
 | 
			
		||||
                Windows.main.focus()
 | 
			
		||||
            }
 | 
			
		||||
        #else
 | 
			
		||||
            player.updateRemoteCommandCenter()
 | 
			
		||||
        #endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func configureNowPlayingInfoCenter() {
 | 
			
		||||
        #if !os(macOS)
 | 
			
		||||
            try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
 | 
			
		||||
 | 
			
		||||
            UIApplication.shared.beginReceivingRemoteControlEvents()
 | 
			
		||||
        #endif
 | 
			
		||||
 | 
			
		||||
        MPRemoteCommandCenter.shared().playCommand.addTarget { _ in
 | 
			
		||||
            player.play()
 | 
			
		||||
            return .success
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MPRemoteCommandCenter.shared().pauseCommand.addTarget { _ in
 | 
			
		||||
            player.pause()
 | 
			
		||||
            return .success
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MPRemoteCommandCenter.shared().previousTrackCommand.isEnabled = false
 | 
			
		||||
        MPRemoteCommandCenter.shared().nextTrackCommand.isEnabled = false
 | 
			
		||||
 | 
			
		||||
        MPRemoteCommandCenter.shared().changePlaybackPositionCommand.addTarget { remoteEvent in
 | 
			
		||||
            guard let event = remoteEvent as? MPChangePlaybackPositionCommandEvent
 | 
			
		||||
            else {
 | 
			
		||||
                return .commandFailed
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            player.backend.seek(to: event.positionTime)
 | 
			
		||||
 | 
			
		||||
            return .success
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let skipForwardCommand = MPRemoteCommandCenter.shared().skipForwardCommand
 | 
			
		||||
        skipForwardCommand.isEnabled = true
 | 
			
		||||
        skipForwardCommand.preferredIntervals = [10]
 | 
			
		||||
 | 
			
		||||
        skipForwardCommand.addTarget { _ in
 | 
			
		||||
            player.backend.seek(relative: .secondsInDefaultTimescale(10))
 | 
			
		||||
            return .success
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let skipBackwardCommand = MPRemoteCommandCenter.shared().skipBackwardCommand
 | 
			
		||||
        skipBackwardCommand.isEnabled = true
 | 
			
		||||
        skipBackwardCommand.preferredIntervals = [10]
 | 
			
		||||
 | 
			
		||||
        skipBackwardCommand.addTarget { _ in
 | 
			
		||||
            player.backend.seek(relative: .secondsInDefaultTimescale(-10))
 | 
			
		||||
            return .success
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user