Commit graph

143 commits

Author SHA1 Message Date
4dea87ae64 Merge remote-tracking branch 'refs/remotes/origin/main' 2026-04-12 13:52:25 -07:00
Dallas Groot
8ee74aed91 hotfix 2026-04-12 13:52:10 -07:00
9a595931f7 quick fix 2026-04-12 13:49:03 -07:00
Dallas Groot
53539e5a67 quick fix 2026-04-12 13:39:13 -07:00
Dallas Groot
8aa47319f2 quick fix 2026-04-12 13:21:20 -07:00
Dallas Groot
098e9b9363 added widget support 2026-04-12 12:57:42 -07:00
Dallas Groot
346b3ef378 quick fix 2026-04-11 19:28:18 -07:00
Dallas Groot
282eb5d80c Fixed idle visualizer bug
AudioPlayer.swift — startRadioSimulation() replaces
startLevelSimulation() for radio streams. It generates two overlapping
sine waves with a slow breathing amplitude envelope. The key property:
it’s driven by an internal radioPhase counter that increments at a
fixed rate, completely independent of currentTime. Buffer seeks, HLS
restarts, and stream reconnects have zero visual effect.
MitsuhaVisualizerView.swift — Two fixes:
	1.	TimelineView(.periodic(from: .distantPast, by: tickInterval)) —
using .distantPast as the stable schedule origin instead of .now.
Previously, any recalculation of tickInterval (triggered by
isRenderingActive flipping during a radio buffer hiccup) would reset
the origin to the current moment, deferring the next tick by a full
interval — the visible stall.
	2.	songId parameter with .onChange(of: songId) that resets
peakFollower, levelHistoryBuf, and lastTickTime on song change. This
prevents the outgoing song’s simulation spike from “raising” the wave
at the start of the next song.
NowPlayingView.swift and MainTabView.swift — Pass songId:
audioPlayer.currentSong?.id through to MitsuhaVisualizerView and
CompactVisualizerView at all call sites.
2026-04-11 19:01:10 -07:00
Dallas Groot
7d78b22ce2 quick fix 2026-04-11 18:25:09 -07:00
Dallas Groot
3f3010a8fd fixed dynamicminiplayer placement 2026-04-11 18:15:10 -07:00
Dallas Groot
8eaab0bc93 Three changes to DynamicIslandView:
•	Top spacer 54 → 12 — pulls the pill up to sit directly under the
Dynamic Island cutout
	•	Horizontal padding 32 → 56 — narrows the pill from both sides
	•	Height 48 → 44 — slightly more compact to match the island’s
