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) } }