advplyr.audiobookshelf-app/ios/App/Shared/player/AudioPlayerSleepTimer.swift

158 lines
5.4 KiB
Swift
Raw Normal View History

2022-09-02 16:31:47 -04:00
//
// AudioPlayerSleepTimer.swift
// App
//
// Created by Ron Heft on 9/2/22.
//
import Foundation
import AVFoundation
extension AudioPlayer {
// MARK: - Public API
2022-09-02 18:22:42 -04:00
public func isSleepTimerSet() -> Bool {
return self.isCountdownSleepTimerSet() || self.isChapterSleepTimerSet()
}
2022-09-02 16:31:47 -04:00
public func getSleepTimeRemaining() -> Double? {
guard let currentTime = self.getCurrentTime() else { return nil }
// Return the player time until sleep
2022-09-02 18:22:42 -04:00
var sleepTimeRemaining: Double? = nil
2022-09-02 16:31:47 -04:00
if let chapterStopAt = self.sleepTimeChapterStopAt {
sleepTimeRemaining = (chapterStopAt - currentTime) / Double(self.rate > 0 ? self.rate : 1.0)
2022-09-02 18:22:42 -04:00
} else if self.isCountdownSleepTimerSet() {
sleepTimeRemaining = self.sleepTimeRemaining
2022-09-02 16:31:47 -04:00
}
2022-09-02 18:22:42 -04:00
return sleepTimeRemaining
2022-09-02 16:31:47 -04:00
}
2022-09-02 18:22:42 -04:00
public func setSleepTimer(secondsUntilSleep: Double) {
logger.log("SLEEP TIMER: Sleeping in \(secondsUntilSleep) seconds")
2022-09-02 18:22:42 -04:00
self.removeSleepTimer()
self.sleepTimeRemaining = secondsUntilSleep
2022-09-02 16:31:47 -04:00
2022-09-02 18:22:42 -04:00
DispatchQueue.runOnMainQueue {
2022-09-03 16:34:31 -04:00
self.sleepTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
if self?.isPlaying() ?? false {
self?.decrementSleepTimerIfRunning()
2022-09-02 18:22:42 -04:00
}
}
2022-09-02 16:31:47 -04:00
}
2022-09-02 18:22:42 -04:00
// Update the UI
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: nil)
}
2022-09-18 14:21:41 -04:00
public func setChapterSleepTimer(stopAt: Double?) {
2022-09-02 18:22:42 -04:00
self.removeSleepTimer()
2022-09-18 14:21:41 -04:00
guard let stopAt = stopAt else { return }
guard let currentTime = self.getCurrentTime() else { return }
guard stopAt >= currentTime else { return }
logger.log("SLEEP TIMER: Scheduling for chapter end \(stopAt)")
2022-09-02 16:31:47 -04:00
// Schedule the observation time
2022-09-02 18:22:42 -04:00
self.sleepTimeChapterStopAt = stopAt
// Get the current track
guard let playbackSession = self.getPlaybackSession() else { return }
let currentTrack = playbackSession.audioTracks[currentTrackIndex]
// Set values
guard let trackStartTime = currentTrack.startOffset else { return }
guard let trackEndTime = currentTrack.endOffset else { return }
// Verify the stop is during the current audio track
guard trackEndTime >= stopAt else { return }
// Schedule the observation time
let trackBasedStopTime = stopAt - trackStartTime
let sleepTime = CMTime(seconds: trackBasedStopTime, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
2022-09-02 16:31:47 -04:00
var times = [NSValue]()
times.append(NSValue(time: sleepTime))
2022-09-02 18:22:42 -04:00
self.sleepTimeChapterToken = self.audioPlayer.addBoundaryTimeObserver(forTimes: times, queue: self.queue) { [weak self] in
self?.handleSleepEnd()
2022-09-02 16:31:47 -04:00
}
// Update the UI
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: nil)
}
public func increaseSleepTime(extraTimeInSeconds: Double) {
2022-09-02 18:22:42 -04:00
self.removeChapterSleepTimer()
guard let sleepTimeRemaining = self.sleepTimeRemaining else { return }
self.sleepTimeRemaining = sleepTimeRemaining + extraTimeInSeconds
// Update the UI
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: nil)
2022-09-02 16:31:47 -04:00
}
public func decreaseSleepTime(removeTimeInSeconds: Double) {
2022-09-02 18:22:42 -04:00
self.removeChapterSleepTimer()
guard let sleepTimeRemaining = self.sleepTimeRemaining else { return }
self.sleepTimeRemaining = sleepTimeRemaining - removeTimeInSeconds
// Update the UI
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: nil)
2022-09-02 16:31:47 -04:00
}
2022-09-02 18:22:42 -04:00
public func removeSleepTimer() {
self.sleepTimer?.invalidate()
self.sleepTimer = nil
self.removeChapterSleepTimer()
self.sleepTimeRemaining = nil
2022-09-02 16:31:47 -04:00
// Update the UI
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepEnded.rawValue), object: self)
2022-09-02 16:31:47 -04:00
}
// MARK: - Internal helpers
2022-09-18 14:21:41 -04:00
internal func handleTrackChangeForChapterSleepTimer() {
// If no sleep timer is set, this does nothing
self.setChapterSleepTimer(stopAt: self.sleepTimeChapterStopAt)
}
private func decrementSleepTimerIfRunning() {
2022-09-02 18:22:42 -04:00
if var sleepTimeRemaining = self.sleepTimeRemaining {
sleepTimeRemaining -= 1
self.sleepTimeRemaining = sleepTimeRemaining
// Handle the sleep if the timer has expired
if sleepTimeRemaining <= 0 {
self.handleSleepEnd()
}
2022-09-02 16:31:47 -04:00
}
2022-09-02 18:22:42 -04:00
}
private func handleSleepEnd() {
logger.log("SLEEP TIMER: Pausing audio")
2022-09-02 18:22:42 -04:00
self.pause()
self.removeSleepTimer()
}
private func removeChapterSleepTimer() {
if let token = self.sleepTimeChapterToken {
self.audioPlayer.removeTimeObserver(token)
2022-09-02 16:31:47 -04:00
}
2022-09-02 18:22:42 -04:00
self.sleepTimeChapterToken = nil
self.sleepTimeChapterStopAt = nil
2022-09-02 16:31:47 -04:00
}
2022-09-18 14:21:41 -04:00
private func isCountdownSleepTimerSet() -> Bool {
2022-09-02 18:22:42 -04:00
return self.sleepTimeRemaining != nil
2022-09-02 16:31:47 -04:00
}
2022-09-18 14:21:41 -04:00
private func isChapterSleepTimerSet() -> Bool {
2022-09-02 16:31:47 -04:00
return self.sleepTimeChapterStopAt != nil
}
}