2022-08-19 22:15:06 -04:00
|
|
|
//
|
|
|
|
// PlayerProgressSync.swift
|
|
|
|
// App
|
|
|
|
//
|
|
|
|
// Created by Ron Heft on 8/19/22.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import UIKit
|
|
|
|
import RealmSwift
|
|
|
|
|
|
|
|
class PlayerProgress {
|
|
|
|
|
2022-08-21 12:06:37 -04:00
|
|
|
public static let shared = PlayerProgress()
|
|
|
|
|
2022-08-22 17:04:48 -04:00
|
|
|
private static let TIME_BETWEEN_SESSION_SYNC_IN_SECONDS = 10.0
|
|
|
|
|
2022-08-19 22:15:06 -04:00
|
|
|
private init() {}
|
|
|
|
|
2022-08-22 17:04:48 -04:00
|
|
|
|
|
|
|
// MARK: - SYNC HOOKS
|
|
|
|
|
|
|
|
public func syncFromPlayer(currentTime: Double, includesPlayProgress: Bool, isStopping: Bool) async {
|
2022-08-21 12:36:29 -04:00
|
|
|
let backgroundToken = await UIApplication.shared.beginBackgroundTask(withName: "ABS:syncFromPlayer")
|
2022-08-22 17:04:48 -04:00
|
|
|
let session = await updateLocalSessionFromPlayer(currentTime: currentTime, includesPlayProgress: includesPlayProgress)
|
2022-08-19 23:00:40 -04:00
|
|
|
updateLocalMediaProgressFromLocalSession()
|
2022-08-22 17:04:48 -04:00
|
|
|
if let session = session {
|
|
|
|
await updateServerSessionFromLocalSession(session, rateLimitSync: !isStopping)
|
|
|
|
}
|
2022-08-21 12:36:29 -04:00
|
|
|
await UIApplication.shared.endBackgroundTask(backgroundToken)
|
2022-08-19 22:15:06 -04:00
|
|
|
}
|
|
|
|
|
2022-08-21 12:06:37 -04:00
|
|
|
public func syncToServer() async {
|
2022-08-19 23:00:40 -04:00
|
|
|
let backgroundToken = await UIApplication.shared.beginBackgroundTask(withName: "ABS:syncToServer")
|
|
|
|
updateAllServerSessionFromLocalSession()
|
|
|
|
await UIApplication.shared.endBackgroundTask(backgroundToken)
|
2022-08-19 22:15:06 -04:00
|
|
|
}
|
|
|
|
|
2022-08-21 12:06:37 -04:00
|
|
|
public func syncFromServer() async {
|
2022-08-19 23:00:40 -04:00
|
|
|
let backgroundToken = await UIApplication.shared.beginBackgroundTask(withName: "ABS:syncFromServer")
|
2022-08-19 22:15:06 -04:00
|
|
|
await updateLocalSessionFromServerMediaProgress()
|
|
|
|
await UIApplication.shared.endBackgroundTask(backgroundToken)
|
|
|
|
}
|
|
|
|
|
2022-08-22 17:04:48 -04:00
|
|
|
|
|
|
|
// MARK: - SYNC LOGIC
|
|
|
|
|
|
|
|
private func updateLocalSessionFromPlayer(currentTime: Double, includesPlayProgress: Bool) async -> PlaybackSession? {
|
|
|
|
guard let session = PlayerHandler.getPlaybackSession() else { return nil }
|
2022-08-21 12:36:29 -04:00
|
|
|
|
2022-08-22 17:04:48 -04:00
|
|
|
let now = Date().timeIntervalSince1970 * 1000
|
|
|
|
let lastUpdate = session.updatedAt ?? now
|
|
|
|
let timeSinceLastUpdate = now - lastUpdate
|
|
|
|
|
|
|
|
session.update {
|
|
|
|
session.currentTime = currentTime
|
|
|
|
session.updatedAt = now
|
|
|
|
|
|
|
|
if includesPlayProgress {
|
|
|
|
session.timeListening += timeSinceLastUpdate
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return session.freeze()
|
2022-08-21 12:36:29 -04:00
|
|
|
}
|
|
|
|
|
2022-08-21 12:06:37 -04:00
|
|
|
private func updateLocalMediaProgressFromLocalSession() {
|
2022-08-19 23:00:40 -04:00
|
|
|
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)
|
|
|
|
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)
|
|
|
|
Database.shared.saveLocalMediaProgress(localMediaProgress)
|
|
|
|
|
|
|
|
NSLog("Local progress saved to the database")
|
2022-08-19 22:15:06 -04:00
|
|
|
|
2022-08-19 23:00:40 -04:00
|
|
|
// Send the local progress back to front-end
|
|
|
|
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.localProgress.rawValue), object: nil)
|
2022-08-19 22:15:06 -04:00
|
|
|
}
|
|
|
|
|
2022-08-21 12:06:37 -04:00
|
|
|
private func updateAllServerSessionFromLocalSession() {
|
2022-08-19 23:00:40 -04:00
|
|
|
let sessions = try! Realm().objects(PlaybackSession.self).where({ $0.serverConnectionConfigId == Store.serverConfig?.id })
|
|
|
|
for session in sessions {
|
|
|
|
let session = session.freeze()
|
|
|
|
Task { await updateServerSessionFromLocalSession(session) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-22 17:04:48 -04:00
|
|
|
private func updateServerSessionFromLocalSession(_ session: PlaybackSession, rateLimitSync: Bool = false) async {
|
|
|
|
// If required, rate limit requests based on session last update
|
|
|
|
if rateLimitSync {
|
|
|
|
let now = Date().timeIntervalSince1970 * 1000
|
|
|
|
let lastUpdate = session.updatedAt ?? now
|
|
|
|
let timeSinceLastSync = now - lastUpdate
|
|
|
|
let timeBetweenSessionSync = PlayerProgress.TIME_BETWEEN_SESSION_SYNC_IN_SECONDS * 1000
|
|
|
|
guard timeSinceLastSync > timeBetweenSessionSync else {
|
|
|
|
// Skipping sync since last occurred within session sync time
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-19 23:00:40 -04:00
|
|
|
NSLog("Sending sessionId(\(session.id)) to server")
|
2022-08-19 22:15:06 -04:00
|
|
|
|
2022-08-19 23:00:40 -04:00
|
|
|
var success = false
|
|
|
|
if session.isLocal {
|
|
|
|
success = await ApiClient.reportLocalPlaybackProgress(session)
|
|
|
|
} else {
|
|
|
|
let playbackReport = PlaybackReport(currentTime: session.currentTime, duration: session.duration, timeListened: session.timeListening)
|
|
|
|
success = await ApiClient.reportPlaybackProgress(report: playbackReport, sessionId: session.id)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove old sessions after they synced with the server
|
|
|
|
if success && !session.isActiveSession {
|
|
|
|
NSLog("Deleting sessionId(\(session.id)) as is no longer active")
|
|
|
|
session.thaw()?.delete()
|
|
|
|
}
|
2022-08-19 22:15:06 -04:00
|
|
|
}
|
|
|
|
|
2022-08-21 12:06:37 -04:00
|
|
|
private func updateLocalSessionFromServerMediaProgress() async {
|
2022-08-19 22:15:06 -04:00
|
|
|
NSLog("checkCurrentSessionProgress: Checking if local media progress was updated on server")
|
2022-08-19 23:00:40 -04:00
|
|
|
guard let session = PlayerHandler.getPlaybackSession()?.freeze() else { return }
|
2022-08-19 22:15:06 -04:00
|
|
|
|
|
|
|
// Fetch the current progress
|
|
|
|
let progress = await ApiClient.getMediaProgress(libraryItemId: session.libraryItemId!, episodeId: session.episodeId)
|
|
|
|
guard let progress = progress else { return }
|
|
|
|
|
|
|
|
// Determine which session is newer
|
|
|
|
let serverLastUpdate = progress.lastUpdate
|
|
|
|
guard let localLastUpdate = session.updatedAt else { return }
|
|
|
|
let serverCurrentTime = progress.currentTime
|
|
|
|
let localCurrentTime = session.currentTime
|
|
|
|
|
|
|
|
let serverIsNewerThanLocal = serverLastUpdate > localLastUpdate
|
|
|
|
let currentTimeIsDifferent = serverCurrentTime != localCurrentTime
|
|
|
|
|
|
|
|
// Update the session, if needed
|
|
|
|
if serverIsNewerThanLocal && currentTimeIsDifferent {
|
2022-08-19 23:00:40 -04:00
|
|
|
guard let session = session.thaw() else { return }
|
2022-08-19 22:15:06 -04:00
|
|
|
session.update {
|
|
|
|
session.currentTime = serverCurrentTime
|
|
|
|
session.updatedAt = serverLastUpdate
|
|
|
|
}
|
|
|
|
PlayerHandler.seek(amount: session.currentTime)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|