advplyr.audiobookshelf-app/ios/App/Shared/util/ApiClient.swift

251 lines
10 KiB
Swift
Raw Normal View History

//
// ApiClient.swift
// App
//
// Created by Rasmus Krämer on 13.04.22.
//
import Foundation
import Alamofire
class ApiClient {
2022-05-03 12:55:13 +02:00
public static func getData(from url: URL, completion: @escaping (UIImage?) -> Void) {
URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in
if let data = data {
completion(UIImage(data:data))
}
}).resume()
}
public static func postResource<T: Decodable>(endpoint: String, parameters: [String: Any], decodable: T.Type = T.self, callback: ((_ param: T) -> Void)?) {
if (Store.serverConfig == nil) {
NSLog("Server config not set")
return
}
let headers: HTTPHeaders = [
2022-04-15 12:21:46 +02:00
"Authorization": "Bearer \(Store.serverConfig!.token)"
]
AF.request("\(Store.serverConfig!.address)/\(endpoint)", method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseDecodable(of: decodable) { response in
2022-04-15 12:21:46 +02:00
switch response.result {
case .success(let obj):
callback?(obj)
case .failure(let error):
NSLog("api request to \(endpoint) failed")
print(error)
}
}
}
2022-08-12 21:58:54 -04:00
2022-08-13 12:41:20 -04:00
public static func postResource<T: Encodable, U: Decodable>(endpoint: String, parameters: T, decodable: U.Type = U.self, callback: ((_ param: U) -> Void)?) {
if (Store.serverConfig == nil) {
NSLog("Server config not set")
return
}
let headers: HTTPHeaders = [
"Authorization": "Bearer \(Store.serverConfig!.token)"
]
AF.request("\(Store.serverConfig!.address)/\(endpoint)", method: .post, parameters: parameters, encoder: JSONParameterEncoder.default, headers: headers).responseDecodable(of: decodable) { response in
switch response.result {
case .success(let obj):
callback?(obj)
case .failure(let error):
NSLog("api request to \(endpoint) failed")
print(error)
}
}
}
public static func postResource<T:Encodable>(endpoint: String, parameters: T, callback: ((_ success: Bool) -> Void)?) {
if (Store.serverConfig == nil) {
NSLog("Server config not set")
callback?(false)
return
}
let headers: HTTPHeaders = [
2022-04-15 10:16:11 +02:00
"Authorization": "Bearer \(Store.serverConfig!.token)"
]
2022-04-15 12:21:46 +02:00
AF.request("\(Store.serverConfig!.address)/\(endpoint)", method: .post, parameters: parameters, encoder: JSONParameterEncoder.default, headers: headers).response { response in
switch response.result {
2022-08-12 21:58:54 -04:00
case .success(_):
2022-04-15 12:21:46 +02:00
callback?(true)
case .failure(let error):
NSLog("api request to \(endpoint) failed")
print(error)
2022-04-15 12:21:46 +02:00
callback?(false)
}
}
}
2022-08-12 21:58:54 -04:00
2022-08-13 12:41:20 -04:00
public static func patchResource<T: Encodable>(endpoint: String, parameters: T, callback: ((_ success: Bool) -> Void)?) {
2022-08-12 21:58:54 -04:00
if (Store.serverConfig == nil) {
NSLog("Server config not set")
callback?(false)
return
}
let headers: HTTPHeaders = [
"Authorization": "Bearer \(Store.serverConfig!.token)"
]
AF.request("\(Store.serverConfig!.address)/\(endpoint)", method: .patch, parameters: parameters, encoder: JSONParameterEncoder.default, headers: headers).response { response in
switch response.result {
case .success(_):
callback?(true)
case .failure(let error):
NSLog("api request to \(endpoint) failed")
print(error)
callback?(false)
}
}
}
public static func getResource<T: Decodable>(endpoint: String, decodable: T.Type = T.self, callback: ((_ param: T?) -> Void)?) {
if (Store.serverConfig == nil) {
NSLog("Server config not set")
callback?(nil)
return
}
let headers: HTTPHeaders = [
"Authorization": "Bearer \(Store.serverConfig!.token)"
]
AF.request("\(Store.serverConfig!.address)/\(endpoint)", method: .get, encoding: JSONEncoding.default, headers: headers).responseDecodable(of: decodable) { response in
switch response.result {
case .success(let obj):
callback?(obj)
case .failure(let error):
NSLog("api request to \(endpoint) failed")
print(error)
}
}
}
public static func startPlaybackSession(libraryItemId: String, episodeId: String?, forceTranscode:Bool, callback: @escaping (_ param: PlaybackSession) -> Void) {
var endpoint = "api/items/\(libraryItemId)/play"
if episodeId != nil {
endpoint += "/\(episodeId!)"
}
var systemInfo = utsname()
uname(&systemInfo)
let modelCode = withUnsafePointer(to: &systemInfo.machine) {
$0.withMemoryRebound(to: CChar.self, capacity: 1) {
ptr in String.init(validatingUTF8: ptr)
}
}
2022-08-13 12:41:20 -04:00
let parameters: [String: Any] = [
"forceDirectPlay": !forceTranscode ? "1" : "",
"forceTranscode": forceTranscode ? "1" : "",
"mediaPlayer": "AVPlayer",
"deviceInfo": [
"manufacturer": "Apple",
"model": modelCode,
"clientVersion": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
]
2022-08-13 12:41:20 -04:00
]
ApiClient.postResource(endpoint: endpoint, parameters: parameters, decodable: PlaybackSession.self) { obj in
2022-04-14 14:39:09 +02:00
var session = obj
2022-04-15 10:16:11 +02:00
session.serverConnectionConfigId = Store.serverConfig!.id
session.serverAddress = Store.serverConfig!.address
2022-04-14 14:39:09 +02:00
callback(session)
}
}
2022-04-15 12:21:46 +02:00
public static func reportPlaybackProgress(report: PlaybackReport, sessionId: String) {
try? postResource(endpoint: "api/session/\(sessionId)/sync", parameters: report.asDictionary().mapValues({ value in "\(value)" }), callback: nil)
}
public static func reportLocalMediaProgress(_ localMediaProgress: LocalMediaProgress, callback: @escaping (_ success: Bool) -> Void) {
2022-08-16 12:32:22 -04:00
let progress = localMediaProgress.freeze()
postResource(endpoint: "api/session/local", parameters: progress, callback: callback)
}
2022-08-12 21:58:54 -04:00
public static func syncMediaProgress(callback: @escaping (_ results: LocalMediaProgressSyncResultsPayload) -> Void) {
let localMediaProgressList = Database.shared.getAllLocalMediaProgress().filter {
$0.serverConnectionConfigId == Store.serverConfig?.id
2022-08-16 12:32:22 -04:00
}.map { $0.freeze() }
2022-08-12 21:58:54 -04:00
if ( !localMediaProgressList.isEmpty ) {
let payload = LocalMediaProgressSyncPayload(localMediaProgress: localMediaProgressList)
NSLog("Sending sync local progress request with \(localMediaProgressList.count) progress items")
2022-08-13 12:41:20 -04:00
postResource(endpoint: "api/me/sync-local-progress", parameters: payload, decodable: MediaProgressSyncResponsePayload.self) { response in
2022-08-12 21:58:54 -04:00
let resultsPayload = LocalMediaProgressSyncResultsPayload(numLocalMediaProgressForServer: localMediaProgressList.count, numServerProgressUpdates: response.numServerProgressUpdates, numLocalProgressUpdates: response.localProgressUpdates?.count)
NSLog("Media Progress Sync | \(String(describing: try? resultsPayload.asDictionary()))")
if let updates = response.localProgressUpdates {
for update in updates {
Database.shared.saveLocalMediaProgress(update)
}
}
callback(resultsPayload)
}
} else {
NSLog("No local media progress to sync")
callback(LocalMediaProgressSyncResultsPayload(numLocalMediaProgressForServer: 0, numServerProgressUpdates: 0, numLocalProgressUpdates: 0))
}
}
public static func updateMediaProgress<T:Encodable>(libraryItemId: String, episodeId: String?, payload: T, callback: @escaping () -> Void) {
NSLog("updateMediaProgress \(libraryItemId) \(episodeId ?? "NIL") \(payload)")
2022-08-13 12:41:20 -04:00
let endpoint = episodeId?.isEmpty ?? true ? "api/me/progress/\(libraryItemId)" : "api/me/progress/\(libraryItemId)/\(episodeId ?? "")"
2022-08-12 21:58:54 -04:00
patchResource(endpoint: endpoint, parameters: payload) { success in
callback()
}
}
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 {
endpoint += "&episodeId=\(episodeId!)"
}
ApiClient.getResource(endpoint: endpoint, decodable: LibraryItem.self) { obj in
callback(obj)
}
}
}
2022-08-12 21:58:54 -04:00
struct LocalMediaProgressSyncPayload: Codable {
var localMediaProgress: [LocalMediaProgress]
}
2022-08-13 12:41:20 -04:00
struct MediaProgressSyncResponsePayload: Decodable {
2022-08-12 21:58:54 -04:00
var numServerProgressUpdates: Int?
var localProgressUpdates: [LocalMediaProgress]?
2022-08-13 12:41:20 -04:00
private enum CodingKeys : String, CodingKey {
case numServerProgressUpdates, localProgressUpdates
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
numServerProgressUpdates = try? values.intOrStringDecoder(key: .numServerProgressUpdates)
localProgressUpdates = try? values.decode([LocalMediaProgress].self, forKey: .localProgressUpdates)
}
2022-08-12 21:58:54 -04:00
}
struct LocalMediaProgressSyncResultsPayload: Codable {
var numLocalMediaProgressForServer: Int?
var numServerProgressUpdates: Int?
var numLocalProgressUpdates: Int?
}
struct Connectivity {
static private let sharedInstance = NetworkReachabilityManager()!
static var isConnectedToInternet:Bool {
return self.sharedInstance.isReachable
}
}