add spotify liked songs playlist

pull/80/head
Broque Thomas 5 months ago
parent 781d869f74
commit b16318f37e

@ -288,11 +288,73 @@ class SpotifyClient:
logger.info(f"Retrieved {len(playlists)} total playlist metadata")
return playlists
except Exception as e:
logger.error(f"Error fetching user playlists metadata: {e}")
return []
@rate_limited
def get_saved_tracks_count(self) -> int:
"""Get the total count of user's saved/liked songs without fetching all tracks"""
if not self.is_authenticated():
logger.error("Not authenticated with Spotify")
return 0
try:
# Just fetch first page to get the total count
results = self.sp.current_user_saved_tracks(limit=1)
if results and 'total' in results:
total_count = results['total']
logger.info(f"User has {total_count} saved tracks")
return total_count
return 0
except Exception as e:
logger.error(f"Error fetching saved tracks count: {e}")
return 0
@rate_limited
def get_saved_tracks(self) -> List[Track]:
"""Fetch all user's saved/liked songs from Spotify"""
if not self.is_authenticated():
logger.error("Not authenticated with Spotify")
return []
tracks = []
try:
limit = 50 # Maximum allowed by Spotify API
offset = 0
total_fetched = 0
while True:
results = self.sp.current_user_saved_tracks(limit=limit, offset=offset)
if not results or 'items' not in results:
break
batch_count = 0
for item in results['items']:
if item['track'] and item['track']['id']:
track = Track.from_spotify_track(item['track'])
tracks.append(track)
batch_count += 1
total_fetched += batch_count
logger.info(f"Retrieved {batch_count} saved tracks in batch (offset {offset}), total: {total_fetched}")
# Check if we've fetched all saved tracks
if len(results['items']) < limit or not results.get('next'):
break
offset += limit
logger.info(f"Retrieved {len(tracks)} total saved tracks")
return tracks
except Exception as e:
logger.error(f"Error fetching saved tracks: {e}")
return []
@rate_limited
def _get_playlist_tracks(self, playlist_id: str) -> List[Track]:
if not self.is_authenticated():

@ -11606,24 +11606,26 @@ def get_spotify_playlists():
try:
playlists = spotify_client.get_user_playlists_metadata_only()
sync_statuses = _load_sync_status_file()
playlist_data = []
# Add regular playlists first
for p in playlists:
status_info = sync_statuses.get(p.id, {})
sync_status = "Never Synced"
# Handle snapshot_id safely - may not exist in core Playlist class
playlist_snapshot = getattr(p, 'snapshot_id', '')
print(f"🔍 Processing playlist: {p.name} (ID: {p.id})")
print(f" - Playlist snapshot: '{playlist_snapshot}'")
print(f" - Status info: {status_info}")
if 'last_synced' in status_info:
stored_snapshot = status_info.get('snapshot_id')
last_sync_time = datetime.fromisoformat(status_info['last_synced']).strftime('%b %d, %H:%M')
print(f" - Stored snapshot: '{stored_snapshot}'")
print(f" - Snapshots match: {playlist_snapshot == stored_snapshot}")
if playlist_snapshot != stored_snapshot:
sync_status = f"Last Sync: {last_sync_time}"
print(f" - Result: Needs Sync (showing: {sync_status})")
@ -11635,11 +11637,43 @@ def get_spotify_playlists():
playlist_data.append({
"id": p.id, "name": p.name, "owner": p.owner,
"track_count": p.total_tracks,
"track_count": p.total_tracks,
"image_url": getattr(p, 'image_url', None),
"sync_status": sync_status,
"sync_status": sync_status,
"snapshot_id": playlist_snapshot
})
# Add virtual "Liked Songs" playlist at the END (just count, no full fetch)
try:
liked_songs_count = spotify_client.get_saved_tracks_count()
if liked_songs_count > 0:
liked_songs_id = "spotify:liked-songs"
status_info = sync_statuses.get(liked_songs_id, {})
sync_status = "Never Synced"
if 'last_synced' in status_info:
last_sync_time = datetime.fromisoformat(status_info['last_synced']).strftime('%b %d, %H:%M')
sync_status = f"Synced: {last_sync_time}"
# Get user info for owner name
user_info = spotify_client.get_user_info()
owner_name = user_info.get('display_name', 'You') if user_info else 'You'
# Add Liked Songs as LAST playlist
playlist_data.append({
"id": liked_songs_id,
"name": "Liked Songs",
"owner": owner_name,
"track_count": liked_songs_count,
"image_url": None, # Spotify doesn't provide image for Liked Songs
"sync_status": sync_status,
"snapshot_id": "" # Liked Songs doesn't have a snapshot_id
})
print(f"🔍 Added virtual 'Liked Songs' playlist with {liked_songs_count} tracks (count only)")
except Exception as liked_error:
print(f"⚠️ Failed to add Liked Songs playlist: {liked_error}")
# Don't fail the entire request if Liked Songs fails
return jsonify(playlist_data)
except Exception as e:
return jsonify({"error": str(e)}), 500
@ -11650,6 +11684,28 @@ def get_playlist_tracks(playlist_id):
if not spotify_client or not spotify_client.is_authenticated():
return jsonify({"error": "Spotify not authenticated."}), 401
try:
# Handle special "Liked Songs" virtual playlist
if playlist_id == "spotify:liked-songs":
saved_tracks = spotify_client.get_saved_tracks()
user_info = spotify_client.get_user_info()
owner_name = user_info.get('display_name', 'You') if user_info else 'You'
# Create virtual playlist dict for Liked Songs
playlist_dict = {
'id': 'spotify:liked-songs',
'name': 'Liked Songs',
'description': 'Your saved tracks on Spotify',
'owner': owner_name,
'public': False,
'collaborative': False,
'track_count': len(saved_tracks),
'image_url': None,
'snapshot_id': '',
'tracks': [{'id': t.id, 'name': t.name, 'artists': t.artists, 'album': t.album, 'duration_ms': t.duration_ms, 'popularity': t.popularity} for t in saved_tracks]
}
return jsonify(playlist_dict)
# Handle regular playlists
# This reuses the robust track fetching logic from your GUI's sync.py
full_playlist = spotify_client.get_playlist_by_id(playlist_id)
if not full_playlist:

Loading…
Cancel
Save