proportions
If it’s sitting too high or too low after testing, the spacer value is
the one knob to turn — increase it if it overlaps the island, decrease
if there’s too much gap.
2026-04-11 17:53:59 -07:00
Dallas Groot
f19d21e4cc Fix: Album streaming, seek bar gesture, and offline playback
Resolve several playback bugs introduced during the CPU optimization
work. Stop SyncEngine from overwriting the Subsonic album cache with
synthetic Companion IDs, which was silently breaking every album in
the Albums tab by routing through the Companion song path instead of
Navidrome streaming. Fix MiniProgressBar seek gesture dropping touches
by moving the DragGesture outside the TimelineView closure — the 10Hz
timer was reconstructing the gesture recognizer every tick,
interrupting in-progress drags. Add AVPlayerItem status observation to
AudioPlayer so stream failures surface in the debug console instead of
silently stalling. Add fuzzy title/artist/duration fallback to
OfflineManager.localURL so songs remain playable offline after a
Companion API restructure changes their Navidrome ID.
2026-04-11 17:41:20 -07:00
7657b5841e 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.​​​​​​​​​​​​​​​​
2026-04-11 17:33:13 -07:00
Dallas Groot
fc69d8a3cf CPU: Remove @Published from AudioPlayer time properties
Replace @Published var currentTime/duration with plain vars and drive
progress bars via TimelineView(.periodic) instead of SwiftUI
observation. This stops objectWillChange from firing 20x/second on
AudioPlayer, eliminating continuous body re-evaluation on
NowPlayingView, MiniPlayerBar, and MyMusicView regardless of
visualizer state.
2026-04-11 16:44:56 -07:00
Dallas Groot
758d7a5ebd quick fix
NowPlayingView.swift — The _radioProgressBarImpl stub I left as a
“reference” comment block was still compiled by Swift and referenced
isDraggingSlider, dragPosition, and playbackTime which no longer exist
on NowPlayingView. Removed the entire stub. Also transportControls had
one remaining playbackTime reference — replaced with
audioPlayer.currentTime directly since transport controls genuinely
need the current position for the timeshift enable/disable logic and
don’t cause a body re-eval issue there.
NavidromeWatchApp.swift — syncLibrary() is declared async not async
throws, so try? was redundant. Removed it.
2026-04-11 16:31:24 -07:00
Dallas Groot
9add1e014a quick fix 2026-04-11 16:23:41 -07:00
Dallas Groot
3b56626d6d debugging high cpu usages 2026-04-11 16:15:27 -07:00
Dallas Groot
85c85c2090 memory improvements 2026-04-11 15:37:14 -07:00
Dallas Groot
0730fa11f8 bug fixes 2026-04-11 15:09:06 -07:00
Dallas Groot
f3b9483b23 overhaul
AUDIT-036 — Slider/button fixes (direct Liquid Glass cause)
scheduleFlush() now runs Task { @MainActor } instead of bare Task. The
pendingSaves dictionary is now only ever read/written on the main
thread. Before this fix, a UserDefaults write could race with a slider
didSet, causing values to snap back or write the wrong value — which
is exactly why buttons were switching state unexpectedly.
AUDIT-034 — 60fps idle Canvas (direct Liquid Glass cause)
TimelineView now uses isRenderingActive ? settings.effectiveFPS : 2.0.
When paused or not visible, the Canvas drops from 60fps to 2fps. This
stops the continuous GPU wakeups that were fighting Liquid Glass
gesture tracking, which is why sliders needed multiple attempts.
AUDIT-001 — FFT real-time heap allocation
processFFT no longer allocates any heap memory. The Hann window is
computed once in init(). All four scratch buffers (fftWindow,
fftWindowed, fftRealp/fftImagp, fftMagnitudes) are pre-allocated and
reused every render callback — zero allocations on the real-time audio
thread.
AUDIT-002 — WatchOfflineStore data race
taskToSongId and pendingSongs now protected by a dedicated serial
storeQueue. URLSession delegate reads and main thread writes are
serialised.
AUDIT-019 — URLSession per AsyncCoverArt render
CompanionAPIService() no longer instantiated per render. Companion
cover art URLs now built directly from
CompanionSettings.shared.baseURL — no URLSession created.
AUDIT-020 — Synchronous disk read on main thread
CachedImageLoader now uses memoryOnlyImage (sync, no I/O) for the
first check, then cachedImageAsync (disk read on ioQueue) for the
second. Main thread never blocks on disk I/O.
AUDIT-033 — Lost star/unstar actions offline
Star/unstar now routes through OptimisticActionQueue — actions survive
Tailscale reconnection and are retried automatically.
AUDIT-035 — OptimisticActionQueue flush race
flush() Task is now @MainActor — pendingActions only ever touched on
main thread, no more race between rapid taps and in-flight flushes.
AUDIT-038 — O(n²) deduplication
deduplicateAlbums now O(n) using a frequency dictionary. For 843
albums: ~7.1M string comparisons/second during playback → ~1,700.
AUDIT-026, AUDIT-015 — Duplicate setResourceValue removed, cacheSize
now uses totalSize directly
2026-04-11 11:17:40 -07:00
Dallas Groot
2f65da3ccc quick fix
Companion (main.py):
	•	NAVIDROME_TAGS whitelist — the single source of truth for what tags
