Files
ABS-Client/Audiobookshelf swift/ABSService.swift
2026-05-14 19:24:52 +02:00

99 lines
3.6 KiB
Swift

import Foundation
class ABSService: ObservableObject {
static let shared = ABSService()
func fetchLibraries(serverURL: String, token: String) async throws -> [Library] {
let url = URL(string: "\(serverURL)/api/libraries")!
var request = URLRequest(url: url)
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
let response = try JSONDecoder().decode(LibrariesResponse.self, from: data)
return response.libraries
}
func fetchBooks(serverURL: String, token: String, libraryId: String) async throws -> [AudiobookItem] {
let url = URL(string: "\(serverURL)/api/libraries/\(libraryId)/items?limit=100")!
var request = URLRequest(url: url)
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
let response = try JSONDecoder().decode(LibraryItemsResponse.self, from: data)
return response.results.compactMap { item in
AudiobookItem(
id: item.id,
title: item.media.metadata.title ?? "Unbekannt",
author: item.media.metadata.authorName ?? "Unbekannt",
coverURL: item.media.coverPath != nil ? "\(serverURL)/api/items/\(item.id)/cover" : nil,
duration: item.media.duration ?? 0,
mediaFiles: item.media.audioFiles?.map {
AudiobookItem.MediaFile(ino: $0.ino, name: $0.metadata.filename, path: $0.metadata.path)
} ?? []
)
}
}
func fetchProgress(serverURL: String, token: String, itemId: String) async throws -> LibraryProgress? {
let url = URL(string: "\(serverURL)/api/me/progress/\(itemId)")!
var request = URLRequest(url: url)
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let (data, response) = try await URLSession.shared.data(for: request)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { return nil }
return try? JSONDecoder().decode(LibraryProgress.self, from: data)
}
func updateProgress(serverURL: String, token: String, itemId: String, currentTime: Double, duration: Double) async {
guard let url = URL(string: "\(serverURL)/api/me/progress/\(itemId)") else { return }
var request = URLRequest(url: url)
request.httpMethod = "PATCH"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"currentTime": currentTime,
"duration": duration,
"isFinished": currentTime >= duration - 5
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
try? await URLSession.shared.data(for: request)
}
}
// MARK: - API Response Models
struct LibrariesResponse: Codable {
let libraries: [Library]
}
struct LibraryItemsResponse: Codable {
let results: [RawLibraryItem]
}
struct RawLibraryItem: Codable {
let id: String
let media: RawMedia
}
struct RawMedia: Codable {
let metadata: RawMetadata
let coverPath: String?
let duration: Double?
let audioFiles: [RawAudioFile]?
}
struct RawMetadata: Codable {
let title: String?
let authorName: String?
}
struct RawAudioFile: Codable {
let ino: String
let metadata: RawFileMetadata
}
struct RawFileMetadata: Codable {
let filename: String
let path: String
}