Improved error handling

This commit is contained in:
ronaldheft
2022-08-25 15:42:37 -04:00
parent 01678f2c91
commit 8c87b31e56
14 changed files with 220 additions and 141 deletions
@@ -88,8 +88,8 @@ extension DownloadItem {
self.downloadItemParts.allSatisfy({ $0.failed == false })
}
func delete() {
try! self.realm?.write {
func delete() throws {
try self.realm?.write {
self.realm?.delete(self.downloadItemParts)
self.realm?.delete(self)
}
@@ -198,8 +198,8 @@ extension LocalLibraryItem {
)
}
func delete() {
try! self.realm?.write {
func delete() throws {
try self.realm?.write {
self.realm?.delete(self.localFiles)
self.realm?.delete(self)
}
@@ -119,8 +119,8 @@ extension LocalMediaProgress {
self.finishedAt = progress.finishedAt
}
func updateIsFinished(_ finished: Bool) {
try! self.realm?.write {
func updateIsFinished(_ finished: Bool) throws {
try self.realm?.write {
if self.isFinished != finished {
self.progress = finished ? 1.0 : 0.0
}
@@ -135,8 +135,8 @@ extension LocalMediaProgress {
}
}
func updateFromPlaybackSession(_ playbackSession: PlaybackSession) {
try! self.realm?.write {
func updateFromPlaybackSession(_ playbackSession: PlaybackSession) throws {
try self.realm?.write {
self.currentTime = playbackSession.currentTime
self.progress = playbackSession.progress
self.lastUpdate = Date().timeIntervalSince1970 * 1000
@@ -145,8 +145,8 @@ extension LocalMediaProgress {
}
}
func updateFromServerMediaProgress(_ serverMediaProgress: MediaProgress) {
try! self.realm?.write {
func updateFromServerMediaProgress(_ serverMediaProgress: MediaProgress) throws {
try self.realm?.write {
self.isFinished = serverMediaProgress.isFinished
self.progress = serverMediaProgress.progress
self.currentTime = serverMediaProgress.currentTime
@@ -157,9 +157,9 @@ extension LocalMediaProgress {
}
}
static func fetchOrCreateLocalMediaProgress(localMediaProgressId: String?, localLibraryItemId: String?, localEpisodeId: String?) -> LocalMediaProgress? {
let realm = try! Realm()
return try! realm.write { () -> LocalMediaProgress? in
static func fetchOrCreateLocalMediaProgress(localMediaProgressId: String?, localLibraryItemId: String?, localEpisodeId: String?) throws -> LocalMediaProgress? {
let realm = try Realm()
return try realm.write { () -> LocalMediaProgress? in
if let localMediaProgressId = localMediaProgressId {
// Check if it existing in the database, if not, we need to create it
if let progress = Database.shared.getLocalMediaProgress(localMediaProgressId: localMediaProgressId) {
@@ -37,7 +37,7 @@ class AudioTrack: EmbeddedObject, Codable {
contentUrl = try? values.decode(String.self, forKey: .contentUrl)
mimeType = try values.decode(String.self, forKey: .mimeType)
metadata = try? values.decode(FileMetadata.self, forKey: .metadata)
localFileId = try! values.decodeIfPresent(String.self, forKey: .localFileId)
localFileId = try? values.decodeIfPresent(String.self, forKey: .localFileId)
serverIndex = try? values.decode(Int.self, forKey: .serverIndex)
}
+1 -1
View File
@@ -306,7 +306,7 @@ class AudioPlayer: NSObject {
if (self.currentTrackIndex != indexOfSeek) {
self.currentTrackIndex = indexOfSeek
playbackSession.update {
try? playbackSession.update {
playbackSession.currentTime = to
}
+13 -8
View File
@@ -158,16 +158,21 @@ class PlayerHandler {
// MARK: - Helper logic
private static func cleanupOldSessions(currentSessionId: String?) {
let realm = try! Realm()
let oldSessions = realm.objects(PlaybackSession.self) .where({
$0.isActiveSession == true && $0.serverConnectionConfigId == Store.serverConfig?.id
})
try! realm.write {
for s in oldSessions {
if s.id != currentSessionId {
s.isActiveSession = false
do {
let realm = try Realm()
let oldSessions = realm.objects(PlaybackSession.self) .where({
$0.isActiveSession == true && $0.serverConnectionConfigId == Store.serverConfig?.id
})
try realm.write {
for s in oldSessions {
if s.id != currentSessionId {
s.isActiveSession = false
}
}
}
} catch {
debugPrint("Failed to cleanup sessions")
debugPrint(error)
}
}
}
+37 -22
View File
@@ -21,34 +21,49 @@ class PlayerProgress {
public func syncFromPlayer(currentTime: Double, includesPlayProgress: Bool, isStopping: Bool) async {
let backgroundToken = await UIApplication.shared.beginBackgroundTask(withName: "ABS:syncFromPlayer")
let session = updateLocalSessionFromPlayer(currentTime: currentTime, includesPlayProgress: includesPlayProgress)
updateLocalMediaProgressFromLocalSession()
if let session = session {
await updateServerSessionFromLocalSession(session, rateLimitSync: !isStopping)
do {
let session = try updateLocalSessionFromPlayer(currentTime: currentTime, includesPlayProgress: includesPlayProgress)
try updateLocalMediaProgressFromLocalSession()
if let session = session {
try await updateServerSessionFromLocalSession(session, rateLimitSync: !isStopping)
}
} catch {
debugPrint("Failed to syncFromPlayer")
debugPrint(error)
}
await UIApplication.shared.endBackgroundTask(backgroundToken)
}
public func syncToServer() async {
let backgroundToken = await UIApplication.shared.beginBackgroundTask(withName: "ABS:syncToServer")
await updateAllServerSessionFromLocalSession()
do {
try await updateAllServerSessionFromLocalSession()
} catch {
debugPrint("Failed to syncToServer")
debugPrint(error)
}
await UIApplication.shared.endBackgroundTask(backgroundToken)
}
public func syncFromServer() async {
let backgroundToken = await UIApplication.shared.beginBackgroundTask(withName: "ABS:syncFromServer")
await updateLocalSessionFromServerMediaProgress()
do {
try await updateLocalSessionFromServerMediaProgress()
} catch {
debugPrint("Failed to syncFromServer")
debugPrint(error)
}
await UIApplication.shared.endBackgroundTask(backgroundToken)
}
// MARK: - SYNC LOGIC
private func updateLocalSessionFromPlayer(currentTime: Double, includesPlayProgress: Bool) -> PlaybackSession? {
private func updateLocalSessionFromPlayer(currentTime: Double, includesPlayProgress: Bool) throws -> PlaybackSession? {
guard let session = PlayerHandler.getPlaybackSession() else { return nil }
guard !currentTime.isNaN else { return nil } // Prevent bad data on player stop
session.update {
try session.update {
session.realm?.refresh()
let nowInSeconds = Date().timeIntervalSince1970
@@ -68,18 +83,18 @@ class PlayerProgress {
return session.freeze()
}
private func updateLocalMediaProgressFromLocalSession() {
private func updateLocalMediaProgressFromLocalSession() throws {
guard let session = PlayerHandler.getPlaybackSession() else { return }
guard session.isLocal else { return }
let localMediaProgress = LocalMediaProgress.fetchOrCreateLocalMediaProgress(localMediaProgressId: session.localMediaProgressId, localLibraryItemId: session.localLibraryItem?.id, localEpisodeId: session.episodeId)
let localMediaProgress = try LocalMediaProgress.fetchOrCreateLocalMediaProgress(localMediaProgressId: session.localMediaProgressId, localLibraryItemId: session.localLibraryItem?.id, localEpisodeId: session.episodeId)
guard let localMediaProgress = localMediaProgress else {
// Local media progress should have been created
// If we're here, it means a library id is invalid
return
}
localMediaProgress.updateFromPlaybackSession(session)
try localMediaProgress.updateFromPlaybackSession(session)
NSLog("Local progress saved to the database")
@@ -87,25 +102,25 @@ class PlayerProgress {
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.localProgress.rawValue), object: nil)
}
private func updateAllServerSessionFromLocalSession() async {
await withTaskGroup(of: Void.self) { [self] group in
for session in try! await Realm().objects(PlaybackSession.self).where({ $0.serverConnectionConfigId == Store.serverConfig?.id }) {
private func updateAllServerSessionFromLocalSession() async throws {
try await withThrowingTaskGroup(of: Void.self) { [self] group in
for session in try await Realm().objects(PlaybackSession.self).where({ $0.serverConnectionConfigId == Store.serverConfig?.id }) {
let session = session.freeze()
group.addTask {
await self.updateServerSessionFromLocalSession(session)
try await self.updateServerSessionFromLocalSession(session)
}
}
await group.waitForAll()
try await group.waitForAll()
}
}
private func updateServerSessionFromLocalSession(_ session: PlaybackSession, rateLimitSync: Bool = false) async {
private func updateServerSessionFromLocalSession(_ session: PlaybackSession, rateLimitSync: Bool = false) async throws {
var safeToSync = true
guard var session = session.thaw() else { return }
// We need to update and check the server time in a transaction for thread-safety
session.update {
try session.update {
session.realm?.refresh()
let nowInMilliseconds = Date().timeIntervalSince1970 * 1000
@@ -140,14 +155,14 @@ class PlayerProgress {
// Remove old sessions after they synced with the server
if success && !session.isActiveSession {
if let session = session.thaw() {
session.delete()
try session.delete()
}
}
}
private func updateLocalSessionFromServerMediaProgress() async {
private func updateLocalSessionFromServerMediaProgress() async throws {
NSLog("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
})?.freeze() else {
NSLog("updateLocalSessionFromServerMediaProgress: Failed to get session")
@@ -177,7 +192,7 @@ class PlayerProgress {
if serverIsNewerThanLocal && currentTimeIsDifferent {
NSLog("updateLocalSessionFromServerMediaProgress: Server has newer time than local serverLastUpdate=\(serverLastUpdate) localLastUpdate=\(localLastUpdate)")
guard let session = session.thaw() else { return }
session.update {
try session.update {
session.currentTime = serverCurrentTime
session.updatedAt = serverLastUpdate
}
+6 -1
View File
@@ -200,7 +200,12 @@ class ApiClient {
if let updates = response.localProgressUpdates {
for update in updates {
Database.shared.saveLocalMediaProgress(update)
do {
try update.save()
} catch {
debugPrint("Failed to update local media progress")
debugPrint(error)
}
}
}
+8 -8
View File
@@ -9,15 +9,15 @@ import Foundation
import RealmSwift
extension Object {
func save() {
let realm = try! Realm()
try! realm.write {
func save() throws {
let realm = try Realm()
try realm.write {
realm.add(self, update: .modified)
}
}
func update(handler: () -> Void) {
try! self.realm?.write {
func update(handler: () -> Void) throws {
try self.realm?.write {
handler()
}
}
@@ -33,12 +33,12 @@ extension EmbeddedObject {
}
protocol Deletable {
func delete()
func delete() throws
}
extension Deletable where Self: Object {
func delete() {
try! self.realm?.write {
func delete() throws {
try self.realm?.write {
self.realm?.delete(self)
}
}
+79 -34
View File
@@ -112,48 +112,83 @@ class Database {
}
public func getLocalLibraryItems(mediaType: MediaType? = nil) -> [LocalLibraryItem] {
let realm = try! Realm()
return Array(realm.objects(LocalLibraryItem.self))
do {
let realm = try Realm()
return Array(realm.objects(LocalLibraryItem.self))
} catch {
debugPrint(error)
return []
}
}
public func getLocalLibraryItem(byServerLibraryItemId: String) -> LocalLibraryItem? {
let realm = try! Realm()
return realm.objects(LocalLibraryItem.self).first(where: { $0.libraryItemId == byServerLibraryItemId })
do {
let realm = try Realm()
return realm.objects(LocalLibraryItem.self).first(where: { $0.libraryItemId == byServerLibraryItemId })
} catch {
debugPrint(error)
return nil
}
}
public func getLocalLibraryItem(localLibraryItemId: String) -> LocalLibraryItem? {
let realm = try! Realm()
return realm.object(ofType: LocalLibraryItem.self, forPrimaryKey: localLibraryItemId)
do {
let realm = try Realm()
return realm.object(ofType: LocalLibraryItem.self, forPrimaryKey: localLibraryItemId)
} catch {
debugPrint(error)
return nil
}
}
public func saveLocalLibraryItem(localLibraryItem: LocalLibraryItem) {
let realm = try! Realm()
try! realm.write { realm.add(localLibraryItem, update: .modified) }
public func saveLocalLibraryItem(localLibraryItem: LocalLibraryItem) throws {
let realm = try Realm()
try realm.write { realm.add(localLibraryItem, update: .modified) }
}
public func getLocalFile(localFileId: String) -> LocalFile? {
let realm = try! Realm()
return realm.object(ofType: LocalFile.self, forPrimaryKey: localFileId)
do {
let realm = try Realm()
return realm.object(ofType: LocalFile.self, forPrimaryKey: localFileId)
} catch {
debugPrint(error)
return nil
}
}
public func getDownloadItem(downloadItemId: String) -> DownloadItem? {
let realm = try! Realm()
return realm.object(ofType: DownloadItem.self, forPrimaryKey: downloadItemId)
do {
let realm = try Realm()
return realm.object(ofType: DownloadItem.self, forPrimaryKey: downloadItemId)
} catch {
debugPrint(error)
return nil
}
}
public func getDownloadItem(libraryItemId: String) -> DownloadItem? {
let realm = try! Realm()
return realm.objects(DownloadItem.self).filter("libraryItemId == %@", libraryItemId).first
do {
let realm = try Realm()
return realm.objects(DownloadItem.self).filter("libraryItemId == %@", libraryItemId).first
} catch {
debugPrint(error)
return nil
}
}
public func getDownloadItem(downloadItemPartId: String) -> DownloadItem? {
let realm = try! Realm()
return realm.objects(DownloadItem.self).filter("SUBQUERY(downloadItemParts, $part, $part.id == %@) .@count > 0", downloadItemPartId).first
do {
let realm = try Realm()
return realm.objects(DownloadItem.self).filter("SUBQUERY(downloadItemParts, $part, $part.id == %@) .@count > 0", downloadItemPartId).first
} catch {
debugPrint(error)
return nil
}
}
public func saveDownloadItem(_ downloadItem: DownloadItem) {
let realm = try! Realm()
return try! realm.write { realm.add(downloadItem, update: .modified) }
public func saveDownloadItem(_ downloadItem: DownloadItem) throws {
let realm = try Realm()
return try realm.write { realm.add(downloadItem, update: .modified) }
}
public func getDeviceSettings() -> DeviceSettings {
@@ -162,31 +197,41 @@ class Database {
}
public func getAllLocalMediaProgress() -> [LocalMediaProgress] {
let realm = try! Realm()
return Array(realm.objects(LocalMediaProgress.self))
}
public func saveLocalMediaProgress(_ mediaProgress: LocalMediaProgress) {
let realm = try! Realm()
try! realm.write { realm.add(mediaProgress, update: .modified) }
do {
let realm = try Realm()
return Array(realm.objects(LocalMediaProgress.self))
} catch {
debugPrint(error)
return []
}
}
// For books this will just be the localLibraryItemId for podcast episodes this will be "{localLibraryItemId}-{episodeId}"
public func getLocalMediaProgress(localMediaProgressId: String) -> LocalMediaProgress? {
let realm = try! Realm()
return realm.object(ofType: LocalMediaProgress.self, forPrimaryKey: localMediaProgressId)
do {
let realm = try Realm()
return realm.object(ofType: LocalMediaProgress.self, forPrimaryKey: localMediaProgressId)
} catch {
debugPrint(error)
return nil
}
}
public func removeLocalMediaProgress(localMediaProgressId: String) {
let realm = try! Realm()
try! realm.write {
public func removeLocalMediaProgress(localMediaProgressId: String) throws {
let realm = try Realm()
try realm.write {
let progress = realm.object(ofType: LocalMediaProgress.self, forPrimaryKey: localMediaProgressId)
realm.delete(progress!)
}
}
public func getPlaybackSession(id: String) -> PlaybackSession? {
let realm = try! Realm()
return realm.object(ofType: PlaybackSession.self, forPrimaryKey: id)
do {
let realm = try Realm()
return realm.object(ofType: PlaybackSession.self, forPrimaryKey: id)
} catch {
debugPrint(error)
return nil
}
}
}