diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 56cd9cc5..525df168 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -34,7 +34,7 @@ 504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; }; 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; - A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; }; + AC98F95F300E46A27FAE47A4 /* Pods_Audiobookshelf.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B775656E49492CFD6763B7B6 /* Pods_Audiobookshelf.framework */; }; E9D5504628AC1A3900C746DD /* LibraryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D5504528AC1A3900C746DD /* LibraryItem.swift */; }; E9D5504828AC1A7A00C746DD /* MediaType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D5504728AC1A7A00C746DD /* MediaType.swift */; }; E9D5504A28AC1AA600C746DD /* Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D5504928AC1AA600C746DD /* Metadata.swift */; }; @@ -94,8 +94,10 @@ 504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; - AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5F7EC3E03050241EA35154B3 /* Pods-Audiobookshelf.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Audiobookshelf.release.xcconfig"; path = "Pods/Target Support Files/Pods-Audiobookshelf/Pods-Audiobookshelf.release.xcconfig"; sourceTree = ""; }; + 8FBD5FE4076DDFA0DA782256 /* Pods-Audiobookshelf.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Audiobookshelf.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Audiobookshelf/Pods-Audiobookshelf.debug.xcconfig"; sourceTree = ""; }; AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = ""; }; + B775656E49492CFD6763B7B6 /* Pods_Audiobookshelf.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Audiobookshelf.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E9D5504528AC1A3900C746DD /* LibraryItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryItem.swift; sourceTree = ""; }; E9D5504728AC1A7A00C746DD /* MediaType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaType.swift; sourceTree = ""; }; E9D5504928AC1AA600C746DD /* Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Metadata.swift; sourceTree = ""; }; @@ -130,7 +132,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */, + AC98F95F300E46A27FAE47A4 /* Pods_Audiobookshelf.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -140,7 +142,7 @@ 27E2DDA53C4D2A4D1A88CE4A /* Frameworks */ = { isa = PBXGroup; children = ( - AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */, + B775656E49492CFD6763B7B6 /* Pods_Audiobookshelf.framework */, ); name = Frameworks; sourceTree = ""; @@ -254,6 +256,8 @@ children = ( FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */, AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */, + 8FBD5FE4076DDFA0DA782256 /* Pods-Audiobookshelf.debug.xcconfig */, + 5F7EC3E03050241EA35154B3 /* Pods-Audiobookshelf.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -382,7 +386,7 @@ ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-App-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Audiobookshelf-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -401,7 +405,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Audiobookshelf/Pods-Audiobookshelf-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -598,7 +602,7 @@ }; 504EC3171FED79650016851F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */; + baseConfigurationReference = 8FBD5FE4076DDFA0DA782256 /* Pods-Audiobookshelf.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = Icons; CLANG_ENABLE_MODULES = YES; @@ -622,7 +626,7 @@ }; 504EC3181FED79650016851F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */; + baseConfigurationReference = 5F7EC3E03050241EA35154B3 /* Pods-Audiobookshelf.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = Icons; CLANG_ENABLE_MODULES = YES; diff --git a/ios/App/App/AppDelegate.swift b/ios/App/App/AppDelegate.swift index 8286734d..887477bf 100644 --- a/ios/App/App/AppDelegate.swift +++ b/ios/App/App/AppDelegate.swift @@ -7,7 +7,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private let logger = AppLogger(category: "AppDelegate") - var window: UIWindow? + lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds) var backgroundCompletionHandler: (() -> Void)? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { @@ -85,10 +85,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { override func touchesBegan(_ touches: Set, with event: UIEvent?) { super.touchesBegan(touches, with: event) - let statusBarRect = UIApplication.shared.statusBarFrame + let statusBarRect = self.window?.windowScene?.statusBarManager?.statusBarFrame guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return } - if statusBarRect.contains(touchPoint) { + if statusBarRect?.contains(touchPoint) ?? false { NotificationCenter.default.post(name: .capacitorStatusBarTapped, object: nil) } } diff --git a/ios/App/App/plugins/AbsAudioPlayer.swift b/ios/App/App/plugins/AbsAudioPlayer.swift index 5eb6bb5e..e5c7ed37 100644 --- a/ios/App/App/plugins/AbsAudioPlayer.swift +++ b/ios/App/App/plugins/AbsAudioPlayer.swift @@ -14,7 +14,6 @@ public class AbsAudioPlayer: CAPPlugin { private let logger = AppLogger(category: "AbsAudioPlayer") private var initialPlayWhenReady = false - private var isUIReady = false override public func load() { NotificationCenter.default.addObserver(self, selector: #selector(sendMetadata), name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil) @@ -35,15 +34,14 @@ public class AbsAudioPlayer: CAPPlugin { } func restorePlaybackSession() async { - // We don't need to restore if we have an active session - guard PlayerHandler.getPlaybackSession() == nil else { return } - do { // Fetch the most recent active session let activeSession = try await Realm().objects(PlaybackSession.self).where({ $0.isActiveSession == true && $0.serverConnectionConfigId == Store.serverConfig?.id }).last?.freeze() + if let activeSession = activeSession { + PlayerHandler.stopPlayback(currentSessionId: activeSession.id) await PlayerProgress.shared.syncFromServer() try self.startPlaybackSession(activeSession, playWhenReady: false, playbackRate: PlayerSettings.main().playbackRate) } diff --git a/ios/App/Podfile b/ios/App/Podfile index dcdae730..5f4f5572 100644 --- a/ios/App/Podfile +++ b/ios/App/Podfile @@ -17,7 +17,7 @@ def capacitor_pods pod 'CapacitorStorage', :path => '../../node_modules/@capacitor/storage' end -target 'App' do +target 'Audiobookshelf' do capacitor_pods # Add your Pods here diff --git a/ios/App/Shared/player/AudioPlayer.swift b/ios/App/Shared/player/AudioPlayer.swift index 91ae336a..b8b33e54 100644 --- a/ios/App/Shared/player/AudioPlayer.swift +++ b/ios/App/Shared/player/AudioPlayer.swift @@ -104,9 +104,6 @@ class AudioPlayer: NSObject { } deinit { - self.stopPausedTimer() - self.removeSleepTimer() - self.removeTimeObserver() self.queueObserver?.invalidate() self.queueItemStatusObserver?.invalidate() } @@ -133,8 +130,13 @@ class AudioPlayer: NSObject { // Remove observers self.audioPlayer.removeObserver(self, forKeyPath: #keyPath(AVPlayer.rate), context: &playerContext) self.audioPlayer.removeObserver(self, forKeyPath: #keyPath(AVPlayer.currentItem), context: &playerContext) + self.removeTimeObserver() NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.closed.rawValue), object: nil) + + // Remove timers + self.stopPausedTimer() + self.removeSleepTimer() } public func isInitialized() -> Bool { @@ -300,9 +302,12 @@ class AudioPlayer: NSObject { // Start playback, with a seek, for as smooth a scrub bar start as possible let currentTrackStartOffset = session.audioTracks[self.currentTrackIndex].startOffset ?? 0.0 let seekTime = currentTime - currentTrackStartOffset - self.audioPlayer.seek(to: CMTime(seconds: seekTime, preferredTimescale: 1000), toleranceBefore: .zero, toleranceAfter: .zero) { [weak self] completed in - guard completed else { return } - self?.resumePlayback() + + DispatchQueue.runOnMainQueue { + self.audioPlayer.seek(to: CMTime(seconds: seekTime, preferredTimescale: 1000), toleranceBefore: .zero, toleranceAfter: .zero) { [weak self] completed in + guard completed else { return } + self?.resumePlayback() + } } } @@ -338,8 +343,10 @@ class AudioPlayer: NSObject { self.stopPausedTimer() self.markAudioSessionAs(active: true) - self.audioPlayer.play() - self.audioPlayer.rate = self.tmpRate + DispatchQueue.runOnMainQueue { + self.audioPlayer.play() + self.audioPlayer.rate = self.tmpRate + } self.status = 1 // Update the progress @@ -350,7 +357,9 @@ class AudioPlayer: NSObject { guard self.isInitialized() else { return } logger.log("PAUSE: Pausing playback") - self.audioPlayer.pause() + DispatchQueue.runOnMainQueue { + self.audioPlayer.pause() + } self.markAudioSessionAs(active: false) Task { @@ -395,9 +404,11 @@ class AudioPlayer: NSObject { self.status = -1 let playerItems = self.allPlayerItems[indexOfSeek..