survive
	•	enforce_tag_whitelist() — whitelist enforcer, replaces blacklist
approach
	•	All 5 write points updated: apply_tags, apply_tags_dict,
upload-track, upload-tracks, restructure_all
	•	preserve_composer and preserve_lyrics flags on both upload
endpoints (default False)
	•	/library/clean-tags now uses whitelist enforcer
iOS:
	•	UploadMetadata — preserveComposer and preserveLyrics fields
	•	buildMultipartBody — sends both flags as form fields
	•	BatchUploadView — two toggles, both off by default, wired end-to-end
	•	MultiAlbumEditorSheet — full rewrite matching
BatchAlbumEditorSheet: MusicBrainz search, swipe to exclude/include
tracks, Reset button, cover art widget with red glow
2026-04-11 09:40:51 -07:00
Dallas Groot
7a9c837650 batch upload quick fix
Companion (main.py):
	•	NAVIDROME_TAGS whitelist — the single source of truth for what tags
survive
	•	enforce_tag_whitelist() — whitelist enforcer, replaces blacklist
approach
	•	All 5 write points updated: apply_tags, apply_tags_dict,
upload-track, upload-tracks, restructure_all
	•	preserve_composer and preserve_lyrics flags on both upload
endpoints (default False)
	•	/library/clean-tags now uses whitelist enforcer
iOS:
	•	UploadMetadata — preserveComposer and preserveLyrics fields
	•	buildMultipartBody — sends both flags as form fields
	•	BatchUploadView — two toggles, both off by default, wired end-to-end
	•	MultiAlbumEditorSheet — full rewrite matching
BatchAlbumEditorSheet: MusicBrainz search, swipe to exclude/include
tracks, Reset button, cover art widget with red glow
2026-04-11 09:37:22 -07:00
Dallas Groot
92a5a54b8d quick fix 2026-04-11 08:36:32 -07:00
Dallas Groot
d32d63a749 batch updated with cover art support 2026-04-11 08:07:55 -07:00
Dallas Groot
3ea57fa99b quick fix 2026-04-11 02:23:03 -07:00
Dallas Groot
b4d2a5ce92 Merge ; commit '07d125da3c29035a8bca6a992229655d2ff446a0' 2026-04-11 02:22:29 -07:00
Dallas Groot
a9acd65001 quick fix 2026-04-11 02:20:54 -07:00
Dallas Groot
07d125da3c Ignored IDs persisted in UserDefaults
Active tab — all unignored issues, swipe left → “Ignore” (grey)
	•	Ignored tab — all ignored issues shown dimmed, swipe left →
“Restore” (pink) to bring them back
	•	Fix buttons hidden on ignored issues
	•	Ignored IDs persisted in @AppStorage so they survive app restarts
	•	Tab labels show live counts: Active (1) | Ignored (31)
2026-04-11 02:13:06 -07:00
Dallas Groot
d3434f1911 quick fix 2026-04-11 01:59:36 -07:00
Dallas Groot
12dab4cc37 quick fix 2026-04-11 01:52:16 -07:00
Dallas Groot
01baf8792f quick fix 2026-04-11 01:47:30 -07:00
Dallas Groot
9bbe7bafe4 quick fix 2026-04-11 01:44:36 -07:00
Dallas Groot
ef7116d0bf quick fix 2026-04-11 01:41:58 -07:00
Dallas Groot
aae53a17d8 quick fix 2026-04-11 01:39:31 -07:00
Dallas Groot
db9d79f023 safeguards to id3 tagging and mismatch info 2026-04-11 01:36:13 -07:00
Dallas Groot
ffcddc86e2 quick fix 2026-04-11 01:14:38 -07:00
Dallas Groot
15ed38e13b Musicbrainz api add on
The MusicBrainz feature adds:
	•	Magnifying glass in the toolbar — searches by current title + artist
	•	A picker sheet showing up to 10 results with album, year, country,
