Sync playback position (online only)

This commit is contained in:
Rasmus Krämer
2022-04-15 12:21:46 +02:00
parent 29fd29728a
commit 363bfd206c
6 changed files with 74 additions and 23 deletions
+5 -4
View File
@@ -2,12 +2,13 @@
// PlaybackReport.swift
// App
//
// Created by Rasmus Krämer on 12.04.22.
// Created by Rasmus Krämer on 15.04.22.
//
import Foundation
import RealmSwift
class PlaybackReport: Object {
@Persisted var token: String
struct PlaybackReport: Decodable, Encodable {
var currentTime: Double
var duration: Double
var timeListened: Double
}
+1 -1
View File
@@ -57,7 +57,7 @@ class AudioPlayer: NSObject {
playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: .new, context: &playerItemContext)
self.audioPlayer.replaceCurrentItem(with: playerItem)
seek(self.playbackSession.currentTime)
seek(playbackSession.currentTime)
NSLog("Audioplayer ready")
}
+41
View File
@@ -10,6 +10,9 @@ import Foundation
class PlayerHandler {
private static var player: AudioPlayer?
private static var session: PlaybackSession?
private static var timer: Timer?
private static var listningTimePassedSinceLastSync = 0.0
public static func startPlayback(session: PlaybackSession, playWhenReady: Bool) {
if player != nil {
@@ -18,13 +21,23 @@ class PlayerHandler {
}
NowPlayingInfo.setSessionMetadata(metadata: NowPlayingMetadata(id: session.id, itemId: session.libraryItemId!, artworkUrl: session.coverPath, title: session.displayTitle ?? "Unknown title", author: session.displayAuthor, series: nil))
self.session = session
player = AudioPlayer(playbackSession: session, playWhenReady: playWhenReady)
// DispatchQueue.main.sync {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
self.tick()
}
// }
}
public static func stopPlayback() {
player?.destroy()
player = nil
timer?.invalidate()
timer = nil
NowPlayingInfo.reset()
}
@@ -74,6 +87,10 @@ class PlayerHandler {
}
public static func getMetdata() -> [String: Any] {
DispatchQueue.main.async {
syncProgress()
}
return [
"duration": player?.getDuration() ?? 0,
"currentTime": player?.getCurrentTime() ?? 0,
@@ -81,4 +98,28 @@ class PlayerHandler {
"currentRate": player?.rate ?? 0,
]
}
private static func tick() {
if !paused() {
listningTimePassedSinceLastSync += 1
}
if listningTimePassedSinceLastSync > 3 {
syncProgress()
}
}
public static func syncProgress() {
if player == nil || session == nil {
return
}
let report = PlaybackReport(currentTime: player!.getCurrentTime(), duration: player!.getDuration(), timeListened: listningTimePassedSinceLastSync)
session!.currentTime = player!.getCurrentTime()
listningTimePassedSinceLastSync = 0
// TODO: check if online
NSLog("sending playback report")
ApiClient.reportPlaybackProgress(report: report, sessionId: session!.id)
}
}
+20 -13
View File
@@ -9,19 +9,6 @@ import Foundation
import Alamofire
class ApiClient {
/*
public static func getResource<T: Decodable>(endpoint: String, decodable: T.Type = T.self, callback: ((_ param: DataRequest) -> Void)?) {
let headers: HTTPHeaders = [
"Authorization": "Bearer \(Store.serverConfig.token)"
]
AF.request("\(Store.serverConfig.address)/\(endpoint)", headers: headers).responseDecodable(of: decodable) { response in
// callback(response)
debugPrint("Response: \(response)")
}
}
*/
public static func postResource<T: Decodable>(endpoint: String, parameters: [String: String], decodable: T.Type = T.self, callback: ((_ param: T) -> Void)?) {
let headers: HTTPHeaders = [
"Authorization": "Bearer \(Store.serverConfig!.token)"
@@ -37,6 +24,23 @@ class ApiClient {
}
}
}
public static func postResource(endpoint: String, parameters: [String: String], callback: ((_ success: Bool) -> Void)?) {
let headers: HTTPHeaders = [
"Authorization": "Bearer \(Store.serverConfig!.token)"
]
AF.request("\(Store.serverConfig!.address)/\(endpoint)", method: .post, parameters: parameters, encoder: JSONParameterEncoder.default, headers: headers).response { response in
switch response.result {
case .success(let _):
callback?(true)
case .failure(let error):
NSLog("api request to \(endpoint) failed")
print(error)
callback?(false)
}
}
}
public static func startPlaybackSession(libraryItemId: String, episodeId: String?, callback: @escaping (_ param: PlaybackSession) -> Void) {
var endpoint = "api/items/\(libraryItemId)/play"
@@ -56,4 +60,7 @@ class ApiClient {
callback(session)
}
}
public static func reportPlaybackProgress(report: PlaybackReport, sessionId: String) {
try? postResource(endpoint: "api/session/\(sessionId)/sync", parameters: report.asDictionary().mapValues({ value in "\(value)" }), callback: nil)
}
}
+3 -1
View File
@@ -21,7 +21,9 @@ class Store {
Database.setLastActiveConfigIndexToNil()
}
_serverConfig = nil
Database.realmQueue.sync {
_serverConfig = updated
}
}
}
}