Handle iOS crashes when no session

This commit is contained in:
ronaldheft
2022-08-30 17:05:06 -04:00
parent 5775a51731
commit b583a8e6e2
2 changed files with 78 additions and 40 deletions
+69 -35
View File
@@ -63,15 +63,22 @@ class AudioPlayer: NSObject {
initAudioSession() initAudioSession()
setupRemoteTransportControls() setupRemoteTransportControls()
let playbackSession = Database.shared.getPlaybackSession(id: self.sessionId)! let playbackSession = self.getPlaybackSession()
guard let playbackSession = playbackSession else {
NSLog("Failed to fetch playback session. Player will not initialize")
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.failed.rawValue), object: nil)
return
}
// Listen to player events // Listen to player events
self.audioPlayer.addObserver(self, forKeyPath: #keyPath(AVPlayer.rate), options: .new, context: &playerContext) self.audioPlayer.addObserver(self, forKeyPath: #keyPath(AVPlayer.rate), options: .new, context: &playerContext)
self.audioPlayer.addObserver(self, forKeyPath: #keyPath(AVPlayer.currentItem), options: .new, context: &playerContext) self.audioPlayer.addObserver(self, forKeyPath: #keyPath(AVPlayer.currentItem), options: .new, context: &playerContext)
for track in playbackSession.audioTracks { for track in playbackSession.audioTracks {
let playerItem = AVPlayerItem(asset: createAsset(itemId: playbackSession.libraryItemId!, track: track)) if let playerAsset = createAsset(itemId: playbackSession.libraryItemId!, track: track) {
self.allPlayerItems.append(playerItem) let playerItem = AVPlayerItem(asset: playerAsset)
self.allPlayerItems.append(playerItem)
}
} }
self.currentTrackIndex = getItemIndexForTime(time: playbackSession.currentTime) self.currentTrackIndex = getItemIndexForTime(time: playbackSession.currentTime)
@@ -118,12 +125,16 @@ class AudioPlayer: NSObject {
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.closed.rawValue), object: nil) NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.closed.rawValue), object: nil)
} }
func isInitialized() -> Bool { public func isInitialized() -> Bool {
return self.status != -1 return self.status != -1
} }
func getItemIndexForTime(time:Double) -> Int { public func getPlaybackSession() -> PlaybackSession? {
let playbackSession = Database.shared.getPlaybackSession(id: self.sessionId)! return Database.shared.getPlaybackSession(id: self.sessionId)
}
private func getItemIndexForTime(time:Double) -> Int {
guard let playbackSession = self.getPlaybackSession() else { return 0 }
for index in 0..<self.allPlayerItems.count { for index in 0..<self.allPlayerItems.count {
let startOffset = playbackSession.audioTracks[index].startOffset ?? 0.0 let startOffset = playbackSession.audioTracks[index].startOffset ?? 0.0
let duration = playbackSession.audioTracks[index].duration let duration = playbackSession.audioTracks[index].duration
@@ -147,7 +158,7 @@ class AudioPlayer: NSObject {
self.timeObserverToken = self.audioPlayer.addPeriodicTimeObserver(forInterval: time, queue: self.queue) { [weak self] time in self.timeObserverToken = self.audioPlayer.addPeriodicTimeObserver(forInterval: time, queue: self.queue) { [weak self] time in
guard let self = self else { return } guard let self = self else { return }
let currentTime = self.getCurrentTime() guard let currentTime = self.getCurrentTime() else { return }
let isPlaying = self.isPlaying() let isPlaying = self.isPlaying()
Task { Task {
@@ -170,7 +181,7 @@ class AudioPlayer: NSObject {
} }
} }
func setupQueueObserver() { private func setupQueueObserver() {
self.queueObserver = self.audioPlayer.observe(\.currentItem, options: [.new]) {_,_ in self.queueObserver = self.audioPlayer.observe(\.currentItem, options: [.new]) {_,_ in
let prevTrackIndex = self.currentTrackIndex let prevTrackIndex = self.currentTrackIndex
self.audioPlayer.currentItem.map { item in self.audioPlayer.currentItem.map { item in
@@ -182,10 +193,13 @@ class AudioPlayer: NSObject {
} }
} }
func setupQueueItemStatusObserver() { private func setupQueueItemStatusObserver() {
self.queueItemStatusObserver?.invalidate() self.queueItemStatusObserver?.invalidate()
self.queueItemStatusObserver = self.audioPlayer.currentItem?.observe(\.status, options: [.new, .old], changeHandler: { (playerItem, change) in self.queueItemStatusObserver = self.audioPlayer.currentItem?.observe(\.status, options: [.new, .old], changeHandler: { (playerItem, change) in
let playbackSession = Database.shared.getPlaybackSession(id: self.sessionId)! guard let playbackSession = self.getPlaybackSession() else {
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.failed.rawValue), object: nil)
return
}
if (playerItem.status == .readyToPlay) { if (playerItem.status == .readyToPlay) {
NSLog("queueStatusObserver: Current Item Ready to play. PlayWhenReady: \(self.playWhenReady)") NSLog("queueStatusObserver: Current Item Ready to play. PlayWhenReady: \(self.playWhenReady)")
self.updateNowPlaying() self.updateNowPlaying()
@@ -233,7 +247,7 @@ class AudioPlayer: NSObject {
// Capture remaining sleep time before changing the track position // Capture remaining sleep time before changing the track position
let sleepSecondsRemaining = PlayerHandler.remainingSleepTime let sleepSecondsRemaining = PlayerHandler.remainingSleepTime
if allowSeekBack, let session = Database.shared.getPlaybackSession(id: self.sessionId) { if allowSeekBack, let session = self.getPlaybackSession() {
let lastPlayed = (session.updatedAt ?? 0)/1000 let lastPlayed = (session.updatedAt ?? 0)/1000
let difference = Date.timeIntervalSinceReferenceDate - lastPlayed let difference = Date.timeIntervalSinceReferenceDate - lastPlayed
var time: Int? var time: Int?
@@ -255,14 +269,20 @@ class AudioPlayer: NSObject {
} }
if time != nil { if time != nil {
seek(getCurrentTime() - Double(time!), from: "play") guard let currentTime = self.getCurrentTime() else {
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.failed.rawValue), object: nil)
return
}
seek(currentTime - Double(time!), from: "play")
} }
} }
self.stopPausedTimer() self.stopPausedTimer()
Task { Task {
await PlayerProgress.shared.syncFromPlayer(currentTime: self.getCurrentTime(), includesPlayProgress: self.isPlaying(), isStopping: false) if let currentTime = self.getCurrentTime() {
await PlayerProgress.shared.syncFromPlayer(currentTime: currentTime, includesPlayProgress: self.isPlaying(), isStopping: false)
}
} }
self.audioPlayer.play() self.audioPlayer.play()
@@ -271,7 +291,9 @@ class AudioPlayer: NSObject {
self.audioPlayer.rate = self.tmpRate self.audioPlayer.rate = self.tmpRate
// If we have an active sleep timer, reschedule based on rate // If we have an active sleep timer, reschedule based on rate
self.rescheduleSleepTimerAtTime(time: self.getCurrentTime(), secondsRemaining: sleepSecondsRemaining) if let currentTime = self.getCurrentTime() {
self.rescheduleSleepTimerAtTime(time: currentTime, secondsRemaining: sleepSecondsRemaining)
}
updateNowPlaying() updateNowPlaying()
} }
@@ -282,7 +304,9 @@ class AudioPlayer: NSObject {
self.audioPlayer.pause() self.audioPlayer.pause()
Task { Task {
await PlayerProgress.shared.syncFromPlayer(currentTime: self.getCurrentTime(), includesPlayProgress: self.isPlaying(), isStopping: true) if let currentTime = self.getCurrentTime() {
await PlayerProgress.shared.syncFromPlayer(currentTime: currentTime, includesPlayProgress: self.isPlaying(), isStopping: true)
}
} }
self.status = 0 self.status = 0
@@ -300,7 +324,7 @@ class AudioPlayer: NSObject {
NSLog("Seek to \(to) from \(from)") NSLog("Seek to \(to) from \(from)")
let playbackSession = Database.shared.getPlaybackSession(id: self.sessionId)! 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
@@ -372,7 +396,9 @@ class AudioPlayer: NSObject {
self.tmpRate = rate self.tmpRate = rate
// If we have an active sleep timer, reschedule based on rate // If we have an active sleep timer, reschedule based on rate
self.rescheduleSleepTimerAtTime(time: self.getCurrentTime(), secondsRemaining: sleepSecondsRemaining) if let currentTime = self.getCurrentTime() {
self.rescheduleSleepTimerAtTime(time: currentTime, secondsRemaining: sleepSecondsRemaining)
}
// Setup the time observer again at the new rate // Setup the time observer again at the new rate
self.setupTimeObserver() self.setupTimeObserver()
@@ -390,7 +416,10 @@ class AudioPlayer: NSObject {
// Reset any previous sleep timer // Reset any previous sleep timer
self.removeSleepTimer() self.removeSleepTimer()
let currentTime = getCurrentTime() guard let currentTime = getCurrentTime() else {
NSLog("Failed to get currenTime")
return
}
// Mark the time to stop playing // Mark the time to stop playing
if scaleBasedOnSpeed { if scaleBasedOnSpeed {
@@ -454,8 +483,7 @@ class AudioPlayer: NSObject {
} }
public func increaseSleepTime(extraTimeInSeconds: Double) { public func increaseSleepTime(extraTimeInSeconds: Double) {
if let sleepTime = PlayerHandler.remainingSleepTime { if let sleepTime = PlayerHandler.remainingSleepTime, let currentTime = getCurrentTime() {
let currentTime = getCurrentTime()
let newSleepTimerPosition = currentTime + Double(sleepTime) + extraTimeInSeconds let newSleepTimerPosition = currentTime + Double(sleepTime) + extraTimeInSeconds
if newSleepTimerPosition > currentTime { if newSleepTimerPosition > currentTime {
self.setSleepTime(stopAt: newSleepTimerPosition, scaleBasedOnSpeed: true) self.setSleepTime(stopAt: newSleepTimerPosition, scaleBasedOnSpeed: true)
@@ -464,8 +492,7 @@ class AudioPlayer: NSObject {
} }
public func decreaseSleepTime(removeTimeInSeconds: Double) { public func decreaseSleepTime(removeTimeInSeconds: Double) {
if let sleepTime = PlayerHandler.remainingSleepTime { if let sleepTime = PlayerHandler.remainingSleepTime, let currentTime = getCurrentTime() {
let currentTime = getCurrentTime()
let newSleepTimerPosition = currentTime + Double(sleepTime) - removeTimeInSeconds let newSleepTimerPosition = currentTime + Double(sleepTime) - removeTimeInSeconds
guard newSleepTimerPosition > currentTime else { return } guard newSleepTimerPosition > currentTime else { return }
if newSleepTimerPosition > currentTime { if newSleepTimerPosition > currentTime {
@@ -486,16 +513,16 @@ class AudioPlayer: NSObject {
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepEnded.rawValue), object: self) NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.sleepEnded.rawValue), object: self)
} }
public func getCurrentTime() -> Double { public func getCurrentTime() -> Double? {
let playbackSession = Database.shared.getPlaybackSession(id: self.sessionId)! guard let playbackSession = self.getPlaybackSession() else { return nil }
let currentTrackTime = self.audioPlayer.currentTime().seconds let currentTrackTime = self.audioPlayer.currentTime().seconds
let audioTrack = playbackSession.audioTracks[currentTrackIndex] let audioTrack = playbackSession.audioTracks[currentTrackIndex]
let startOffset = audioTrack.startOffset ?? 0.0 let startOffset = audioTrack.startOffset ?? 0.0
return startOffset + currentTrackTime return startOffset + currentTrackTime
} }
public func getPlayMethod() -> Int { public func getPlayMethod() -> Int? {
let playbackSession = Database.shared.getPlaybackSession(id: self.sessionId)! guard let playbackSession = self.getPlaybackSession() else { return nil }
return playbackSession.playMethod return playbackSession.playMethod
} }
@@ -503,8 +530,8 @@ class AudioPlayer: NSObject {
return self.sessionId return self.sessionId
} }
public func getDuration() -> Double { public func getDuration() -> Double? {
let playbackSession = Database.shared.getPlaybackSession(id: self.sessionId)! guard let playbackSession = self.getPlaybackSession() else { return nil }
return playbackSession.duration return playbackSession.duration
} }
@@ -513,8 +540,9 @@ class AudioPlayer: NSObject {
} }
// MARK: - Private // MARK: - Private
private func createAsset(itemId:String, track:AudioTrack) -> AVAsset { private func createAsset(itemId:String, track:AudioTrack) -> AVAsset? {
let playbackSession = Database.shared.getPlaybackSession(id: self.sessionId)! guard let playbackSession = self.getPlaybackSession() else { return nil }
if (playbackSession.playMethod == PlayMethod.directplay.rawValue) { if (playbackSession.playMethod == PlayMethod.directplay.rawValue) {
// The only reason this is separate is because the filename needs to be encoded // The only reason this is separate is because the filename needs to be encoded
let filename = track.metadata?.filename ?? "" let filename = track.metadata?.filename ?? ""
@@ -576,8 +604,10 @@ class AudioPlayer: NSObject {
guard let command = event.command as? MPSkipIntervalCommand else { guard let command = event.command as? MPSkipIntervalCommand else {
return .noSuchContent return .noSuchContent
} }
guard let currentTime = self.getCurrentTime() else {
seek(getCurrentTime() + command.preferredIntervals[0].doubleValue, from: "remote") return .commandFailed
}
seek(currentTime + command.preferredIntervals[0].doubleValue, from: "remote")
return .success return .success
} }
commandCenter.skipBackwardCommand.isEnabled = true commandCenter.skipBackwardCommand.isEnabled = true
@@ -586,8 +616,10 @@ class AudioPlayer: NSObject {
guard let command = event.command as? MPSkipIntervalCommand else { guard let command = event.command as? MPSkipIntervalCommand else {
return .noSuchContent return .noSuchContent
} }
guard let currentTime = self.getCurrentTime() else {
seek(getCurrentTime() - command.preferredIntervals[0].doubleValue, from: "remote") return .commandFailed
}
seek(currentTime - command.preferredIntervals[0].doubleValue, from: "remote")
return .success return .success
} }
@@ -614,7 +646,9 @@ class AudioPlayer: NSObject {
} }
private func updateNowPlaying() { private func updateNowPlaying() {
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil) NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil)
NowPlayingInfo.shared.update(duration: getDuration(), currentTime: getCurrentTime(), rate: rate) if let duration = self.getDuration(), let currentTime = self.getCurrentTime() {
NowPlayingInfo.shared.update(duration: duration, currentTime: currentTime, rate: rate)
}
} }
// MARK: - Observer // MARK: - Observer
+9 -5
View File
@@ -62,13 +62,14 @@ class PlayerHandler {
public static var remainingSleepTime: Int? { public static var remainingSleepTime: Int? {
get { get {
guard let player = player else { return nil } guard let player = player else { return nil }
guard let currentTime = player.getCurrentTime() else { return nil }
// Return the player time until sleep // Return the player time until sleep
var timeUntilSleep: Double? = nil var timeUntilSleep: Double? = nil
if let sleepTimerChapterStopTime = sleepTimerChapterStopTime { if let sleepTimerChapterStopTime = sleepTimerChapterStopTime {
timeUntilSleep = Double(sleepTimerChapterStopTime) - player.getCurrentTime() timeUntilSleep = Double(sleepTimerChapterStopTime) - currentTime
} else if let stopAt = player.getSleepStopAt() { } else if let stopAt = player.getSleepStopAt() {
timeUntilSleep = stopAt - player.getCurrentTime() timeUntilSleep = stopAt - currentTime
} }
// Scale the time until sleep based on the playback rate // Scale the time until sleep based on the playback rate
@@ -96,7 +97,8 @@ class PlayerHandler {
public static func setSleepTime(secondsUntilSleep: Double) { public static func setSleepTime(secondsUntilSleep: Double) {
guard let player = player else { return } guard let player = player else { return }
let stopAt = secondsUntilSleep + player.getCurrentTime() guard let currentTime = player.getCurrentTime() else { return }
let stopAt = secondsUntilSleep + currentTime
player.setSleepTime(stopAt: stopAt, scaleBasedOnSpeed: true) player.setSleepTime(stopAt: stopAt, scaleBasedOnSpeed: true)
} }
@@ -134,15 +136,17 @@ class PlayerHandler {
public static func seekForward(amount: Double) { public static func seekForward(amount: Double) {
guard let player = player else { return } guard let player = player else { return }
guard let currentTime = player.getCurrentTime() else { return }
let destinationTime = player.getCurrentTime() + amount let destinationTime = currentTime + amount
player.seek(destinationTime, from: "handler") player.seek(destinationTime, from: "handler")
} }
public static func seekBackward(amount: Double) { public static func seekBackward(amount: Double) {
guard let player = player else { return } guard let player = player else { return }
guard let currentTime = player.getCurrentTime() else { return }
let destinationTime = player.getCurrentTime() - amount let destinationTime = currentTime - amount
player.seek(destinationTime, from: "handler") player.seek(destinationTime, from: "handler")
} }