iOS: stem playlist refresh and covers, download queue page, reorderable Library, whole-library radio
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7c8dfb54ca
commit
a4c870251c
18 files changed
+514
-30
No files matched your search
@@ -69,13 +69,14 @@ final class CarLibrary: @unchecked Sendable {
|
||||
func children(_ id: String) async throws -> [CarItem]? {
|
||||
let away = await away()
|
||||
switch id {
|
||||
case CarIds.downloads: return await songs(CarIds.downloads, try tracksOf(CarIds.downloads), shuffleRow: true, showArtist: true)
|
||||
case CarIds.downloads: return downloadsMenu()
|
||||
case CarIds.playlists: return try await playlists(away)
|
||||
case CarIds.albums: return try await albumsTab(away)
|
||||
case CarIds.artists: return try await artistsTab(away)
|
||||
default:
|
||||
guard let (kind, key) = CarIds.split(id) else { return nil }
|
||||
switch kind {
|
||||
case "dl": return try await downloadsView(key)
|
||||
case "playlist", "stemplaylist": return await songs(id, try tracksOf(id), shuffleRow: true, showArtist: true)
|
||||
case "album": return await songs(id, try tracksOf(id), shuffleRow: false, showArtist: false)
|
||||
case "artist": return try await artistPage(key, away)
|
||||
@@ -87,15 +88,42 @@ final class CarLibrary: @unchecked Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
private func playlists(_ away: Bool) async throws -> [CarItem] {
|
||||
/// Downloads opens onto the same categories as the other tabs, each limited to what's downloaded.
|
||||
private func downloadsMenu() -> [CarItem] {
|
||||
let dl = downloadedTracks()
|
||||
guard !dl.isEmpty else { return [] }
|
||||
return [
|
||||
CarItem(id: "dl:playlists", title: "Playlists", art: .letter("P"), playable: false),
|
||||
CarItem(id: "dl:albums", title: "Albums", subtitle: CarTree.countLabel(albumsOf(dl).count, "album", "albums"), art: .letter("A"), playable: false),
|
||||
CarItem(id: "dl:artists", title: "Artists", subtitle: CarTree.countLabel(Set(dl.compactMap(\.artistKey)).count, "artist", "artists"), art: .letter("A"), playable: false),
|
||||
CarItem(id: "dl:songs", title: "Songs", subtitle: CarTree.countLabel(dl.count, "song", "songs"), art: .letter("S"), playable: false),
|
||||
]
|
||||
}
|
||||
|
||||
private func downloadsView(_ key: String) async throws -> [CarItem]? {
|
||||
switch key {
|
||||
case "playlists": return try await playlists(true, onlyDownloaded: true)
|
||||
case "albums": return try await albumsFor(true).map { albumItem($0) }
|
||||
case "artists": return try await artistsFor(true).map { artistItem($0) }
|
||||
case "songs": return await songs(CarIds.downloads, try tracksOf(CarIds.downloads), shuffleRow: true, showArtist: true)
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func playlists(_ away: Bool, onlyDownloaded: Bool = false) async throws -> [CarItem] {
|
||||
let all = try await plex.playlists()
|
||||
var shown: [Playlist] = []
|
||||
if away {
|
||||
if onlyDownloaded {
|
||||
let have = Set(downloadedTracks().map(\.ratingKey))
|
||||
for p in all where ((try? await plex.playlistTracks(p.ratingKey))?.contains { have.contains($0.ratingKey) } == true) { shown.append(p) }
|
||||
} else if away {
|
||||
for p in all where ((try? await tracksOf(CarIds.playlist(p.ratingKey)))?.isEmpty == false) { shown.append(p) }
|
||||
} else {
|
||||
shown = all
|
||||
}
|
||||
let stem = await MainActor.run { AppGraph.shared.stemPlaylists.lists.filter { !$0.items.isEmpty } }
|
||||
let have = Set(downloadedTracks().map(\.ratingKey))
|
||||
let stem = await MainActor.run { AppGraph.shared.stemPlaylists.lists.filter { l in
|
||||
onlyDownloaded ? (l.tracks.contains { have.contains($0.ratingKey) }) : !l.items.isEmpty } }
|
||||
let stemItems = stem.map { l in
|
||||
CarItem(id: "stemplaylist:\(l.id)", title: l.title, subtitle: CarTree.countLabel(l.items.count, "song", "songs"),
|
||||
art: artOr(l.thumb, l.title), playable: false, group: "Stem Playlists")
|
||||
@@ -263,7 +291,7 @@ final class CarLibrary: @unchecked Sendable {
|
||||
private func songItem(_ container: String, _ t: Track, _ showArtist: Bool, _ group: String?) -> CarItem {
|
||||
// Stems are the norm, so only the exceptions are marked.
|
||||
let noStems = stems.index.size > 0 && !pm.hasStems(t)
|
||||
let sub = [noStems ? "No stems" : nil, showArtist ? t.artist : nil].compactMap { $0 }.joined(separator: " · ")
|
||||
let sub = [noStems ? (stems.state(t) == .stemming ? "Stemming" : stems.state(t) == .queued ? "Queued for stems" : "No stems") : nil, showArtist ? t.artist : nil].compactMap { $0 }.joined(separator: " · ")
|
||||
return CarItem(id: CarIds.play(container, t.ratingKey), title: t.title, subtitle: sub.isEmpty ? nil : sub,
|
||||
art: t.thumb.map { .thumb($0) } ?? .letter(CarTree.letterOf(t.album)), playable: true, group: group,
|
||||
downloaded: pm.playableOffline(t), trackKey: t.ratingKey)
|
||||
|
||||
Reference in new issue
Block a user