Merge remote-tracking branch 'upstream/master' into ios-general-fixes

This commit is contained in:
ronaldheft
2022-09-14 19:55:42 -04:00
14 changed files with 233 additions and 136 deletions
+12 -8
View File
@@ -60,6 +60,7 @@
E9D5507528AEF93100C746DD /* PlayerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D5507428AEF93100C746DD /* PlayerSettings.swift */; }; E9D5507528AEF93100C746DD /* PlayerSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D5507428AEF93100C746DD /* PlayerSettings.swift */; };
E9DFCBFB28C28F4A00B36356 /* AudioPlayerSleepTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DFCBFA28C28F4A00B36356 /* AudioPlayerSleepTimer.swift */; }; E9DFCBFB28C28F4A00B36356 /* AudioPlayerSleepTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DFCBFA28C28F4A00B36356 /* AudioPlayerSleepTimer.swift */; };
E9E985F828B02D9400957F23 /* PlayerProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E985F728B02D9400957F23 /* PlayerProgress.swift */; }; E9E985F828B02D9400957F23 /* PlayerProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E985F728B02D9400957F23 /* PlayerProgress.swift */; };
E9FA07E328C82848005520B0 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9FA07E228C82848005520B0 /* Logger.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@@ -86,7 +87,7 @@
4D8D412C26E187E400BA5F0D /* App-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "App-Bridging-Header.h"; sourceTree = "<group>"; }; 4D8D412C26E187E400BA5F0D /* App-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "App-Bridging-Header.h"; sourceTree = "<group>"; };
4DF74911287105C600AC7814 /* DeviceSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceSettings.swift; sourceTree = "<group>"; }; 4DF74911287105C600AC7814 /* DeviceSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceSettings.swift; sourceTree = "<group>"; };
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; }; 50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; 504EC3041FED79650016851F /* Audiobookshelf.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Audiobookshelf.app; sourceTree = BUILT_PRODUCTS_DIR; };
504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; 504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -120,6 +121,7 @@
E9D5507428AEF93100C746DD /* PlayerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSettings.swift; sourceTree = "<group>"; }; E9D5507428AEF93100C746DD /* PlayerSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSettings.swift; sourceTree = "<group>"; };
E9DFCBFA28C28F4A00B36356 /* AudioPlayerSleepTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerSleepTimer.swift; sourceTree = "<group>"; }; E9DFCBFA28C28F4A00B36356 /* AudioPlayerSleepTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerSleepTimer.swift; sourceTree = "<group>"; };
E9E985F728B02D9400957F23 /* PlayerProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerProgress.swift; sourceTree = "<group>"; }; E9E985F728B02D9400957F23 /* PlayerProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerProgress.swift; sourceTree = "<group>"; };
E9FA07E228C82848005520B0 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; }; FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@@ -199,6 +201,7 @@
3AFCB5E627EA23F700ECCC05 /* util */ = { 3AFCB5E627EA23F700ECCC05 /* util */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E9FA07E228C82848005520B0 /* Logger.swift */,
3AFCB5E727EA240D00ECCC05 /* NowPlayingInfo.swift */, 3AFCB5E727EA240D00ECCC05 /* NowPlayingInfo.swift */,
3AD4FCEA280443DD006DB301 /* Database.swift */, 3AD4FCEA280443DD006DB301 /* Database.swift */,
3AD4FCEC28044E6C006DB301 /* Store.swift */, 3AD4FCEC28044E6C006DB301 /* Store.swift */,
@@ -224,7 +227,7 @@
504EC3051FED79650016851F /* Products */ = { 504EC3051FED79650016851F /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
504EC3041FED79650016851F /* App.app */, 504EC3041FED79650016851F /* Audiobookshelf.app */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -298,9 +301,9 @@
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
504EC3031FED79650016851F /* App */ = { 504EC3031FED79650016851F /* Audiobookshelf */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */; buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "Audiobookshelf" */;
buildPhases = ( buildPhases = (
6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */, 6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */,
504EC3001FED79650016851F /* Sources */, 504EC3001FED79650016851F /* Sources */,
@@ -312,9 +315,9 @@
); );
dependencies = ( dependencies = (
); );
name = App; name = Audiobookshelf;
productName = App; productName = App;
productReference = 504EC3041FED79650016851F /* App.app */; productReference = 504EC3041FED79650016851F /* Audiobookshelf.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
@@ -346,7 +349,7 @@
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
targets = ( targets = (
504EC3031FED79650016851F /* App */, 504EC3031FED79650016851F /* Audiobookshelf */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@@ -412,6 +415,7 @@
E9D5506228AC1CC900C746DD /* PlayerState.swift in Sources */, E9D5506228AC1CC900C746DD /* PlayerState.swift in Sources */,
3AD4FCE728043E72006DB301 /* AbsDatabase.m in Sources */, 3AD4FCE728043E72006DB301 /* AbsDatabase.m in Sources */,
504EC3081FED79650016851F /* AppDelegate.swift in Sources */, 504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
E9FA07E328C82848005520B0 /* Logger.swift in Sources */,
3A90295F280968E700E1D427 /* PlaybackReport.swift in Sources */, 3A90295F280968E700E1D427 /* PlaybackReport.swift in Sources */,
E9D5505A28AC1C4500C746DD /* Folder.swift in Sources */, E9D5505A28AC1C4500C746DD /* Folder.swift in Sources */,
3ABF580928059BAE005DFBE5 /* PlaybackSession.swift in Sources */, 3ABF580928059BAE005DFBE5 /* PlaybackSession.swift in Sources */,
@@ -650,7 +654,7 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = { 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "Audiobookshelf" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
504EC3171FED79650016851F /* Debug */, 504EC3171FED79650016851F /* Debug */,
@@ -15,8 +15,8 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F" BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app" BuildableName = "Audiobookshelf.app"
BlueprintName = "App" BlueprintName = "Audiobookshelf"
ReferencedContainer = "container:App.xcodeproj"> ReferencedContainer = "container:App.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
@@ -45,8 +45,8 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F" BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app" BuildableName = "Audiobookshelf.app"
BlueprintName = "App" BlueprintName = "Audiobookshelf"
ReferencedContainer = "container:App.xcodeproj"> ReferencedContainer = "container:App.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
@@ -62,8 +62,8 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F" BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app" BuildableName = "Audiobookshelf.app"
BlueprintName = "App" BlueprintName = "Audiobookshelf"
ReferencedContainer = "container:App.xcodeproj"> ReferencedContainer = "container:App.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
+9 -7
View File
@@ -5,6 +5,8 @@ import RealmSwift
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate {
private let logger = AppLogger(category: "AppDelegate")
lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds) lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
var backgroundCompletionHandler: (() -> Void)? var backgroundCompletionHandler: (() -> Void)?
@@ -13,15 +15,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
let configuration = Realm.Configuration( let configuration = Realm.Configuration(
schemaVersion: 4, schemaVersion: 4,
migrationBlock: { migration, oldSchemaVersion in migrationBlock: { [weak self] migration, oldSchemaVersion in
if (oldSchemaVersion < 1) { if (oldSchemaVersion < 1) {
NSLog("Realm schema version was \(oldSchemaVersion)") self?.logger.log("Realm schema version was \(oldSchemaVersion)")
migration.enumerateObjects(ofType: DeviceSettings.className()) { oldObject, newObject in migration.enumerateObjects(ofType: DeviceSettings.className()) { oldObject, newObject in
newObject?["enableAltView"] = false newObject?["enableAltView"] = false
} }
} }
if (oldSchemaVersion < 4) { if (oldSchemaVersion < 4) {
NSLog("Realm schema version was \(oldSchemaVersion)... Reindexing server configs") self?.logger.log("Realm schema version was \(oldSchemaVersion)... Reindexing server configs")
var indexCounter = 1 var indexCounter = 1
migration.enumerateObjects(ofType: ServerConnectionConfig.className()) { oldObject, newObject in migration.enumerateObjects(ofType: ServerConnectionConfig.className()) { oldObject, newObject in
newObject?["index"] = indexCounter newObject?["index"] = indexCounter
@@ -43,22 +45,22 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidEnterBackground(_ application: UIApplication) { func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
NSLog("Audiobookself is now in the background") logger.log("Audiobookself is now in the background")
} }
func applicationWillEnterForeground(_ application: UIApplication) { func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
NSLog("Audiobookself is now in the foreground") logger.log("Audiobookself is now in the foreground")
} }
func applicationDidBecomeActive(_ application: UIApplication) { func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
NSLog("Audiobookself is now active") logger.log("Audiobookself is now active")
} }
func applicationWillTerminate(_ application: UIApplication) { func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
NSLog("Audiobookself is terminating") logger.log("Audiobookself is terminating")
} }
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
+16 -13
View File
@@ -11,6 +11,8 @@ import RealmSwift
@objc(AbsAudioPlayer) @objc(AbsAudioPlayer)
public class AbsAudioPlayer: CAPPlugin { public class AbsAudioPlayer: CAPPlugin {
private let logger = AppLogger(category: "AbsAudioPlayer")
private var initialPlayWhenReady = false private var initialPlayWhenReady = false
private var isUIReady = false private var isUIReady = false
@@ -46,7 +48,7 @@ public class AbsAudioPlayer: CAPPlugin {
try self.startPlaybackSession(activeSession, playWhenReady: false, playbackRate: PlayerSettings.main().playbackRate) try self.startPlaybackSession(activeSession, playWhenReady: false, playbackRate: PlayerSettings.main().playbackRate)
} }
} catch { } catch {
NSLog("Failed to restore playback session") logger.error("Failed to restore playback session")
debugPrint(error) debugPrint(error)
} }
} }
@@ -67,7 +69,7 @@ public class AbsAudioPlayer: CAPPlugin {
let playbackRate = call.getFloat("playbackRate", 1) let playbackRate = call.getFloat("playbackRate", 1)
if libraryItemId == nil { if libraryItemId == nil {
NSLog("provide library item id") logger.error("provide library item id")
return call.resolve() return call.resolve()
} }
@@ -78,7 +80,7 @@ public class AbsAudioPlayer: CAPPlugin {
let item = Database.shared.getLocalLibraryItem(localLibraryItemId: libraryItemId!) let item = Database.shared.getLocalLibraryItem(localLibraryItemId: libraryItemId!)
let episode = item?.getPodcastEpisode(episodeId: episodeId) let episode = item?.getPodcastEpisode(episodeId: episodeId)
guard let playbackSession = item?.getPlaybackSession(episode: episode) else { guard let playbackSession = item?.getPlaybackSession(episode: episode) else {
NSLog("Failed to get local playback session") logger.error("Failed to get local playback session")
return call.resolve([:]) return call.resolve([:])
} }
@@ -87,18 +89,18 @@ public class AbsAudioPlayer: CAPPlugin {
try self.startPlaybackSession(playbackSession, playWhenReady: playWhenReady, playbackRate: playbackRate) try self.startPlaybackSession(playbackSession, playWhenReady: playWhenReady, playbackRate: playbackRate)
call.resolve(try playbackSession.asDictionary()) call.resolve(try playbackSession.asDictionary())
} catch(let exception) { } catch(let exception) {
NSLog("Failed to start session") logger.error("Failed to start session")
debugPrint(exception) debugPrint(exception)
call.resolve([:]) call.resolve([:])
} }
} else { // Playing from the server } else { // Playing from the server
ApiClient.startPlaybackSession(libraryItemId: libraryItemId!, episodeId: episodeId, forceTranscode: false) { session in ApiClient.startPlaybackSession(libraryItemId: libraryItemId!, episodeId: episodeId, forceTranscode: false) { [weak self] session in
do { do {
try session.save() try session.save()
try self.startPlaybackSession(session, playWhenReady: playWhenReady, playbackRate: playbackRate) try self?.startPlaybackSession(session, playWhenReady: playWhenReady, playbackRate: playbackRate)
call.resolve(try session.asDictionary()) call.resolve(try session.asDictionary())
} catch(let exception) { } catch(let exception) {
NSLog("Failed to start session") self?.logger.error("Failed to start session")
debugPrint(exception) debugPrint(exception)
call.resolve([:]) call.resolve([:])
} }
@@ -107,7 +109,7 @@ public class AbsAudioPlayer: CAPPlugin {
} }
@objc func closePlayback(_ call: CAPPluginCall) { @objc func closePlayback(_ call: CAPPluginCall) {
NSLog("Close playback") logger.log("Close playback")
PlayerHandler.stopPlayback() PlayerHandler.stopPlayback()
call.resolve() call.resolve()
@@ -193,7 +195,7 @@ public class AbsAudioPlayer: CAPPlugin {
let seconds = time / 1000 let seconds = time / 1000
NSLog("chapter time: \(isChapterTime)") logger.log("chapter time: \(isChapterTime)")
if isChapterTime { if isChapterTime {
PlayerHandler.setChapterSleepTime(stopAt: seconds) PlayerHandler.setChapterSleepTime(stopAt: seconds)
return call.resolve([ "success": true ]) return call.resolve([ "success": true ])
@@ -230,7 +232,7 @@ public class AbsAudioPlayer: CAPPlugin {
guard let localMediaProgressId = PlayerHandler.getPlaybackSession()?.localMediaProgressId else { return } guard let localMediaProgressId = PlayerHandler.getPlaybackSession()?.localMediaProgressId else { return }
guard let localMediaProgress = Database.shared.getLocalMediaProgress(localMediaProgressId: localMediaProgressId) else { return } guard let localMediaProgress = Database.shared.getLocalMediaProgress(localMediaProgressId: localMediaProgressId) else { return }
guard let progressUpdate = try? localMediaProgress.asDictionary() else { return } guard let progressUpdate = try? localMediaProgress.asDictionary() else { return }
NSLog("Sending local progress back to the UI") logger.log("Sending local progress back to the UI")
self.notifyListeners("onLocalMediaProgressUpdate", data: progressUpdate) self.notifyListeners("onLocalMediaProgressUpdate", data: progressUpdate)
} }
@@ -239,17 +241,18 @@ public class AbsAudioPlayer: CAPPlugin {
let session = PlayerHandler.getPlaybackSession() let session = PlayerHandler.getPlaybackSession()
let libraryItemId = session?.libraryItemId ?? "" let libraryItemId = session?.libraryItemId ?? ""
let episodeId = session?.episodeId ?? nil let episodeId = session?.episodeId ?? nil
NSLog("Forcing Transcode") logger.log("Forcing Transcode")
// If direct playing then fallback to transcode // If direct playing then fallback to transcode
ApiClient.startPlaybackSession(libraryItemId: libraryItemId, episodeId: episodeId, forceTranscode: true) { session in ApiClient.startPlaybackSession(libraryItemId: libraryItemId, episodeId: episodeId, forceTranscode: true) { [weak self] session in
do { do {
guard let self = self else { return }
try session.save() try session.save()
PlayerHandler.startPlayback(sessionId: session.id, playWhenReady: self.initialPlayWhenReady, playbackRate: PlayerSettings.main().playbackRate) PlayerHandler.startPlayback(sessionId: session.id, playWhenReady: self.initialPlayWhenReady, playbackRate: PlayerSettings.main().playbackRate)
self.sendPlaybackSession(session: try session.asDictionary()) self.sendPlaybackSession(session: try session.asDictionary())
self.sendMetadata() self.sendMetadata()
} catch(let exception) { } catch(let exception) {
NSLog("Failed to start transcoded session") self?.logger.error("Failed to start transcoded session")
debugPrint(exception) debugPrint(exception)
} }
} }
+8 -6
View File
@@ -28,6 +28,8 @@ extension String {
@objc(AbsDatabase) @objc(AbsDatabase)
public class AbsDatabase: CAPPlugin { public class AbsDatabase: CAPPlugin {
private let logger = AppLogger(category: "AbsDatabase")
@objc func setCurrentServerConnectionConfig(_ call: CAPPluginCall) { @objc func setCurrentServerConnectionConfig(_ call: CAPPluginCall) {
var id = call.getString("id") var id = call.getString("id")
let address = call.getString("address", "") let address = call.getString("address", "")
@@ -82,7 +84,7 @@ public class AbsDatabase: CAPPlugin {
let items = Database.shared.getLocalLibraryItems() let items = Database.shared.getLocalLibraryItems()
call.resolve([ "value": try items.asDictionaryArray()]) call.resolve([ "value": try items.asDictionaryArray()])
} catch(let exception) { } catch(let exception) {
NSLog("error while readling local library items") logger.error("error while readling local library items")
debugPrint(exception) debugPrint(exception)
call.resolve() call.resolve()
} }
@@ -98,7 +100,7 @@ public class AbsDatabase: CAPPlugin {
call.resolve() call.resolve()
} }
} catch(let exception) { } catch(let exception) {
NSLog("error while readling local library items") logger.error("error while readling local library items")
debugPrint(exception) debugPrint(exception)
call.resolve() call.resolve()
} }
@@ -114,7 +116,7 @@ public class AbsDatabase: CAPPlugin {
call.resolve() call.resolve()
} }
} catch(let exception) { } catch(let exception) {
NSLog("error while readling local library items") logger.error("error while readling local library items")
debugPrint(exception) debugPrint(exception)
call.resolve() call.resolve()
} }
@@ -128,7 +130,7 @@ public class AbsDatabase: CAPPlugin {
do { do {
call.resolve([ "value": try Database.shared.getAllLocalMediaProgress().asDictionaryArray() ]) call.resolve([ "value": try Database.shared.getAllLocalMediaProgress().asDictionaryArray() ])
} catch { } catch {
NSLog("Error while loading local media progress") logger.error("Error while loading local media progress")
debugPrint(error) debugPrint(error)
call.resolve(["value": []]) call.resolve(["value": []])
} }
@@ -178,7 +180,7 @@ public class AbsDatabase: CAPPlugin {
return return
} }
NSLog("syncServerMediaProgressWithLocalMediaProgress: Saving local media progress") logger.log("syncServerMediaProgressWithLocalMediaProgress: Saving local media progress")
try localMediaProgress.updateFromServerMediaProgress(serverMediaProgress) try localMediaProgress.updateFromServerMediaProgress(serverMediaProgress)
call.resolve(try localMediaProgress.asDictionary()) call.resolve(try localMediaProgress.asDictionary())
@@ -194,7 +196,7 @@ public class AbsDatabase: CAPPlugin {
let localMediaProgressId = call.getString("localMediaProgressId") let localMediaProgressId = call.getString("localMediaProgressId")
let isFinished = call.getBool("isFinished", false) let isFinished = call.getBool("isFinished", false)
NSLog("updateLocalMediaProgressFinished \(localMediaProgressId ?? "Unknown") | Is Finished: \(isFinished)") logger.log("updateLocalMediaProgressFinished \(localMediaProgressId ?? "Unknown") | Is Finished: \(isFinished)")
do { do {
let localMediaProgress = try LocalMediaProgress.fetchOrCreateLocalMediaProgress(localMediaProgressId: localMediaProgressId, localLibraryItemId: localLibraryItemId, localEpisodeId: localEpisodeId) let localMediaProgress = try LocalMediaProgress.fetchOrCreateLocalMediaProgress(localMediaProgressId: localMediaProgressId, localLibraryItemId: localLibraryItemId, localEpisodeId: localEpisodeId)
+21 -19
View File
@@ -14,6 +14,8 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
static private let downloadsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] static private let downloadsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
private let logger = AppLogger(category: "AbsDownloader")
private lazy var session: URLSession = { private lazy var session: URLSession = {
let config = URLSessionConfiguration.background(withIdentifier: "AbsDownloader") let config = URLSessionConfiguration.background(withIdentifier: "AbsDownloader")
let queue = OperationQueue() let queue = OperationQueue()
@@ -94,7 +96,7 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
private func handleDownloadTaskUpdate(downloadTask: URLSessionTask, progressHandler: DownloadProgressHandler) { private func handleDownloadTaskUpdate(downloadTask: URLSessionTask, progressHandler: DownloadProgressHandler) {
do { do {
guard let downloadItemPartId = downloadTask.taskDescription else { throw LibraryItemDownloadError.noTaskDescription } guard let downloadItemPartId = downloadTask.taskDescription else { throw LibraryItemDownloadError.noTaskDescription }
NSLog("Received download update for \(downloadItemPartId)") logger.log("Received download update for \(downloadItemPartId)")
// Find the download item // Find the download item
let downloadItem = Database.shared.getDownloadItem(downloadItemPartId: downloadItemPartId) let downloadItem = Database.shared.getDownloadItem(downloadItemPartId: downloadItemPartId)
@@ -108,7 +110,7 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
do { do {
try progressHandler(downloadItem, part) try progressHandler(downloadItem, part)
} catch { } catch {
NSLog("Error while processing progress") logger.error("Error while processing progress")
debugPrint(error) debugPrint(error)
} }
@@ -119,7 +121,7 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
} }
self.notifyDownloadProgress() self.notifyDownloadProgress()
} catch { } catch {
NSLog("DownloadItemError") logger.error("DownloadItemError")
debugPrint(error) debugPrint(error)
} }
} }
@@ -127,18 +129,18 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
// We want to handle updating the UI in the background and throttled so we don't overload the UI with progress updates // We want to handle updating the UI in the background and throttled so we don't overload the UI with progress updates
private func notifyDownloadProgress() { private func notifyDownloadProgress() {
if self.monitoringProgressTimer?.isValid ?? false { if self.monitoringProgressTimer?.isValid ?? false {
NSLog("Already monitoring progress, no need to start timer again") logger.log("Already monitoring progress, no need to start timer again")
} else { } else {
DispatchQueue.runOnMainQueue { DispatchQueue.runOnMainQueue {
self.monitoringProgressTimer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true, block: { t in self.monitoringProgressTimer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true, block: { [unowned self] t in
NSLog("Starting monitoring download progress...") self.logger.log("Starting monitoring download progress...")
// Fetch active downloads in a thread-safe way // Fetch active downloads in a thread-safe way
func fetchActiveDownloads() -> [String: DownloadItem]? { func fetchActiveDownloads() -> [String: DownloadItem]? {
self.progressStatusQueue.sync { self.progressStatusQueue.sync {
let activeDownloads = self.downloadItemProgress let activeDownloads = self.downloadItemProgress
if activeDownloads.isEmpty { if activeDownloads.isEmpty {
NSLog("Finishing monitoring download progress...") logger.log("Finishing monitoring download progress...")
t.invalidate() t.invalidate()
} }
return activeDownloads return activeDownloads
@@ -173,8 +175,8 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
statusNotification["libraryItemId"] = downloadItem.id statusNotification["libraryItemId"] = downloadItem.id
if ( downloadItem.didDownloadSuccessfully() ) { if ( downloadItem.didDownloadSuccessfully() ) {
ApiClient.getLibraryItemWithProgress(libraryItemId: downloadItem.libraryItemId!, episodeId: downloadItem.episodeId) { libraryItem in ApiClient.getLibraryItemWithProgress(libraryItemId: downloadItem.libraryItemId!, episodeId: downloadItem.episodeId) { [weak self] libraryItem in
guard let libraryItem = libraryItem else { NSLog("LibraryItem not found"); return } guard let libraryItem = libraryItem else { self?.logger.error("LibraryItem not found"); return }
let localDirectory = libraryItem.id let localDirectory = libraryItem.id
var coverFile: String? var coverFile: String?
@@ -206,7 +208,7 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
statusNotification["localMediaProgress"] = try? localMediaProgress.asDictionary() statusNotification["localMediaProgress"] = try? localMediaProgress.asDictionary()
} }
self.notifyListeners("onItemDownloadComplete", data: statusNotification) self?.notifyListeners("onItemDownloadComplete", data: statusNotification)
} }
} else { } else {
self.notifyListeners("onItemDownloadComplete", data: statusNotification) self.notifyListeners("onItemDownloadComplete", data: statusNotification)
@@ -221,22 +223,22 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
var episodeId = call.getString("episodeId") var episodeId = call.getString("episodeId")
if ( episodeId == "null" ) { episodeId = nil } if ( episodeId == "null" ) { episodeId = nil }
NSLog("Download library item \(libraryItemId ?? "N/A") / episode \(episodeId ?? "N/A")") logger.log("Download library item \(libraryItemId ?? "N/A") / episode \(episodeId ?? "N/A")")
guard let libraryItemId = libraryItemId else { return call.resolve(["error": "libraryItemId not specified"]) } guard let libraryItemId = libraryItemId else { return call.resolve(["error": "libraryItemId not specified"]) }
ApiClient.getLibraryItemWithProgress(libraryItemId: libraryItemId, episodeId: episodeId) { libraryItem in ApiClient.getLibraryItemWithProgress(libraryItemId: libraryItemId, episodeId: episodeId) { [weak self] libraryItem in
if let libraryItem = libraryItem { if let libraryItem = libraryItem {
NSLog("Got library item from server \(libraryItem.id)") self?.logger.log("Got library item from server \(libraryItem.id)")
do { do {
if let episodeId = episodeId { if let episodeId = episodeId {
// Download a podcast episode // Download a podcast episode
guard libraryItem.mediaType == "podcast" else { throw LibraryItemDownloadError.libraryItemNotPodcast } guard libraryItem.mediaType == "podcast" else { throw LibraryItemDownloadError.libraryItemNotPodcast }
let episode = libraryItem.media?.episodes.enumerated().first(where: { $1.id == episodeId })?.element let episode = libraryItem.media?.episodes.enumerated().first(where: { $1.id == episodeId })?.element
guard let episode = episode else { throw LibraryItemDownloadError.podcastEpisodeNotFound } guard let episode = episode else { throw LibraryItemDownloadError.podcastEpisodeNotFound }
try self.startLibraryItemDownload(libraryItem, episode: episode) try self?.startLibraryItemDownload(libraryItem, episode: episode)
} else { } else {
// Download a book // Download a book
try self.startLibraryItemDownload(libraryItem) try self?.startLibraryItemDownload(libraryItem)
} }
call.resolve() call.resolve()
} catch { } catch {
@@ -298,7 +300,7 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
} }
private func startLibraryItemTrackDownload(item: LibraryItem, position: Int, track: AudioTrack, episode: PodcastEpisode?) throws -> DownloadItemPartTask { private func startLibraryItemTrackDownload(item: LibraryItem, position: Int, track: AudioTrack, episode: PodcastEpisode?) throws -> DownloadItemPartTask {
NSLog("TRACK \(track.contentUrl!)") logger.log("TRACK \(track.contentUrl!)")
// If we don't name metadata, then we can't proceed // If we don't name metadata, then we can't proceed
guard let filename = track.metadata?.filename else { guard let filename = track.metadata?.filename else {
@@ -342,10 +344,10 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
private func createLibraryItemFileDirectory(item: LibraryItem) throws -> String { private func createLibraryItemFileDirectory(item: LibraryItem) throws -> String {
let itemDirectory = item.id let itemDirectory = item.id
NSLog("ITEM DIR \(itemDirectory)") logger.log("ITEM DIR \(itemDirectory)")
guard AbsDownloader.itemDownloadFolder(path: itemDirectory) != nil else { guard AbsDownloader.itemDownloadFolder(path: itemDirectory) != nil else {
NSLog("Failed to CREATE LI DIRECTORY \(itemDirectory)") logger.error("Failed to CREATE LI DIRECTORY \(itemDirectory)")
throw LibraryItemDownloadError.failedDirectory throw LibraryItemDownloadError.failedDirectory
} }
@@ -367,7 +369,7 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
return itemFolder return itemFolder
} catch { } catch {
NSLog("Failed to CREATE LI DIRECTORY \(error)") AppLogger().error("Failed to CREATE LI DIRECTORY \(error)")
return nil return nil
} }
} }
+13 -11
View File
@@ -10,10 +10,12 @@ import Capacitor
@objc(AbsFileSystem) @objc(AbsFileSystem)
public class AbsFileSystem: CAPPlugin { public class AbsFileSystem: CAPPlugin {
private let logger = AppLogger(category: "AbsFileSystem")
@objc func selectFolder(_ call: CAPPluginCall) { @objc func selectFolder(_ call: CAPPluginCall) {
let mediaType = call.getString("mediaType") let mediaType = call.getString("mediaType")
NSLog("Select Folder for media type \(mediaType ?? "UNSET")") logger.log("Select Folder for media type \(mediaType ?? "UNSET")")
call.unavailable("Not available on iOS") call.unavailable("Not available on iOS")
} }
@@ -21,7 +23,7 @@ public class AbsFileSystem: CAPPlugin {
@objc func checkFolderPermission(_ call: CAPPluginCall) { @objc func checkFolderPermission(_ call: CAPPluginCall) {
let folderUrl = call.getString("folderUrl") let folderUrl = call.getString("folderUrl")
NSLog("checkFolderPermission for folder \(folderUrl ?? "UNSET")") logger.log("checkFolderPermission for folder \(folderUrl ?? "UNSET")")
call.unavailable("Not available on iOS") call.unavailable("Not available on iOS")
} }
@@ -30,7 +32,7 @@ public class AbsFileSystem: CAPPlugin {
let folderId = call.getString("folderId") let folderId = call.getString("folderId")
let forceAudioProbe = call.getBool("forceAudioProbe", false) let forceAudioProbe = call.getBool("forceAudioProbe", false)
NSLog("scanFolder \(folderId ?? "UNSET") | Force Probe = \(forceAudioProbe)") logger.log("scanFolder \(folderId ?? "UNSET") | Force Probe = \(forceAudioProbe)")
call.unavailable("Not available on iOS") call.unavailable("Not available on iOS")
} }
@@ -38,7 +40,7 @@ public class AbsFileSystem: CAPPlugin {
@objc func removeFolder(_ call: CAPPluginCall) { @objc func removeFolder(_ call: CAPPluginCall) {
let folderId = call.getString("folderId") let folderId = call.getString("folderId")
NSLog("removeFolder \(folderId ?? "UNSET")") logger.log("removeFolder \(folderId ?? "UNSET")")
call.unavailable("Not available on iOS") call.unavailable("Not available on iOS")
} }
@@ -46,7 +48,7 @@ public class AbsFileSystem: CAPPlugin {
@objc func removeLocalLibraryItem(_ call: CAPPluginCall) { @objc func removeLocalLibraryItem(_ call: CAPPluginCall) {
let localLibraryItemId = call.getString("localLibraryItemId") let localLibraryItemId = call.getString("localLibraryItemId")
NSLog("removeLocalLibraryItem \(localLibraryItemId ?? "UNSET")") logger.log("removeLocalLibraryItem \(localLibraryItemId ?? "UNSET")")
call.unavailable("Not available on iOS") call.unavailable("Not available on iOS")
} }
@@ -55,7 +57,7 @@ public class AbsFileSystem: CAPPlugin {
let localLibraryItemId = call.getString("localLibraryItemId") let localLibraryItemId = call.getString("localLibraryItemId")
let forceAudioProbe = call.getBool("forceAudioProbe", false) let forceAudioProbe = call.getBool("forceAudioProbe", false)
NSLog("scanLocalLibraryItem \(localLibraryItemId ?? "UNSET") | Force Probe = \(forceAudioProbe)") logger.log("scanLocalLibraryItem \(localLibraryItemId ?? "UNSET") | Force Probe = \(forceAudioProbe)")
call.unavailable("Not available on iOS") call.unavailable("Not available on iOS")
} }
@@ -64,7 +66,7 @@ public class AbsFileSystem: CAPPlugin {
let localLibraryItemId = call.getString("id") let localLibraryItemId = call.getString("id")
let contentUrl = call.getString("contentUrl") let contentUrl = call.getString("contentUrl")
NSLog("deleteItem \(localLibraryItemId ?? "UNSET") url \(contentUrl ?? "UNSET")") logger.log("deleteItem \(localLibraryItemId ?? "UNSET") url \(contentUrl ?? "UNSET")")
var success = false var success = false
do { do {
@@ -74,7 +76,7 @@ public class AbsFileSystem: CAPPlugin {
success = true success = true
} }
} catch { } catch {
NSLog("Failed to delete \(error)") logger.error("Failed to delete \(error)")
success = false success = false
} }
@@ -85,7 +87,7 @@ public class AbsFileSystem: CAPPlugin {
let localLibraryItemId = call.getString("id") let localLibraryItemId = call.getString("id")
let trackLocalFileId = call.getString("trackLocalFileId") let trackLocalFileId = call.getString("trackLocalFileId")
NSLog("deleteTrackFromItem \(localLibraryItemId ?? "UNSET") track file \(trackLocalFileId ?? "UNSET")") logger.log("deleteTrackFromItem \(localLibraryItemId ?? "UNSET") track file \(trackLocalFileId ?? "UNSET")")
var success = false var success = false
if let localLibraryItemId = localLibraryItemId, let trackLocalFileId = trackLocalFileId, let item = Database.shared.getLocalLibraryItem(localLibraryItemId: localLibraryItemId) { if let localLibraryItemId = localLibraryItemId, let trackLocalFileId = trackLocalFileId, let item = Database.shared.getLocalLibraryItem(localLibraryItemId: localLibraryItemId) {
@@ -105,12 +107,12 @@ public class AbsFileSystem: CAPPlugin {
success = true success = true
} }
} catch { } catch {
NSLog("Failed to delete \(error)") logger.error("Failed to delete \(error)")
success = false success = false
} }
} }
} catch { } catch {
NSLog("Failed to delete \(error)") logger.error("Failed to delete \(error)")
success = false success = false
} }
} }
+31 -27
View File
@@ -19,6 +19,7 @@ enum PlayMethod:Int {
class AudioPlayer: NSObject { class AudioPlayer: NSObject {
internal let queue = DispatchQueue(label: "ABSAudioPlayerQueue") internal let queue = DispatchQueue(label: "ABSAudioPlayerQueue")
internal let logger = AppLogger(category: "AudioPlayer")
// enums and @objc are not compatible // enums and @objc are not compatible
@objc dynamic var status: Int @objc dynamic var status: Int
@@ -68,7 +69,7 @@ class AudioPlayer: NSObject {
let playbackSession = self.getPlaybackSession() let playbackSession = self.getPlaybackSession()
guard let playbackSession = playbackSession else { 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) NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.failed.rawValue), object: nil)
return return
} }
@@ -86,10 +87,10 @@ class AudioPlayer: NSObject {
} }
self.currentTrackIndex = getItemIndexForTime(time: playbackSession.currentTime) 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] 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) { for item in Array(playerItems) {
self.audioPlayer.insert(item, after:self.audioPlayer.items().last) self.audioPlayer.insert(item, after:self.audioPlayer.items().last)
@@ -99,7 +100,7 @@ class AudioPlayer: NSObject {
setupQueueObserver() setupQueueObserver()
setupQueueItemStatusObserver() setupQueueItemStatusObserver()
NSLog("Audioplayer ready") logger.log("Audioplayer ready")
} }
deinit { deinit {
@@ -120,8 +121,8 @@ class AudioPlayer: NSObject {
do { do {
try AVAudioSession.sharedInstance().setActive(false) try AVAudioSession.sharedInstance().setActive(false)
} catch { } catch {
NSLog("Failed to set AVAudioSession inactive") logger.error("Failed to set AVAudioSession inactive")
print(error) logger.error(error)
} }
self.removeAudioSessionNotifications() self.removeAudioSessionNotifications()
@@ -214,14 +215,14 @@ class AudioPlayer: NSObject {
self.audioPlayer.currentItem.map { item in self.audioPlayer.currentItem.map { item in
self.currentTrackIndex = self.allPlayerItems.firstIndex(of:item) ?? 0 self.currentTrackIndex = self.allPlayerItems.firstIndex(of:item) ?? 0
if (self.currentTrackIndex != prevTrackIndex) { if (self.currentTrackIndex != prevTrackIndex) {
NSLog("New Current track index \(self.currentTrackIndex)") self.logger.log("New Current track index \(self.currentTrackIndex)")
} }
} }
} }
} }
private func setupQueueItemStatusObserver() { private func setupQueueItemStatusObserver() {
NSLog("queueStatusObserver: Setting up") logger.log("queueStatusObserver: Setting up")
// Listen for player item updates // Listen for player item updates
self.queueItemStatusObserver?.invalidate() self.queueItemStatusObserver?.invalidate()
@@ -236,13 +237,13 @@ class AudioPlayer: NSObject {
} }
private func handleQueueItemStatus(playerItem: AVPlayerItem) { private func handleQueueItemStatus(playerItem: AVPlayerItem) {
NSLog("queueStatusObserver: Current item status changed") logger.log("queueStatusObserver: Current item status changed")
guard let playbackSession = self.getPlaybackSession() else { guard let playbackSession = self.getPlaybackSession() else {
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.failed.rawValue), object: nil) NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.failed.rawValue), object: nil)
return return
} }
if (playerItem.status == .readyToPlay) { 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 // Seek the player before initializing, so a currentTime of 0 does not appear in MediaProgress / session
let firstReady = self.status < 0 let firstReady = self.status < 0
@@ -261,7 +262,7 @@ class AudioPlayer: NSObject {
self.play() self.play()
} }
} else if (playerItem.status == .failed) { } 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) NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.failed.rawValue), object: nil)
} }
} }
@@ -269,8 +270,8 @@ class AudioPlayer: NSObject {
private func startPausedTimer() { private func startPausedTimer() {
guard self.pausedTimer == nil else { return } guard self.pausedTimer == nil else { return }
self.queue.async { self.queue.async {
self.pausedTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in self.pausedTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { [weak self] timer in
NSLog("PAUSE TIMER: Syncing from server") self?.logger.log("PAUSE TIMER: Syncing from server")
Task { await PlayerProgress.shared.syncFromServer() } Task { await PlayerProgress.shared.syncFromServer() }
} }
} }
@@ -334,7 +335,7 @@ class AudioPlayer: NSObject {
} }
private func resumePlayback() { private func resumePlayback() {
NSLog("PLAY: Resuming playback") logger.log("PLAY: Resuming playback")
// Stop the paused timer // Stop the paused timer
self.stopPausedTimer() self.stopPausedTimer()
@@ -353,7 +354,7 @@ class AudioPlayer: NSObject {
public func pause() { public func pause() {
guard self.isInitialized() else { return } guard self.isInitialized() else { return }
NSLog("PAUSE: Pausing playback") logger.log("PAUSE: Pausing playback")
DispatchQueue.runOnMainQueue { DispatchQueue.runOnMainQueue {
self.audioPlayer.pause() self.audioPlayer.pause()
} }
@@ -377,17 +378,17 @@ class AudioPlayer: NSObject {
self.pause() self.pause()
NSLog("SEEK: Seek to \(to) from \(from)") logger.log("SEEK: Seek to \(to) from \(from)")
guard let playbackSession = self.getPlaybackSession() else { return } guard let playbackSession = self.getPlaybackSession() else { return }
let currentTrack = playbackSession.audioTracks[self.currentTrackIndex] let currentTrack = playbackSession.audioTracks[self.currentTrackIndex]
let ctso = currentTrack.startOffset ?? 0.0 let ctso = currentTrack.startOffset ?? 0.0
let trackEnd = ctso + currentTrack.duration 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) 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 // Reconstruct queue if seeking to a different track
if (self.currentTrackIndex != indexOfSeek) { if (self.currentTrackIndex != indexOfSeek) {
@@ -410,13 +411,16 @@ class AudioPlayer: NSObject {
setupQueueItemStatusObserver() setupQueueItemStatusObserver()
} else { } 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 currentTrackStartOffset = playbackSession.audioTracks[self.currentTrackIndex].startOffset ?? 0.0
let seekTime = to - currentTrackStartOffset let seekTime = to - currentTrackStartOffset
DispatchQueue.runOnMainQueue { DispatchQueue.runOnMainQueue {
self.audioPlayer.seek(to: CMTime(seconds: seekTime, preferredTimescale: 1000)) { [weak self] completed in 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 } guard let self = self else { return }
if continuePlaying { if continuePlaying {
@@ -433,7 +437,7 @@ class AudioPlayer: NSObject {
let playbackSpeedChanged = rate > 0.0 && rate != self.tmpRate && !(observed && rate == 1) let playbackSpeedChanged = rate > 0.0 && rate != self.tmpRate && !(observed && rate == 1)
if self.audioPlayer.rate != rate { 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 { DispatchQueue.runOnMainQueue {
self.audioPlayer.rate = rate self.audioPlayer.rate = rate
} }
@@ -490,7 +494,7 @@ class AudioPlayer: NSObject {
} else if (playbackSession.playMethod == PlayMethod.local.rawValue) { } else if (playbackSession.playMethod == PlayMethod.local.rawValue) {
guard let localFile = track.getLocalFile() else { guard let localFile = track.getLocalFile() else {
// Worst case we can stream the file // 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 filename = track.metadata?.filename ?? ""
let filenameEncoded = filename.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed) let filenameEncoded = filename.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
let urlstr = "\(Store.serverConfig!.address)/s/item/\(itemId)/\(filenameEncoded ?? "")?token=\(Store.serverConfig!.token)" let urlstr = "\(Store.serverConfig!.address)/s/item/\(itemId)/\(filenameEncoded ?? "")?token=\(Store.serverConfig!.token)"
@@ -510,8 +514,8 @@ class AudioPlayer: NSObject {
do { do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .spokenAudio) try AVAudioSession.sharedInstance().setCategory(.playback, mode: .spokenAudio)
} catch { } catch {
NSLog("Failed to set AVAudioSession category") logger.error("Failed to set AVAudioSession category")
print(error) logger.error(error)
} }
} }
@@ -519,7 +523,7 @@ class AudioPlayer: NSObject {
do { do {
try AVAudioSession.sharedInstance().setActive(active) try AVAudioSession.sharedInstance().setActive(active)
} catch { } 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?) { public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &playerContext { if context == &playerContext {
if keyPath == #keyPath(AVPlayer.rate) { 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) 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) NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil)
NSLog("WARNING: Item ended") logger.log("WARNING: Item ended")
} }
} else { } else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
@@ -31,7 +31,7 @@ extension AudioPlayer {
} }
public func setSleepTimer(secondsUntilSleep: Double) { public func setSleepTimer(secondsUntilSleep: Double) {
NSLog("SLEEP TIMER: Sleeping in \(secondsUntilSleep) seconds") logger.log("SLEEP TIMER: Sleeping in \(secondsUntilSleep) seconds")
self.removeSleepTimer() self.removeSleepTimer()
self.sleepTimeRemaining = secondsUntilSleep self.sleepTimeRemaining = secondsUntilSleep
@@ -48,7 +48,7 @@ extension AudioPlayer {
} }
public func setChapterSleepTimer(stopAt: Double) { public func setChapterSleepTimer(stopAt: Double) {
NSLog("SLEEP TIMER: Scheduling for chapter end \(stopAt)") logger.log("SLEEP TIMER: Scheduling for chapter end \(stopAt)")
self.removeSleepTimer() self.removeSleepTimer()
// Schedule the observation time // Schedule the observation time
@@ -124,7 +124,7 @@ extension AudioPlayer {
} }
private func handleSleepEnd() { private func handleSleepEnd() {
NSLog("SLEEP TIMER: Pausing audio") logger.log("SLEEP TIMER: Pausing audio")
self.pause() self.pause()
self.removeSleepTimer() self.removeSleepTimer()
} }
+17 -15
View File
@@ -14,6 +14,8 @@ class PlayerProgress {
private static let TIME_BETWEEN_SESSION_SYNC_IN_SECONDS = 10.0 private static let TIME_BETWEEN_SESSION_SYNC_IN_SECONDS = 10.0
private let logger = AppLogger(category: "PlayerProgress")
private init() {} private init() {}
@@ -28,8 +30,8 @@ class PlayerProgress {
try await updateServerSessionFromLocalSession(session, rateLimitSync: !isStopping) try await updateServerSessionFromLocalSession(session, rateLimitSync: !isStopping)
} }
} catch { } catch {
debugPrint("Failed to syncFromPlayer") logger.error("Failed to syncFromPlayer")
debugPrint(error) logger.error(error)
} }
await UIApplication.shared.endBackgroundTask(backgroundToken) await UIApplication.shared.endBackgroundTask(backgroundToken)
} }
@@ -39,8 +41,8 @@ class PlayerProgress {
do { do {
try await updateAllServerSessionFromLocalSession() try await updateAllServerSessionFromLocalSession()
} catch { } catch {
debugPrint("Failed to syncToServer") logger.error("Failed to syncToServer")
debugPrint(error) logger.error(error)
} }
await UIApplication.shared.endBackgroundTask(backgroundToken) await UIApplication.shared.endBackgroundTask(backgroundToken)
} }
@@ -50,8 +52,8 @@ class PlayerProgress {
do { do {
try await updateLocalSessionFromServerMediaProgress() try await updateLocalSessionFromServerMediaProgress()
} catch { } catch {
debugPrint("Failed to syncFromServer") logger.error("Failed to syncFromServer")
debugPrint(error) logger.error(error)
} }
await UIApplication.shared.endBackgroundTask(backgroundToken) await UIApplication.shared.endBackgroundTask(backgroundToken)
} }
@@ -96,7 +98,7 @@ class PlayerProgress {
try localMediaProgress.updateFromPlaybackSession(session) 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 // Send the local progress back to front-end
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.localProgress.rawValue), object: nil) NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.localProgress.rawValue), object: nil)
@@ -141,7 +143,7 @@ class PlayerProgress {
session = session.freeze() session = session.freeze()
guard safeToSync else { return } 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 var success = false
if session.isLocal { if session.isLocal {
@@ -161,25 +163,25 @@ class PlayerProgress {
} }
private func updateLocalSessionFromServerMediaProgress() async throws { 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: { guard let session = try await Realm().objects(PlaybackSession.self).last(where: {
$0.isActiveSession == true && $0.serverConnectionConfigId == Store.serverConfig?.id $0.isActiveSession == true && $0.serverConnectionConfigId == Store.serverConfig?.id
})?.freeze() else { })?.freeze() else {
NSLog("updateLocalSessionFromServerMediaProgress: Failed to get session") logger.log("updateLocalSessionFromServerMediaProgress: Failed to get session")
return return
} }
// Fetch the current progress // Fetch the current progress
let progress = await ApiClient.getMediaProgress(libraryItemId: session.libraryItemId!, episodeId: session.episodeId) let progress = await ApiClient.getMediaProgress(libraryItemId: session.libraryItemId!, episodeId: session.episodeId)
guard let progress = progress else { guard let progress = progress else {
NSLog("updateLocalSessionFromServerMediaProgress: No progress object") logger.log("updateLocalSessionFromServerMediaProgress: No progress object")
return return
} }
// Determine which session is newer // Determine which session is newer
let serverLastUpdate = progress.lastUpdate let serverLastUpdate = progress.lastUpdate
guard let localLastUpdate = session.updatedAt else { guard let localLastUpdate = session.updatedAt else {
NSLog("updateLocalSessionFromServerMediaProgress: No local session updatedAt") logger.log("updateLocalSessionFromServerMediaProgress: No local session updatedAt")
return return
} }
let serverCurrentTime = progress.currentTime let serverCurrentTime = progress.currentTime
@@ -190,16 +192,16 @@ class PlayerProgress {
// Update the session, if needed // Update the session, if needed
if serverIsNewerThanLocal && currentTimeIsDifferent { 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 } guard let session = session.thaw() else { return }
try session.update { try session.update {
session.currentTime = serverCurrentTime session.currentTime = serverCurrentTime
session.updatedAt = serverLastUpdate 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) PlayerHandler.seek(amount: session.currentTime)
} else { } 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")
} }
} }
+17 -15
View File
@@ -9,6 +9,8 @@ import Foundation
import Alamofire import Alamofire
class ApiClient { class ApiClient {
private static let logger = AppLogger(category: "ApiClient")
public static func getData(from url: URL, completion: @escaping (UIImage?) -> Void) { public static func getData(from url: URL, completion: @escaping (UIImage?) -> Void) {
URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in
if let data = data { 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)?) { public static func postResource<T: Decodable>(endpoint: String, parameters: [String: Any], decodable: T.Type = T.self, callback: ((_ param: T) -> Void)?) {
if (Store.serverConfig == nil) { if (Store.serverConfig == nil) {
NSLog("Server config not set") logger.error("Server config not set")
return return
} }
@@ -32,7 +34,7 @@ class ApiClient {
case .success(let obj): case .success(let obj):
callback?(obj) callback?(obj)
case .failure(let error): case .failure(let error):
NSLog("api request to \(endpoint) failed") logger.error("api request to \(endpoint) failed")
print(error) 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)?) { 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) { if (Store.serverConfig == nil) {
NSLog("Server config not set") logger.error("Server config not set")
return return
} }
@@ -53,7 +55,7 @@ class ApiClient {
case .success(let obj): case .success(let obj):
callback?(obj) callback?(obj)
case .failure(let error): case .failure(let error):
NSLog("api request to \(endpoint) failed") logger.error("api request to \(endpoint) failed")
print(error) print(error)
} }
} }
@@ -69,7 +71,7 @@ class ApiClient {
public static func postResource<T:Encodable>(endpoint: String, parameters: T, callback: ((_ success: Bool) -> Void)?) { public static func postResource<T:Encodable>(endpoint: String, parameters: T, callback: ((_ success: Bool) -> Void)?) {
if (Store.serverConfig == nil) { if (Store.serverConfig == nil) {
NSLog("Server config not set") logger.error("Server config not set")
callback?(false) callback?(false)
return return
} }
@@ -83,7 +85,7 @@ class ApiClient {
case .success(_): case .success(_):
callback?(true) callback?(true)
case .failure(let error): case .failure(let error):
NSLog("api request to \(endpoint) failed") logger.error("api request to \(endpoint) failed")
print(error) print(error)
callback?(false) callback?(false)
@@ -93,7 +95,7 @@ class ApiClient {
public static func patchResource<T: Encodable>(endpoint: String, parameters: T, callback: ((_ success: Bool) -> Void)?) { public static func patchResource<T: Encodable>(endpoint: String, parameters: T, callback: ((_ success: Bool) -> Void)?) {
if (Store.serverConfig == nil) { if (Store.serverConfig == nil) {
NSLog("Server config not set") logger.error("Server config not set")
callback?(false) callback?(false)
return return
} }
@@ -107,7 +109,7 @@ class ApiClient {
case .success(_): case .success(_):
callback?(true) callback?(true)
case .failure(let error): case .failure(let error):
NSLog("api request to \(endpoint) failed") logger.error("api request to \(endpoint) failed")
print(error) print(error)
callback?(false) 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)?) { public static func getResource<T: Decodable>(endpoint: String, decodable: T.Type = T.self, callback: ((_ param: T?) -> Void)?) {
if (Store.serverConfig == nil) { if (Store.serverConfig == nil) {
NSLog("Server config not set") logger.error("Server config not set")
callback?(nil) callback?(nil)
return return
} }
@@ -138,7 +140,7 @@ class ApiClient {
case .success(let obj): case .success(let obj):
callback?(obj) callback?(obj)
case .failure(let error): case .failure(let error):
NSLog("api request to \(endpoint) failed") logger.error("api request to \(endpoint) failed")
print(error) print(error)
} }
} }
@@ -193,10 +195,10 @@ class ApiClient {
if ( !localMediaProgressList.isEmpty ) { if ( !localMediaProgressList.isEmpty ) {
let payload = LocalMediaProgressSyncPayload(localMediaProgress: localMediaProgressList) 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 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) 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 { if let updates = response.localProgressUpdates {
for update in updates { for update in updates {
@@ -212,13 +214,13 @@ class ApiClient {
callback(resultsPayload) callback(resultsPayload)
} }
} else { } else {
NSLog("No local media progress to sync") logger.log("No local media progress to sync")
callback(LocalMediaProgressSyncResultsPayload(numLocalMediaProgressForServer: 0, numServerProgressUpdates: 0, numLocalProgressUpdates: 0)) callback(LocalMediaProgressSyncResultsPayload(numLocalMediaProgressForServer: 0, numServerProgressUpdates: 0, numLocalProgressUpdates: 0))
} }
} }
public static func updateMediaProgress<T:Encodable>(libraryItemId: String, episodeId: String?, payload: T, callback: @escaping () -> Void) { 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 ?? "")" let endpoint = episodeId?.isEmpty ?? true ? "api/me/progress/\(libraryItemId)" : "api/me/progress/\(libraryItemId)/\(episodeId ?? "")"
patchResource(endpoint: endpoint, parameters: payload) { success in patchResource(endpoint: endpoint, parameters: payload) { success in
callback() callback()
@@ -226,7 +228,7 @@ class ApiClient {
} }
public static func getMediaProgress(libraryItemId: String, episodeId: String?) async -> MediaProgress? { 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 ?? "")" let endpoint = episodeId?.isEmpty ?? true ? "api/me/progress/\(libraryItemId)" : "api/me/progress/\(libraryItemId)/\(episodeId ?? "")"
return await getResource(endpoint: endpoint, decodable: MediaProgress.self) return await getResource(endpoint: endpoint, decodable: MediaProgress.self)
} }
+7 -5
View File
@@ -13,6 +13,8 @@ class Database {
return Database() return Database()
}() }()
private let logger = AppLogger(category: "Database")
private init() {} private init() {}
public func setServerConnectionConfig(config: ServerConnectionConfig) { public func setServerConnectionConfig(config: ServerConnectionConfig) {
@@ -30,7 +32,7 @@ class Database {
existing.token = config.token existing.token = config.token
} }
} catch { } catch {
NSLog("failed to update server config") logger.error("failed to update server config")
debugPrint(error) debugPrint(error)
} }
@@ -51,7 +53,7 @@ class Database {
realm.add(config) realm.add(config)
} }
} catch(let exception) { } catch(let exception) {
NSLog("failed to save server config") logger.error("failed to save server config")
debugPrint(exception) debugPrint(exception)
} }
@@ -70,7 +72,7 @@ class Database {
} }
} }
} catch(let exception) { } catch(let exception) {
NSLog("failed to delete server config") logger.error("failed to delete server config")
debugPrint(exception) debugPrint(exception)
} }
} }
@@ -101,7 +103,7 @@ class Database {
} }
} }
} catch(let exception) { } catch(let exception) {
NSLog("failed to save server config active index") logger.error("failed to save server config active index")
debugPrint(exception) debugPrint(exception)
} }
} }
@@ -121,7 +123,7 @@ class Database {
realm.add(deviceSettings) realm.add(deviceSettings)
} }
} catch { } catch {
NSLog("failed to save device settings") logger.error("failed to save device settings")
} }
} }
+1 -1
View File
@@ -59,7 +59,7 @@ extension CAPPluginCall {
let json = try JSONSerialization.data(withJSONObject: value) let json = try JSONSerialization.data(withJSONObject: value)
return try JSONDecoder().decode(type, from: json) return try JSONDecoder().decode(type, from: json)
} catch { } catch {
NSLog("Failed to get json for \(key)") AppLogger().error("Failed to get json for \(key)")
debugPrint(error) debugPrint(error)
return nil return nil
} }
+72
View File
@@ -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))")
}
}