0411064f0c
Persist authoritative per-video positions in UserDefaults. Save periodic checkpoints, pause, close, replacement, and quit; restore a validated position before autoplay. Canonical YouTube webpage URLs survive fresh stream extraction, and local symlinks share file progress. Clear completed or out-of-duration bookmarks. Leave failed opens and live/non-seekable inputs out of persistence. Pending opens hide errors belonging to the previous source. Verified separate-process restart, source replacement, failed-open recovery, completion, checkpoints, real YouTube short-link resume, live exclusion, and make all check.
30 lines
1.1 KiB
Swift
30 lines
1.1 KiB
Swift
import Foundation
|
|
|
|
struct PlaybackProgress {
|
|
private let defaults: UserDefaults
|
|
private var positions: [String: Double]
|
|
|
|
init(defaults: UserDefaults = .standard) {
|
|
self.defaults = defaults
|
|
positions = defaults.dictionary(forKey: "playbackPositions") as? [String: Double] ?? [:]
|
|
}
|
|
|
|
func position(for url: URL) -> Double? {
|
|
guard let position = positions[key(for: url)], position.isFinite, position > 0 else { return nil }
|
|
return position
|
|
}
|
|
|
|
mutating func save(_ position: Double?, for url: URL) {
|
|
let key = key(for: url)
|
|
let position = position.flatMap { $0.isFinite && $0 > 0 ? $0 : nil }
|
|
guard positions[key] != position else { return }
|
|
positions[key] = position
|
|
defaults.set(positions, forKey: "playbackPositions")
|
|
}
|
|
|
|
private func key(for url: URL) -> String {
|
|
// YouTube callers supply the canonical webpage URL, never an expiring media URL.
|
|
return url.isFileURL ? url.standardizedFileURL.resolvingSymlinksInPath().absoluteString : url.absoluteString
|
|
}
|
|
}
|