Merge pull request #166 from rasmuslos/master

Added sleep timer
This commit is contained in:
advplyr
2022-05-05 07:49:44 -05:00
committed by GitHub
12 changed files with 213 additions and 133 deletions
+3 -3
View File
@@ -460,13 +460,13 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 6; CURRENT_PROJECT_VERSION = 6;
DEVELOPMENT_TEAM = 7UFJ7D8V6A; DEVELOPMENT_TEAM = N8AA4S3S96;
INFOPLIST_FILE = App/Info.plist; INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 0.9.43; MARKETING_VERSION = 0.9.43;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; 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)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OBJC_BRIDGING_HEADER = "App/App-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "App/App-Bridging-Header.h";
@@ -484,7 +484,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 6; CURRENT_PROJECT_VERSION = 6;
DEVELOPMENT_TEAM = 7UFJ7D8V6A; DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = App/Info.plist; INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+7 -1
View File
@@ -14,13 +14,19 @@ CAP_PLUGIN(AbsAudioPlayer, "AbsAudioPlayer",
CAP_PLUGIN_METHOD(setPlaybackSpeed, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(setPlaybackSpeed, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(playPause, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(playPlayer, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(playPlayer, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(pausePlayer, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(pausePlayer, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(playPause, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(seek, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(seek, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(seekForward, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(seekForward, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(seekBackward, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(seekBackward, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(getCurrentTime, 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);
) )
+68 -36
View File
@@ -13,9 +13,12 @@ public class AbsAudioPlayer: CAPPlugin {
override public func load() { 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(sendMetadata), name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(sendPlaybackClosedEvent), name: NSNotification.Name(PlayerEvents.closed.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.didBecomeActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(sendMetadata), name: UIApplication.willEnterForegroundNotification, 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) { @objc func prepareLibraryItem(_ call: CAPPluginCall) {
@@ -68,18 +71,19 @@ public class AbsAudioPlayer: CAPPlugin {
call.resolve() call.resolve()
} }
@objc func playPause(_ call: CAPPluginCall) {
PlayerHandler.playPause()
call.resolve([ "playing": !PlayerHandler.paused() ])
}
@objc func playPlayer(_ call: CAPPluginCall) { @objc func playPlayer(_ call: CAPPluginCall) {
PlayerHandler.play() PlayerHandler.paused = false
call.resolve() call.resolve()
} }
@objc func pausePlayer(_ call: CAPPluginCall) { @objc func pausePlayer(_ call: CAPPluginCall) {
PlayerHandler.pause() PlayerHandler.paused = true
call.resolve() 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) { @objc func seek(_ call: CAPPluginCall) {
PlayerHandler.seek(amount: call.getDouble("value", 0.0)) PlayerHandler.seek(amount: call.getDouble("value", 0.0))
@@ -95,13 +99,69 @@ public class AbsAudioPlayer: CAPPlugin {
} }
@objc func sendMetadata() { @objc func sendMetadata() {
self.notifyListeners("onPlayingUpdate", data: [ "value": !PlayerHandler.paused() ]) self.notifyListeners("onPlayingUpdate", data: [ "value": !PlayerHandler.paused ])
self.notifyListeners("onMetadata", data: PlayerHandler.getMetdata()) self.notifyListeners("onMetadata", data: PlayerHandler.getMetdata())
} }
@objc func sendPlaybackClosedEvent() { @objc func sendPlaybackClosedEvent() {
self.notifyListeners("onPlaybackClosed", data: [ "value": true ]) 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 / 1000)
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 / 1000)
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) { @objc func sendPrepareMetadataEvent(itemId: String, playWhenReady: Bool) {
self.notifyListeners("onPrepareMedia", data: [ self.notifyListeners("onPrepareMedia", data: [
"audiobookId": itemId, "audiobookId": itemId,
@@ -111,32 +171,4 @@ public class AbsAudioPlayer: CAPPlugin {
@objc func sendPlaybackSession(session: [String: Any]) { @objc func sendPlaybackSession(session: [String: Any]) {
self.notifyListeners("onPlaybackSession", data: session) self.notifyListeners("onPlaybackSession", data: session)
} }
/*
IMPLEMENTED:
cancelSleepTimer
decreaseSleepTime
increaseSleepTime
getSleepTimerTime
setSleepTimer
* closePlayback
* setPlaybackSpeed
* seekBackward
* seekForward
* seek
* playPause
* playPlayer
* pausePlayer
* getCurrentTime
* onPlaybackSession
* onPrepareMedia
* onPlaybackClosed
* onPlayingUpdate
* onMetadata
onSleepTimerEnded
onSleepTimerSet
*/
} }
+4 -5
View File
@@ -53,7 +53,7 @@ public class AbsDatabase: CAPPlugin {
} }
@objc func removeServerConnectionConfig(_ call: CAPPluginCall) { @objc func removeServerConnectionConfig(_ call: CAPPluginCall) {
let id = call.getString("serverConnectionConfigId", "") let id = call.getString("serverConnectionConfigId", "")
Database.deleteServerConnectionConfig(id: id) Database.shared.deleteServerConnectionConfig(id: id)
call.resolve() call.resolve()
} }
@@ -63,13 +63,12 @@ public class AbsDatabase: CAPPlugin {
} }
@objc func getDeviceData(_ call: CAPPluginCall) { @objc func getDeviceData(_ call: CAPPluginCall) {
let configs = Database.getServerConnectionConfigs() let configs = Database.shared.getServerConnectionConfigs()
let index = Database.getLastActiveConfigIndex() let index = Database.shared.getLastActiveConfigIndex()
call.resolve([ call.resolve([
"serverConnectionConfigs": configs.map { config in convertServerConnectionConfigToJSON(config: config) }, "serverConnectionConfigs": configs.map { config in convertServerConnectionConfigToJSON(config: config) },
"lastServerConnectionConfigId": configs.first { config in config.index == index }?.id, "lastServerConnectionConfigId": configs.first { config in config.index == index }?.id as Any,
// Luckily this isn't implemented yet
// "currentLocalPlaybackSession": nil, // "currentLocalPlaybackSession": nil,
]) ])
} }
+9 -10
View File
@@ -80,6 +80,7 @@ class AudioPlayer: NSObject {
public func destroy() { public func destroy() {
// Pause is not synchronous causing this error on below lines: // 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 // 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() pause()
audioPlayer.replaceCurrentItem(with: nil) audioPlayer.replaceCurrentItem(with: nil)
@@ -90,11 +91,9 @@ class AudioPlayer: NSObject {
print(error) print(error)
} }
// Throws error Possibly related to the error above DispatchQueue.runOnMainQueue {
// DispatchQueue.main.sync { UIApplication.shared.endReceivingRemoteControlEvents()
// UIApplication.shared.endReceivingRemoteControlEvents() }
// }
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.closed.rawValue), object: nil) NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.closed.rawValue), object: nil)
} }
@@ -187,7 +186,7 @@ class AudioPlayer: NSObject {
lastPlayTime = Date.timeIntervalSinceReferenceDate lastPlayTime = Date.timeIntervalSinceReferenceDate
} }
public func seek(_ to: Double, from:String) { public func seek(_ to: Double, from: String) {
let continuePlaying = rate > 0.0 let continuePlaying = rate > 0.0
pause() pause()
@@ -247,7 +246,6 @@ class AudioPlayer: NSObject {
} }
self.rate = rate self.rate = rate
self.updateNowPlaying() self.updateNowPlaying()
} }
@@ -289,9 +287,9 @@ class AudioPlayer: NSObject {
// MARK: - Now playing // MARK: - Now playing
private func setupRemoteTransportControls() { private func setupRemoteTransportControls() {
// DispatchQueue.main.sync { DispatchQueue.runOnMainQueue {
UIApplication.shared.beginReceivingRemoteControlEvents() UIApplication.shared.beginReceivingRemoteControlEvents()
// } }
let commandCenter = MPRemoteCommandCenter.shared() let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = true commandCenter.playCommand.isEnabled = true
@@ -349,7 +347,7 @@ class AudioPlayer: NSObject {
} }
private func updateNowPlaying() { private func updateNowPlaying() {
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil) 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 // MARK: - Observer
@@ -359,6 +357,7 @@ class AudioPlayer: NSObject {
NSLog("TEST: playerContext observer player rate") NSLog("TEST: playerContext observer player rate")
self.setPlaybackRate(change?[.newKey] as? Float ?? 1.0, observed: true) self.setPlaybackRate(change?[.newKey] as? Float ?? 1.0, observed: true)
} else if keyPath == #keyPath(AVPlayer.currentItem) { } else if keyPath == #keyPath(AVPlayer.currentItem) {
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil)
NSLog("WARNING: Item ended") NSLog("WARNING: Item ended")
} }
} else { } else {
+63 -40
View File
@@ -12,8 +12,44 @@ class PlayerHandler {
private static var session: PlaybackSession? private static var session: PlaybackSession?
private static var timer: Timer? private static var timer: Timer?
private static var listeningTimePassedSinceLastSync = 0.0 private static var _remainingSleepTime: Int? = nil
private static var lastSyncReport:PlaybackReport? 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
private static var lastSyncReport: PlaybackReport?
public static var paused: Bool {
get {
guard let player = player else {
return true
}
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) { public static func startPlayback(session: PlaybackSession, playWhenReady: Bool, playbackRate: Float) {
if player != nil { if player != nil {
@@ -21,16 +57,16 @@ class PlayerHandler {
player = nil 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 self.session = session
player = AudioPlayer(playbackSession: session, playWhenReady: playWhenReady, playbackRate: playbackRate) player = AudioPlayer(playbackSession: session, playWhenReady: playWhenReady, playbackRate: playbackRate)
// DispatchQueue.main.sync { DispatchQueue.runOnMainQueue {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
self.tick() self.tick()
}
} }
// }
} }
public static func stopPlayback() { public static func stopPlayback() {
player?.destroy() player?.destroy()
@@ -39,7 +75,7 @@ class PlayerHandler {
timer?.invalidate() timer?.invalidate()
timer = nil timer = nil
NowPlayingInfo.reset() NowPlayingInfo.shared.reset()
} }
public static func getCurrentTime() -> Double? { public static func getCurrentTime() -> Double? {
@@ -49,44 +85,25 @@ class PlayerHandler {
self.player?.setPlaybackRate(speed) 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) { public static func seekForward(amount: Double) {
if player == nil { guard let player = player else {
return return
} }
let destinationTime = player!.getCurrentTime() + amount let destinationTime = player.getCurrentTime() + amount
player!.seek(destinationTime, from: "handler") player.seek(destinationTime, from: "handler")
} }
public static func seekBackward(amount: Double) { public static func seekBackward(amount: Double) {
if player == nil { guard let player = player else {
return return
} }
let destinationTime = player!.getCurrentTime() - amount let destinationTime = player.getCurrentTime() - amount
player!.seek(destinationTime, from: "handler") player.seek(destinationTime, from: "handler")
} }
public static func seek(amount: Double) { public static func seek(amount: Double) {
player?.seek(amount, from: "handler") player?.seek(amount, from: "handler")
} }
public static func paused() -> Bool {
player?.rate == 0.0
}
public static func getMetdata() -> [String: Any] { public static func getMetdata() -> [String: Any] {
DispatchQueue.main.async { DispatchQueue.main.async {
syncProgress() syncProgress()
@@ -95,32 +112,38 @@ class PlayerHandler {
return [ return [
"duration": player?.getDuration() ?? 0, "duration": player?.getDuration() ?? 0,
"currentTime": player?.getCurrentTime() ?? 0, "currentTime": player?.getCurrentTime() ?? 0,
"playerState": !paused(), "playerState": !paused,
"currentRate": player?.rate ?? 0, "currentRate": player?.rate ?? 0,
] ]
} }
private static func tick() { private static func tick() {
if !paused() { if !paused {
listeningTimePassedSinceLastSync += 1 listeningTimePassedSinceLastSync += 1
} }
if listeningTimePassedSinceLastSync > 3 { if listeningTimePassedSinceLastSync > 3 {
syncProgress() syncProgress()
} }
if remainingSleepTime != nil {
if remainingSleepTime! == 0 {
paused = true
}
remainingSleepTime! -= 1
}
} }
public static func syncProgress() { public static func syncProgress() {
if player == nil || session == nil { if session == nil { return }
return guard let player = player else { return }
}
let playerCurrentTime = player!.getCurrentTime() let playerCurrentTime = player.getCurrentTime()
if (lastSyncReport != nil && lastSyncReport?.currentTime == playerCurrentTime) { if (lastSyncReport != nil && lastSyncReport?.currentTime == playerCurrentTime) {
// No need to syncProgress // No need to syncProgress
return return
} }
let report = PlaybackReport(currentTime: playerCurrentTime, duration: player!.getDuration(), timeListened: listeningTimePassedSinceLastSync) let report = PlaybackReport(currentTime: playerCurrentTime, duration: player.getDuration(), timeListened: listeningTimePassedSinceLastSync)
session!.currentTime = playerCurrentTime session!.currentTime = playerCurrentTime
listeningTimePassedSinceLastSync = 0 listeningTimePassedSinceLastSync = 0
+8 -1
View File
@@ -9,6 +9,14 @@ import Foundation
import Alamofire import Alamofire
class ApiClient { 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<T: Decodable>(endpoint: String, parameters: [String: String], decodable: T.Type = T.self, callback: ((_ param: T) -> Void)?) { public static func postResource<T: Decodable>(endpoint: String, parameters: [String: String], decodable: T.Type = T.self, callback: ((_ param: T) -> Void)?) {
if (Store.serverConfig == nil) { if (Store.serverConfig == nil) {
NSLog("Server config not set") NSLog("Server config not set")
@@ -54,7 +62,6 @@ class ApiClient {
} }
public static func startPlaybackSession(libraryItemId: String, episodeId: String?, callback: @escaping (_ param: PlaybackSession) -> Void) { public static func startPlaybackSession(libraryItemId: String, episodeId: String?, callback: @escaping (_ param: PlaybackSession) -> Void) {
var endpoint = "api/items/\(libraryItemId)/play" var endpoint = "api/items/\(libraryItemId)/play"
if episodeId != nil { if episodeId != nil {
endpoint += "/\(episodeId!)" endpoint += "/\(episodeId!)"
+22 -13
View File
@@ -10,16 +10,25 @@ import RealmSwift
class Database { class Database {
// All DB releated actions must be executed on "realm-queue" // All DB releated actions must be executed on "realm-queue"
public static let realmQueue = DispatchQueue(label: "realm-queue") public static let realmQueue: DispatchQueue = DispatchQueue(label: "realm-queue")
private static var instance: Realm = try! Realm(queue: realmQueue) 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<ServerConnectionConfig>? var refrence: ThreadSafeReference<ServerConnectionConfig>?
if config.realm != nil { if config.realm != nil {
refrence = ThreadSafeReference(to: config) refrence = ThreadSafeReference(to: config)
} }
realmQueue.sync { Database.realmQueue.sync {
let existing: ServerConnectionConfig? = instance.object(ofType: ServerConnectionConfig.self, forPrimaryKey: config.id) let existing: ServerConnectionConfig? = instance.object(ofType: ServerConnectionConfig.self, forPrimaryKey: config.id)
if config.index == 0 { if config.index == 0 {
@@ -55,8 +64,8 @@ class Database {
setLastActiveConfigIndex(index: config.index) setLastActiveConfigIndex(index: config.index)
} }
} }
public static func deleteServerConnectionConfig(id: String) { public func deleteServerConnectionConfig(id: String) {
realmQueue.sync { Database.realmQueue.sync {
let config = instance.object(ofType: ServerConnectionConfig.self, forPrimaryKey: id) let config = instance.object(ofType: ServerConnectionConfig.self, forPrimaryKey: id)
do { do {
@@ -71,10 +80,10 @@ class Database {
} }
} }
} }
public static func getServerConnectionConfigs() -> [ServerConnectionConfig] { public func getServerConnectionConfigs() -> [ServerConnectionConfig] {
var refrences: [ThreadSafeReference<ServerConnectionConfig>] = [] var refrences: [ThreadSafeReference<ServerConnectionConfig>] = []
realmQueue.sync { Database.realmQueue.sync {
let configs = instance.objects(ServerConnectionConfig.self) let configs = instance.objects(ServerConnectionConfig.self)
refrences = configs.map { config in refrences = configs.map { config in
return ThreadSafeReference(to: config) return ThreadSafeReference(to: config)
@@ -94,12 +103,12 @@ class Database {
} }
} }
public static func setLastActiveConfigIndexToNil() { public func setLastActiveConfigIndexToNil() {
realmQueue.sync { Database.realmQueue.sync {
setLastActiveConfigIndex(index: nil) setLastActiveConfigIndex(index: nil)
} }
} }
public static func setLastActiveConfigIndex(index: Int?) { public func setLastActiveConfigIndex(index: Int?) {
let existing = instance.objects(ServerConnectionConfigActiveIndex.self) let existing = instance.objects(ServerConnectionConfigActiveIndex.self)
let obj = ServerConnectionConfigActiveIndex() let obj = ServerConnectionConfigActiveIndex()
obj.index = index obj.index = index
@@ -114,8 +123,8 @@ class Database {
debugPrint(exception) debugPrint(exception)
} }
} }
public static func getLastActiveConfigIndex() -> Int? { public func getLastActiveConfigIndex() -> Int? {
return realmQueue.sync { return Database.realmQueue.sync {
return instance.objects(ServerConnectionConfigActiveIndex.self).first?.index ?? nil return instance.objects(ServerConnectionConfigActiveIndex.self).first?.index ?? nil
} }
} }
+11
View File
@@ -18,3 +18,14 @@ extension Encodable {
return dictionary return dictionary
} }
} }
extension DispatchQueue {
static func runOnMainQueue(callback: @escaping (() -> Void)) {
if Thread.isMainThread {
callback()
} else {
DispatchQueue.main.sync {
callback()
}
}
}
}
+14 -22
View File
@@ -8,14 +8,6 @@
import Foundation import Foundation
import MediaPlayer 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 { struct NowPlayingMetadata {
var id: String var id: String
var itemId: String var itemId: String
@@ -26,22 +18,22 @@ struct NowPlayingMetadata {
} }
class NowPlayingInfo { 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) 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 { guard let url = URL(string: "\(Store.serverConfig!.address)/api/items/\(metadata.itemId)/cover?token=\(Store.serverConfig!.token)") else {
return return
} }
ApiClient.getData(from: url) { [self] image in
getData(from: url) { [self] image in
guard let downloadedImage = image else { guard let downloadedImage = image else {
return return
} }
@@ -52,7 +44,7 @@ class NowPlayingInfo {
self.setMetadata(artwork: artwork, metadata: metadata) 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[MPMediaItemPropertyPlaybackDuration] = duration
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentTime nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentTime
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = rate nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = rate
@@ -60,12 +52,12 @@ class NowPlayingInfo {
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
} }
public static func reset() { public func reset() {
nowPlayingInfo = [:] nowPlayingInfo = [:]
MPNowPlayingInfoCenter.default().nowPlayingInfo = nil MPNowPlayingInfoCenter.default().nowPlayingInfo = nil
} }
private static func setMetadata(artwork: MPMediaItemArtwork?, metadata: NowPlayingMetadata?) { private func setMetadata(artwork: MPMediaItemArtwork?, metadata: NowPlayingMetadata?) {
if metadata == nil { if metadata == nil {
return return
} }
@@ -84,7 +76,7 @@ class NowPlayingInfo {
nowPlayingInfo[MPMediaItemPropertyArtist] = metadata!.author ?? "unknown" nowPlayingInfo[MPMediaItemPropertyArtist] = metadata!.author ?? "unknown"
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = metadata!.series 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 nowPlayingInfo[MPNowPlayingInfoPropertyExternalContentIdentifier] as? String != id || nowPlayingInfo[MPMediaItemPropertyArtwork] == nil
} }
} }
+2
View File
@@ -10,4 +10,6 @@ import Foundation
enum PlayerEvents: String { enum PlayerEvents: String {
case update = "com.audiobookshelf.app.player.update" case update = "com.audiobookshelf.app.player.update"
case closed = "com.audiobookshelf.app.player.closed" case closed = "com.audiobookshelf.app.player.closed"
case sleepSet = "com.audiobookshelf.app.player.sleep.set"
case sleepEnded = "com.audiobookshelf.app.player.sleep.ended"
} }
+2 -2
View File
@@ -16,9 +16,9 @@ class Store {
} }
set(updated) { set(updated) {
if updated != nil { if updated != nil {
Database.setServerConnectionConfig(config: updated!) Database.shared.setServerConnectionConfig(config: updated!)
} else { } else {
Database.setLastActiveConfigIndexToNil() Database.shared.setLastActiveConfigIndexToNil()
} }
Database.realmQueue.sync { Database.realmQueue.sync {