Files
ABS-Client/ABS Client/Audiobookshelf swift/Models/Models.swift
Scarriffle fa47cae664 Add chapters, history, bookmarks, live download progress, and i18n
- Chapter navigation with auto-scroll to current chapter and end-of-chapter sleep timer
- Opt-in listening history (local-only) with XML export and per-item quick menu
- Bookmarks with server sync via Audiobookshelf API
- Live MB counter during downloads via URLSessionDownloadTask delegate
- In-progress downloads shown in "Heruntergeladen" with dimmed cover + ring overlay
- Cover image cache (50 MB memory / 500 MB disk URLCache)
- German/English localization (de.lproj, en.lproj)
- Loading spinner now triggers immediately on view switch instead of waiting for the network

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 18:43:16 +02:00

90 lines
2.2 KiB
Swift

import Foundation
struct Chapter: Codable, Identifiable, Hashable {
let id: Int
let start: Double
let end: Double
let title: String
}
struct Library: Codable, Identifiable, Hashable {
let id: String
let name: String
let mediaType: String?
}
struct LibraryItem: Codable, Identifiable, Hashable {
let id: String
let title: String
let author: String
let durationSeconds: Double
let audioFiles: [AudioFile]
var mediaType: String = "book"
var episodeId: String? = nil
var description: String? = nil
var chapters: [Chapter] = []
var isPodcast: Bool { mediaType == "podcast" }
var isPodcastContainer: Bool { isPodcast && episodeId == nil }
var isPodcastEpisode: Bool { isPodcast && episodeId != nil }
static func == (lhs: LibraryItem, rhs: LibraryItem) -> Bool {
lhs.id == rhs.id && lhs.episodeId == rhs.episodeId
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(episodeId)
}
}
struct PodcastEpisode: Identifiable, Hashable, Codable {
let id: String
let title: String
let pubDate: String?
let publishedAtMillis: Double?
let season: String?
let episode: String?
let durationSeconds: Double
let audioFile: AudioFile
var formattedDate: String? {
if let ms = publishedAtMillis, ms > 0 {
let date = Date(timeIntervalSince1970: ms / 1000.0)
let df = DateFormatter()
df.dateStyle = .medium
df.timeStyle = .none
return df.string(from: date)
}
return pubDate
}
}
struct AudioFile: Codable, Hashable {
let ino: String
let filename: String
let ext: String
let durationSeconds: Double
let index: Int
}
struct PlaybackProgress: Codable, Hashable {
let itemId: String
var episodeId: String?
var currentTime: Double
var duration: Double
var isFinished: Bool
var updatedAt: Date
var syncKey: String {
if let episodeId { return "\(itemId)|\(episodeId)" }
return itemId
}
}
enum DownloadState: Equatable {
case notDownloaded
case downloading(progress: Double)
case downloaded
case failed(message: String)
}