Files
terebi/Sources/App/PlaybackProgress.swift

58 lines
2.3 KiB
Swift

import Foundation
struct PlaybackProgress {
struct RecentMedia: Equatable, Identifiable {
let url: URL
let title: String
var id: URL { url }
}
private let defaults: UserDefaults
init(defaults: UserDefaults = .standard) {
self.defaults = defaults
// Migrate the old shared dictionary once; separate keys prevent cross-player lost updates.
if let positions = defaults.dictionary(forKey: "playbackPositions") as? [String: Double] {
for (identity, position) in positions where position.isFinite && position > 0 {
let key = "playbackPosition:" + identity
if defaults.object(forKey: key) == nil { defaults.set(position, forKey: key) }
}
defaults.removeObject(forKey: "playbackPositions")
}
}
func position(for url: URL) -> Double? {
guard let position = defaults.object(forKey: key(for: url)) as? Double,
position.isFinite, position > 0 else { return nil }
return position
}
func save(_ position: Double?, for url: URL) {
let key = key(for: url)
let position = position.flatMap { $0.isFinite && $0 > 0 ? $0 : nil }
guard (defaults.object(forKey: key) as? Double) != position else { return }
defaults.set(position, forKey: key)
}
var recentMedia: [RecentMedia] {
(defaults.array(forKey: "recentMedia") as? [[String: String]] ?? []).compactMap {
guard let text = $0["url"], let url = URL(string: text), let title = $0["title"] else { return nil }
return RecentMedia(url: url, title: title)
}
}
func recordRecent(_ url: URL, title: String) {
let url = url.isFileURL ? url.standardizedFileURL.resolvingSymlinksInPath() : url
var entries = recentMedia
entries.removeAll { $0.url == url }
entries.insert(RecentMedia(url: url, title: title), at: 0)
defaults.set(entries.prefix(6).map { ["url": $0.url.absoluteString, "title": $0.title] },
forKey: "recentMedia")
}
private func key(for url: URL) -> String {
// YouTube callers supply the canonical webpage URL, never an expiring media URL.
return "playbackPosition:" + (url.isFileURL ? url.standardizedFileURL.resolvingSymlinksInPath().absoluteString : url.absoluteString)
}
}