Fix Explore tab checkmark badge not persisting after refresh

Explored status was stored only in frontend memory; on reload the badge
disappeared because the API never returned it. Added explored_at column
to mirrored_playlists (auto-migrated), written when build-tree completes,
and read back via SELECT * so the badge survives page refreshes.
This commit is contained in:
Broque Thomas 2026-04-15 12:09:41 -07:00
parent 211c7b451a
commit aac75d6a3b
3 changed files with 33 additions and 3 deletions

View file

@ -517,6 +517,9 @@ class MusicDatabase:
""")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_arh_automation_id ON automation_run_history(automation_id)")
# Add explored_at to mirrored_playlists (migration)
self._add_mirrored_playlist_explored_column(cursor)
# Add notification columns to automations (migration)
self._add_automation_notify_columns(cursor)
self._add_automation_system_column(cursor)
@ -684,6 +687,17 @@ class MusicDatabase:
logger.error(f"Error initializing database: {e}")
raise
def _add_mirrored_playlist_explored_column(self, cursor):
"""Add explored_at column to mirrored_playlists to persist explore badge."""
try:
cursor.execute("PRAGMA table_info(mirrored_playlists)")
cols = [c[1] for c in cursor.fetchall()]
if 'explored_at' not in cols:
cursor.execute("ALTER TABLE mirrored_playlists ADD COLUMN explored_at TIMESTAMP DEFAULT NULL")
logger.info("Added explored_at column to mirrored_playlists table")
except Exception as e:
logger.error(f"Error adding explored_at column to mirrored_playlists: {e}")
def _add_automation_notify_columns(self, cursor):
"""Add notification and result columns to automations table."""
try:
@ -10490,6 +10504,21 @@ class MusicDatabase:
logger.error(f"Error getting mirrored playlists: {e}")
return []
def mark_mirrored_playlist_explored(self, playlist_id: int) -> bool:
"""Set explored_at to now for a mirrored playlist."""
try:
with self._get_connection() as conn:
cursor = conn.cursor()
cursor.execute(
"UPDATE mirrored_playlists SET explored_at = CURRENT_TIMESTAMP WHERE id = ?",
(playlist_id,)
)
conn.commit()
return cursor.rowcount > 0
except Exception as e:
logger.error(f"Error marking playlist {playlist_id} as explored: {e}")
return False
def get_mirrored_playlist(self, playlist_id: int) -> Optional[Dict]:
"""Return a single mirrored playlist by id."""
try:

View file

@ -49288,6 +49288,7 @@ def playlist_explorer_build_tree():
if idx < len(artist_groups) - 1:
time.sleep(0.2)
get_database().mark_mirrored_playlist_explored(playlist_id)
yield json.dumps({"type": "complete", "total_artists": len(artist_groups), "total_albums": total_albums}) + '\n'
return Response(generate(), mimetype='application/x-ndjson', headers={

View file

@ -71743,7 +71743,7 @@ function explorerRenderPickerCards(source) {
const isReady = pct >= 50;
const isActive = _explorer.playlistId === p.id;
const isFullyDiscovered = pct === 100;
const wasExplored = p.explored || false;
const wasExplored = !!(p.explored_at || p.explored);
const wishlisted = p.wishlisted_count || 0;
const inLibrary = p.in_library_count || 0;
@ -71967,10 +71967,10 @@ async function explorerBuildTree() {
if (progress) progress.style.display = 'none';
_explorerUpdateCount();
// Mark playlist as explored (persists in session for badge display)
// Mark playlist as explored (server persists via explored_at; update local copy too)
const exploredPl = _explorer._playlists.find(p => p.id === playlistId);
if (exploredPl) {
exploredPl.explored = true;
exploredPl.explored_at = new Date().toISOString();
// Update card badge without full re-render
const card = document.querySelector(`.explorer-picker-card[data-id="${playlistId}"]`);
if (card) {