Fix per-profile ListenBrainz playlist cache scoping and stale data recovery
This commit is contained in:
parent
8f0b9518bc
commit
264e696fe3
3 changed files with 56 additions and 2 deletions
|
|
@ -399,6 +399,35 @@ class ListenBrainzManager:
|
|||
conn.close()
|
||||
return playlists
|
||||
|
||||
def get_playlist_type(self, playlist_mbid: str) -> str:
|
||||
"""Get the playlist_type for a cached playlist, or None if not found"""
|
||||
conn = self._get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"SELECT playlist_type FROM listenbrainz_playlists WHERE playlist_mbid = ? AND profile_id = ?",
|
||||
(playlist_mbid, self.profile_id)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
conn.close()
|
||||
return row[0] if row else None
|
||||
|
||||
def delete_cached_playlist(self, playlist_mbid: str):
|
||||
"""Delete a cached playlist and its tracks (CASCADE handles tracks via FK)"""
|
||||
conn = self._get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
# Delete tracks first (SQLite FK CASCADE requires PRAGMA foreign_keys=ON)
|
||||
cursor.execute("""
|
||||
DELETE FROM listenbrainz_tracks WHERE playlist_id IN (
|
||||
SELECT id FROM listenbrainz_playlists WHERE playlist_mbid = ? AND profile_id = ?
|
||||
)
|
||||
""", (playlist_mbid, self.profile_id))
|
||||
cursor.execute(
|
||||
"DELETE FROM listenbrainz_playlists WHERE playlist_mbid = ? AND profile_id = ?",
|
||||
(playlist_mbid, self.profile_id)
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def get_cached_tracks(self, playlist_mbid: str) -> List[Dict]:
|
||||
"""Get cached tracks for a playlist from database"""
|
||||
conn = self._get_db_connection()
|
||||
|
|
|
|||
|
|
@ -2328,6 +2328,17 @@ class MusicDatabase:
|
|||
cursor.execute("DROP TABLE listenbrainz_playlists")
|
||||
cursor.execute("ALTER TABLE listenbrainz_playlists_new RENAME TO listenbrainz_playlists")
|
||||
|
||||
# Clean up playlists that lost their tracks during table recreation
|
||||
# (track playlist_id foreign keys may reference stale IDs).
|
||||
# This forces a fresh re-fetch from ListenBrainz on next page load.
|
||||
cursor.execute("""
|
||||
DELETE FROM listenbrainz_playlists
|
||||
WHERE id NOT IN (SELECT DISTINCT playlist_id FROM listenbrainz_tracks)
|
||||
""")
|
||||
cleaned = cursor.rowcount
|
||||
if cleaned:
|
||||
logger.info(f"Cleaned up {cleaned} stale playlists (will re-fetch from ListenBrainz)")
|
||||
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_lb_playlists_profile ON listenbrainz_playlists (profile_id)")
|
||||
|
||||
cursor.execute("""
|
||||
|
|
|
|||
|
|
@ -32009,7 +32009,8 @@ def get_discover_genre_playlist(genre_name):
|
|||
# ===============================
|
||||
|
||||
def _get_profile_lb_manager():
|
||||
"""Create a profile-aware ListenBrainzManager for the current user"""
|
||||
"""Create a profile-aware ListenBrainzManager for the current user.
|
||||
Always uses the actual profile_id so each profile has its own playlist cache."""
|
||||
from core.listenbrainz_manager import ListenBrainzManager
|
||||
profile_id = get_current_profile_id()
|
||||
token, base_url, username, source = _get_lb_credentials_for_profile(profile_id)
|
||||
|
|
@ -32089,11 +32090,24 @@ def get_listenbrainz_collaborative():
|
|||
|
||||
@app.route('/api/discover/listenbrainz/playlist/<playlist_mbid>', methods=['GET'])
|
||||
def get_listenbrainz_playlist_tracks(playlist_mbid):
|
||||
"""Get tracks from a specific ListenBrainz playlist (from cache)"""
|
||||
"""Get tracks from a specific ListenBrainz playlist (from cache, with on-demand refresh)"""
|
||||
try:
|
||||
lb_manager, username, source = _get_profile_lb_manager()
|
||||
tracks = lb_manager.get_cached_tracks(playlist_mbid)
|
||||
|
||||
if not tracks:
|
||||
# Cache miss or stale entry with no tracks — try fetching from LB API
|
||||
if lb_manager.client.is_authenticated():
|
||||
print(f"🔄 Cache miss for playlist {playlist_mbid}, fetching from ListenBrainz...")
|
||||
# Remove stale playlist row (if any) so _update_playlist doesn't
|
||||
# skip due to matching track_count with 0 actual tracks
|
||||
existing_type = lb_manager.get_playlist_type(playlist_mbid) or 'created_for'
|
||||
lb_manager.delete_cached_playlist(playlist_mbid)
|
||||
full_playlist = lb_manager.client.get_playlist_details(playlist_mbid)
|
||||
if full_playlist:
|
||||
lb_manager._update_playlist(full_playlist, existing_type)
|
||||
tracks = lb_manager.get_cached_tracks(playlist_mbid)
|
||||
|
||||
if not tracks:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
|
|
|
|||
Loading…
Reference in a new issue