diff --git a/Model/Player/PlayerModel.swift b/Model/Player/PlayerModel.swift index c4affa24..bc9b8760 100644 --- a/Model/Player/PlayerModel.swift +++ b/Model/Player/PlayerModel.swift @@ -47,11 +47,19 @@ final class PlayerModel: ObservableObject { private var shouldResumePlaying = true private var statusObservation: NSKeyValueObservation? + #if os(macOS) + var playerTimeControlStatusObserver: Any? + #endif + init(accounts: AccountsModel? = nil, instances: InstancesModel? = nil) { self.accounts = accounts ?? AccountsModel() self.instances = instances ?? InstancesModel() addItemDidPlayToEndTimeObserver() addTimeObserver() + + #if os(macOS) + addPlayerTimeControlStatusObserver() + #endif } func presentPlayer() { @@ -123,6 +131,9 @@ final class PlayerModel: ObservableObject { of video: Video, preservingTime: Bool = false ) { + #if !os(macOS) + try? AVAudioSession.sharedInstance().setActive(false) + #endif resetSegments() sponsorBlock.loadSegments(videoID: video.videoID) @@ -172,6 +183,10 @@ final class PlayerModel: ObservableObject { } let startPlaying = { + #if !os(macOS) + try? AVAudioSession.sharedInstance().setActive(true) + #endif + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.play() } @@ -302,6 +317,10 @@ final class PlayerModel: ObservableObject { } @objc func itemDidPlayToEndTime() { + #if !os(macOS) + try? AVAudioSession.sharedInstance().setActive(false) + #endif + if queue.isEmpty { addCurrentItemToHistory() resetQueue() @@ -360,4 +379,19 @@ final class PlayerModel: ObservableObject { self.handleSegments(at: time) } } + + #if os(macOS) + private func addPlayerTimeControlStatusObserver() { + playerTimeControlStatusObserver = player.observe(\.timeControlStatus) { player, _ in + guard self.player == player else { + return + } + if player.timeControlStatus == .playing { + ScreenSaverManager.shared.disable(reason: "Yattee is playing video") + } else { + ScreenSaverManager.shared.enable() + } + } + } + #endif } diff --git a/Model/Player/ScreenSaverManager.swift b/Model/Player/ScreenSaverManager.swift new file mode 100644 index 00000000..c981f753 --- /dev/null +++ b/Model/Player/ScreenSaverManager.swift @@ -0,0 +1,34 @@ +import Foundation +import IOKit.pwr_mgt + +struct ScreenSaverManager { + static var shared = ScreenSaverManager() + + var noSleepAssertion: IOPMAssertionID = 0 + var noSleepReturn: IOReturn? + + var enabled: Bool { + noSleepReturn == nil + } + + @discardableResult mutating func disable(reason: String = "Unknown reason") -> Bool { + guard enabled else { + return false + } + + noSleepReturn = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep as CFString, + IOPMAssertionLevel(kIOPMAssertionLevelOn), + reason as CFString, + &noSleepAssertion) + return noSleepReturn == kIOReturnSuccess + } + + @discardableResult mutating func enable() -> Bool { + if noSleepReturn != nil { + _ = IOPMAssertionRelease(noSleepAssertion) == kIOReturnSuccess + noSleepReturn = nil + return true + } + return false + } +} diff --git a/Pearvidious.xcodeproj/project.pbxproj b/Pearvidious.xcodeproj/project.pbxproj index 2c480aed..0d171da5 100644 --- a/Pearvidious.xcodeproj/project.pbxproj +++ b/Pearvidious.xcodeproj/project.pbxproj @@ -301,6 +301,7 @@ 37BE0BD726A1D4A90092E2DB /* PlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BE0BD526A1D4A90092E2DB /* PlayerViewController.swift */; }; 37BE0BDA26A214630092E2DB /* PlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BE0BD926A214630092E2DB /* PlayerViewController.swift */; }; 37BE0BDC26A2367F0092E2DB /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BE0BDB26A2367F0092E2DB /* Player.swift */; }; + 37C069782725962F00F7F6CB /* ScreenSaverManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C069772725962F00F7F6CB /* ScreenSaverManager.swift */; }; 37C194C726F6A9C8005D3B96 /* RecentsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C194C626F6A9C8005D3B96 /* RecentsModel.swift */; }; 37C194C826F6A9C8005D3B96 /* RecentsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C194C626F6A9C8005D3B96 /* RecentsModel.swift */; }; 37C3A241272359900087A57A /* Double+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C3A240272359900087A57A /* Double+Format.swift */; }; @@ -575,6 +576,7 @@ 37BE0BD526A1D4A90092E2DB /* PlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerViewController.swift; sourceTree = ""; }; 37BE0BD926A214630092E2DB /* PlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerViewController.swift; sourceTree = ""; }; 37BE0BDB26A2367F0092E2DB /* Player.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Player.swift; sourceTree = ""; }; + 37C069772725962F00F7F6CB /* ScreenSaverManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenSaverManager.swift; sourceTree = ""; }; 37C194C626F6A9C8005D3B96 /* RecentsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentsModel.swift; sourceTree = ""; }; 37C3A240272359900087A57A /* Double+Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Format.swift"; sourceTree = ""; }; 37C3A24427235DA70087A57A /* ChannelPlaylist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelPlaylist.swift; sourceTree = ""; }; @@ -815,6 +817,7 @@ 374C053E272472C0009BDDBE /* PlayerSegments.swift */, 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */, 374C053A2724614F009BDDBE /* PlayerTVMenu.swift */, + 37C069772725962F00F7F6CB /* ScreenSaverManager.swift */, ); path = Player; sourceTree = ""; @@ -1777,6 +1780,7 @@ 3797758C2689345500DD52A8 /* Store.swift in Sources */, 37141674267A8E10006CA35D /* Country.swift in Sources */, 37FD43E42704847C0073EE42 /* View+Fixtures.swift in Sources */, + 37C069782725962F00F7F6CB /* ScreenSaverManager.swift in Sources */, 37AAF2A126741C97007FC770 /* SubscriptionsView.swift in Sources */, 37732FF12703A26300F04329 /* AccountValidationStatus.swift in Sources */, 37BA794C26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */, diff --git a/Shared/Navigation/ContentView.swift b/Shared/Navigation/ContentView.swift index dd3e00aa..0a656c22 100644 --- a/Shared/Navigation/ContentView.swift +++ b/Shared/Navigation/ContentView.swift @@ -1,3 +1,4 @@ +import AVFAudio import Defaults import SDWebImage import SDWebImagePINPlugin @@ -91,6 +92,9 @@ struct ContentView: View { SiestaLog.Category.enabled = .common SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared) SDWebImageManager.defaultImageCache = PINCache(name: "net.yattee.app") + #if !os(macOS) + try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback) + #endif if let account = accounts.lastUsed ?? instances.lastUsed?.anonymousAccount ?? diff --git a/Shared/Player/PlayerViewController.swift b/Shared/Player/PlayerViewController.swift index dfcdf4df..bdf4915d 100644 --- a/Shared/Player/PlayerViewController.swift +++ b/Shared/Player/PlayerViewController.swift @@ -11,9 +11,6 @@ final class PlayerViewController: UIViewController { super.viewWillAppear(animated) loadPlayer() - - try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback) - try? AVAudioSession.sharedInstance().setActive(true) } func loadPlayer() { diff --git a/macOS/AppDelegate.swift b/macOS/AppDelegate.swift index b097f46c..b89b9573 100644 --- a/macOS/AppDelegate.swift +++ b/macOS/AppDelegate.swift @@ -5,4 +5,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate { func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool { true } + + func applicationWillTerminate(_: Notification) { + ScreenSaverManager.shared.enable() + } }