soulsync/webui/static
BoulderBadgeDad 4d1b9a5639 Artist Sync: guard stale-removal against an unreachable mount + gate it admin-only
The enhanced-tab "Sync" button's stale-removal phase deleted any track whose file
wasn't on disk, with NO guard — so if the music storage was momentarily
unavailable (sleeping NAS, dropped mount, unmounted Docker volume, WSL hiccup),
os.path.exists returned False for EVERY file and one click wiped the whole artist
(tracks + their now-"empty" albums) from the DB. The deep-scan path already had a
50%-stale safety net (#828); this endpoint never got one.

- New core/library/stale_guard.py: is_implausible_stale_removal(missing, total) —
  a tested rule (skip removal when missing > 50% of a >=5-track set), centralised
  so every stale-removal site can share it.
- sync_artist_library: if the guard trips, SKIP removal (delete nothing), return
  removal_skipped + warn; the frontend shows "storage may be offline — skipped"
  instead of silently deleting. Empty-album cleanup now also only runs on the
  non-skipped path and uses `album_id IS NOT NULL` (fixes the NOT IN-with-NULL
  no-op). Frontend also refreshes the view on additions, not just removals.
- @admin_only on the endpoint — it deletes tracks + albums but was ungated, while
  the sibling delete_album endpoint is gated.

Deep scan was already safe (different mechanism: server-diff + its own 50% guard).

Tests: guard unit rules; endpoint skips removal when all files missing (keeps the
tracks), removes only the genuinely-gone few otherwise, and 403s for non-admins.
7 new tests pass.
2026-06-10 19:33:44 -07:00
..
docs
vendor
amazon.svg
api-monitor.js Watchlist page: hued action chips, meta chips, Global Settings reskin (#831 round 3) 2026-06-09 21:22:02 -07:00
artists.png
audiodb.png
auto-sync.js
automation.png
basic-search-v2.css
beatport-ui.js
blocklist.js Blocklist Phase 1 (backfill + API + modal): the Blocklist button on the watchlist page 2026-06-07 15:25:52 -07:00
core.js Auto-Sync tile: light for the WHOLE pipeline, including scheduled auto-sync 2026-06-06 16:32:10 -07:00
dashboard.png
discover-section-controller.js
discover.js
discover.png
docs-images-needed.md
docs.js
downloads.js Fix: a '/' in a song title was treated as a path separator (#835) 2026-06-10 16:29:09 -07:00
enrichment-manager.js Show "Running (Spotify Free)" instead of "rate limited" while the worker bridges 2026-06-05 22:08:07 -07:00
enrichment.js Expired Download Cleaner: retention-based cleanup of watchlist/playlist downloads (Boulder) 2026-06-07 22:06:56 -07:00
explorer.png
favicon.png
help.png
helper.js Release 2.6.9: version bump + docker-publish default + What's New changelog 2026-06-09 23:03:49 -07:00
hydrabase.png
import.png
init.js Profiles: visual revamp of the Manage Profiles modal 2026-06-10 16:17:04 -07:00
library.js Artist Sync: guard stale-removal against an unreachable mount + gate it admin-only 2026-06-10 19:33:44 -07:00
library.png
manifest.json
media-player.js #809 review follow-up: crossfade preload also streams un-mounted Navidrome tracks 2026-06-07 13:55:58 -07:00
mobile.css Mobile: notification panel fits the screen (override inline JS positioning) 2026-06-04 20:50:33 -07:00
my-accounts.js Profiles: Tidal logo on a light disc (dark logo) in My Accounts 2026-06-10 13:49:25 -07:00
origin-history.js Watchlist: show WHICH tracks a scan found/added + group Download Origins (#831) 2026-06-09 20:14:02 -07:00
pages-extra.js Fix: Find & Add library search buried exact matches (case-sensitive ordering) 2026-06-10 17:23:13 -07:00
particles.js
placeholder-album.png
pwa-icon-192.png
pwa-icon-512.png
search.js Multi-artist tags: Search → Download Now finally knows its metadata source (Netti93) 2026-06-09 17:20:16 -07:00
search.png
service-switch.js Profiles: revert Service Status modal to admin-only 2026-06-10 11:43:22 -07:00
settings.js Profiles: remove the admin Connected Accounts manager (pivot to pure self-auth) 2026-06-10 11:48:31 -07:00
settings.png
setup-wizard.css
setup-wizard.js
shared-helpers.js Profiles: label the no-auth Spotify composite everywhere it's shown 2026-06-10 10:41:09 -07:00
shell-bridge.js
stats-automations.js Downloads: fix collapsed-batch overflow (#814) + Retry Failed result feedback (#815) 2026-06-07 23:53:18 -07:00
style.css Profiles: visual revamp of the Manage Profiles modal 2026-06-10 16:17:04 -07:00
sw.js
sync-lastfm.js
sync-listenbrainz.js
sync-services.js Downloads: fix collapsed-batch overflow (#814) + Retry Failed result feedback (#815) 2026-06-07 23:53:18 -07:00
sync-soulsync-discovery.js
sync-spotify.js
sync.png
track-detail.js
trans.png
trans2.png
watchlist-history.js Watchlist: bespoke live scan deck + persistent per-run Scan History (#831 round 2) 2026-06-09 20:35:16 -07:00
whisoul.png
wishlist-tools.js Fix: "Discovery state not found" when fixing a match after restart/import (#843) 2026-06-10 18:49:32 -07:00
worker-orbs.js Dashboard animations: GPU pass — same visuals, compositor-only where possible 2026-06-06 16:05:05 -07:00