63 lines
2.3 KiB
Swift
63 lines
2.3 KiB
Swift
import Foundation
|
|
|
|
struct PlaybackQueue {
|
|
struct Item: Identifiable {
|
|
let id = UUID()
|
|
let url: URL
|
|
}
|
|
|
|
private(set) var items: [Item] = []
|
|
private(set) var currentID: UUID?
|
|
var currentURL: URL? { items.first { $0.id == currentID }?.url }
|
|
private var currentIndex: Int? { items.firstIndex { $0.id == currentID } }
|
|
var canPrevious: Bool { currentIndex.map { $0 > 0 } ?? false }
|
|
var canNext: Bool { currentIndex.map { $0 + 1 < items.count } ?? false }
|
|
|
|
mutating func replace(_ urls: [URL]) {
|
|
items = sortedFiles(urls).map { Item(url: $0) }
|
|
currentID = items.first?.id
|
|
}
|
|
|
|
mutating func append(_ urls: [URL]) {
|
|
items.append(contentsOf: sortedFiles(urls).map { Item(url: $0) })
|
|
}
|
|
|
|
mutating func select(_ id: UUID) {
|
|
guard items.contains(where: { $0.id == id }) else { return }
|
|
currentID = id
|
|
}
|
|
|
|
mutating func advance(_ offset: Int) -> URL? {
|
|
guard let index = currentIndex else { return nil }
|
|
let (target, overflow) = index.addingReportingOverflow(offset)
|
|
guard !overflow, items.indices.contains(target) else { return nil }
|
|
currentID = items[target].id
|
|
return items[target].url
|
|
}
|
|
|
|
mutating func remove(_ id: UUID) -> URL? {
|
|
guard let index = items.firstIndex(where: { $0.id == id }) else { return nil }
|
|
let wasCurrent = currentID == id
|
|
items.remove(at: index)
|
|
guard wasCurrent else { return nil }
|
|
currentID = items.indices.contains(index) ? items[index].id : nil
|
|
return currentURL
|
|
}
|
|
|
|
mutating func move(fromOffsets offsets: IndexSet, toOffset destination: Int) {
|
|
guard !offsets.isEmpty, offsets.allSatisfy(items.indices.contains),
|
|
(0...items.count).contains(destination) else { return }
|
|
let moving = offsets.map { items[$0] }
|
|
for index in offsets.reversed() { items.remove(at: index) }
|
|
let insertion = destination - offsets.filter { $0 < destination }.count
|
|
items.insert(contentsOf: moving, at: insertion)
|
|
}
|
|
|
|
private func sortedFiles(_ urls: [URL]) -> [URL] {
|
|
urls.filter(\.isFileURL).sorted {
|
|
let order = $0.lastPathComponent.localizedStandardCompare($1.lastPathComponent)
|
|
return order == .orderedSame ? $0.path < $1.path : order == .orderedAscending
|
|
}
|
|
}
|
|
}
|