mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-25 21:04:02 +02:00
Merge remote-tracking branch 'upstream/master' into ios-general-fixes
This commit is contained in:
@@ -19,6 +19,7 @@ enum PlayMethod:Int {
|
||||
|
||||
class AudioPlayer: NSObject {
|
||||
internal let queue = DispatchQueue(label: "ABSAudioPlayerQueue")
|
||||
internal let logger = AppLogger(category: "AudioPlayer")
|
||||
|
||||
// enums and @objc are not compatible
|
||||
@objc dynamic var status: Int
|
||||
@@ -68,7 +69,7 @@ class AudioPlayer: NSObject {
|
||||
|
||||
let playbackSession = self.getPlaybackSession()
|
||||
guard let playbackSession = playbackSession else {
|
||||
NSLog("Failed to fetch playback session. Player will not initialize")
|
||||
logger.error("Failed to fetch playback session. Player will not initialize")
|
||||
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.failed.rawValue), object: nil)
|
||||
return
|
||||
}
|
||||
@@ -86,10 +87,10 @@ class AudioPlayer: NSObject {
|
||||
}
|
||||
|
||||
self.currentTrackIndex = getItemIndexForTime(time: playbackSession.currentTime)
|
||||
NSLog("Starting track index \(self.currentTrackIndex) for start time \(playbackSession.currentTime)")
|
||||
logger.log("Starting track index \(self.currentTrackIndex) for start time \(playbackSession.currentTime)")
|
||||
|
||||
let playerItems = self.allPlayerItems[self.currentTrackIndex..<self.allPlayerItems.count]
|
||||
NSLog("Setting player items \(playerItems.count)")
|
||||
logger.log("Setting player items \(playerItems.count)")
|
||||
|
||||
for item in Array(playerItems) {
|
||||
self.audioPlayer.insert(item, after:self.audioPlayer.items().last)
|
||||
@@ -99,7 +100,7 @@ class AudioPlayer: NSObject {
|
||||
setupQueueObserver()
|
||||
setupQueueItemStatusObserver()
|
||||
|
||||
NSLog("Audioplayer ready")
|
||||
logger.log("Audioplayer ready")
|
||||
}
|
||||
|
||||
deinit {
|
||||
@@ -120,8 +121,8 @@ class AudioPlayer: NSObject {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setActive(false)
|
||||
} catch {
|
||||
NSLog("Failed to set AVAudioSession inactive")
|
||||
print(error)
|
||||
logger.error("Failed to set AVAudioSession inactive")
|
||||
logger.error(error)
|
||||
}
|
||||
|
||||
self.removeAudioSessionNotifications()
|
||||
@@ -214,14 +215,14 @@ class AudioPlayer: NSObject {
|
||||
self.audioPlayer.currentItem.map { item in
|
||||
self.currentTrackIndex = self.allPlayerItems.firstIndex(of:item) ?? 0
|
||||
if (self.currentTrackIndex != prevTrackIndex) {
|
||||
NSLog("New Current track index \(self.currentTrackIndex)")
|
||||
self.logger.log("New Current track index \(self.currentTrackIndex)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setupQueueItemStatusObserver() {
|
||||
NSLog("queueStatusObserver: Setting up")
|
||||
logger.log("queueStatusObserver: Setting up")
|
||||
|
||||
// Listen for player item updates
|
||||
self.queueItemStatusObserver?.invalidate()
|
||||
@@ -236,13 +237,13 @@ class AudioPlayer: NSObject {
|
||||
}
|
||||
|
||||
private func handleQueueItemStatus(playerItem: AVPlayerItem) {
|
||||
NSLog("queueStatusObserver: Current item status changed")
|
||||
logger.log("queueStatusObserver: Current item status changed")
|
||||
guard let playbackSession = self.getPlaybackSession() else {
|
||||
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.failed.rawValue), object: nil)
|
||||
return
|
||||
}
|
||||
if (playerItem.status == .readyToPlay) {
|
||||
NSLog("queueStatusObserver: Current Item Ready to play. PlayWhenReady: \(self.playWhenReady)")
|
||||
logger.log("queueStatusObserver: Current Item Ready to play. PlayWhenReady: \(self.playWhenReady)")
|
||||
|
||||
// Seek the player before initializing, so a currentTime of 0 does not appear in MediaProgress / session
|
||||
let firstReady = self.status < 0
|
||||
@@ -261,7 +262,7 @@ class AudioPlayer: NSObject {
|
||||
self.play()
|
||||
}
|
||||
} else if (playerItem.status == .failed) {
|
||||
NSLog("queueStatusObserver: FAILED \(playerItem.error?.localizedDescription ?? "")")
|
||||
logger.error("queueStatusObserver: FAILED \(playerItem.error?.localizedDescription ?? "")")
|
||||
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.failed.rawValue), object: nil)
|
||||
}
|
||||
}
|
||||
@@ -269,8 +270,8 @@ class AudioPlayer: NSObject {
|
||||
private func startPausedTimer() {
|
||||
guard self.pausedTimer == nil else { return }
|
||||
self.queue.async {
|
||||
self.pausedTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in
|
||||
NSLog("PAUSE TIMER: Syncing from server")
|
||||
self.pausedTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { [weak self] timer in
|
||||
self?.logger.log("PAUSE TIMER: Syncing from server")
|
||||
Task { await PlayerProgress.shared.syncFromServer() }
|
||||
}
|
||||
}
|
||||
@@ -334,7 +335,7 @@ class AudioPlayer: NSObject {
|
||||
}
|
||||
|
||||
private func resumePlayback() {
|
||||
NSLog("PLAY: Resuming playback")
|
||||
logger.log("PLAY: Resuming playback")
|
||||
|
||||
// Stop the paused timer
|
||||
self.stopPausedTimer()
|
||||
@@ -353,7 +354,7 @@ class AudioPlayer: NSObject {
|
||||
public func pause() {
|
||||
guard self.isInitialized() else { return }
|
||||
|
||||
NSLog("PAUSE: Pausing playback")
|
||||
logger.log("PAUSE: Pausing playback")
|
||||
DispatchQueue.runOnMainQueue {
|
||||
self.audioPlayer.pause()
|
||||
}
|
||||
@@ -377,17 +378,17 @@ class AudioPlayer: NSObject {
|
||||
|
||||
self.pause()
|
||||
|
||||
NSLog("SEEK: Seek to \(to) from \(from)")
|
||||
logger.log("SEEK: Seek to \(to) from \(from)")
|
||||
|
||||
guard let playbackSession = self.getPlaybackSession() else { return }
|
||||
|
||||
let currentTrack = playbackSession.audioTracks[self.currentTrackIndex]
|
||||
let ctso = currentTrack.startOffset ?? 0.0
|
||||
let trackEnd = ctso + currentTrack.duration
|
||||
NSLog("SEEK: Seek current track END = \(trackEnd)")
|
||||
logger.log("SEEK: Seek current track END = \(trackEnd)")
|
||||
|
||||
let indexOfSeek = getItemIndexForTime(time: to)
|
||||
NSLog("SEEK: Seek to index \(indexOfSeek) | Current index \(self.currentTrackIndex)")
|
||||
logger.log("SEEK: Seek to index \(indexOfSeek) | Current index \(self.currentTrackIndex)")
|
||||
|
||||
// Reconstruct queue if seeking to a different track
|
||||
if (self.currentTrackIndex != indexOfSeek) {
|
||||
@@ -410,13 +411,16 @@ class AudioPlayer: NSObject {
|
||||
|
||||
setupQueueItemStatusObserver()
|
||||
} else {
|
||||
NSLog("SEEK: Seeking in current item \(to)")
|
||||
logger.log("SEEK: Seeking in current item \(to)")
|
||||
let currentTrackStartOffset = playbackSession.audioTracks[self.currentTrackIndex].startOffset ?? 0.0
|
||||
let seekTime = to - currentTrackStartOffset
|
||||
|
||||
DispatchQueue.runOnMainQueue {
|
||||
self.audioPlayer.seek(to: CMTime(seconds: seekTime, preferredTimescale: 1000)) { [weak self] completed in
|
||||
guard completed else { return NSLog("SEEK: WARNING: seeking not completed (to \(seekTime)") }
|
||||
guard completed else {
|
||||
self?.logger.log("SEEK: WARNING: seeking not completed (to \(seekTime)")
|
||||
return
|
||||
}
|
||||
guard let self = self else { return }
|
||||
|
||||
if continuePlaying {
|
||||
@@ -433,7 +437,7 @@ class AudioPlayer: NSObject {
|
||||
let playbackSpeedChanged = rate > 0.0 && rate != self.tmpRate && !(observed && rate == 1)
|
||||
|
||||
if self.audioPlayer.rate != rate {
|
||||
NSLog("setPlaybakRate rate changed from \(self.audioPlayer.rate) to \(rate)")
|
||||
logger.log("setPlaybakRate rate changed from \(self.audioPlayer.rate) to \(rate)")
|
||||
DispatchQueue.runOnMainQueue {
|
||||
self.audioPlayer.rate = rate
|
||||
}
|
||||
@@ -490,7 +494,7 @@ class AudioPlayer: NSObject {
|
||||
} else if (playbackSession.playMethod == PlayMethod.local.rawValue) {
|
||||
guard let localFile = track.getLocalFile() else {
|
||||
// Worst case we can stream the file
|
||||
NSLog("Unable to play local file. Resulting to streaming \(track.localFileId ?? "Unknown")")
|
||||
logger.log("Unable to play local file. Resulting to streaming \(track.localFileId ?? "Unknown")")
|
||||
let filename = track.metadata?.filename ?? ""
|
||||
let filenameEncoded = filename.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
|
||||
let urlstr = "\(Store.serverConfig!.address)/s/item/\(itemId)/\(filenameEncoded ?? "")?token=\(Store.serverConfig!.token)"
|
||||
@@ -510,8 +514,8 @@ class AudioPlayer: NSObject {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .spokenAudio)
|
||||
} catch {
|
||||
NSLog("Failed to set AVAudioSession category")
|
||||
print(error)
|
||||
logger.error("Failed to set AVAudioSession category")
|
||||
logger.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +523,7 @@ class AudioPlayer: NSObject {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setActive(active)
|
||||
} catch {
|
||||
NSLog("Failed to set audio session as active=\(active)")
|
||||
logger.error("Failed to set audio session as active=\(active)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,11 +654,11 @@ class AudioPlayer: NSObject {
|
||||
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
if context == &playerContext {
|
||||
if keyPath == #keyPath(AVPlayer.rate) {
|
||||
NSLog("playerContext observer player rate")
|
||||
logger.log("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")
|
||||
logger.log("WARNING: Item ended")
|
||||
}
|
||||
} else {
|
||||
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||
|
||||
@@ -31,7 +31,7 @@ extension AudioPlayer {
|
||||
}
|
||||
|
||||
public func setSleepTimer(secondsUntilSleep: Double) {
|
||||
NSLog("SLEEP TIMER: Sleeping in \(secondsUntilSleep) seconds")
|
||||
logger.log("SLEEP TIMER: Sleeping in \(secondsUntilSleep) seconds")
|
||||
self.removeSleepTimer()
|
||||
self.sleepTimeRemaining = secondsUntilSleep
|
||||
|
||||
@@ -48,7 +48,7 @@ extension AudioPlayer {
|
||||
}
|
||||
|
||||
public func setChapterSleepTimer(stopAt: Double) {
|
||||
NSLog("SLEEP TIMER: Scheduling for chapter end \(stopAt)")
|
||||
logger.log("SLEEP TIMER: Scheduling for chapter end \(stopAt)")
|
||||
self.removeSleepTimer()
|
||||
|
||||
// Schedule the observation time
|
||||
@@ -124,7 +124,7 @@ extension AudioPlayer {
|
||||
}
|
||||
|
||||
private func handleSleepEnd() {
|
||||
NSLog("SLEEP TIMER: Pausing audio")
|
||||
logger.log("SLEEP TIMER: Pausing audio")
|
||||
self.pause()
|
||||
self.removeSleepTimer()
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ class PlayerProgress {
|
||||
|
||||
private static let TIME_BETWEEN_SESSION_SYNC_IN_SECONDS = 10.0
|
||||
|
||||
private let logger = AppLogger(category: "PlayerProgress")
|
||||
|
||||
private init() {}
|
||||
|
||||
|
||||
@@ -28,8 +30,8 @@ class PlayerProgress {
|
||||
try await updateServerSessionFromLocalSession(session, rateLimitSync: !isStopping)
|
||||
}
|
||||
} catch {
|
||||
debugPrint("Failed to syncFromPlayer")
|
||||
debugPrint(error)
|
||||
logger.error("Failed to syncFromPlayer")
|
||||
logger.error(error)
|
||||
}
|
||||
await UIApplication.shared.endBackgroundTask(backgroundToken)
|
||||
}
|
||||
@@ -39,8 +41,8 @@ class PlayerProgress {
|
||||
do {
|
||||
try await updateAllServerSessionFromLocalSession()
|
||||
} catch {
|
||||
debugPrint("Failed to syncToServer")
|
||||
debugPrint(error)
|
||||
logger.error("Failed to syncToServer")
|
||||
logger.error(error)
|
||||
}
|
||||
await UIApplication.shared.endBackgroundTask(backgroundToken)
|
||||
}
|
||||
@@ -50,8 +52,8 @@ class PlayerProgress {
|
||||
do {
|
||||
try await updateLocalSessionFromServerMediaProgress()
|
||||
} catch {
|
||||
debugPrint("Failed to syncFromServer")
|
||||
debugPrint(error)
|
||||
logger.error("Failed to syncFromServer")
|
||||
logger.error(error)
|
||||
}
|
||||
await UIApplication.shared.endBackgroundTask(backgroundToken)
|
||||
}
|
||||
@@ -96,7 +98,7 @@ class PlayerProgress {
|
||||
|
||||
try localMediaProgress.updateFromPlaybackSession(session)
|
||||
|
||||
NSLog("Local progress saved to the database")
|
||||
logger.log("Local progress saved to the database")
|
||||
|
||||
// Send the local progress back to front-end
|
||||
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.localProgress.rawValue), object: nil)
|
||||
@@ -141,7 +143,7 @@ class PlayerProgress {
|
||||
session = session.freeze()
|
||||
|
||||
guard safeToSync else { return }
|
||||
NSLog("Sending sessionId(\(session.id)) to server with currentTime(\(session.currentTime))")
|
||||
logger.log("Sending sessionId(\(session.id)) to server with currentTime(\(session.currentTime))")
|
||||
|
||||
var success = false
|
||||
if session.isLocal {
|
||||
@@ -161,25 +163,25 @@ class PlayerProgress {
|
||||
}
|
||||
|
||||
private func updateLocalSessionFromServerMediaProgress() async throws {
|
||||
NSLog("updateLocalSessionFromServerMediaProgress: Checking if local media progress was updated on server")
|
||||
logger.log("updateLocalSessionFromServerMediaProgress: Checking if local media progress was updated on server")
|
||||
guard let session = try await Realm().objects(PlaybackSession.self).last(where: {
|
||||
$0.isActiveSession == true && $0.serverConnectionConfigId == Store.serverConfig?.id
|
||||
})?.freeze() else {
|
||||
NSLog("updateLocalSessionFromServerMediaProgress: Failed to get session")
|
||||
logger.log("updateLocalSessionFromServerMediaProgress: Failed to get session")
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch the current progress
|
||||
let progress = await ApiClient.getMediaProgress(libraryItemId: session.libraryItemId!, episodeId: session.episodeId)
|
||||
guard let progress = progress else {
|
||||
NSLog("updateLocalSessionFromServerMediaProgress: No progress object")
|
||||
logger.log("updateLocalSessionFromServerMediaProgress: No progress object")
|
||||
return
|
||||
}
|
||||
|
||||
// Determine which session is newer
|
||||
let serverLastUpdate = progress.lastUpdate
|
||||
guard let localLastUpdate = session.updatedAt else {
|
||||
NSLog("updateLocalSessionFromServerMediaProgress: No local session updatedAt")
|
||||
logger.log("updateLocalSessionFromServerMediaProgress: No local session updatedAt")
|
||||
return
|
||||
}
|
||||
let serverCurrentTime = progress.currentTime
|
||||
@@ -190,16 +192,16 @@ class PlayerProgress {
|
||||
|
||||
// Update the session, if needed
|
||||
if serverIsNewerThanLocal && currentTimeIsDifferent {
|
||||
NSLog("updateLocalSessionFromServerMediaProgress: Server has newer time than local serverLastUpdate=\(serverLastUpdate) localLastUpdate=\(localLastUpdate)")
|
||||
logger.log("updateLocalSessionFromServerMediaProgress: Server has newer time than local serverLastUpdate=\(serverLastUpdate) localLastUpdate=\(localLastUpdate)")
|
||||
guard let session = session.thaw() else { return }
|
||||
try session.update {
|
||||
session.currentTime = serverCurrentTime
|
||||
session.updatedAt = serverLastUpdate
|
||||
}
|
||||
NSLog("updateLocalSessionFromServerMediaProgress: Updated session currentTime newCurrentTime=\(serverCurrentTime) previousCurrentTime=\(localCurrentTime)")
|
||||
logger.log("updateLocalSessionFromServerMediaProgress: Updated session currentTime newCurrentTime=\(serverCurrentTime) previousCurrentTime=\(localCurrentTime)")
|
||||
PlayerHandler.seek(amount: session.currentTime)
|
||||
} else {
|
||||
NSLog("updateLocalSessionFromServerMediaProgress: Local session does not need updating; local has latest progress")
|
||||
logger.log("updateLocalSessionFromServerMediaProgress: Local session does not need updating; local has latest progress")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import Foundation
|
||||
import Alamofire
|
||||
|
||||
class ApiClient {
|
||||
private static let logger = AppLogger(category: "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 {
|
||||
@@ -19,7 +21,7 @@ class ApiClient {
|
||||
|
||||
public static func postResource<T: Decodable>(endpoint: String, parameters: [String: Any], decodable: T.Type = T.self, callback: ((_ param: T) -> Void)?) {
|
||||
if (Store.serverConfig == nil) {
|
||||
NSLog("Server config not set")
|
||||
logger.error("Server config not set")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -32,7 +34,7 @@ class ApiClient {
|
||||
case .success(let obj):
|
||||
callback?(obj)
|
||||
case .failure(let error):
|
||||
NSLog("api request to \(endpoint) failed")
|
||||
logger.error("api request to \(endpoint) failed")
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
@@ -40,7 +42,7 @@ class ApiClient {
|
||||
|
||||
public static func postResource<T: Encodable, U: Decodable>(endpoint: String, parameters: T, decodable: U.Type = U.self, callback: ((_ param: U) -> Void)?) {
|
||||
if (Store.serverConfig == nil) {
|
||||
NSLog("Server config not set")
|
||||
logger.error("Server config not set")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -53,7 +55,7 @@ class ApiClient {
|
||||
case .success(let obj):
|
||||
callback?(obj)
|
||||
case .failure(let error):
|
||||
NSLog("api request to \(endpoint) failed")
|
||||
logger.error("api request to \(endpoint) failed")
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
@@ -69,7 +71,7 @@ class ApiClient {
|
||||
|
||||
public static func postResource<T:Encodable>(endpoint: String, parameters: T, callback: ((_ success: Bool) -> Void)?) {
|
||||
if (Store.serverConfig == nil) {
|
||||
NSLog("Server config not set")
|
||||
logger.error("Server config not set")
|
||||
callback?(false)
|
||||
return
|
||||
}
|
||||
@@ -83,7 +85,7 @@ class ApiClient {
|
||||
case .success(_):
|
||||
callback?(true)
|
||||
case .failure(let error):
|
||||
NSLog("api request to \(endpoint) failed")
|
||||
logger.error("api request to \(endpoint) failed")
|
||||
print(error)
|
||||
|
||||
callback?(false)
|
||||
@@ -93,7 +95,7 @@ class ApiClient {
|
||||
|
||||
public static func patchResource<T: Encodable>(endpoint: String, parameters: T, callback: ((_ success: Bool) -> Void)?) {
|
||||
if (Store.serverConfig == nil) {
|
||||
NSLog("Server config not set")
|
||||
logger.error("Server config not set")
|
||||
callback?(false)
|
||||
return
|
||||
}
|
||||
@@ -107,7 +109,7 @@ class ApiClient {
|
||||
case .success(_):
|
||||
callback?(true)
|
||||
case .failure(let error):
|
||||
NSLog("api request to \(endpoint) failed")
|
||||
logger.error("api request to \(endpoint) failed")
|
||||
print(error)
|
||||
callback?(false)
|
||||
}
|
||||
@@ -124,7 +126,7 @@ class ApiClient {
|
||||
|
||||
public static func getResource<T: Decodable>(endpoint: String, decodable: T.Type = T.self, callback: ((_ param: T?) -> Void)?) {
|
||||
if (Store.serverConfig == nil) {
|
||||
NSLog("Server config not set")
|
||||
logger.error("Server config not set")
|
||||
callback?(nil)
|
||||
return
|
||||
}
|
||||
@@ -138,7 +140,7 @@ class ApiClient {
|
||||
case .success(let obj):
|
||||
callback?(obj)
|
||||
case .failure(let error):
|
||||
NSLog("api request to \(endpoint) failed")
|
||||
logger.error("api request to \(endpoint) failed")
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
@@ -193,10 +195,10 @@ class ApiClient {
|
||||
|
||||
if ( !localMediaProgressList.isEmpty ) {
|
||||
let payload = LocalMediaProgressSyncPayload(localMediaProgress: localMediaProgressList)
|
||||
NSLog("Sending sync local progress request with \(localMediaProgressList.count) progress items")
|
||||
logger.log("Sending sync local progress request with \(localMediaProgressList.count) progress items")
|
||||
postResource(endpoint: "api/me/sync-local-progress", parameters: payload, decodable: MediaProgressSyncResponsePayload.self) { response in
|
||||
let resultsPayload = LocalMediaProgressSyncResultsPayload(numLocalMediaProgressForServer: localMediaProgressList.count, numServerProgressUpdates: response.numServerProgressUpdates, numLocalProgressUpdates: response.localProgressUpdates?.count)
|
||||
NSLog("Media Progress Sync | \(String(describing: try? resultsPayload.asDictionary()))")
|
||||
logger.log("Media Progress Sync | \(String(describing: try? resultsPayload.asDictionary()))")
|
||||
|
||||
if let updates = response.localProgressUpdates {
|
||||
for update in updates {
|
||||
@@ -212,13 +214,13 @@ class ApiClient {
|
||||
callback(resultsPayload)
|
||||
}
|
||||
} else {
|
||||
NSLog("No local media progress to sync")
|
||||
logger.log("No local media progress to sync")
|
||||
callback(LocalMediaProgressSyncResultsPayload(numLocalMediaProgressForServer: 0, numServerProgressUpdates: 0, numLocalProgressUpdates: 0))
|
||||
}
|
||||
}
|
||||
|
||||
public static func updateMediaProgress<T:Encodable>(libraryItemId: String, episodeId: String?, payload: T, callback: @escaping () -> Void) {
|
||||
NSLog("updateMediaProgress \(libraryItemId) \(episodeId ?? "NIL") \(payload)")
|
||||
logger.log("updateMediaProgress \(libraryItemId) \(episodeId ?? "NIL") \(payload)")
|
||||
let endpoint = episodeId?.isEmpty ?? true ? "api/me/progress/\(libraryItemId)" : "api/me/progress/\(libraryItemId)/\(episodeId ?? "")"
|
||||
patchResource(endpoint: endpoint, parameters: payload) { success in
|
||||
callback()
|
||||
@@ -226,7 +228,7 @@ class ApiClient {
|
||||
}
|
||||
|
||||
public static func getMediaProgress(libraryItemId: String, episodeId: String?) async -> MediaProgress? {
|
||||
NSLog("getMediaProgress \(libraryItemId) \(episodeId ?? "NIL")")
|
||||
logger.log("getMediaProgress \(libraryItemId) \(episodeId ?? "NIL")")
|
||||
let endpoint = episodeId?.isEmpty ?? true ? "api/me/progress/\(libraryItemId)" : "api/me/progress/\(libraryItemId)/\(episodeId ?? "")"
|
||||
return await getResource(endpoint: endpoint, decodable: MediaProgress.self)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ class Database {
|
||||
public static var shared = {
|
||||
return Database()
|
||||
}()
|
||||
|
||||
private let logger = AppLogger(category: "Database")
|
||||
|
||||
private init() {}
|
||||
|
||||
@@ -30,7 +32,7 @@ class Database {
|
||||
existing.token = config.token
|
||||
}
|
||||
} catch {
|
||||
NSLog("failed to update server config")
|
||||
logger.error("failed to update server config")
|
||||
debugPrint(error)
|
||||
}
|
||||
|
||||
@@ -51,7 +53,7 @@ class Database {
|
||||
realm.add(config)
|
||||
}
|
||||
} catch(let exception) {
|
||||
NSLog("failed to save server config")
|
||||
logger.error("failed to save server config")
|
||||
debugPrint(exception)
|
||||
}
|
||||
|
||||
@@ -70,7 +72,7 @@ class Database {
|
||||
}
|
||||
}
|
||||
} catch(let exception) {
|
||||
NSLog("failed to delete server config")
|
||||
logger.error("failed to delete server config")
|
||||
debugPrint(exception)
|
||||
}
|
||||
}
|
||||
@@ -101,7 +103,7 @@ class Database {
|
||||
}
|
||||
}
|
||||
} catch(let exception) {
|
||||
NSLog("failed to save server config active index")
|
||||
logger.error("failed to save server config active index")
|
||||
debugPrint(exception)
|
||||
}
|
||||
}
|
||||
@@ -121,7 +123,7 @@ class Database {
|
||||
realm.add(deviceSettings)
|
||||
}
|
||||
} catch {
|
||||
NSLog("failed to save device settings")
|
||||
logger.error("failed to save device settings")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ extension CAPPluginCall {
|
||||
let json = try JSONSerialization.data(withJSONObject: value)
|
||||
return try JSONDecoder().decode(type, from: json)
|
||||
} catch {
|
||||
NSLog("Failed to get json for \(key)")
|
||||
AppLogger().error("Failed to get json for \(key)")
|
||||
debugPrint(error)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// AppLogger.swift
|
||||
// AppLogger
|
||||
//
|
||||
// Created by Fernando Fernandes on 29.06.20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
/// Logs app info by using the newest Swift unified logging APIs.
|
||||
///
|
||||
/// Reference:
|
||||
/// - [Explore logging in Swift (WWDC20)](https://developer.apple.com/wwdc20/10168)
|
||||
/// - [Unified Logging](https://developer.apple.com/documentation/os/logging)
|
||||
/// - [OSLog](https://developer.apple.com/documentation/os/oslog)
|
||||
/// - [Logger](https://developer.apple.com/documentation/os/logger)
|
||||
public struct AppLogger {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// Default values used by the `AppLogger`.
|
||||
public struct Defaults {
|
||||
public static let subsystem = Bundle.main.bundleIdentifier ?? "ABS"
|
||||
public static let category = "default"
|
||||
public static let isPrivate = false
|
||||
}
|
||||
|
||||
// MARK: - Private Properties
|
||||
|
||||
private let logger: Logger
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
/// Creates an `AppLogger` instance.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - subsystem: `String`. Organizes large topic areas within the app or apps. For example, you might define
|
||||
/// a subsystem for each process that you create. The default is `Bundle.main.bundleIdentifier ?? "AppLogger"`.
|
||||
/// - category: `String`. Within a `subsystem`, you define categories to further distinguish parts of that
|
||||
/// subsystem. For example, if you used a single subsystem for your app, you might create separate categories for
|
||||
/// model code and user-interface code. In a game, you might use categories to distinguish between physics, AI,
|
||||
/// world simulation, and rendering. The default is `default`.
|
||||
public init(subsystem: String = Defaults.subsystem, category: String = Defaults.category) {
|
||||
self.logger = Logger(subsystem: subsystem, category: category)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Interface
|
||||
|
||||
public extension AppLogger {
|
||||
|
||||
func log(_ information: String, isPrivate: Bool = Defaults.isPrivate) {
|
||||
if isPrivate {
|
||||
logger.log("\(information, privacy: .private)")
|
||||
} else {
|
||||
logger.log("\(information, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
func error(_ information: String, isPrivate: Bool = Defaults.isPrivate) {
|
||||
if isPrivate {
|
||||
logger.error("\(information, privacy: .private)")
|
||||
} else {
|
||||
logger.error("\(information, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
func error(_ error: Error) {
|
||||
logger.error("\(String(describing: error))")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user