label, track number
	•	Blue suggestion pills appear below each field that differs from the
MB match
	•	Tap a pill to accept it — auto-checks the field for saving
	•	Green magnifying glass once a match is selected, tap again to change
2026-04-11 01:08:24 -07:00
Dallas Groot
d37dc8fb44 quick fixes 2026-04-11 00:59:53 -07:00
Dallas Groot
f761f65e87 edit-metadata endpoint
iOS — 6 fixes across 5 files:
Models.swift — Song, Album, AlbumWithSongs all now have albumArtist:
String?. CompanionSong.toSong() passes albumArtist.
CompanionAlbum.toAlbum() passes albumArtist to the new field.
TrackEditorView.swift — Album Artist field now initialises from
song.albumArtist ?? song.artist instead of just song.artist.
fetchCompanionDetails no longer requires companion: prefix — it
fetches for all songs using the album name, decodes properly using
CompanionLibraryResponse, and matches by relative_path.
BatchAlbumEditorSheet.swift — initialises from album.albumArtist ??
album.artist.
MultiAlbumEditorSheet.swift — pre-fills from first.albumArtist ??
first.artist.
AlbumDetailView.swift — buildAlbumWithSongs now passes albumArtist
from the companion songs.
Companion — 2 fixes:
apply_tags — now does full FLAC cleanup (removes all Picard legacy
variants) before writing, same as apply_tags_dict already did.
edit-metadata endpoint — no longer calls restructure_file. File
renaming only happens via /bulk-fix. This was the root cause of the
500 errors on compilation tracks with disc numbers.
2026-04-11 00:50:37 -07:00
Dallas Groot
55820fdb38 my music view fix 2026-04-10 20:44:23 -07:00
Dallas Groot
228b186569 updated readme 2026-04-10 20:05:45 -07:00
Dallas Groot
0e6f4852e5 visualizer fix 2026-04-10 19:44:55 -07:00
Dallas Groot
f43fe73769 quick final fix (fingers crossed this time) 2026-04-10 19:29:42 -07:00
Dallas Groot
454d169a11 pause fix on visualizer 2026-04-10 19:20:27 -07:00
Dallas Groot
188a46d49c quickfix build error 2026-04-10 19:09:12 -07:00
Dallas Groot
a4103c8250 bug fixes and ready to upload to testflight 2026-04-10 19:05:45 -07:00
Dallas Groot
ea50bd4537 pip fixes and improvements
•	Group toggles — iPhone / Watch / Companion / Audio, all persistent
via @AppStorage so your filter state survives app restarts
	•	Level toggles — ERROR / WARN / INFO / DEBUG in a single scrollable
row next to the group toggles, color coded red/yellow/cyan/gray
	•	Delta timestamps — each row shows +42ms between it and the previous
entry, so you can see timing without mental math
	•	Pause/Resume — bottom bar button snapshots the current log so you
can read it without it scrolling, while still capturing in background
	•	Auto background/foreground markers — ── Background ── / ──
Foreground ── lines are auto-inserted by NotificationCenter, making
crash correlation much easier
	•	PiP button in toolbar sets logger.isPiP = true, which MainTabView’s
.onChange picks up and activates the existing floating PiP window
MainTabView.swift — minimal changes:
	•	Added .onChange(of: debugLogger.isPiP) to sync the console’s PiP
button to the existing debugPipMode state
	•	Updated debugLogRow to show level dot + marker support, consistent
with the full console
2026-04-10 18:50:31 -07:00
Dallas Groot
9a613ff2ab final cpu fix 2026-04-10 18:23:58 -07:00
Dallas Groot
16fd347b44 fixes hopefully 2026-04-10 18:05:10 -07:00
Dallas Groot
3cfcf026d7 fixes 2026-04-10 17:50:26 -07:00