From 394363c8cb6c91e9a493c6ebd6de8930de134121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Kr=C3=A4mer?= Date: Sat, 30 Apr 2022 10:58:08 +0200 Subject: [PATCH 1/6] Send report when playback ends --- ios/App/App.xcodeproj/project.pbxproj | 6 +++--- ios/App/Shared/player/AudioPlayer.swift | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 9859b22d..4df66f67 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -460,13 +460,13 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 6; - DEVELOPMENT_TEAM = 7UFJ7D8V6A; + DEVELOPMENT_TEAM = N8AA4S3S96; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 0.9.43; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; - PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app.development; + PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app.dev; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OBJC_BRIDGING_HEADER = "App/App-Bridging-Header.h"; @@ -484,7 +484,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 6; - DEVELOPMENT_TEAM = 7UFJ7D8V6A; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; diff --git a/ios/App/Shared/player/AudioPlayer.swift b/ios/App/Shared/player/AudioPlayer.swift index d088400e..52e2b886 100644 --- a/ios/App/Shared/player/AudioPlayer.swift +++ b/ios/App/Shared/player/AudioPlayer.swift @@ -60,6 +60,7 @@ class AudioPlayer: NSObject { playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: .new, context: &playerItemContext) self.audioPlayer.replaceCurrentItem(with: playerItem) + seek(playbackSession.currentTime) NSLog("Audioplayer ready") } @@ -155,7 +156,6 @@ class AudioPlayer: NSObject { } self.rate = rate - self.updateNowPlaying() } @@ -258,14 +258,11 @@ class AudioPlayer: NSObject { if playerStatus == .readyToPlay { self.updateNowPlaying() - let firstReady = self.status < 0 self.status = 0 if self.playWhenReady { seek(playbackSession.currentTime) self.playWhenReady = false self.play() - } else if (firstReady) { // Only seek on first readyToPlay - seek(playbackSession.currentTime) } } } @@ -273,6 +270,7 @@ class AudioPlayer: NSObject { if keyPath == #keyPath(AVPlayer.rate) { self.setPlaybackRate(change?[.newKey] as? Float ?? 1.0, observed: true) } else if keyPath == #keyPath(AVPlayer.currentItem) { + NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil) NSLog("WARNING: Item ended") } } else { From 9701c767b226dfbe3835afa02b90a1f244477588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Kr=C3=A4mer?= Date: Tue, 3 May 2022 12:55:13 +0200 Subject: [PATCH 2/6] Small improvements --- ios/App/App/plugins/AbsDatabase.swift | 9 +++--- ios/App/Shared/player/AudioPlayer.swift | 15 +++++----- ios/App/Shared/player/PlayerHandler.swift | 33 +++++++++++---------- ios/App/Shared/util/ApiClient.swift | 9 +++++- ios/App/Shared/util/Database.swift | 35 ++++++++++++++-------- ios/App/Shared/util/Extensions.swift | 11 +++++++ ios/App/Shared/util/NowPlayingInfo.swift | 36 +++++++++-------------- ios/App/Shared/util/Store.swift | 4 +-- 8 files changed, 86 insertions(+), 66 deletions(-) diff --git a/ios/App/App/plugins/AbsDatabase.swift b/ios/App/App/plugins/AbsDatabase.swift index de9c5c89..99a403e9 100644 --- a/ios/App/App/plugins/AbsDatabase.swift +++ b/ios/App/App/plugins/AbsDatabase.swift @@ -53,7 +53,7 @@ public class AbsDatabase: CAPPlugin { } @objc func removeServerConnectionConfig(_ call: CAPPluginCall) { let id = call.getString("serverConnectionConfigId", "") - Database.deleteServerConnectionConfig(id: id) + Database.shared.deleteServerConnectionConfig(id: id) call.resolve() } @@ -63,13 +63,12 @@ public class AbsDatabase: CAPPlugin { } @objc func getDeviceData(_ call: CAPPluginCall) { - let configs = Database.getServerConnectionConfigs() - let index = Database.getLastActiveConfigIndex() + let configs = Database.shared.getServerConnectionConfigs() + let index = Database.shared.getLastActiveConfigIndex() call.resolve([ "serverConnectionConfigs": configs.map { config in convertServerConnectionConfigToJSON(config: config) }, - "lastServerConnectionConfigId": configs.first { config in config.index == index }?.id, - // Luckily this isn't implemented yet + "lastServerConnectionConfigId": configs.first { config in config.index == index }?.id as Any, // "currentLocalPlaybackSession": nil, ]) } diff --git a/ios/App/Shared/player/AudioPlayer.swift b/ios/App/Shared/player/AudioPlayer.swift index 52e2b886..29156d40 100644 --- a/ios/App/Shared/player/AudioPlayer.swift +++ b/ios/App/Shared/player/AudioPlayer.swift @@ -70,6 +70,7 @@ class AudioPlayer: NSObject { public func destroy() { // Pause is not synchronous causing this error on below lines: // AVAudioSession_iOS.mm:1206 Deactivating an audio session that has running I/O. All I/O should be stopped or paused prior to deactivating the audio session + // It is related to L79 `AVAudioSession.sharedInstance().setActive(false)` pause() audioPlayer.replaceCurrentItem(with: nil) @@ -80,11 +81,9 @@ class AudioPlayer: NSObject { print(error) } - // Throws error Possibly related to the error above -// DispatchQueue.main.sync { -// UIApplication.shared.endReceivingRemoteControlEvents() -// } - + DispatchQueue.runOnMainQueue { + UIApplication.shared.endReceivingRemoteControlEvents() + } NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.closed.rawValue), object: nil) } @@ -186,9 +185,9 @@ class AudioPlayer: NSObject { // MARK: - Now playing private func setupRemoteTransportControls() { - // DispatchQueue.main.sync { + DispatchQueue.runOnMainQueue { UIApplication.shared.beginReceivingRemoteControlEvents() - // } + } let commandCenter = MPRemoteCommandCenter.shared() commandCenter.playCommand.isEnabled = true @@ -246,7 +245,7 @@ class AudioPlayer: NSObject { } private func updateNowPlaying() { NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil) - NowPlayingInfo.update(duration: getDuration(), currentTime: getCurrentTime(), rate: rate) + NowPlayingInfo.shared.update(duration: getDuration(), currentTime: getCurrentTime(), rate: rate) } // MARK: - Observer diff --git a/ios/App/Shared/player/PlayerHandler.swift b/ios/App/Shared/player/PlayerHandler.swift index c2009e7b..b7554139 100644 --- a/ios/App/Shared/player/PlayerHandler.swift +++ b/ios/App/Shared/player/PlayerHandler.swift @@ -20,16 +20,16 @@ class PlayerHandler { player = nil } - NowPlayingInfo.setSessionMetadata(metadata: NowPlayingMetadata(id: session.id, itemId: session.libraryItemId!, artworkUrl: session.coverPath, title: session.displayTitle ?? "Unknown title", author: session.displayAuthor, series: nil)) + NowPlayingInfo.shared.setSessionMetadata(metadata: NowPlayingMetadata(id: session.id, itemId: session.libraryItemId!, artworkUrl: session.coverPath, title: session.displayTitle ?? "Unknown title", author: session.displayAuthor, series: nil)) self.session = session player = AudioPlayer(playbackSession: session, playWhenReady: playWhenReady, playbackRate: playbackRate) - // DispatchQueue.main.sync { - timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in - self.tick() + DispatchQueue.runOnMainQueue { + timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in + self.tick() + } } - // } } public static func stopPlayback() { player?.destroy() @@ -38,7 +38,7 @@ class PlayerHandler { timer?.invalidate() timer = nil - NowPlayingInfo.reset() + NowPlayingInfo.shared.reset() } public static func getCurrentTime() -> Double? { @@ -63,20 +63,20 @@ class PlayerHandler { } public static func seekForward(amount: Double) { - if player == nil { + guard let player = player else { return } - let destinationTime = player!.getCurrentTime() + amount - player!.seek(destinationTime) + let destinationTime = player.getCurrentTime() + amount + player.seek(destinationTime) } public static func seekBackward(amount: Double) { - if player == nil { + guard let player = player else { return } - let destinationTime = player!.getCurrentTime() - amount - player!.seek(destinationTime) + let destinationTime = player.getCurrentTime() - amount + player.seek(destinationTime) } public static func seek(amount: Double) { player?.seek(amount) @@ -109,13 +109,16 @@ class PlayerHandler { } } public static func syncProgress() { - if player == nil || session == nil { + if session == nil { + return + } + guard let player = player else { return } - let report = PlaybackReport(currentTime: player!.getCurrentTime(), duration: player!.getDuration(), timeListened: listeningTimePassedSinceLastSync) + let report = PlaybackReport(currentTime: player.getCurrentTime(), duration: player.getDuration(), timeListened: listeningTimePassedSinceLastSync) - session!.currentTime = player!.getCurrentTime() + session!.currentTime = player.getCurrentTime() listeningTimePassedSinceLastSync = 0 // TODO: check if online diff --git a/ios/App/Shared/util/ApiClient.swift b/ios/App/Shared/util/ApiClient.swift index 2b3f06bd..3a0404cc 100644 --- a/ios/App/Shared/util/ApiClient.swift +++ b/ios/App/Shared/util/ApiClient.swift @@ -9,6 +9,14 @@ import Foundation import Alamofire class ApiClient { + public static func getData(from url: URL, completion: @escaping (UIImage?) -> Void) { + URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in + if let data = data { + completion(UIImage(data:data)) + } + }).resume() + } + public static func postResource(endpoint: String, parameters: [String: String], decodable: T.Type = T.self, callback: ((_ param: T) -> Void)?) { if (Store.serverConfig == nil) { NSLog("Server config not set") @@ -54,7 +62,6 @@ class ApiClient { } public static func startPlaybackSession(libraryItemId: String, episodeId: String?, callback: @escaping (_ param: PlaybackSession) -> Void) { - var endpoint = "api/items/\(libraryItemId)/play" if episodeId != nil { endpoint += "/\(episodeId!)" diff --git a/ios/App/Shared/util/Database.swift b/ios/App/Shared/util/Database.swift index d9e0b63a..1c528dfe 100644 --- a/ios/App/Shared/util/Database.swift +++ b/ios/App/Shared/util/Database.swift @@ -10,16 +10,25 @@ import RealmSwift class Database { // All DB releated actions must be executed on "realm-queue" - public static let realmQueue = DispatchQueue(label: "realm-queue") - private static var instance: Realm = try! Realm(queue: realmQueue) + public static let realmQueue: DispatchQueue = DispatchQueue(label: "realm-queue") + public static var shared = { + realmQueue.sync { + return Database() + } + }() + + private var instance: Realm + private init() { + self.instance = try! Realm(queue: Database.realmQueue) + } - public static func setServerConnectionConfig(config: ServerConnectionConfig) { + public func setServerConnectionConfig(config: ServerConnectionConfig) { var refrence: ThreadSafeReference? if config.realm != nil { refrence = ThreadSafeReference(to: config) } - realmQueue.sync { + Database.realmQueue.sync { let existing: ServerConnectionConfig? = instance.object(ofType: ServerConnectionConfig.self, forPrimaryKey: config.id) if config.index == 0 { @@ -55,8 +64,8 @@ class Database { setLastActiveConfigIndex(index: config.index) } } - public static func deleteServerConnectionConfig(id: String) { - realmQueue.sync { + public func deleteServerConnectionConfig(id: String) { + Database.realmQueue.sync { let config = instance.object(ofType: ServerConnectionConfig.self, forPrimaryKey: id) do { @@ -71,10 +80,10 @@ class Database { } } } - public static func getServerConnectionConfigs() -> [ServerConnectionConfig] { + public func getServerConnectionConfigs() -> [ServerConnectionConfig] { var refrences: [ThreadSafeReference] = [] - realmQueue.sync { + Database.realmQueue.sync { let configs = instance.objects(ServerConnectionConfig.self) refrences = configs.map { config in return ThreadSafeReference(to: config) @@ -94,12 +103,12 @@ class Database { } } - public static func setLastActiveConfigIndexToNil() { - realmQueue.sync { + public func setLastActiveConfigIndexToNil() { + Database.realmQueue.sync { setLastActiveConfigIndex(index: nil) } } - public static func setLastActiveConfigIndex(index: Int?) { + public func setLastActiveConfigIndex(index: Int?) { let existing = instance.objects(ServerConnectionConfigActiveIndex.self) let obj = ServerConnectionConfigActiveIndex() obj.index = index @@ -114,8 +123,8 @@ class Database { debugPrint(exception) } } - public static func getLastActiveConfigIndex() -> Int? { - return realmQueue.sync { + public func getLastActiveConfigIndex() -> Int? { + return Database.realmQueue.sync { return instance.objects(ServerConnectionConfigActiveIndex.self).first?.index ?? nil } } diff --git a/ios/App/Shared/util/Extensions.swift b/ios/App/Shared/util/Extensions.swift index 22afddd2..e4badb99 100644 --- a/ios/App/Shared/util/Extensions.swift +++ b/ios/App/Shared/util/Extensions.swift @@ -18,3 +18,14 @@ extension Encodable { return dictionary } } +extension DispatchQueue { + static func runOnMainQueue(callback: @escaping (() -> Void)) { + if Thread.isMainThread { + callback() + } else { + DispatchQueue.main.sync { + callback() + } + } + } +} diff --git a/ios/App/Shared/util/NowPlayingInfo.swift b/ios/App/Shared/util/NowPlayingInfo.swift index e268ec4c..71e0528a 100644 --- a/ios/App/Shared/util/NowPlayingInfo.swift +++ b/ios/App/Shared/util/NowPlayingInfo.swift @@ -8,14 +8,6 @@ import Foundation import MediaPlayer -func getData(from url: URL, completion: @escaping (UIImage?) -> Void) { - URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in - if let data = data { - completion(UIImage(data:data)) - } - }).resume() -} - struct NowPlayingMetadata { var id: String var itemId: String @@ -26,22 +18,22 @@ struct NowPlayingMetadata { } class NowPlayingInfo { - private static var nowPlayingInfo: [String: Any] = [:] + static var shared = { + return NowPlayingInfo() + }() - public static func setSessionMetadata(metadata: NowPlayingMetadata) { + private var nowPlayingInfo: [String: Any] + private init() { + self.nowPlayingInfo = [:] + } + + public func setSessionMetadata(metadata: NowPlayingMetadata) { setMetadata(artwork: nil, metadata: metadata) - /* - if !shouldFetchCover(id: metadata.id) || metadata.artworkUrl == nil { - return - } - */ - guard let url = URL(string: "\(Store.serverConfig!.address)/api/items/\(metadata.itemId)/cover?token=\(Store.serverConfig!.token)") else { return } - - getData(from: url) { [self] image in + ApiClient.getData(from: url) { [self] image in guard let downloadedImage = image else { return } @@ -52,7 +44,7 @@ class NowPlayingInfo { self.setMetadata(artwork: artwork, metadata: metadata) } } - public static func update(duration: Double, currentTime: Double, rate: Float) { + public func update(duration: Double, currentTime: Double, rate: Float) { nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = duration nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentTime nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = rate @@ -60,12 +52,12 @@ class NowPlayingInfo { MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo } - public static func reset() { + public func reset() { nowPlayingInfo = [:] MPNowPlayingInfoCenter.default().nowPlayingInfo = nil } - private static func setMetadata(artwork: MPMediaItemArtwork?, metadata: NowPlayingMetadata?) { + private func setMetadata(artwork: MPMediaItemArtwork?, metadata: NowPlayingMetadata?) { if metadata == nil { return } @@ -84,7 +76,7 @@ class NowPlayingInfo { nowPlayingInfo[MPMediaItemPropertyArtist] = metadata!.author ?? "unknown" nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = metadata!.series } - private static func shouldFetchCover(id: String) -> Bool { + private func shouldFetchCover(id: String) -> Bool { nowPlayingInfo[MPNowPlayingInfoPropertyExternalContentIdentifier] as? String != id || nowPlayingInfo[MPMediaItemPropertyArtwork] == nil } } diff --git a/ios/App/Shared/util/Store.swift b/ios/App/Shared/util/Store.swift index a4ae9863..52fbd059 100644 --- a/ios/App/Shared/util/Store.swift +++ b/ios/App/Shared/util/Store.swift @@ -16,9 +16,9 @@ class Store { } set(updated) { if updated != nil { - Database.setServerConnectionConfig(config: updated!) + Database.shared.setServerConnectionConfig(config: updated!) } else { - Database.setLastActiveConfigIndexToNil() + Database.shared.setLastActiveConfigIndexToNil() } Database.realmQueue.sync { From 4203654ec89b8decf142d6314c2a1d83428a1211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Kr=C3=A4mer?= Date: Tue, 3 May 2022 14:32:46 +0200 Subject: [PATCH 3/6] Added sleep timer --- ios/App/App/plugins/AbsAudioPlayer.m | 6 ++ ios/App/App/plugins/AbsAudioPlayer.swift | 85 +++++++++++++++++++---- ios/App/Shared/player/PlayerHandler.swift | 72 +++++++++++-------- ios/App/Shared/util/PlayerEvents.swift | 2 + 4 files changed, 124 insertions(+), 41 deletions(-) diff --git a/ios/App/App/plugins/AbsAudioPlayer.m b/ios/App/App/plugins/AbsAudioPlayer.m index 2bb2c0c2..7e97d4ae 100644 --- a/ios/App/App/plugins/AbsAudioPlayer.m +++ b/ios/App/App/plugins/AbsAudioPlayer.m @@ -23,4 +23,10 @@ CAP_PLUGIN(AbsAudioPlayer, "AbsAudioPlayer", CAP_PLUGIN_METHOD(seekBackward, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(getCurrentTime, CAPPluginReturnPromise); + + CAP_PLUGIN_METHOD(cancelSleepTimer, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(decreaseSleepTime, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(increaseSleepTime, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(getSleepTimerTime, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(setSleepTimer, CAPPluginReturnPromise); ) diff --git a/ios/App/App/plugins/AbsAudioPlayer.swift b/ios/App/App/plugins/AbsAudioPlayer.swift index 51c1f7a3..3cc31218 100644 --- a/ios/App/App/plugins/AbsAudioPlayer.swift +++ b/ios/App/App/plugins/AbsAudioPlayer.swift @@ -13,9 +13,12 @@ public class AbsAudioPlayer: CAPPlugin { override public func load() { NotificationCenter.default.addObserver(self, selector: #selector(sendMetadata), name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(sendPlaybackClosedEvent), name: NSNotification.Name(PlayerEvents.closed.rawValue), object: nil) - self.bridge?.webView?.allowsBackForwardNavigationGestures = true; NotificationCenter.default.addObserver(self, selector: #selector(sendMetadata), name: UIApplication.didBecomeActiveNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(sendMetadata), name: UIApplication.willEnterForegroundNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(sendSleepTimerSet), name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(sendSleepTimerEnded), name: NSNotification.Name(PlayerEvents.sleepEnded.rawValue), object: nil) + + self.bridge?.webView?.allowsBackForwardNavigationGestures = true; } @objc func prepareLibraryItem(_ call: CAPPluginCall) { @@ -69,15 +72,15 @@ public class AbsAudioPlayer: CAPPlugin { } @objc func playPause(_ call: CAPPluginCall) { - PlayerHandler.playPause() - call.resolve([ "playing": !PlayerHandler.paused() ]) + PlayerHandler.paused = !PlayerHandler.paused + call.resolve([ "playing": !PlayerHandler.paused ]) } @objc func playPlayer(_ call: CAPPluginCall) { - PlayerHandler.play() + PlayerHandler.paused = false call.resolve() } @objc func pausePlayer(_ call: CAPPluginCall) { - PlayerHandler.pause() + PlayerHandler.paused = true call.resolve() } @@ -95,13 +98,69 @@ public class AbsAudioPlayer: CAPPlugin { } @objc func sendMetadata() { - self.notifyListeners("onPlayingUpdate", data: [ "value": !PlayerHandler.paused() ]) + self.notifyListeners("onPlayingUpdate", data: [ "value": !PlayerHandler.paused ]) self.notifyListeners("onMetadata", data: PlayerHandler.getMetdata()) } @objc func sendPlaybackClosedEvent() { self.notifyListeners("onPlaybackClosed", data: [ "value": true ]) } + @objc func decreaseSleepTime(_ 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 currentSleepTime = PlayerHandler.remainingSleepTime else { return call.resolve([ "success": false ]) } + + PlayerHandler.remainingSleepTime = currentSleepTime - time + call.resolve() + } + @objc func increaseSleepTime(_ 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 currentSleepTime = PlayerHandler.remainingSleepTime else { return call.resolve([ "success": false ]) } + + PlayerHandler.remainingSleepTime = currentSleepTime + time + call.resolve() + } + @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 ]) } + + NSLog("chapter time: \(call.getBool("isChapterTime", false))") + + if call.getBool("isChapterTime", false) { + let timeToPause = time / 1000 - Int(PlayerHandler.getCurrentTime() ?? 0) + if timeToPause < 0 { return call.resolve([ "success": false ]) } + + NSLog("oof \(timeToPause)") + + PlayerHandler.remainingSleepTime = timeToPause + return call.resolve([ "success": true ]) + } + + PlayerHandler.remainingSleepTime = time / 1000 + call.resolve([ "success": true ]) + } + @objc func cancelSleepTimer(_ call: CAPPluginCall) { + PlayerHandler.remainingSleepTime = nil + call.resolve() + } + @objc func getSleepTimerTime(_ call: CAPPluginCall) { + call.resolve([ + "value": PlayerHandler.remainingSleepTime + ]) + } + + @objc func sendSleepTimerEnded() { + self.notifyListeners("onSleepTimerEnded", data: [ + "value": PlayerHandler.getCurrentTime() + ]) + } + @objc func sendSleepTimerSet() { + self.notifyListeners("onSleepTimerSet", data: [ + "value": PlayerHandler.remainingSleepTime + ]) + } + @objc func sendPrepareMetadataEvent(itemId: String, playWhenReady: Bool) { self.notifyListeners("onPrepareMedia", data: [ "audiobookId": itemId, @@ -115,11 +174,11 @@ public class AbsAudioPlayer: CAPPlugin { /* IMPLEMENTED: - cancelSleepTimer - decreaseSleepTime - increaseSleepTime - getSleepTimerTime - setSleepTimer + * cancelSleepTimer + decreaseSleepTime (millis) + increaseSleepTime (millis) + * getSleepTimerTime (millis) + * setSleepTimer (millis) * closePlayback * setPlaybackSpeed * seekBackward @@ -136,7 +195,7 @@ public class AbsAudioPlayer: CAPPlugin { * onPlaybackClosed * onPlayingUpdate * onMetadata - onSleepTimerEnded - onSleepTimerSet + onSleepTimerEnded (millis) + onSleepTimerSet (millis) */ } diff --git a/ios/App/Shared/player/PlayerHandler.swift b/ios/App/Shared/player/PlayerHandler.swift index b7554139..5cb05a43 100644 --- a/ios/App/Shared/player/PlayerHandler.swift +++ b/ios/App/Shared/player/PlayerHandler.swift @@ -12,7 +12,39 @@ class PlayerHandler { private static var session: PlaybackSession? private static var timer: Timer? - private static var listeningTimePassedSinceLastSync = 0.0 + private static var _remainingSleepTime: Int? = nil + public static var remainingSleepTime: Int? { + get { + return _remainingSleepTime + } + set(time) { + if time != nil && time! < 0 { + _remainingSleepTime = nil + } else { + _remainingSleepTime = time + } + + if _remainingSleepTime == nil { + NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepEnded.rawValue), object: _remainingSleepTime) + } else { + NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepSet.rawValue), object: _remainingSleepTime) + } + } + } + private static var listeningTimePassedSinceLastSync: Double = 0.0 + + public static var paused: Bool { + get { + return player?.rate == 0.0 + } + set(paused) { + if paused { + self.player?.pause() + } else { + self.player?.play() + } + } + } public static func startPlayback(session: PlaybackSession, playWhenReady: Bool, playbackRate: Float) { if player != nil { @@ -48,20 +80,6 @@ class PlayerHandler { self.player?.setPlaybackRate(speed) } - public static func play() { - self.player?.play() - } - public static func pause() { - self.player?.play() - } - public static func playPause() { - if paused() { - self.player?.play() - } else { - self.player?.pause() - } - } - public static func seekForward(amount: Double) { guard let player = player else { return @@ -81,11 +99,6 @@ class PlayerHandler { public static func seek(amount: Double) { player?.seek(amount) } - - public static func paused() -> Bool { - player?.rate == 0.0 - } - public static func getMetdata() -> [String: Any] { DispatchQueue.main.async { syncProgress() @@ -94,27 +107,30 @@ class PlayerHandler { return [ "duration": player?.getDuration() ?? 0, "currentTime": player?.getCurrentTime() ?? 0, - "playerState": !paused(), + "playerState": !paused, "currentRate": player?.rate ?? 0, ] } private static func tick() { - if !paused() { + if !paused { listeningTimePassedSinceLastSync += 1 } if listeningTimePassedSinceLastSync > 3 { syncProgress() } + + if remainingSleepTime != nil { + if remainingSleepTime! == 0 { + paused = true + } + remainingSleepTime! -= 1 + } } public static func syncProgress() { - if session == nil { - return - } - guard let player = player else { - return - } + if session == nil { return } + guard let player = player else { return } let report = PlaybackReport(currentTime: player.getCurrentTime(), duration: player.getDuration(), timeListened: listeningTimePassedSinceLastSync) diff --git a/ios/App/Shared/util/PlayerEvents.swift b/ios/App/Shared/util/PlayerEvents.swift index 5c794e92..8c04c098 100644 --- a/ios/App/Shared/util/PlayerEvents.swift +++ b/ios/App/Shared/util/PlayerEvents.swift @@ -10,4 +10,6 @@ import Foundation enum PlayerEvents: String { case update = "com.audiobookshelf.app.player.update" case closed = "com.audiobookshelf.app.player.closed" + case sleepSet = "com.audiobookshelf.app.player.sleep.set" + case sleepEnded = "com.audiobookshelf.app.player.sleep.ended" } From 07081337797aa8b14291ded9dce8ec4170af6e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Kr=C3=A4mer?= Date: Tue, 3 May 2022 15:01:30 +0200 Subject: [PATCH 4/6] Fixed crash --- ios/App/App/plugins/AbsAudioPlayer.m | 2 +- ios/App/App/plugins/AbsAudioPlayer.swift | 9 +++++---- ios/App/Shared/player/AudioPlayer.swift | 2 +- ios/App/Shared/player/PlayerHandler.swift | 6 +++++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/ios/App/App/plugins/AbsAudioPlayer.m b/ios/App/App/plugins/AbsAudioPlayer.m index 7e97d4ae..95e705b4 100644 --- a/ios/App/App/plugins/AbsAudioPlayer.m +++ b/ios/App/App/plugins/AbsAudioPlayer.m @@ -14,9 +14,9 @@ CAP_PLUGIN(AbsAudioPlayer, "AbsAudioPlayer", CAP_PLUGIN_METHOD(setPlaybackSpeed, CAPPluginReturnPromise); - CAP_PLUGIN_METHOD(playPause, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(playPlayer, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(pausePlayer, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(playPause, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(seek, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(seekForward, CAPPluginReturnPromise); diff --git a/ios/App/App/plugins/AbsAudioPlayer.swift b/ios/App/App/plugins/AbsAudioPlayer.swift index 3cc31218..735e1d3a 100644 --- a/ios/App/App/plugins/AbsAudioPlayer.swift +++ b/ios/App/App/plugins/AbsAudioPlayer.swift @@ -71,10 +71,6 @@ public class AbsAudioPlayer: CAPPlugin { call.resolve() } - @objc func playPause(_ call: CAPPluginCall) { - PlayerHandler.paused = !PlayerHandler.paused - call.resolve([ "playing": !PlayerHandler.paused ]) - } @objc func playPlayer(_ call: CAPPluginCall) { PlayerHandler.paused = false call.resolve() @@ -83,6 +79,11 @@ public class AbsAudioPlayer: CAPPlugin { PlayerHandler.paused = true call.resolve() } + // I have no clue why but after i moved this block of code from above "playPlayer" to here the app stopped crashing. Move it back up if you want to + @objc func playPause(_ call: CAPPluginCall) { + PlayerHandler.paused = !PlayerHandler.paused + call.resolve([ "playing": !PlayerHandler.paused ]) + } @objc func seek(_ call: CAPPluginCall) { PlayerHandler.seek(amount: call.getDouble("value", 0.0)) diff --git a/ios/App/Shared/player/AudioPlayer.swift b/ios/App/Shared/player/AudioPlayer.swift index 4802558c..bc1a13f7 100644 --- a/ios/App/Shared/player/AudioPlayer.swift +++ b/ios/App/Shared/player/AudioPlayer.swift @@ -186,7 +186,7 @@ class AudioPlayer: NSObject { lastPlayTime = Date.timeIntervalSinceReferenceDate } - public func seek(_ to: Double, from:String) { + public func seek(_ to: Double, from: String) { let continuePlaying = rate > 0.0 pause() diff --git a/ios/App/Shared/player/PlayerHandler.swift b/ios/App/Shared/player/PlayerHandler.swift index 1bdf6593..e29fe9f4 100644 --- a/ios/App/Shared/player/PlayerHandler.swift +++ b/ios/App/Shared/player/PlayerHandler.swift @@ -36,7 +36,11 @@ class PlayerHandler { public static var paused: Bool { get { - return player?.rate == 0.0 + guard let player = player else { + return true + } + + return player.rate == 0.0 } set(paused) { if paused { From 195935112554ac3bb276cbeb4f35e4c676354b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Kr=C3=A4mer?= Date: Tue, 3 May 2022 15:03:53 +0200 Subject: [PATCH 5/6] Removed unnecessary comment --- ios/App/App/plugins/AbsAudioPlayer.swift | 28 ------------------------ 1 file changed, 28 deletions(-) diff --git a/ios/App/App/plugins/AbsAudioPlayer.swift b/ios/App/App/plugins/AbsAudioPlayer.swift index 735e1d3a..923b5156 100644 --- a/ios/App/App/plugins/AbsAudioPlayer.swift +++ b/ios/App/App/plugins/AbsAudioPlayer.swift @@ -171,32 +171,4 @@ public class AbsAudioPlayer: CAPPlugin { @objc func sendPlaybackSession(session: [String: Any]) { self.notifyListeners("onPlaybackSession", data: session) } - - /* - IMPLEMENTED: - - * cancelSleepTimer - decreaseSleepTime (millis) - increaseSleepTime (millis) - * getSleepTimerTime (millis) - * setSleepTimer (millis) - * closePlayback - * setPlaybackSpeed - * seekBackward - * seekForward - * seek - * playPause - * playPlayer - * pausePlayer - * getCurrentTime - - * onPlaybackSession - * onPrepareMedia - - * onPlaybackClosed - * onPlayingUpdate - * onMetadata - onSleepTimerEnded (millis) - onSleepTimerSet (millis) - */ } From d23cf62264b90ed8bd8aff9ed6d7e1cc35e5e928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Kr=C3=A4mer?= Date: Thu, 5 May 2022 14:38:34 +0200 Subject: [PATCH 6/6] Fix sleep timer increase & decrease --- ios/App/App/plugins/AbsAudioPlayer.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/App/App/plugins/AbsAudioPlayer.swift b/ios/App/App/plugins/AbsAudioPlayer.swift index 923b5156..df30068d 100644 --- a/ios/App/App/plugins/AbsAudioPlayer.swift +++ b/ios/App/App/plugins/AbsAudioPlayer.swift @@ -111,7 +111,7 @@ public class AbsAudioPlayer: CAPPlugin { guard let time = Int(timeString) else { return call.resolve([ "success": false ]) } guard let currentSleepTime = PlayerHandler.remainingSleepTime else { return call.resolve([ "success": false ]) } - PlayerHandler.remainingSleepTime = currentSleepTime - time + PlayerHandler.remainingSleepTime = currentSleepTime - (time / 1000) call.resolve() } @objc func increaseSleepTime(_ call: CAPPluginCall) { @@ -119,7 +119,7 @@ public class AbsAudioPlayer: CAPPlugin { guard let time = Int(timeString) else { return call.resolve([ "success": false ]) } guard let currentSleepTime = PlayerHandler.remainingSleepTime else { return call.resolve([ "success": false ]) } - PlayerHandler.remainingSleepTime = currentSleepTime + time + PlayerHandler.remainingSleepTime = currentSleepTime + (time / 1000) call.resolve() } @objc func setSleepTimer(_ call: CAPPluginCall) {