Update:iOS using new sync local sessions endpoint

- remove local session sync function call on starting playback
- Add User model and getCurrentUser api function
This commit is contained in:
advplyr 2023-11-12 13:19:36 -06:00
parent ff5a1bb09f
commit 36be91962c
12 changed files with 128 additions and 97 deletions

View file

@ -0,0 +1,41 @@
//
// User.swift
// Audiobookshelf
//
// Created by advplyr on 11/12/23.
//
import Foundation
import RealmSwift
class User: EmbeddedObject, Codable {
@Persisted var id: String = ""
@Persisted var username: String = ""
@Persisted var mediaProgress = List<MediaProgress>()
private enum CodingKeys : String, CodingKey {
case id, username, mediaProgress
}
override init() {
super.init()
}
required init(from decoder: Decoder) throws {
super.init()
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(String.self, forKey: .id)
username = try values.decode(String.self, forKey: .username)
if let progresses = try? values.decode([MediaProgress].self, forKey: .mediaProgress) {
mediaProgress.append(objectsIn: progresses)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(username, forKey: .username)
try container.encode(Array(mediaProgress), forKey: .mediaProgress)
}
}

View file

@ -19,7 +19,6 @@ class PlayerHandler {
// Cleanup and sync old sessions
cleanupOldSessions(currentSessionId: sessionId)
Task { await PlayerProgress.shared.syncToServer() }
// Set now playing info
NowPlayingInfo.shared.setSessionMetadata(metadata: NowPlayingMetadata(id: session.id, itemId: session.libraryItemId!, title: session.displayTitle ?? "Unknown title", author: session.displayAuthor, series: nil))

View file

@ -36,17 +36,6 @@ class PlayerProgress {
await UIApplication.shared.endBackgroundTask(backgroundToken)
}
public func syncToServer() async {
let backgroundToken = await UIApplication.shared.beginBackgroundTask(withName: "ABS:syncToServer")
do {
try await updateAllServerSessionFromLocalSession()
} catch {
logger.error("Failed to syncToServer")
logger.error(error)
}
await UIApplication.shared.endBackgroundTask(backgroundToken)
}
public func syncFromServer() async {
let backgroundToken = await UIApplication.shared.beginBackgroundTask(withName: "ABS:syncFromServer")
do {

View file

@ -189,34 +189,62 @@ class ApiClient {
return await postResource(endpoint: "api/session/local", parameters: session)
}
public static func syncMediaProgress(callback: @escaping (_ results: LocalMediaProgressSyncResultsPayload) -> Void) {
let localMediaProgressList = Database.shared.getAllLocalMediaProgress().filter {
$0.serverConnectionConfigId == Store.serverConfig?.id
}.map { $0.freeze() }
if ( !localMediaProgressList.isEmpty ) {
let payload = LocalMediaProgressSyncPayload(localMediaProgress: localMediaProgressList)
logger.log("Sending sync local progress request with \(localMediaProgressList.count) progress items")
postResource(endpoint: "api/me/sync-local-progress", parameters: payload, decodable: MediaProgressSyncResponsePayload.self) { response in
let resultsPayload = LocalMediaProgressSyncResultsPayload(numLocalMediaProgressForServer: localMediaProgressList.count, numServerProgressUpdates: response.numServerProgressUpdates, numLocalProgressUpdates: response.localProgressUpdates?.count)
logger.log("Media Progress Sync | \(String(describing: try? resultsPayload.asDictionary()))")
if let updates = response.localProgressUpdates {
for update in updates {
do {
try update.save()
} catch {
debugPrint("Failed to update local media progress")
debugPrint(error)
public static func reportAllLocalPlaybackSessions(_ sessions: [PlaybackSession]) async -> Bool {
return await postResource(endpoint: "api/session/local-all", parameters: LocalPlaybackSessionSyncAllPayload(sessions: sessions))
}
public static func syncLocalSessionsWithServer() async {
do {
// Sync server progress with local media progress
let localMediaProgressList = Database.shared.getAllLocalMediaProgress().filter {
$0.serverConnectionConfigId == Store.serverConfig?.id
}.map { $0.freeze() }
logger.log("syncLocalSessionsWithServer: Found \(localMediaProgressList.count) local media progress for server")
if (localMediaProgressList.isEmpty) {
logger.log("syncLocalSessionsWithServer: No local progress to sync")
} else {
let currentUser = await ApiClient.getCurrentUser()
guard let currentUser = currentUser else {
logger.log("syncLocalSessionsWithServer: No User")
return
}
try currentUser.mediaProgress.forEach { mediaProgress in
let localMediaProgress = localMediaProgressList.first { lmp in
if (lmp.episodeId != nil) {
return lmp.episodeId == mediaProgress.episodeId
} else {
return lmp.libraryItemId == mediaProgress.libraryItemId
}
}
if (localMediaProgress != nil && mediaProgress.lastUpdate > localMediaProgress!.lastUpdate) {
logger.log("syncLocalSessionsWithServer: Updating local media progress \(localMediaProgress!.id) with server media progress")
try localMediaProgress?.updateFromServerMediaProgress(mediaProgress)
} else if (localMediaProgress != nil) {
logger.log("syncLocalSessionsWithServer: Local progress for \(localMediaProgress!.id) is more recent then server progress")
}
}
}
// Send saved playback sessions to server and remove them from db
let playbackSessions = Database.shared.getAllPlaybackSessions().filter {
$0.serverConnectionConfigId == Store.serverConfig?.id
}.map { $0.freeze() }
logger.log("syncLocalSessionsWithServer: Found \(playbackSessions.count) playback sessions for server")
if (!playbackSessions.isEmpty) {
let success = await ApiClient.reportAllLocalPlaybackSessions(playbackSessions)
if (success) {
// Remove sessions from db
try playbackSessions.forEach { session in
if let session = session.thaw() {
try session.delete()
}
}
}
callback(resultsPayload)
}
} else {
logger.log("No local media progress to sync")
callback(LocalMediaProgressSyncResultsPayload(numLocalMediaProgressForServer: 0, numServerProgressUpdates: 0, numLocalProgressUpdates: 0))
} catch {
debugPrint(error)
return
}
}
@ -234,6 +262,11 @@ class ApiClient {
return await getResource(endpoint: endpoint, decodable: MediaProgress.self)
}
public static func getCurrentUser() async -> User? {
logger.log("getCurrentUser")
return await getResource(endpoint: "api/me", decodable: User.self)
}
public static func getLibraryItemWithProgress(libraryItemId:String, episodeId:String?, callback: @escaping (_ param: LibraryItem?) -> Void) {
var endpoint = "api/items/\(libraryItemId)?expanded=1&include=progress"
if episodeId != nil {
@ -287,6 +320,10 @@ struct LocalMediaProgressSyncResultsPayload: Codable {
var numLocalProgressUpdates: Int?
}
struct LocalPlaybackSessionSyncAllPayload: Codable {
var sessions: [PlaybackSession]
}
struct Connectivity {
static private let sharedInstance = NetworkReachabilityManager()!
static var isConnectedToInternet:Bool {

View file

@ -241,6 +241,16 @@ class Database {
}
}
public func getAllPlaybackSessions() -> [PlaybackSession] {
do {
let realm = try Realm()
return Array(realm.objects(PlaybackSession.self))
} catch {
debugPrint(error)
return []
}
}
public func getPlaybackSession(id: String) -> PlaybackSession? {
do {
let realm = try Realm()