From 92a5a54b8dc6bf3da32527bc8cdc7ac7cb6752f9 Mon Sep 17 00:00:00 2001 From: Dallas Groot Date: Sat, 11 Apr 2026 08:36:32 -0700 Subject: [PATCH] quick fix --- iOS/Views/Companion/CompanionAPIService.swift | 3 ++ .../Companion/CompanionSettingsView.swift | 53 +++++++++++++++++++ iOS/Views/Library/DownloadsSettingsView.swift | 2 + 3 files changed, 58 insertions(+) diff --git a/iOS/Views/Companion/CompanionAPIService.swift b/iOS/Views/Companion/CompanionAPIService.swift index edc46ce..ed97b69 100644 --- a/iOS/Views/Companion/CompanionAPIService.swift +++ b/iOS/Views/Companion/CompanionAPIService.swift @@ -538,6 +538,8 @@ class CompanionPushClient: ObservableObject { // Notify ConflictManager to refresh — use NotificationCenter to avoid // a cross-module dependency between Foundation and SwiftUI layers. NotificationCenter.default.post(name: .companionConflictsUpdated, object: nil, userInfo: msg.data) + case "tags_cleaned": + NotificationCenter.default.post(name: .companionTagsCleaned, object: nil, userInfo: msg.data) case "profile": if let path = msg.data?["path"] as? String, let jsonData = try? JSONSerialization.data(withJSONObject: msg.data ?? [:]), @@ -595,6 +597,7 @@ extension Notification.Name { static let companionArtistPhotoUpdated = Notification.Name("companionArtistPhotoUpdated") static let companionLibraryChanged = Notification.Name("companionLibraryChanged") static let companionConflictsUpdated = Notification.Name("companionConflictsUpdated") + static let companionTagsCleaned = Notification.Name("companionTagsCleaned") } // MARK: - Conflict Models diff --git a/iOS/Views/Companion/CompanionSettingsView.swift b/iOS/Views/Companion/CompanionSettingsView.swift index 348b798..2af67f6 100644 --- a/iOS/Views/Companion/CompanionSettingsView.swift +++ b/iOS/Views/Companion/CompanionSettingsView.swift @@ -10,6 +10,8 @@ struct CompanionSettingsView: View { @State private var analyzeMessage: String? @State private var fixLibraryStatus: AnalyzeStatus = .idle @State private var fixLibraryMessage: String? + @State private var cleanTagsStatus: AnalyzeStatus = .idle + @State private var cleanTagsMessage: String? private let accentPink = Color(red: 1.0, green: 0.176, blue: 0.333) @@ -122,6 +124,26 @@ struct CompanionSettingsView: View { } } + Button(action: triggerCleanTags) { + HStack { + Image(systemName: "tag.slash") + .foregroundColor(cleanTagsStatus == .done ? .green : cleanTagsStatus == .failed ? .red : .orange) + .symbolEffect(.pulse, isActive: cleanTagsStatus == .analyzing) + VStack(alignment: .leading, spacing: 2) { + Text("Clean Picard Tags").foregroundColor(.white) + Text("Remove MusicBrainz IDs, AcoustID, and other Picard-only tags from every file.") + .font(.caption).foregroundColor(.gray) + } + } + } + .disabled(cleanTagsStatus == .analyzing) + + if let msg = cleanTagsMessage { + Text(msg) + .font(.caption) + .foregroundColor(cleanTagsStatus == .done ? .green : cleanTagsStatus == .failed ? .red : .gray) + } + Button(action: triggerFixLibrary) { HStack { Image(systemName: "folder.badge.gearshape") @@ -142,6 +164,10 @@ struct CompanionSettingsView: View { .foregroundColor(fixLibraryStatus == .done ? .green : fixLibraryStatus == .failed ? .red : .gray) } } header: { Text("Server Actions") } + .onReceive(NotificationCenter.default.publisher(for: .companionTagsCleaned)) { _ in + cleanTagsStatus = .done + cleanTagsMessage = "✓ Tag cleaning complete" + } Section { Button(action: { triggerPreAnalyze(force: false) }) { @@ -183,6 +209,33 @@ struct CompanionSettingsView: View { .navigationBarTitleDisplayMode(.inline) } + // MARK: - Clean Picard Tags + + private func triggerCleanTags() { + cleanTagsStatus = .analyzing + cleanTagsMessage = "Cleaning tags on server… this may take a minute." + Task { + do { + guard let base = CompanionSettings.shared.baseURL else { return } + var req = URLRequest(url: base.appendingPathComponent("library/clean-tags")) + req.httpMethod = "POST" + req.timeoutInterval = 15 + let (data, _) = try await URLSession.shared.data(for: req) + let msg = (try? JSONDecoder().decode([String: String].self, from: data))?["message"] ?? "Started" + await MainActor.run { + cleanTagsStatus = .analyzing + cleanTagsMessage = "⏳ \(msg)" + } + DebugLogger.shared.log("Clean Picard tags triggered", category: "Companion") + } catch { + await MainActor.run { + cleanTagsStatus = .failed + cleanTagsMessage = "✗ \(error.localizedDescription)" + } + } + } + } + // MARK: - Trigger Server Scan private func triggerScan() { diff --git a/iOS/Views/Library/DownloadsSettingsView.swift b/iOS/Views/Library/DownloadsSettingsView.swift index 5c936e9..650f047 100644 --- a/iOS/Views/Library/DownloadsSettingsView.swift +++ b/iOS/Views/Library/DownloadsSettingsView.swift @@ -1019,6 +1019,7 @@ extension LibraryConflict { case "orphaned_tracks": return "link.badge.plus" case "duplicate_track": return "music.note.list" case "stale_companion_paths": return "externaldrive.badge.xmark" + case "album_reassigned": return "arrow.triangle.2.circlepath.circle.fill" default: return "exclamationmark.circle" } } @@ -1266,6 +1267,7 @@ struct LibraryConflictsView: View { case "fix_picard_tags": return "Fix Tags" case "fix_stale_paths": return "Rescan Library" case "fix_orphaned_tracks": return "Trigger Rescan" + case "fix_album_reassigned": return "Reapply Tags" default: return "Fix" } }