mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-07-22 11:44:33 +02:00
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:
parent
ff5a1bb09f
commit
36be91962c
12 changed files with 128 additions and 97 deletions
|
@ -28,6 +28,7 @@
|
|||
4D66B956282EE951008272D4 /* AbsFileSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D66B955282EE951008272D4 /* AbsFileSystem.m */; };
|
||||
4D66B958282EEA14008272D4 /* AbsFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D66B957282EEA14008272D4 /* AbsFileSystem.swift */; };
|
||||
4D91EEC62A40F28D004807ED /* EBookFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D91EEC52A40F28D004807ED /* EBookFile.swift */; };
|
||||
4DABC04F2B0139CA000F6264 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DABC04E2B0139CA000F6264 /* User.swift */; };
|
||||
4DF74912287105C600AC7814 /* DeviceSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DF74911287105C600AC7814 /* DeviceSettings.swift */; };
|
||||
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };
|
||||
504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };
|
||||
|
@ -99,6 +100,7 @@
|
|||
4D66B957282EEA14008272D4 /* AbsFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbsFileSystem.swift; sourceTree = "<group>"; };
|
||||
4D8D412C26E187E400BA5F0D /* App-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "App-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
4D91EEC52A40F28D004807ED /* EBookFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EBookFile.swift; sourceTree = "<group>"; };
|
||||
4DABC04E2B0139CA000F6264 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.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>"; };
|
||||
504EC3041FED79650016851F /* Audiobookshelf.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Audiobookshelf.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -302,6 +304,7 @@
|
|||
E9D5505B28AC1C6200C746DD /* LibraryFile.swift */,
|
||||
E9D5505D28AC1C8500C746DD /* MediaProgress.swift */,
|
||||
4D91EEC52A40F28D004807ED /* EBookFile.swift */,
|
||||
4DABC04E2B0139CA000F6264 /* User.swift */,
|
||||
);
|
||||
path = server;
|
||||
sourceTree = "<group>";
|
||||
|
@ -542,6 +545,7 @@
|
|||
E9D5505828AC1C1A00C746DD /* Library.swift in Sources */,
|
||||
3AD4FCEB280443DD006DB301 /* Database.swift in Sources */,
|
||||
3AD4FCE528043E50006DB301 /* AbsDatabase.swift in Sources */,
|
||||
4DABC04F2B0139CA000F6264 /* User.swift in Sources */,
|
||||
4D66B952282EE822008272D4 /* AbsDownloader.m in Sources */,
|
||||
E9D5506828AC1DC300C746DD /* LocalPodcastEpisode.swift in Sources */,
|
||||
E9D5505228AC1B5D00C746DD /* Chapter.swift in Sources */,
|
||||
|
|
|
@ -21,8 +21,8 @@ CAP_PLUGIN(AbsDatabase, "AbsDatabase",
|
|||
CAP_PLUGIN_METHOD(getLocalLibraryItemsInFolder, CAPPluginReturnPromise);
|
||||
CAP_PLUGIN_METHOD(getAllLocalMediaProgress, CAPPluginReturnPromise);
|
||||
CAP_PLUGIN_METHOD(removeLocalMediaProgress, CAPPluginReturnPromise);
|
||||
CAP_PLUGIN_METHOD(syncLocalMediaProgressWithServer, CAPPluginReturnPromise);
|
||||
CAP_PLUGIN_METHOD(syncServerMediaProgressWithLocalMediaProgress, CAPPluginReturnPromise);
|
||||
CAP_PLUGIN_METHOD(syncLocalSessionsWithServer, CAPPluginReturnPromise);
|
||||
CAP_PLUGIN_METHOD(updateLocalMediaProgressFinished, CAPPluginReturnPromise);
|
||||
CAP_PLUGIN_METHOD(updateDeviceSettings, CAPPluginReturnPromise);
|
||||
CAP_PLUGIN_METHOD(updateLocalEbookProgress, CAPPluginReturnPromise);
|
||||
|
|
|
@ -146,17 +146,16 @@ public class AbsDatabase: CAPPlugin {
|
|||
call.resolve()
|
||||
}
|
||||
|
||||
@objc func syncLocalMediaProgressWithServer(_ call: CAPPluginCall) {
|
||||
@objc func syncLocalSessionsWithServer(_ call: CAPPluginCall) {
|
||||
logger.log("syncLocalSessionsWithServer: Starting")
|
||||
guard Store.serverConfig != nil else {
|
||||
call.reject("syncLocalMediaProgressWithServer not connected to server")
|
||||
return
|
||||
}
|
||||
ApiClient.syncMediaProgress { results in
|
||||
do {
|
||||
call.resolve(try results.asDictionary())
|
||||
} catch {
|
||||
call.reject("Failed to report synced media progress")
|
||||
call.reject("syncLocalSessionsWithServer not connected to server")
|
||||
return call.resolve()
|
||||
}
|
||||
|
||||
Task {
|
||||
await ApiClient.syncLocalSessionsWithServer()
|
||||
call.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -90,4 +90,4 @@ SPEC CHECKSUMS:
|
|||
|
||||
PODFILE CHECKSUM: 7a8fc177ef0646dd60a1ee8aa387964975fcc1e3
|
||||
|
||||
COCOAPODS: 1.12.0
|
||||
COCOAPODS: 1.12.1
|
||||
|
|
41
ios/App/Shared/models/server/User.swift
Normal file
41
ios/App/Shared/models/server/User.swift
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -189,34 +189,62 @@ class ApiClient {
|
|||
return await postResource(endpoint: "api/session/local", parameters: session)
|
||||
}
|
||||
|
||||
public static func syncMediaProgress(callback: @escaping (_ results: LocalMediaProgressSyncResultsPayload) -> Void) {
|
||||
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 ) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callback(resultsPayload)
|
||||
}
|
||||
if (localMediaProgressList.isEmpty) {
|
||||
logger.log("syncLocalSessionsWithServer: No local progress to sync")
|
||||
} else {
|
||||
logger.log("No local media progress to sync")
|
||||
callback(LocalMediaProgressSyncResultsPayload(numLocalMediaProgressForServer: 0, numServerProgressUpdates: 0, numLocalProgressUpdates: 0))
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -44,12 +44,7 @@ export default {
|
|||
if (timeSinceDisconnect > 5000) {
|
||||
console.log('Time since disconnect was', timeSinceDisconnect, 'sync with server')
|
||||
setTimeout(() => {
|
||||
if (this.$platform === 'ios') {
|
||||
// TODO: Update ios to not use this
|
||||
this.syncLocalMediaProgress()
|
||||
} else {
|
||||
this.syncLocalSessions()
|
||||
}
|
||||
}, 4000)
|
||||
}
|
||||
}
|
||||
|
@ -215,36 +210,6 @@ export default {
|
|||
await this.$store.dispatch('globals/loadLocalMediaProgress')
|
||||
}
|
||||
},
|
||||
async syncLocalMediaProgress() {
|
||||
if (!this.user) {
|
||||
console.log('[default] No need to sync local media progress - not connected to server')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('[default] Calling syncLocalMediaProgress')
|
||||
const response = await this.$db.syncLocalMediaProgressWithServer()
|
||||
if (!response) {
|
||||
if (this.$platform != 'web') this.$toast.error('Failed to sync local media with server')
|
||||
return
|
||||
}
|
||||
const { numLocalMediaProgressForServer, numServerProgressUpdates, numLocalProgressUpdates, serverProgressUpdates } = response
|
||||
if (numLocalMediaProgressForServer > 0) {
|
||||
if (serverProgressUpdates && serverProgressUpdates.length) {
|
||||
serverProgressUpdates.forEach((progress) => {
|
||||
console.log(`[default] Server progress was updated ${progress.id}`)
|
||||
this.$store.commit('user/updateUserMediaProgress', progress)
|
||||
})
|
||||
}
|
||||
|
||||
if (numServerProgressUpdates > 0 || numLocalProgressUpdates > 0) {
|
||||
console.log(`[default] ${numServerProgressUpdates} Server progress updates | ${numLocalProgressUpdates} Local progress updates`)
|
||||
} else {
|
||||
console.log('[default] syncLocalMediaProgress No updates were necessary')
|
||||
}
|
||||
} else {
|
||||
console.log('[default] syncLocalMediaProgress No local media progress to sync')
|
||||
}
|
||||
},
|
||||
userUpdated(user) {
|
||||
// console.log('[default] userUpdated:', JSON.stringify(user))
|
||||
if (this.user && this.user.id == user.id) {
|
||||
|
@ -362,12 +327,7 @@ export default {
|
|||
}
|
||||
|
||||
console.log(`[default] finished connection attempt or already connected ${!!this.user}`)
|
||||
if (this.$platform === 'ios') {
|
||||
// TODO: Update ios to not use this
|
||||
await this.syncLocalMediaProgress()
|
||||
} else {
|
||||
await this.syncLocalSessions()
|
||||
}
|
||||
|
||||
this.loadSavedSettings()
|
||||
this.hasMounted = true
|
||||
|
|
|
@ -194,10 +194,6 @@ class AbsDatabaseWeb extends WebPlugin {
|
|||
return null
|
||||
}
|
||||
|
||||
async syncLocalMediaProgressWithServer() {
|
||||
return null
|
||||
}
|
||||
|
||||
async syncLocalSessionsWithServer() {
|
||||
return null
|
||||
}
|
||||
|
|
|
@ -70,10 +70,6 @@ class DbService {
|
|||
return AbsDatabase.removeLocalMediaProgress({ localMediaProgressId })
|
||||
}
|
||||
|
||||
syncLocalMediaProgressWithServer() {
|
||||
return AbsDatabase.syncLocalMediaProgressWithServer()
|
||||
}
|
||||
|
||||
syncLocalSessionsWithServer() {
|
||||
return AbsDatabase.syncLocalSessionsWithServer()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue