iOS 1.0 RC 2: Update Download and Update All Downloads
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
abb157d140
commit
ee6ca41fd3
4 files changed
+57
-18
No files matched your search
@@ -141,17 +141,31 @@ final class Downloads: @unchecked Sendable {
|
||||
/// Brings every group in line with the server: playlists gain and lose songs, artists gain albums.
|
||||
@MainActor func refresh() async {
|
||||
for gr in groups.values {
|
||||
let tracks: [Track]?
|
||||
switch gr.kind {
|
||||
case .playlist: tracks = try? await plex.playlistTracks(gr.key)
|
||||
case .album: tracks = try? await plex.albumTracks(gr.key)
|
||||
case .artist: tracks = try? await plex.artistTracks(gr.key)
|
||||
case .stemPlaylist: tracks = AppGraph.shared.stemPlaylists.lists.first { $0.id == gr.key }?.tracks
|
||||
}
|
||||
if let tracks, !tracks.isEmpty, let cur = groups[gr.id], cur.trackKeys != tracks.map(\.ratingKey) { download(cur, tracks) }
|
||||
if let tracks = await serverTracks(gr), let cur = groups[gr.id], cur.trackKeys != tracks.map(\.ratingKey) { download(cur, tracks) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Update Download: brings [id] in line with the server now and retries its failed songs.
|
||||
/// False when the server couldn't be reached.
|
||||
@MainActor func update(_ id: String) async -> Bool {
|
||||
guard let gr = groups[id], let tracks = await serverTracks(gr), let cur = groups[id] else { return false }
|
||||
download(cur, tracks)
|
||||
return true
|
||||
}
|
||||
|
||||
/// The songs [gr] holds on the server right now, or nil when they can't be loaded.
|
||||
@MainActor private func serverTracks(_ gr: DownloadGroup) async -> [Track]? {
|
||||
let tracks: [Track]?
|
||||
switch gr.kind {
|
||||
case .playlist: tracks = try? await plex.playlistTracks(gr.key)
|
||||
case .album: tracks = try? await plex.albumTracks(gr.key)
|
||||
case .artist: tracks = try? await plex.artistTracks(gr.key)
|
||||
case .stemPlaylist: tracks = AppGraph.shared.stemPlaylists.lists.first { $0.id == gr.key }?.tracks
|
||||
}
|
||||
guard let tracks, !tracks.isEmpty else { return nil }
|
||||
return tracks
|
||||
}
|
||||
|
||||
private func saveGroups() {
|
||||
let list = groups.values.sorted { $0.added < $1.added }
|
||||
writeQueue.async { [groupFile] in
|
||||
|
||||
@@ -71,8 +71,8 @@ struct MediaMenu {
|
||||
g.playback.addToQueue(pl.tracks, mixes: pl.mixes); overlays.hud("Added to Queue", "text.line.last.and.arrowtriangle.forward")
|
||||
},
|
||||
]
|
||||
a.append(downloadAction(pl.title, DownloadGroup(kind: .stemPlaylist, key: pl.id, title: pl.title, subtitle: "Stem Playlist", thumb: pl.thumb),
|
||||
{ pl.tracks }, confirm: false))
|
||||
a += downloadActions(pl.title, DownloadGroup(kind: .stemPlaylist, key: pl.id, title: pl.title, subtitle: "Stem Playlist", thumb: pl.thumb),
|
||||
{ pl.tracks }, confirm: false)
|
||||
if pl.items.isEmpty { a = [] }
|
||||
let lists = g.stemPlaylists
|
||||
if !pl.items.isEmpty {
|
||||
@@ -134,7 +134,7 @@ struct MediaMenu {
|
||||
private func collection(_ title: String, _ group: DownloadGroup?, _ load: @escaping () async throws -> [Track],
|
||||
playlistKey: String?, extra: [SheetAction], confirmDownload: Bool = false, stemmed: Bool = false) -> [SheetAction] {
|
||||
var a = queueActions(load)
|
||||
if let group { a.append(downloadAction(title, group, load, confirm: confirmDownload)) }
|
||||
if let group { a += downloadActions(title, group, load, confirm: confirmDownload) }
|
||||
a.append(SheetAction("Add to Playlist…", icon: "text.badge.plus") { withTracks(load) { addToPlaylist($0, skip: playlistKey, title: title) } })
|
||||
if !stemmed { a.append(stemNow(load)) }
|
||||
return a + extra
|
||||
@@ -152,14 +152,23 @@ struct MediaMenu {
|
||||
]
|
||||
}
|
||||
|
||||
/// Download, or Remove Download once [group] is downloaded. A downloaded group keeps itself in step with the server.
|
||||
func downloadAction(_ title: String, _ group: DownloadGroup, _ load: @escaping () async throws -> [Track], confirm: Bool) -> SheetAction {
|
||||
/// Download, or Update Download and Remove Download once [group] is downloaded. A downloaded group also
|
||||
/// keeps itself in step with the server on its own; Update does it now and retries songs that failed.
|
||||
func downloadActions(_ title: String, _ group: DownloadGroup, _ load: @escaping () async throws -> [Track], confirm: Bool) -> [SheetAction] {
|
||||
if g.downloads.groups[group.id] != nil {
|
||||
return SheetAction("Remove Download", icon: "trash", destructive: true) {
|
||||
overlays.confirm("Remove Download?", "“\(title)” will no longer be available offline.", "Remove") { g.downloads.removeGroup(group.id) }
|
||||
}
|
||||
return [
|
||||
SheetAction("Update Download", icon: "arrow.triangle.2.circlepath") {
|
||||
withTracks(load) { tracks in
|
||||
g.downloads.download(group, tracks)
|
||||
overlays.hud("Download Updated", "arrow.triangle.2.circlepath")
|
||||
}
|
||||
},
|
||||
SheetAction("Remove Download", icon: "trash", destructive: true) {
|
||||
overlays.confirm("Remove Download?", "“\(title)” will no longer be available offline.", "Remove") { g.downloads.removeGroup(group.id) }
|
||||
},
|
||||
]
|
||||
}
|
||||
return SheetAction("Download", icon: "arrow.down.circle") {
|
||||
return [SheetAction("Download", icon: "arrow.down.circle") {
|
||||
withTracks(load) { tracks in
|
||||
let go = { g.downloads.download(group, tracks); overlays.hud("Downloading", "arrow.down.circle") }
|
||||
if confirm && tracks.count > 20 {
|
||||
@@ -168,7 +177,7 @@ struct MediaMenu {
|
||||
go()
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
/// Has stemd on the server separate the songs that lack stems ahead of the rest of the library.
|
||||
|
||||
@@ -820,6 +820,12 @@ struct DownloadsScreen: View {
|
||||
SheetAction("Shuffle", icon: "shuffle") { g.playback.playTracks(d.tracks, shuffle: true) },
|
||||
]
|
||||
if d.failed > 0 { items.append(SheetAction("Retry Failed Songs", icon: "arrow.clockwise") { g.downloads.retryFailed() }) }
|
||||
items.append(SheetAction("Update Download", icon: "arrow.triangle.2.circlepath") {
|
||||
Task {
|
||||
let ok = await g.downloads.update(d.group.id)
|
||||
ui.overlays.hud(ok ? "Download Updated" : "Couldn't reach the server", ok ? "arrow.triangle.2.circlepath" : "exclamationmark.triangle")
|
||||
}
|
||||
})
|
||||
items.append(SheetAction("Remove Download", icon: "trash", destructive: true) {
|
||||
ui.overlays.confirm("Remove Download?", "“\(d.group.title)” will no longer be available offline.", "Remove") { g.downloads.removeGroup(d.group.id) }
|
||||
})
|
||||
@@ -851,6 +857,15 @@ struct DownloadsScreen: View {
|
||||
private func moreItems(_ all: [DownloadedGroup], _ pinned: String) -> [SheetAction] {
|
||||
var a: [SheetAction] = []
|
||||
if all.contains(where: { $0.failed > 0 }) { a.append(SheetAction("Retry Failed", icon: "arrow.clockwise") { g.downloads.retryFailed() }) }
|
||||
if !g.downloads.groups.isEmpty {
|
||||
a.append(SheetAction("Update All Downloads", icon: "arrow.triangle.2.circlepath") {
|
||||
Task {
|
||||
var failed = 0
|
||||
for id in Array(g.downloads.groups.keys) { if await !g.downloads.update(id) { failed += 1 } }
|
||||
ui.overlays.hud(failed == 0 ? "Downloads Updated" : "\(failed) couldn't be updated", failed == 0 ? "arrow.triangle.2.circlepath" : "exclamationmark.triangle")
|
||||
}
|
||||
})
|
||||
}
|
||||
a.append(SheetAction("Remove All Downloads", icon: "trash", destructive: true) {
|
||||
ui.overlays.confirm("Remove All Downloads?", "Frees \(pinned) GB. Songs stay in your library.", "Remove All") {
|
||||
for id in g.downloads.groups.keys { g.downloads.removeGroup(id) }
|
||||
|
||||
Reference in new issue
Block a user