From 4dc70b3611d0845f9ce0d3ae27c65f1c3d712705 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Tue, 26 May 2026 17:13:36 -0700 Subject: [PATCH] Rolling LB mirrors: also fire on skipped + bulk catch-all in cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two paths were leaving rolling mirror placeholders uncreated: 1. ``_update_playlist`` short-circuits with status "skipped" when the cached track count matches the API result (the smart- comparison fast path). The Phase 1c.2.1 ``_ensure_rolling_series_mirror`` call sat after the short-circuit, so any user whose LB cache was already up-to-date got zero rolling placeholders inserted — their Auto-Sync sidebar showed no ListenBrainz group after refresh. 2. First-time install of the rolling-mirror code on top of an existing LB cache: every per-playlist call goes "skipped" because nothing has changed, so even with fix #1 the user needs a per-playlist trigger to populate. No good. Fix: - ``_update_playlist`` now runs ``_ensure_rolling_series_mirror`` on the skip path too (with an explicit ``conn.commit()`` since the insert needs to land before the connection closes). - ``_cleanup_old_playlists`` gains ``_ensure_rolling_mirrors_from_cache`` — a one-shot bulk pass that walks every cached LB title and ensures the matching rolling mirror exists. Cheap (single SELECT + idempotent INSERT OR IGNORE per row) and catches the first-run + skipped-everything cases. --- core/listenbrainz_manager.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/core/listenbrainz_manager.py b/core/listenbrainz_manager.py index bd48c352..ddae7ae8 100644 --- a/core/listenbrainz_manager.py +++ b/core/listenbrainz_manager.py @@ -166,6 +166,12 @@ class ListenBrainzManager: # Skip if track count hasn't changed (playlist content likely the same) if db_track_count == track_count: logger.debug(f"Playlist '{title}' unchanged, skipping") + # Even on the skip path, make sure the rolling-series + # mirror placeholder exists — otherwise users whose LB + # cache never has "changed" updates would never see the + # rolling Auto-Sync entries appear. + self._ensure_rolling_series_mirror(cursor, title) + conn.commit() conn.close() return "skipped" @@ -223,6 +229,27 @@ class ListenBrainzManager: return result_type + def _ensure_rolling_mirrors_from_cache(self, cursor): + """Walk every cached LB playlist row + ensure its rolling + series mirror exists. Catch-all that runs regardless of which + ``_update_playlist`` paths fired (skipped vs updated vs new). + + Cheap — one SELECT + per-row helper call, helper is + idempotent INSERT OR IGNORE.""" + try: + cursor.execute( + """ + SELECT DISTINCT title FROM listenbrainz_playlists + WHERE profile_id = ? + """, + (self.profile_id,), + ) + titles = [row[0] for row in cursor.fetchall() if row[0]] + for title in titles: + self._ensure_rolling_series_mirror(cursor, title) + except Exception as exc: + logger.debug(f"Bulk rolling-mirror ensure skipped: {exc}") + def _ensure_rolling_series_mirror(self, cursor, playlist_title: str): """Upsert a placeholder ``mirrored_playlists`` row for the rolling series this title belongs to. @@ -467,6 +494,13 @@ class ListenBrainzManager: # Consolidate legacy per-week / per-year LB series mirrors into # the new rolling series mirrors (Phase 1c.2.1). self._cleanup_per_period_series_mirrors(cursor) + # Safety net: ensure rolling mirror placeholders exist for every + # series with at least one cached LB playlist row. Catches the + # case where every ``_update_playlist`` call took the "skipped" + # short-circuit (unchanged track count) and so the ensure-hook + # in the per-playlist path never fired on first run after the + # rolling feature shipped. + self._ensure_rolling_mirrors_from_cache(cursor) # For each playlist type, keep only the N most recent. # Last.fm radios are per-seed-track snapshots that don't update