bug fixes
This commit is contained in:
parent
2bdac607b4
commit
caea96547a
6 changed files with 130 additions and 4 deletions
|
|
@ -89,11 +89,32 @@ class WatchConnectivityManager: NSObject, ObservableObject {
|
|||
wcLog("Started file transfer: \(transfer.file.fileURL.lastPathComponent)")
|
||||
}
|
||||
|
||||
/// Notify the watch that a song's ID3 tags were updated via the Companion API.
|
||||
/// The watch updates its offline store metadata in-place and invalidates stale caches.
|
||||
/// Uses sendMessage if reachable, transferUserInfo as fallback (queued delivery on next activation).
|
||||
func notifyTagsUpdated(songId: String, title: String?, artist: String?,
|
||||
album: String?, albumArtist: String?) {
|
||||
guard let session, session.isPaired, session.isWatchAppInstalled else { return }
|
||||
var payload: [String: Any] = ["type": "tagsUpdated", "songId": songId]
|
||||
if let v = title { payload["title"] = v }
|
||||
if let v = artist { payload["artist"] = v }
|
||||
if let v = album { payload["album"] = v }
|
||||
if let v = albumArtist { payload["albumArtist"] = v }
|
||||
if session.isReachable {
|
||||
session.sendMessage(payload, replyHandler: nil, errorHandler: { [weak self] err in
|
||||
self?.wcLog("tagsUpdated sendMessage failed — queuing: \(err.localizedDescription)")
|
||||
session.transferUserInfo(payload)
|
||||
})
|
||||
} else {
|
||||
session.transferUserInfo(payload)
|
||||
}
|
||||
wcLog("notifyTagsUpdated: \(songId) title=\(title ?? "-")")
|
||||
}
|
||||
|
||||
/// High-level: send a downloaded song to the watch for offline playback.
|
||||
/// Always transcodes to MP3 192kbps via the server's stream endpoint for fast transfer.
|
||||
@discardableResult
|
||||
func sendSongToWatch(_ song: Song) -> Bool {
|
||||
wcLog("sendSongToWatch: \(song.title) (id: \(song.id))")
|
||||
func sendSongToWatch(_ song: Song) -> Bool { wcLog("sendSongToWatch: \(song.title) (id: \(song.id))")
|
||||
|
||||
guard let session = session else {
|
||||
wcLog("FAIL: WCSession is nil")
|
||||
|
|
|
|||
|
|
@ -215,7 +215,20 @@ struct BatchAlbumEditorSheet: View {
|
|||
await MainActor.run { progress = 0.3 }
|
||||
|
||||
let result = try await api.batchEditMetadata(request)
|
||||
|
||||
|
||||
// Notify the watch for each song in the batch
|
||||
if let songs = album.song {
|
||||
for song in songs {
|
||||
WatchConnectivityManager.shared.notifyTagsUpdated(
|
||||
songId: song.id,
|
||||
title: nil, // batch edit never changes titles
|
||||
artist: applyArtistToAll ? (artist.isEmpty ? nil : artist) : nil,
|
||||
album: albumName.isEmpty ? nil : albumName,
|
||||
albumArtist: albumArtist.isEmpty ? nil : albumArtist
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
await MainActor.run {
|
||||
progress = 1.0
|
||||
isSaving = false
|
||||
|
|
@ -250,3 +263,7 @@ struct BatchAlbumEditorSheet: View {
|
|||
.keyboardType(keyboard)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.keyboardDoneButton()
|
||||
}
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
}
|
||||
|
|
@ -296,7 +296,20 @@ struct MultiAlbumEditorSheet: View {
|
|||
await MainActor.run { progress = 0.3 }
|
||||
|
||||
let result = try await api.batchEditMetadata(request)
|
||||
|
||||
|
||||
// Notify the watch for every song across all albums
|
||||
for album in albums {
|
||||
for song in album.song ?? [] {
|
||||
WatchConnectivityManager.shared.notifyTagsUpdated(
|
||||
songId: song.id,
|
||||
title: nil,
|
||||
artist: applyArtistToAll ? (artist.isEmpty ? nil : artist) : nil,
|
||||
album: albumName.isEmpty ? nil : albumName,
|
||||
albumArtist: albumArtist.isEmpty ? nil : albumArtist
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
await MainActor.run {
|
||||
progress = 1.0
|
||||
isSaving = false
|
||||
|
|
@ -330,6 +343,7 @@ struct MultiAlbumEditorSheet: View {
|
|||
TextField(label, text: text)
|
||||
.keyboardType(keyboard)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.keyboardDoneButton()
|
||||
}
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -250,6 +250,15 @@ struct TrackEditorView: View {
|
|||
|
||||
try await api.editMetadata(request)
|
||||
|
||||
// Notify the watch so it can update its offline store metadata
|
||||
WatchConnectivityManager.shared.notifyTagsUpdated(
|
||||
songId: song.id,
|
||||
title: request.title,
|
||||
artist: request.artist,
|
||||
album: request.album,
|
||||
albumArtist: request.albumArtist
|
||||
)
|
||||
|
||||
await MainActor.run {
|
||||
isSaving = false
|
||||
withAnimation { showSuccess = true }
|
||||
|
|
@ -289,6 +298,7 @@ struct TrackEditorView: View {
|
|||
.foregroundColor(isEnabled.wrappedValue ? .white : .gray.opacity(0.5))
|
||||
.disabled(!isEnabled.wrappedValue)
|
||||
.keyboardDoneButton()
|
||||
}
|
||||
.font(.system(size: 13))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,39 @@ class WatchOfflineStore: NSObject, ObservableObject, URLSessionDownloadDelegate
|
|||
totalSize += fileSize
|
||||
saveCatalog()
|
||||
}
|
||||
|
||||
/// Update the embedded Song metadata for an offline song in-place.
|
||||
/// Called when iOS notifies the watch of tag changes via WatchConnectivity.
|
||||
func updateSongMetadata(id: String, title: String?, artist: String?,
|
||||
album: String?, albumArtist: String?) {
|
||||
guard let idx = songs.firstIndex(where: { $0.id == id }) else { return }
|
||||
let old = songs[idx]
|
||||
let updated = Song(
|
||||
id: old.song.id,
|
||||
parent: old.song.parent, isDir: old.song.isDir,
|
||||
title: title ?? old.song.title,
|
||||
album: album ?? old.song.album,
|
||||
artist: artist ?? old.song.artist,
|
||||
track: old.song.track, year: old.song.year, genre: old.song.genre,
|
||||
coverArt: old.song.coverArt,
|
||||
size: old.song.size, contentType: old.song.contentType,
|
||||
suffix: old.song.suffix,
|
||||
transcodedContentType: old.song.transcodedContentType,
|
||||
transcodedSuffix: old.song.transcodedSuffix,
|
||||
duration: old.song.duration, bitRate: old.song.bitRate,
|
||||
path: old.song.path, playCount: old.song.playCount,
|
||||
discNumber: old.song.discNumber, created: old.song.created,
|
||||
albumId: old.song.albumId, artistId: old.song.artistId,
|
||||
type: old.song.type, starred: old.song.starred,
|
||||
bpm: old.song.bpm, musicBrainzId: old.song.musicBrainzId
|
||||
)
|
||||
songs[idx] = WatchOfflineSong(
|
||||
id: old.id, song: updated,
|
||||
localPath: old.localPath, fileSize: old.fileSize, dateAdded: old.dateAdded
|
||||
)
|
||||
saveCatalog()
|
||||
print("[Watch] Updated metadata for \(id): title=\(title ?? "-")")
|
||||
}
|
||||
|
||||
func removeSong(_ songId: String) {
|
||||
guard let idx = songs.firstIndex(where: { $0.id == songId }) else { return }
|
||||
|
|
|
|||
|
|
@ -239,6 +239,33 @@ class WatchSessionManager: NSObject, ObservableObject {
|
|||
await MainActor.run { isSyncing = false }
|
||||
}
|
||||
|
||||
// MARK: - Tag Update Handler
|
||||
|
||||
private func handleTagsUpdated(_ message: [String: Any]) {
|
||||
guard let songId = message["songId"] as? String else { return }
|
||||
let title = message["title"] as? String
|
||||
let artist = message["artist"] as? String
|
||||
let album = message["album"] as? String
|
||||
let albumArtist = message["albumArtist"] as? String
|
||||
|
||||
DispatchQueue.main.async {
|
||||
// Update offline store if this song is downloaded on the watch
|
||||
WatchOfflineStore.shared.updateSongMetadata(
|
||||
id: songId, title: title, artist: artist,
|
||||
album: album, albumArtist: albumArtist
|
||||
)
|
||||
// Invalidate stale library caches — next fetch will repopulate from server
|
||||
for key in ["all_albums", "recent", "playlists"] {
|
||||
UserDefaults.standard.removeObject(forKey: self.cachePrefix + key)
|
||||
}
|
||||
// Refresh now-playing display if this song is currently playing
|
||||
if let t = title { self.phoneNowPlaying?.title = t }
|
||||
if let a = artist { self.phoneNowPlaying?.artist = a }
|
||||
if let b = album { self.phoneNowPlaying?.album = b }
|
||||
print("[Watch] Tags updated for \(songId)")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Server-Direct Download Handler
|
||||
|
||||
/// Called when iPhone tells us to download a song directly from server.
|
||||
|
|
@ -376,6 +403,8 @@ extension WatchSessionManager: WCSessionDelegate {
|
|||
coverArtId: message["coverArtId"] as? String
|
||||
)
|
||||
}
|
||||
} else if type == "tagsUpdated" {
|
||||
handleTagsUpdated(message)
|
||||
} else if type == "downloadSong" {
|
||||
handleDownloadCommand(message)
|
||||
}
|
||||
|
|
@ -439,6 +468,8 @@ extension WatchSessionManager: WCSessionDelegate {
|
|||
print("[Watch] Wake signal received — app is active")
|
||||
} else if type == "downloadSong" {
|
||||
handleDownloadCommand(userInfo)
|
||||
} else if type == "tagsUpdated" {
|
||||
handleTagsUpdated(userInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue