mirror of
https://github.com/sudoxnym/audiobookshelf-atv.git
synced 2026-07-14 16:02:24 +00:00
Rewrite sleep timer
This commit is contained in:
parent
34e68593bc
commit
586405f9de
5 changed files with 209 additions and 193 deletions
|
|
@ -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 = "<group>"; };
|
||||
E9D5507228AC218300C746DD /* DaoExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaoExtensions.swift; sourceTree = "<group>"; };
|
||||
E9D5507428AEF93100C746DD /* PlayerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSettings.swift; sourceTree = "<group>"; };
|
||||
E9DFCBFA28C28F4A00B36356 /* AudioPlayerSleepTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerSleepTimer.swift; sourceTree = "<group>"; };
|
||||
E9E985F728B02D9400957F23 /* PlayerProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerProgress.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
/* 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 */,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
155
ios/App/Shared/player/AudioPlayerSleepTimer.swift
Normal file
155
ios/App/Shared/player/AudioPlayerSleepTimer.swift
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue