From 586405f9de3c77de0e10ee8885d66e0539f3b99d Mon Sep 17 00:00:00 2001 From: ronaldheft Date: Fri, 2 Sep 2022 16:31:47 -0400 Subject: [PATCH] Rewrite sleep timer --- ios/App/App.xcodeproj/project.pbxproj | 4 + ios/App/App/plugins/AbsAudioPlayer.swift | 19 +- ios/App/Shared/player/AudioPlayer.swift | 178 ++++-------------- .../Shared/player/AudioPlayerSleepTimer.swift | 155 +++++++++++++++ ios/App/Shared/player/PlayerHandler.swift | 46 +---- 5 files changed, 209 insertions(+), 193 deletions(-) create mode 100644 ios/App/Shared/player/AudioPlayerSleepTimer.swift diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 7271bb36..1f223fbf 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ E9D5507128AC1EC700C746DD /* DownloadItemPart.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D5507028AC1EC700C746DD /* DownloadItemPart.swift */; }; E9D5507328AC218300C746DD /* DaoExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D5507228AC218300C746DD /* DaoExtensions.swift */; }; E9D5507528AEF93100C746DD /* PlayerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D5507428AEF93100C746DD /* PlayerSettings.swift */; }; + E9DFCBFB28C28F4A00B36356 /* AudioPlayerSleepTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DFCBFA28C28F4A00B36356 /* AudioPlayerSleepTimer.swift */; }; E9E985F828B02D9400957F23 /* PlayerProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E985F728B02D9400957F23 /* PlayerProgress.swift */; }; /* End PBXBuildFile section */ @@ -117,6 +118,7 @@ E9D5507028AC1EC700C746DD /* DownloadItemPart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadItemPart.swift; sourceTree = ""; }; E9D5507228AC218300C746DD /* DaoExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaoExtensions.swift; sourceTree = ""; }; E9D5507428AEF93100C746DD /* PlayerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSettings.swift; sourceTree = ""; }; + E9DFCBFA28C28F4A00B36356 /* AudioPlayerSleepTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerSleepTimer.swift; sourceTree = ""; }; E9E985F728B02D9400957F23 /* PlayerProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerProgress.swift; sourceTree = ""; }; FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -145,6 +147,7 @@ isa = PBXGroup; children = ( 3A200C1427D64D7E00CBF02E /* AudioPlayer.swift */, + E9DFCBFA28C28F4A00B36356 /* AudioPlayerSleepTimer.swift */, 3ABF618E2804325C0070250E /* PlayerHandler.swift */, E9E985F728B02D9400957F23 /* PlayerProgress.swift */, ); @@ -438,6 +441,7 @@ E9D5506028AC1CA900C746DD /* PlaybackMetadata.swift in Sources */, E9D5504828AC1A7A00C746DD /* MediaType.swift in Sources */, E9D5504E28AC1B0700C746DD /* AudioFile.swift in Sources */, + E9DFCBFB28C28F4A00B36356 /* AudioPlayerSleepTimer.swift in Sources */, E9D5505428AC1B7900C746DD /* AudioTrack.swift in Sources */, E9D5505C28AC1C6200C746DD /* LibraryFile.swift in Sources */, 4DF74912287105C600AC7814 /* DeviceSettings.swift in Sources */, diff --git a/ios/App/App/plugins/AbsAudioPlayer.swift b/ios/App/App/plugins/AbsAudioPlayer.swift index 44b3b374..0ec2d225 100644 --- a/ios/App/App/plugins/AbsAudioPlayer.swift +++ b/ios/App/App/plugins/AbsAudioPlayer.swift @@ -169,7 +169,7 @@ public class AbsAudioPlayer: CAPPlugin { @objc func decreaseSleepTime(_ call: CAPPluginCall) { guard let timeString = call.getString("time") else { return call.resolve([ "success": false ]) } guard let time = Double(timeString) else { return call.resolve([ "success": false ]) } - guard let _ = PlayerHandler.remainingSleepTime else { return call.resolve([ "success": false ]) } + guard let _ = PlayerHandler.getSleepTimeRemaining() else { return call.resolve([ "success": false ]) } let seconds = time/1000 PlayerHandler.decreaseSleepTime(decreaseSeconds: seconds) @@ -179,7 +179,7 @@ public class AbsAudioPlayer: CAPPlugin { @objc func increaseSleepTime(_ call: CAPPluginCall) { guard let timeString = call.getString("time") else { return call.resolve([ "success": false ]) } guard let time = Double(timeString) else { return call.resolve([ "success": false ]) } - guard let _ = PlayerHandler.remainingSleepTime else { return call.resolve([ "success": false ]) } + guard let _ = PlayerHandler.getSleepTimeRemaining() else { return call.resolve([ "success": false ]) } let seconds = time/1000 PlayerHandler.increaseSleepTime(increaseSeconds: seconds) @@ -188,30 +188,29 @@ public class AbsAudioPlayer: CAPPlugin { @objc func setSleepTimer(_ call: CAPPluginCall) { guard let timeString = call.getString("time") else { return call.resolve([ "success": false ]) } - guard let time = Int(timeString) else { return call.resolve([ "success": false ]) } + guard let time = Double(timeString) else { return call.resolve([ "success": false ]) } let isChapterTime = call.getBool("isChapterTime", false) let seconds = time / 1000 NSLog("chapter time: \(isChapterTime)") if isChapterTime { - PlayerHandler.setChapterSleepTime(stopAt: Double(seconds)) + PlayerHandler.setChapterSleepTime(stopAt: seconds) return call.resolve([ "success": true ]) + } else { + PlayerHandler.setSleepTime(secondsUntilSleep: seconds) + call.resolve([ "success": true ]) } - - PlayerHandler.setSleepTime(secondsUntilSleep: Double(seconds)) - call.resolve([ "success": true ]) } @objc func cancelSleepTimer(_ call: CAPPluginCall) { PlayerHandler.cancelSleepTime() - PlayerHandler.sleepTimerChapterStopTime = nil call.resolve() } @objc func getSleepTimerTime(_ call: CAPPluginCall) { call.resolve([ - "value": PlayerHandler.remainingSleepTime + "value": PlayerHandler.getSleepTimeRemaining() ]) } @@ -223,7 +222,7 @@ public class AbsAudioPlayer: CAPPlugin { @objc func sendSleepTimerSet() { self.notifyListeners("onSleepTimerSet", data: [ - "value": PlayerHandler.remainingSleepTime + "value": PlayerHandler.getSleepTimeRemaining() ]) } diff --git a/ios/App/Shared/player/AudioPlayer.swift b/ios/App/Shared/player/AudioPlayer.swift index affc56b2..b91c66ce 100644 --- a/ios/App/Shared/player/AudioPlayer.swift +++ b/ios/App/Shared/player/AudioPlayer.swift @@ -18,7 +18,7 @@ enum PlayMethod:Int { } class AudioPlayer: NSObject { - private let queue = DispatchQueue(label: "ABSAudioPlayerQueue") + internal let queue = DispatchQueue(label: "ABSAudioPlayerQueue") // enums and @objc are not compatible @objc dynamic var status: Int @@ -32,15 +32,17 @@ class AudioPlayer: NSObject { private var playWhenReady: Bool private var initialPlaybackRate: Float - private var audioPlayer: AVQueuePlayer + internal var audioPlayer: AVQueuePlayer private var sessionId: String private var timeObserverToken: Any? private var queueObserver:NSKeyValueObservation? private var queueItemStatusObserver:NSKeyValueObservation? - private var sleepTimeStopAt: Double? - private var sleepTimeToken: Any? + // Sleep timer values + internal var sleepTimeChapterStopAt: Double? + internal var sleepTimeStopAt: Double? + internal var sleepTimeToken: Any? private var currentTrackIndex = 0 private var allPlayerItems:[AVPlayerItem] = [] @@ -263,7 +265,7 @@ class AudioPlayer: NSObject { } // Capture remaining sleep time before changing the track position - let sleepSecondsRemaining = PlayerHandler.remainingSleepTime + let sleepSecondsRemaining = self.getSleepTimeRemaining() // Stop the paused timer self.stopPausedTimer() @@ -279,19 +281,17 @@ class AudioPlayer: NSObject { let currentTrackStartOffset = session.audioTracks[self.currentTrackIndex].startOffset ?? 0.0 let seekTime = currentTime - currentTrackStartOffset self.audioPlayer.seek(to: CMTime(seconds: seekTime, preferredTimescale: 1000), toleranceBefore: .zero, toleranceAfter: .zero) { [weak self] completed in - if completed, let self = self { - // Start playback - self.audioPlayer.play() - self.rate = self.tmpRate - self.audioPlayer.rate = self.tmpRate - self.status = 1 - - // If we have an active sleep timer, reschedule based on rate - self.rescheduleSleepTimerAtTime(time: currentTime, secondsRemaining: sleepSecondsRemaining) - - // Update the progress - self.updateNowPlaying() - } + guard completed else { return } + guard let self = self else { return } + + // Start playback + self.audioPlayer.play() + self.rate = self.tmpRate + self.audioPlayer.rate = self.tmpRate + self.status = 1 + + // Update the progress + self.updateNowPlaying() } } @@ -342,22 +342,22 @@ class AudioPlayer: NSObject { public func seek(_ to: Double, from: String) { let continuePlaying = rate > 0.0 + // Capture remaining sleep time before changing the track position or pausing + let sleepSecondsRemaining = self.getSleepTimeRemaining() + pause() - NSLog("Seek to \(to) from \(from)") + NSLog("SEEK: Seek to \(to) from \(from)") guard let playbackSession = self.getPlaybackSession() else { return } let currentTrack = playbackSession.audioTracks[self.currentTrackIndex] let ctso = currentTrack.startOffset ?? 0.0 let trackEnd = ctso + currentTrack.duration - NSLog("Seek current track END = \(trackEnd)") - - // Capture remaining sleep time before changing the track position - let sleepSecondsRemaining = PlayerHandler.remainingSleepTime + NSLog("SEEK: Seek current track END = \(trackEnd)") let indexOfSeek = getItemIndexForTime(time: to) - NSLog("Seek to index \(indexOfSeek) | Current index \(self.currentTrackIndex)") + NSLog("SEEK: Seek to index \(indexOfSeek) | Current index \(self.currentTrackIndex)") // Reconstruct queue if seeking to a different track if (self.currentTrackIndex != indexOfSeek) { @@ -378,32 +378,30 @@ class AudioPlayer: NSObject { setupQueueItemStatusObserver() } else { - NSLog("Seeking in current item \(to)") + NSLog("SEEK: Seeking in current item \(to)") let currentTrackStartOffset = playbackSession.audioTracks[self.currentTrackIndex].startOffset ?? 0.0 let seekTime = to - currentTrackStartOffset self.audioPlayer.seek(to: CMTime(seconds: seekTime, preferredTimescale: 1000)) { [weak self] completed in - if !completed { - NSLog("WARNING: seeking not completed (to \(seekTime)") + guard completed else { return NSLog("SEEK: WARNING: seeking not completed (to \(seekTime)") } + guard let self = self else { return } + + // Reschedule the sleep timer + if let currentTime = self.getCurrentTime() { + self.rescheduleSleepTimerAtTime(time: currentTime, secondsRemaining: sleepSecondsRemaining) } if continuePlaying { - self?.play() - } - self?.updateNowPlaying() - - // If we have an active sleep timer, reschedule based on seek, since seek is fuzzy - // This needs to occur after play() to capture the correct playback rate - if let currentTime = self?.getCurrentTime() { - self?.rescheduleSleepTimerAtTime(time: currentTime, secondsRemaining: sleepSecondsRemaining) + self.play() } + self.updateNowPlaying() } } } public func setPlaybackRate(_ rate: Float, observed: Bool = false) { // Capture remaining sleep time before changing the rate - let sleepSecondsRemaining = PlayerHandler.remainingSleepTime + let sleepSecondsRemaining = self.getSleepTimeRemaining() let playbackSpeedChanged = rate > 0.0 && rate != self.tmpRate && !(observed && rate == 1) if self.audioPlayer.rate != rate { @@ -427,114 +425,6 @@ class AudioPlayer: NSObject { } } - public func getSleepStopAt() -> Double? { - return self.sleepTimeStopAt - } - - // Let iOS handle the sleep timer logic by letting us know when it's time to stop - public func setSleepTime(stopAt: Double, scaleBasedOnSpeed: Bool = false) { - NSLog("SLEEP TIMER: Scheduling for \(stopAt)") - - // Reset any previous sleep timer - self.removeSleepTimer() - - guard let currentTime = getCurrentTime() else { - NSLog("Failed to get currenTime") - return - } - - // Mark the time to stop playing - if scaleBasedOnSpeed { - // Consider paused as playing at 1x - let rate = Double(self.rate > 0 ? self.rate : 1) - - // Calculate the scaled time to stop at - let timeUntilSleep = (stopAt - currentTime) * rate - self.sleepTimeStopAt = currentTime + timeUntilSleep - - NSLog("SLEEP TIMER: Adjusted based on playback speed of \(rate) to \(self.sleepTimeStopAt!)") - } else { - self.sleepTimeStopAt = stopAt - } - - guard let sleepTimeStopAt = self.sleepTimeStopAt else { return } - let sleepTime = CMTime(seconds: sleepTimeStopAt, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) - - // Schedule the observation time - var times = [NSValue]() - times.append(NSValue(time: sleepTime)) - - sleepTimeToken = self.audioPlayer.addBoundaryTimeObserver(forTimes: times, queue: queue) { [weak self] in - NSLog("SLEEP TIMER: Pausing audio") - self?.pause() - PlayerHandler.sleepTimerChapterStopTime = nil - self?.removeSleepTimer() - } - - // Update the UI - NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: nil) - } - - private func rescheduleSleepTimerAtTime(time: Double, secondsRemaining: Int?) { - // Not a chapter sleep timer - let hadToCancelChapterSleepTimer = decideIfChapterSleepTimerNeedsToBeCanceled(time: time) - guard !hadToCancelChapterSleepTimer else { return } - guard PlayerHandler.sleepTimerChapterStopTime == nil else { return } - - // Verify sleep timer is set - guard self.sleepTimeToken != nil else { return } - - // Update the sleep timer - if let secondsRemaining = secondsRemaining { - let newSleepTimerPosition = time + Double(secondsRemaining) - self.setSleepTime(stopAt: newSleepTimerPosition, scaleBasedOnSpeed: true) - } - } - - private func decideIfChapterSleepTimerNeedsToBeCanceled(time: Double) -> Bool { - if let chapterSleepTime = PlayerHandler.sleepTimerChapterStopTime { - let sleepIsBeforeCurrentTime = Double(chapterSleepTime) <= time - if sleepIsBeforeCurrentTime { - PlayerHandler.sleepTimerChapterStopTime = nil - self.removeSleepTimer() - return true - } - } - - return false - } - - public func increaseSleepTime(extraTimeInSeconds: Double) { - if let sleepTime = PlayerHandler.remainingSleepTime, let currentTime = getCurrentTime() { - let newSleepTimerPosition = currentTime + Double(sleepTime) + extraTimeInSeconds - if newSleepTimerPosition > currentTime { - self.setSleepTime(stopAt: newSleepTimerPosition, scaleBasedOnSpeed: true) - } - } - } - - public func decreaseSleepTime(removeTimeInSeconds: Double) { - if let sleepTime = PlayerHandler.remainingSleepTime, let currentTime = getCurrentTime() { - let newSleepTimerPosition = currentTime + Double(sleepTime) - removeTimeInSeconds - guard newSleepTimerPosition > currentTime else { return } - if newSleepTimerPosition > currentTime { - self.setSleepTime(stopAt: newSleepTimerPosition, scaleBasedOnSpeed: true) - } - } - - } - - public func removeSleepTimer() { - self.sleepTimeStopAt = nil - if let token = sleepTimeToken { - self.audioPlayer.removeTimeObserver(token) - sleepTimeToken = nil - } - - // Update the UI - NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepEnded.rawValue), object: self) - } - public func getCurrentTime() -> Double? { guard let playbackSession = self.getPlaybackSession() else { return nil } let currentTrackTime = self.audioPlayer.currentTime().seconds diff --git a/ios/App/Shared/player/AudioPlayerSleepTimer.swift b/ios/App/Shared/player/AudioPlayerSleepTimer.swift new file mode 100644 index 00000000..85839984 --- /dev/null +++ b/ios/App/Shared/player/AudioPlayerSleepTimer.swift @@ -0,0 +1,155 @@ +// +// AudioPlayerSleepTimer.swift +// App +// +// Created by Ron Heft on 9/2/22. +// + +import Foundation +import AVFoundation + +extension AudioPlayer { + + // MARK: - Public API + + public func getSleepTimeRemaining() -> Double? { + guard let currentTime = self.getCurrentTime() else { return nil } + + // Return the player time until sleep + var timeUntilSleep: Double? = nil + if let chapterStopAt = self.sleepTimeChapterStopAt { + timeUntilSleep = chapterStopAt - currentTime + } else if let stopAt = self.sleepTimeStopAt { + timeUntilSleep = stopAt - currentTime + } + + // Scale the time until sleep based on the playback rate + if let timeUntilSleep = timeUntilSleep { + let timeUntilSleepScaled = timeUntilSleep / self.getPlaybackRate() + guard timeUntilSleepScaled.isNaN == false else { return nil } + + return timeUntilSleepScaled.rounded() + } else { + return nil + } + } + + // Let iOS handle the sleep timer logic by letting us know when it's time to stop + public func setSleepTime(stopAt: Double, scaleBasedOnSpeed: Bool = false) { + NSLog("SLEEP TIMER: Scheduling for \(stopAt)") + + // Reset any previous sleep timer + let isChapterSleepTimer = !scaleBasedOnSpeed + self.removeSleepTimer(resetStopAt: !isChapterSleepTimer) + + guard let currentTime = getCurrentTime() else { + NSLog("SLEEP TIMER: Failed to get currenTime") + return + } + + // Mark the time to stop playing + let scaledStopAt = self.calculateScaledStopAt(stopAt, currentTime: currentTime, scaleBasedOnSpeed: scaleBasedOnSpeed) + self.sleepTimeStopAt = scaledStopAt + let sleepTime = CMTime(seconds: scaledStopAt, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) + + // Schedule the observation time + var times = [NSValue]() + times.append(NSValue(time: sleepTime)) + + self.sleepTimeToken = self.audioPlayer.addBoundaryTimeObserver(forTimes: times, queue: self.queue) { [weak self] in + NSLog("SLEEP TIMER: Pausing audio") + self?.pause() + self?.removeSleepTimer() + } + + // Update the UI + NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: nil) + } + + public func increaseSleepTime(extraTimeInSeconds: Double) { + if let sleepTime = self.getSleepTimeRemaining(), let currentTime = getCurrentTime() { + let newSleepTimerPosition = currentTime + sleepTime + extraTimeInSeconds + if newSleepTimerPosition > currentTime { + self.setSleepTime(stopAt: newSleepTimerPosition, scaleBasedOnSpeed: true) + } + } + } + + public func decreaseSleepTime(removeTimeInSeconds: Double) { + if let sleepTime = self.getSleepTimeRemaining(), let currentTime = getCurrentTime() { + let newSleepTimerPosition = currentTime + sleepTime - removeTimeInSeconds + if newSleepTimerPosition > currentTime { + self.setSleepTime(stopAt: newSleepTimerPosition, scaleBasedOnSpeed: true) + } + } + + } + + public func removeSleepTimer(resetStopAt: Bool = true) { + if resetStopAt { + self.sleepTimeStopAt = nil + self.sleepTimeChapterStopAt = nil + } + + if let token = self.sleepTimeToken { + self.audioPlayer.removeTimeObserver(token) + self.sleepTimeToken = nil + } + + // Update the UI + NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepEnded.rawValue), object: self) + } + + + // MARK: - Internal helpers + + internal func rescheduleSleepTimerAtTime(time: Double, secondsRemaining: Double?) { + guard self.isSleepTimerSet() else { return } + + // Cancel a chapter sleep timer that is no longer valid + if isChapterSleepTimerBeforeTime(time) { + return self.removeSleepTimer() + } + + // Update the sleep timer + if !isChapterSleepTimer() { + guard let secondsRemaining = secondsRemaining else { return } + let newSleepTimerPosition = time + secondsRemaining + self.setSleepTime(stopAt: newSleepTimerPosition, scaleBasedOnSpeed: true) + } + } + + private func isChapterSleepTimerBeforeTime(_ time: Double) -> Bool { + if let chapterStopAt = self.sleepTimeChapterStopAt { + return chapterStopAt <= time + } + + return false + } + + private func isSleepTimerSet() -> Bool { + return self.sleepTimeStopAt != nil + } + + private func isChapterSleepTimer() -> Bool { + return self.sleepTimeChapterStopAt != nil + } + + private func getPlaybackRate() -> Double { + // Consider paused as playing at 1x + return Double(self.rate > 0 ? self.rate : 1) + } + + private func calculateScaledStopAt(_ stopAt: Double, currentTime: Double, scaleBasedOnSpeed: Bool) -> Double { + if scaleBasedOnSpeed { + // Calculate the scaled time to stop at + let secondsUntilStopAt1x = stopAt - currentTime + let secondsUntilSleep = secondsUntilStopAt1x * self.getPlaybackRate() + NSLog("SLEEP TIMER: Adjusted based on playback speed of \(self.getPlaybackRate()) to \(secondsUntilSleep)") + return currentTime + secondsUntilSleep + } else { + return stopAt + } + } + +} diff --git a/ios/App/Shared/player/PlayerHandler.swift b/ios/App/Shared/player/PlayerHandler.swift index b2f49587..f11bf2f5 100644 --- a/ios/App/Shared/player/PlayerHandler.swift +++ b/ios/App/Shared/player/PlayerHandler.swift @@ -11,8 +11,6 @@ import RealmSwift class PlayerHandler { private static var player: AudioPlayer? - public static var sleepTimerChapterStopTime: Int? = nil - public static func startPlayback(sessionId: String, playWhenReady: Bool, playbackRate: Float) { guard let session = Database.shared.getPlaybackSession(id: sessionId) else { return } @@ -59,34 +57,6 @@ class PlayerHandler { } } - public static var remainingSleepTime: Int? { - get { - guard let player = player else { return nil } - guard let currentTime = player.getCurrentTime() else { return nil } - - // Return the player time until sleep - var timeUntilSleep: Double? = nil - if let sleepTimerChapterStopTime = sleepTimerChapterStopTime { - timeUntilSleep = Double(sleepTimerChapterStopTime) - currentTime - } else if let stopAt = player.getSleepStopAt() { - timeUntilSleep = stopAt - currentTime - } - - // Scale the time until sleep based on the playback rate - if let timeUntilSleep = timeUntilSleep { - // Consider paused as playing at 1x - let rate = Double(player.rate > 0 ? player.rate : 1) - - let timeUntilSleepScaled = timeUntilSleep / rate - guard timeUntilSleepScaled.isNaN == false else { return nil } - - return Int(timeUntilSleepScaled.rounded()) - } else { - return nil - } - } - } - public static func getCurrentTime() -> Double? { self.player?.getCurrentTime() } @@ -95,31 +65,29 @@ class PlayerHandler { self.player?.setPlaybackRate(speed) } + public static func getSleepTimeRemaining() -> Double? { + return self.player?.getSleepTimeRemaining() + } + public static func setSleepTime(secondsUntilSleep: Double) { - guard let player = player else { return } - guard let currentTime = player.getCurrentTime() else { return } + guard let currentTime = self.player?.getCurrentTime() else { return } let stopAt = secondsUntilSleep + currentTime - player.setSleepTime(stopAt: stopAt, scaleBasedOnSpeed: true) + self.player?.setSleepTime(stopAt: stopAt, scaleBasedOnSpeed: true) } public static func setChapterSleepTime(stopAt: Double) { - guard let player = player else { return } - self.sleepTimerChapterStopTime = Int(stopAt) - player.setSleepTime(stopAt: stopAt, scaleBasedOnSpeed: false) + self.player?.setSleepTime(stopAt: stopAt, scaleBasedOnSpeed: false) } public static func increaseSleepTime(increaseSeconds: Double) { - self.sleepTimerChapterStopTime = nil self.player?.increaseSleepTime(extraTimeInSeconds: increaseSeconds) } public static func decreaseSleepTime(decreaseSeconds: Double) { - self.sleepTimerChapterStopTime = nil self.player?.decreaseSleepTime(removeTimeInSeconds: decreaseSeconds) } public static func cancelSleepTime() { - PlayerHandler.sleepTimerChapterStopTime = nil self.player?.removeSleepTimer() }