The Albums tab was being populated with Companion API IDs (companion:Album Name|Artist Name) instead of real Navidrome IDs. Every time the Companion sync ran, it overwrote the valid Subsonic album cache with these synthetic IDs. AlbumDetailView would detect the companion: prefix, load songs from the Companion API instead of Navidrome, and those songs have Companion song IDs that Navidrome can’t stream.
The Artist → Album path bypassed this entirely because it navigates via artistId which fetches albums fresh from Navidrome each time. After installing this and doing a pull-to-refresh, the Albums tab will use real Navidrome IDs again. You may need to clear the app’s cache once if the stale Companion IDs are already persisted — Settings → clear library cache if that option exists, or just force-quit and relaunch after refreshing.
This commit is contained in:
parent
fc69d8a3cf
commit
7657b5841e
3 changed files with 39 additions and 7 deletions
|
|
@ -705,6 +705,13 @@ class AudioPlayer: NSObject, ObservableObject {
|
|||
// MARK: - AVPlayer Path (streams + fallback)
|
||||
|
||||
private func playWithAVPlayer(_ url: URL) {
|
||||
#if os(iOS)
|
||||
let isStream = url.scheme == "http" || url.scheme == "https"
|
||||
let display = isStream
|
||||
? "\(url.scheme ?? "")://\(url.host ?? "")...\(url.lastPathComponent)"
|
||||
: url.lastPathComponent
|
||||
DebugLogger.shared.log("▶ \(display)", category: "Audio")
|
||||
#endif
|
||||
alog("AVPlayer path: \(url.scheme ?? "file")://...\(url.lastPathComponent)")
|
||||
stopAll()
|
||||
|
||||
|
|
@ -720,7 +727,30 @@ class AudioPlayer: NSObject, ObservableObject {
|
|||
|
||||
let asset = AVURLAsset(url: url)
|
||||
playerItem = AVPlayerItem(asset: asset)
|
||||
|
||||
|
||||
// Observe item status — catches stream 404s, auth failures, and codec
|
||||
// errors that AVPlayer swallows silently otherwise
|
||||
#if os(iOS)
|
||||
let itemToObserve = playerItem!
|
||||
Task { @MainActor [weak self] in
|
||||
for await status in itemToObserve.publisher(for: \.status).values {
|
||||
guard let self = self, self.playerItem === itemToObserve else { break }
|
||||
switch status {
|
||||
case .failed:
|
||||
let err = itemToObserve.error?.localizedDescription ?? "unknown"
|
||||
let urlStr = url.absoluteString.contains("stream") ? url.absoluteString : url.lastPathComponent
|
||||
DebugLogger.shared.log("✗ Playback failed: \(err) | \(urlStr)",
|
||||
category: "Audio", level: .error)
|
||||
case .readyToPlay:
|
||||
DebugLogger.shared.log("✓ Ready: \(url.lastPathComponent)", category: "Audio")
|
||||
default:
|
||||
break
|
||||
}
|
||||
if status == .failed { break }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if player == nil {
|
||||
player = AVPlayer(playerItem: playerItem)
|
||||
} else {
|
||||
|
|
@ -1702,3 +1732,4 @@ class AudioPlayer: NSObject, ObservableObject {
|
|||
stop()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ class OfflineManager: ObservableObject {
|
|||
let filename = (ds.localPath as NSString).lastPathComponent
|
||||
let url = downloadDirectory.appendingPathComponent(filename)
|
||||
if fileManager.fileExists(atPath: url.path) {
|
||||
print("[OfflineManager] ID changed: \(ds.id) → \(song.id) (\(song.title))", flush: true)
|
||||
print("[OfflineManager] ID changed: \(ds.id) → \(song.id) (\(song.title))")
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -321,14 +321,15 @@ class SyncEngine: ObservableObject {
|
|||
await setProgress("Syncing Companion library...")
|
||||
DebugLogger.shared.log("Companion sync started", category: "Companion")
|
||||
|
||||
// Fetch albums with correct sort order from Companion
|
||||
// Fetch albums from Companion for sorting/metadata purposes
|
||||
let companionAlbums = try await service.fetchAllAlbums()
|
||||
cache.cacheCompanionAlbums(companionAlbums)
|
||||
|
||||
// Overwrite standard album cache with Companion-sorted data so
|
||||
// MyMusicView and search results immediately reflect correct ordering
|
||||
let standardAlbums = companionAlbums.map { $0.toAlbum() }
|
||||
cache.save(standardAlbums, key: "all_albums")
|
||||
// Do NOT overwrite "all_albums" with Companion IDs.
|
||||
// Companion album IDs are synthetic ("companion:{name}|{artist}") and
|
||||
// unknown to Navidrome — AlbumDetailView would fail to stream any song
|
||||
// because getAlbum(id:) returns nothing for those IDs.
|
||||
// The Subsonic-fetched all_albums (real Navidrome IDs) stays as-is.
|
||||
|
||||
DebugLogger.shared.log("Companion sync: \(companionAlbums.count) albums", category: "Companion")
|
||||
await MainActor.run {
|
||||
|
|
|
|||
Loading…
Reference in a new issue