Version one on the App Store

This commit is contained in:
2026-04-05 19:44:30 +02:00
parent c780be089d
commit 3ebf1763ed
26 changed files with 2088 additions and 842 deletions
@@ -41,6 +41,9 @@ final class MALibraryManager {
// MARK: - Disk Cache
/// Increment this whenever the model format changes to invalidate stale caches.
private static let cacheVersion = 2
private let cacheDirectory: URL = {
let caches = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let dir = caches.appendingPathComponent("MMLibrary", isDirectory: true)
@@ -52,9 +55,24 @@ final class MALibraryManager {
init(service: MAService?) {
self.service = service
migrateIfNeeded()
loadFromDisk()
}
/// Clears all disk-cached library data when the model format changes.
private func migrateIfNeeded() {
let storedVersion = UserDefaults.standard.integer(forKey: "lib.cacheVersion")
guard storedVersion < Self.cacheVersion else { return }
logger.info("Cache version mismatch (\(storedVersion)\(Self.cacheVersion)), clearing library cache")
try? FileManager.default.removeItem(at: cacheDirectory)
try? FileManager.default.createDirectory(at: cacheDirectory, withIntermediateDirectories: true)
for key in ["lib.lastArtistsRefresh", "lib.lastAlbumsRefresh", "lib.lastPlaylistsRefresh"] {
UserDefaults.standard.removeObject(forKey: key)
}
UserDefaults.standard.set(Self.cacheVersion, forKey: "lib.cacheVersion")
}
func setService(_ service: MAService) {
self.service = service
}
@@ -110,10 +128,10 @@ final class MALibraryManager {
guard !isLoadingArtists else { return }
guard let service else { throw MAWebSocketClient.ClientError.notConnected }
// For refresh, reset pagination counters but keep existing data visible until new data arrives
let fetchOffset = refresh ? 0 : artistsOffset
if refresh {
artistsOffset = 0
hasMoreArtists = true
artists = []
}
guard hasMoreArtists else { return }
@@ -121,20 +139,25 @@ final class MALibraryManager {
isLoadingArtists = true
defer { isLoadingArtists = false }
logger.info("Loading artists (offset: \(self.artistsOffset))")
logger.info("Loading artists (offset: \(fetchOffset), refresh: \(refresh))")
let newArtists = try await service.getArtists(limit: pageSize, offset: artistsOffset)
let newArtists = try await service.getArtists(limit: pageSize, offset: fetchOffset)
if refresh {
artists = newArtists
} else {
artists.append(contentsOf: newArtists)
// DEBUG: log first artist's image state so we can trace artwork loading
if let a = newArtists.first {
logger.debug("DEBUG Artist[0] name=\(a.name) metadata=\(String(describing: a.metadata)) imageUrl=\(a.imageUrl ?? "nil") imageProvider=\(a.imageProvider ?? "nil")")
}
artistsOffset += newArtists.count
// Replace or append atomically no intermediate empty state
if refresh {
artists = newArtists
artistsOffset = newArtists.count
} else {
artists.append(contentsOf: newArtists)
artistsOffset += newArtists.count
}
hasMoreArtists = newArtists.count >= pageSize
// Persist to disk after a full load or first page of refresh
if refresh || artistsOffset <= pageSize {
save(artists, "artists.json")
lastArtistsRefresh = markRefreshed("lib.lastArtistsRefresh")
@@ -159,10 +182,9 @@ final class MALibraryManager {
guard !isLoadingAlbums else { return }
guard let service else { throw MAWebSocketClient.ClientError.notConnected }
let fetchOffset = refresh ? 0 : albumsOffset
if refresh {
albumsOffset = 0
hasMoreAlbums = true
albums = []
}
guard hasMoreAlbums else { return }
@@ -170,17 +192,17 @@ final class MALibraryManager {
isLoadingAlbums = true
defer { isLoadingAlbums = false }
logger.info("Loading albums (offset: \(self.albumsOffset))")
logger.info("Loading albums (offset: \(fetchOffset), refresh: \(refresh))")
let newAlbums = try await service.getAlbums(limit: pageSize, offset: albumsOffset)
let newAlbums = try await service.getAlbums(limit: pageSize, offset: fetchOffset)
if refresh {
albums = newAlbums
albumsOffset = newAlbums.count
} else {
albums.append(contentsOf: newAlbums)
albumsOffset += newAlbums.count
}
albumsOffset += newAlbums.count
hasMoreAlbums = newAlbums.count >= pageSize
if refresh || albumsOffset <= pageSize {