mirror of
https://github.com/sudoxnym/audiobookshelf-atv.git
synced 2026-05-23 06:06:41 +00:00
commit
8bbfcdeb82
12 changed files with 213 additions and 133 deletions
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -14,13 +14,19 @@ 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);
|
||||
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);
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -68,18 +71,19 @@ public class AbsAudioPlayer: CAPPlugin {
|
|||
call.resolve()
|
||||
}
|
||||
|
||||
@objc func playPause(_ call: CAPPluginCall) {
|
||||
PlayerHandler.playPause()
|
||||
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()
|
||||
}
|
||||
// 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))
|
||||
|
|
@ -95,13 +99,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 / 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) {
|
||||
self.notifyListeners("onPrepareMedia", data: [
|
||||
"audiobookId": itemId,
|
||||
|
|
@ -111,32 +171,4 @@ public class AbsAudioPlayer: CAPPlugin {
|
|||
@objc func sendPlaybackSession(session: [String: Any]) {
|
||||
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
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,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)
|
||||
|
||||
|
|
@ -90,11 +91,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)
|
||||
}
|
||||
|
||||
|
|
@ -187,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()
|
||||
|
|
@ -247,7 +246,6 @@ class AudioPlayer: NSObject {
|
|||
}
|
||||
|
||||
self.rate = rate
|
||||
|
||||
self.updateNowPlaying()
|
||||
}
|
||||
|
||||
|
|
@ -289,9 +287,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
|
||||
|
|
@ -349,7 +347,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
|
||||
|
|
@ -359,6 +357,7 @@ class AudioPlayer: NSObject {
|
|||
NSLog("TEST: playerContext observer player 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 {
|
||||
|
|
|
|||
|
|
@ -12,8 +12,44 @@ class PlayerHandler {
|
|||
private static var session: PlaybackSession?
|
||||
private static var timer: Timer?
|
||||
|
||||
private static var listeningTimePassedSinceLastSync = 0.0
|
||||
private static var lastSyncReport:PlaybackReport?
|
||||
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
|
||||
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) {
|
||||
if player != nil {
|
||||
|
|
@ -21,16 +57,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()
|
||||
|
|
@ -39,7 +75,7 @@ class PlayerHandler {
|
|||
timer?.invalidate()
|
||||
timer = nil
|
||||
|
||||
NowPlayingInfo.reset()
|
||||
NowPlayingInfo.shared.reset()
|
||||
}
|
||||
|
||||
public static func getCurrentTime() -> Double? {
|
||||
|
|
@ -49,44 +85,25 @@ 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) {
|
||||
if player == nil {
|
||||
guard let player = player else {
|
||||
return
|
||||
}
|
||||
|
||||
let destinationTime = player!.getCurrentTime() + amount
|
||||
player!.seek(destinationTime, from: "handler")
|
||||
let destinationTime = player.getCurrentTime() + amount
|
||||
player.seek(destinationTime, from: "handler")
|
||||
}
|
||||
public static func seekBackward(amount: Double) {
|
||||
if player == nil {
|
||||
guard let player = player else {
|
||||
return
|
||||
}
|
||||
|
||||
let destinationTime = player!.getCurrentTime() - amount
|
||||
player!.seek(destinationTime, from: "handler")
|
||||
let destinationTime = player.getCurrentTime() - amount
|
||||
player.seek(destinationTime, from: "handler")
|
||||
}
|
||||
public static func seek(amount: Double) {
|
||||
player?.seek(amount, from: "handler")
|
||||
}
|
||||
|
||||
public static func paused() -> Bool {
|
||||
player?.rate == 0.0
|
||||
}
|
||||
|
||||
public static func getMetdata() -> [String: Any] {
|
||||
DispatchQueue.main.async {
|
||||
syncProgress()
|
||||
|
|
@ -95,32 +112,38 @@ 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 player == nil || session == nil {
|
||||
return
|
||||
}
|
||||
if session == nil { return }
|
||||
guard let player = player else { return }
|
||||
|
||||
let playerCurrentTime = player!.getCurrentTime()
|
||||
let playerCurrentTime = player.getCurrentTime()
|
||||
if (lastSyncReport != nil && lastSyncReport?.currentTime == playerCurrentTime) {
|
||||
// No need to syncProgress
|
||||
return
|
||||
}
|
||||
|
||||
let report = PlaybackReport(currentTime: playerCurrentTime, duration: player!.getDuration(), timeListened: listeningTimePassedSinceLastSync)
|
||||
let report = PlaybackReport(currentTime: playerCurrentTime, duration: player.getDuration(), timeListened: listeningTimePassedSinceLastSync)
|
||||
|
||||
session!.currentTime = playerCurrentTime
|
||||
listeningTimePassedSinceLastSync = 0
|
||||
|
|
|
|||
|
|
@ -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<T: Decodable>(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!)"
|
||||
|
|
|
|||
|
|
@ -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<ServerConnectionConfig>?
|
||||
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<ServerConnectionConfig>] = []
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue