fix: push wishlist pagination to SQL
The wishlist list endpoint previously loaded and JSON-decoded the full wishlist, filtered by category in Python, then sliced in memory. Cost grew linearly with wishlist size on every page request. get_wishlist_tracks now accepts offset and category parameters, both applied in SQL via LIMIT/OFFSET and json_extract. get_wishlist_count also accepts category so COUNT(*) matches the filtered page. The API endpoint uses these to return only the requested page. Backward compatible: other callers (core/wishlist_service) pass no offset/category and still receive the full list.
This commit is contained in:
parent
133af45199
commit
327275a3fa
2 changed files with 49 additions and 33 deletions
|
|
@ -19,21 +19,19 @@ def register_routes(bp):
|
|||
fields = parse_fields(request)
|
||||
profile_id = parse_profile_id(request)
|
||||
|
||||
category_filter = category if category in ("singles", "albums") else None
|
||||
|
||||
try:
|
||||
from database.music_database import get_database
|
||||
db = get_database()
|
||||
raw_tracks = db.get_wishlist_tracks(profile_id=profile_id)
|
||||
|
||||
# Category filter
|
||||
if category in ("singles", "albums"):
|
||||
raw_tracks = [
|
||||
t for t in raw_tracks
|
||||
if _track_category(t) == category
|
||||
]
|
||||
|
||||
total = len(raw_tracks)
|
||||
start = (page - 1) * limit
|
||||
tracks = raw_tracks[start:start + limit]
|
||||
offset = (page - 1) * limit
|
||||
tracks = db.get_wishlist_tracks(
|
||||
profile_id=profile_id,
|
||||
category=category_filter,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
total = db.get_wishlist_count(profile_id=profile_id, category=category_filter)
|
||||
|
||||
return api_success(
|
||||
{"tracks": [serialize_wishlist_track(t, fields) for t in tracks]},
|
||||
|
|
@ -104,15 +102,3 @@ def register_routes(bp):
|
|||
return api_error("NOT_AVAILABLE", "Wishlist processing function not available.", 501)
|
||||
except Exception as e:
|
||||
return api_error("WISHLIST_ERROR", str(e), 500)
|
||||
|
||||
|
||||
def _track_category(track):
|
||||
"""Determine if a wishlist track is a single or album track."""
|
||||
album_type = ""
|
||||
if isinstance(track, dict):
|
||||
sd = track.get("spotify_data", {})
|
||||
if isinstance(sd, dict):
|
||||
album = sd.get("album", {})
|
||||
if isinstance(album, dict):
|
||||
album_type = album.get("album_type", "")
|
||||
return "albums" if album_type == "album" else "singles"
|
||||
|
|
|
|||
|
|
@ -6632,8 +6632,14 @@ class MusicDatabase:
|
|||
logger.error(f"Error removing track from wishlist: {e}")
|
||||
return False
|
||||
|
||||
def get_wishlist_tracks(self, limit: Optional[int] = None, profile_id: int = 1) -> List[Dict[str, Any]]:
|
||||
"""Get all tracks in the wishlist for the given profile, ordered by date added (oldest first for retry priority)"""
|
||||
def get_wishlist_tracks(self, limit: Optional[int] = None, profile_id: int = 1,
|
||||
offset: int = 0, category: Optional[str] = None) -> List[Dict[str, Any]]:
|
||||
"""Get tracks in the wishlist for the given profile, ordered by date added
|
||||
(oldest first for retry priority).
|
||||
|
||||
Supports SQL-level pagination via limit/offset and optional category
|
||||
filtering (singles vs albums) pushed down to SQL using json_extract.
|
||||
"""
|
||||
try:
|
||||
with self._get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
|
|
@ -6643,12 +6649,26 @@ class MusicDatabase:
|
|||
last_attempted, date_added, source_type, source_info
|
||||
FROM wishlist_tracks
|
||||
WHERE profile_id = ?
|
||||
ORDER BY date_added
|
||||
"""
|
||||
|
||||
params = [profile_id]
|
||||
params: List[Any] = [profile_id]
|
||||
|
||||
if category == "albums":
|
||||
query += " AND json_extract(spotify_data, '$.album.album_type') = 'album'"
|
||||
elif category == "singles":
|
||||
query += (
|
||||
" AND (json_extract(spotify_data, '$.album.album_type') IS NULL"
|
||||
" OR json_extract(spotify_data, '$.album.album_type') != 'album')"
|
||||
)
|
||||
|
||||
query += " ORDER BY date_added"
|
||||
|
||||
if limit:
|
||||
query += f" LIMIT {limit}"
|
||||
query += " LIMIT ?"
|
||||
params.append(int(limit))
|
||||
if offset:
|
||||
query += " OFFSET ?"
|
||||
params.append(int(offset))
|
||||
|
||||
cursor.execute(query, params)
|
||||
rows = cursor.fetchall()
|
||||
|
|
@ -6679,7 +6699,7 @@ class MusicDatabase:
|
|||
except Exception as e:
|
||||
logger.error(f"Error getting wishlist tracks: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def update_wishlist_retry(self, spotify_track_id: str, success: bool, error_message: str = None, profile_id: int = 1) -> bool:
|
||||
"""Update retry count and status for a wishlist track"""
|
||||
try:
|
||||
|
|
@ -6706,12 +6726,22 @@ class MusicDatabase:
|
|||
logger.error(f"Error updating wishlist retry status: {e}")
|
||||
return False
|
||||
|
||||
def get_wishlist_count(self, profile_id: int = 1) -> int:
|
||||
"""Get the total number of tracks in the wishlist for the given profile"""
|
||||
def get_wishlist_count(self, profile_id: int = 1, category: Optional[str] = None) -> int:
|
||||
"""Get the total number of tracks in the wishlist for the given profile,
|
||||
optionally filtered by category ('singles' or 'albums')."""
|
||||
try:
|
||||
with self._get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT COUNT(*) FROM wishlist_tracks WHERE profile_id = ?", (profile_id,))
|
||||
query = "SELECT COUNT(*) FROM wishlist_tracks WHERE profile_id = ?"
|
||||
params: List[Any] = [profile_id]
|
||||
if category == "albums":
|
||||
query += " AND json_extract(spotify_data, '$.album.album_type') = 'album'"
|
||||
elif category == "singles":
|
||||
query += (
|
||||
" AND (json_extract(spotify_data, '$.album.album_type') IS NULL"
|
||||
" OR json_extract(spotify_data, '$.album.album_type') != 'album')"
|
||||
)
|
||||
cursor.execute(query, params)
|
||||
result = cursor.fetchone()
|
||||
return result[0] if result else 0
|
||||
except Exception as e:
|
||||
|
|
|
|||
Loading…
Reference in a new issue