Merge pull request #519 from benonymity/playerUpdates

iOS Player Updates
This commit is contained in:
advplyr
2023-01-29 14:08:29 -06:00
committed by GitHub
4 changed files with 43 additions and 12 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ target 'Audiobookshelf' do
capacitor_pods capacitor_pods
# Add your Pods here # Add your Pods here
pod 'RealmSwift', '~>10' pod 'RealmSwift', '~> 10'
pod 'Alamofire', '~> 5.5' pod 'Alamofire', '~> 5.5'
end end
+20 -10
View File
@@ -37,6 +37,7 @@ class AudioPlayer: NSObject {
private var sessionId: String private var sessionId: String
private var timeObserverToken: Any? private var timeObserverToken: Any?
private var sleepTimerObserverToken: Any?
private var queueObserver:NSKeyValueObservation? private var queueObserver:NSKeyValueObservation?
private var queueItemStatusObserver:NSKeyValueObservation? private var queueItemStatusObserver:NSKeyValueObservation?
@@ -96,7 +97,7 @@ class AudioPlayer: NSObject {
self.audioPlayer.insert(item, after:self.audioPlayer.items().last) self.audioPlayer.insert(item, after:self.audioPlayer.items().last)
} }
setupTimeObserver() setupTimeObservers()
setupQueueObserver() setupQueueObserver()
setupQueueItemStatusObserver() setupQueueItemStatusObserver()
@@ -130,7 +131,7 @@ class AudioPlayer: NSObject {
// Remove observers // Remove observers
self.audioPlayer.removeObserver(self, forKeyPath: #keyPath(AVPlayer.rate), context: &playerContext) self.audioPlayer.removeObserver(self, forKeyPath: #keyPath(AVPlayer.rate), context: &playerContext)
self.audioPlayer.removeObserver(self, forKeyPath: #keyPath(AVPlayer.currentItem), context: &playerContext) self.audioPlayer.removeObserver(self, forKeyPath: #keyPath(AVPlayer.currentItem), context: &playerContext)
self.removeTimeObserver() self.removeTimeObservers()
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.closed.rawValue), object: nil) NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.closed.rawValue), object: nil)
@@ -170,15 +171,15 @@ class AudioPlayer: NSObject {
NotificationCenter.default.removeObserver(self, name: AVAudioSession.routeChangeNotification, object: AVAudioSession.sharedInstance()) NotificationCenter.default.removeObserver(self, name: AVAudioSession.routeChangeNotification, object: AVAudioSession.sharedInstance())
} }
private func setupTimeObserver() { private func setupTimeObservers() {
// Time observer should be configured on the main queue // Time observer should be configured on the main queue
DispatchQueue.runOnMainQueue { DispatchQueue.runOnMainQueue {
self.removeTimeObserver() self.removeTimeObservers()
let timeScale = CMTimeScale(NSEC_PER_SEC) let timeScale = CMTimeScale(NSEC_PER_SEC)
// Rate will be different depending on playback speed, aim for 2 observations/sec // Save the current time every 15 seconds
let seconds = 0.5 * (self.rate > 0 ? self.rate : 1.0) var seconds = 15.0
let time = CMTime(seconds: Double(seconds), preferredTimescale: timeScale) var time = CMTime(seconds: Double(seconds), preferredTimescale: timeScale)
self.timeObserverToken = self.audioPlayer.addPeriodicTimeObserver(forInterval: time, queue: self.queue) { [weak self] time in self.timeObserverToken = self.audioPlayer.addPeriodicTimeObserver(forInterval: time, queue: self.queue) { [weak self] time in
guard let self = self else { return } guard let self = self else { return }
guard self.isInitialized() else { return } guard self.isInitialized() else { return }
@@ -190,7 +191,12 @@ class AudioPlayer: NSObject {
// Let the player update the current playback positions // Let the player update the current playback positions
await PlayerProgress.shared.syncFromPlayer(currentTime: currentTime, includesPlayProgress: isPlaying, isStopping: false) await PlayerProgress.shared.syncFromPlayer(currentTime: currentTime, includesPlayProgress: isPlaying, isStopping: false)
} }
}
// Update the sleep timer every second
seconds = 1.0
time = CMTime(seconds: Double(seconds), preferredTimescale: timeScale)
self.sleepTimerObserverToken = self.audioPlayer.addPeriodicTimeObserver(forInterval: time, queue: self.queue) { [weak self] time in
guard let self = self else { return }
if self.isSleepTimerSet() { if self.isSleepTimerSet() {
// Update the UI // Update the UI
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: nil) NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: nil)
@@ -199,11 +205,15 @@ class AudioPlayer: NSObject {
} }
} }
private func removeTimeObserver() { private func removeTimeObservers() {
if let timeObserverToken = timeObserverToken { if let timeObserverToken = timeObserverToken {
self.audioPlayer.removeTimeObserver(timeObserverToken) self.audioPlayer.removeTimeObserver(timeObserverToken)
self.timeObserverToken = nil self.timeObserverToken = nil
} }
if let sleepTimerObserverToken = sleepTimerObserverToken {
self.audioPlayer.removeTimeObserver(sleepTimerObserverToken)
self.sleepTimerObserverToken = nil
}
} }
private func setupQueueObserver() { private func setupQueueObserver() {
@@ -426,7 +436,7 @@ class AudioPlayer: NSObject {
self.tmpRate = rate self.tmpRate = rate
// Setup the time observer again at the new rate // Setup the time observer again at the new rate
self.setupTimeObserver() self.setupTimeObservers()
} }
} }
+6 -1
View File
@@ -12,7 +12,7 @@ import RealmSwift
class PlayerProgress { class PlayerProgress {
public static let shared = PlayerProgress() public static let shared = PlayerProgress()
private static let TIME_BETWEEN_SESSION_SYNC_IN_SECONDS = 10.0 private static var TIME_BETWEEN_SESSION_SYNC_IN_SECONDS = 15.0
private let logger = AppLogger(category: "PlayerProgress") private let logger = AppLogger(category: "PlayerProgress")
@@ -117,6 +117,11 @@ class PlayerProgress {
} }
private func updateServerSessionFromLocalSession(_ session: PlaybackSession, rateLimitSync: Bool = false) async throws { private func updateServerSessionFromLocalSession(_ session: PlaybackSession, rateLimitSync: Bool = false) async throws {
if ProcessInfo.processInfo.isLowPowerModeEnabled == true {
PlayerProgress.TIME_BETWEEN_SESSION_SYNC_IN_SECONDS = 60.0
} else {
PlayerProgress.TIME_BETWEEN_SESSION_SYNC_IN_SECONDS = 15.0
}
var safeToSync = true var safeToSync = true
guard var session = session.thaw() else { return } guard var session = session.thaw() else { return }
+16
View File
@@ -243,12 +243,28 @@ class ApiClient {
callback(obj) callback(obj)
} }
} }
public static func pingServer() async -> Bool {
var status = true
AF.request("\(Store.serverConfig!.address)/ping", method: .get).responseDecodable(of: PingResponsePayload.self) { response in
switch response.result {
case .success:
status = true
case .failure:
status = false
}
}
return status
}
} }
struct LocalMediaProgressSyncPayload: Codable { struct LocalMediaProgressSyncPayload: Codable {
var localMediaProgress: [LocalMediaProgress] var localMediaProgress: [LocalMediaProgress]
} }
struct PingResponsePayload: Codable {
var success: Bool
}
struct MediaProgressSyncResponsePayload: Decodable { struct MediaProgressSyncResponsePayload: Decodable {
var numServerProgressUpdates: Int? var numServerProgressUpdates: Int?
var localProgressUpdates: [LocalMediaProgress]? var localProgressUpdates: [LocalMediaProgress]?