From b60a882dabc1399a044a5e9195d85401b1700892 Mon Sep 17 00:00:00 2001 From: Kamil Date: Wed, 27 Nov 2024 17:20:47 +0000 Subject: [PATCH] Better Errorhandling in case of spotify api errors Addresses issue #20 --- app/functions.py | 33 +++++++++++--------- app/jellyfin_routes.py | 10 ++++-- app/routes.py | 51 ++++++++++++++++++++----------- app/tasks.py | 51 ++++++++++++++++--------------- templates/items.html | 11 +++++++ templates/jellyfin_playlists.html | 12 +++++++- 6 files changed, 109 insertions(+), 59 deletions(-) diff --git a/app/functions.py b/app/functions.py index 3642417..7eed5ac 100644 --- a/app/functions.py +++ b/app/functions.py @@ -5,6 +5,8 @@ from functools import wraps from celery.result import AsyncResult from app.tasks import download_missing_tracks,check_for_playlist_updates, update_all_playlists_track_status, update_jellyfin_id_for_downloaded_tracks from jellyfin.objects import PlaylistMetadata +from spotipy.exceptions import SpotifyException + import re TASK_STATUS = { @@ -129,12 +131,9 @@ def get_cached_spotify_playlist(playlist_id): :param playlist_id: The Spotify playlist ID. :return: Playlist data as a dictionary, or None if an error occurs. """ - try: - playlist_data = sp.playlist(playlist_id) # Fetch data from Spotify API - return playlist_data - except Exception as e: - app.logger.error(f"Error fetching playlist {playlist_id} from Spotify: {str(e)}") - return None + playlist_data = sp.playlist(playlist_id) # Fetch data from Spotify API + return playlist_data + @cache.memoize(timeout=3600*24*10) def get_cached_spotify_track(track_id): """ @@ -180,15 +179,21 @@ def prepArtistData(data): -def getFeaturedPlaylists(country,offset): - playlists_data = sp.featured_playlists(country=country, limit=16, offset=offset) - - return prepPlaylistData(playlists_data), playlists_data['playlists']['total'],'Featured Playlists' +def getFeaturedPlaylists(country: str, offset: int): + try: + playlists_data = sp.featured_playlists(country=country, limit=16, offset=offset) + return prepPlaylistData(playlists_data), playlists_data['playlists']['total'], 'Featured Playlists' + except SpotifyException as e: + app.logger.error(f"Spotify API error in getFeaturedPlaylists: {e}") + return [], e, f'Error: Could not load featured playlists. Please try again later. This is most likely due to an Error in the Spotify API or an rate limit has been reached.' -def getCategoryPlaylists(category,offset): - playlists_data = sp.category_playlists(category_id=category, limit=16, offset=offset) - - return prepPlaylistData(playlists_data), playlists_data['playlists']['total'],f"Category {playlists_data['message']}" +def getCategoryPlaylists(category: str, offset: int): + try: + playlists_data = sp.category_playlists(category_id=category, limit=16, offset=offset) + return prepPlaylistData(playlists_data), playlists_data['playlists']['total'], f"Category {playlists_data['message']}" + except SpotifyException as e: + app.logger.error(f"Spotify API error in getCategoryPlaylists: {e}") + return [], e, 'Error: Could not load category playlists. Please try again later. This is most likely due to an Error in the Spotify API or an rate limit has been reached.' def getCategories(country,offset): categories_data = sp.categories(limit=16, offset= offset) diff --git a/app/jellyfin_routes.py b/app/jellyfin_routes.py index 90978d0..2190c53 100644 --- a/app/jellyfin_routes.py +++ b/app/jellyfin_routes.py @@ -3,7 +3,7 @@ from flask import Flask, jsonify, render_template, request, redirect, url_for, s from sqlalchemy import insert from app import app, db, jellyfin, functions, device_id,sp from app.models import Playlist,Track, playlist_tracks - +from spotipy.exceptions import SpotifyException from jellyfin.objects import PlaylistMetadata @@ -38,11 +38,15 @@ def jellyfin_playlists(): prepared_data = functions.prepPlaylistData(spotify_data) return render_template('jellyfin_playlists.html', playlists=prepared_data) - + except SpotifyException as e: + app.logger.error(f"Error fetching monitored playlists: {e}") + error_data, error_message = e, f'Could not retrieve monitored Playlists. Please try again later. This is most likely due to an Error in the Spotify API or an rate limit has been reached.' + return render_template('jellyfin_playlists.html', playlists=functions.prepPlaylistData({'playlists': {'items': []}}), error_message=error_message,error_data = error_data) + except Exception as e: app.logger.error(f"Error in /jellyfin_playlists route: {str(e)}") flash('An error occurred while fetching playlists.', 'danger') - return render_template('jellyfin_playlists.html', playlists=functions.prepPlaylistData({'playlists': {'items': []}})) + return render_template('jellyfin_playlists.html', playlists=functions.prepPlaylistData({'playlists': {'items': []}}), error_message='An error occurred while fetching playlists.',error_data = e) @app.route('/addplaylist', methods=['POST']) diff --git a/app/routes.py b/app/routes.py index 63c1ad1..a346f7d 100644 --- a/app/routes.py +++ b/app/routes.py @@ -3,6 +3,7 @@ from app import app, db, functions, sp, jellyfin, celery, jellyfin_admin_token, from app.models import JellyfinUser,Playlist,Track from celery.result import AsyncResult from .version import __version__ +from spotipy.exceptions import SpotifyException @app.context_processor def add_context(): @@ -140,32 +141,46 @@ def loaditems(): limit = 20 # Define a limit for pagination additional_query = '' items_subtitle = '' - + error_message = None # Placeholder for error messages + error_data = '' if request.path == '/playlists/monitored': - # Step 1: Query the database for monitored playlists - db_playlists = db.session.query(Playlist).offset(offset).limit(limit).all() - max_items = db.session.query(Playlist).count() - - # Collect Spotify Playlist IDs from the database - spotify_playlist_ids = [playlist.spotify_playlist_id for playlist in db_playlists] - - spotify_data = functions.get_cached_spotify_playlists(tuple(spotify_playlist_ids)) - - # Step 3: Pass the Spotify data to prepPlaylistData for processing - data = functions.prepPlaylistData(spotify_data) - items_title = "Monitored Playlists" - items_subtitle = "This playlists are already monitored by the Server, if you add one of these to your Jellyfin account, they will be available immediately." + try: + db_playlists = db.session.query(Playlist).offset(offset).limit(limit).all() + max_items = db.session.query(Playlist).count() + spotify_playlist_ids = [playlist.spotify_playlist_id for playlist in db_playlists] + spotify_data = functions.get_cached_spotify_playlists(tuple(spotify_playlist_ids)) + data = functions.prepPlaylistData(spotify_data) + items_title = "Monitored Playlists" + items_subtitle = "These playlists are already monitored by the Server. If you add one to your Jellyfin account, they will be available immediately." + except SpotifyException as e: + app.logger.error(f"Error fetching monitored playlists: {e}") + data, max_items, items_title = [], e, f'Could not retrieve monitored Playlists. Please try again later. This is most likely due to an Error in the Spotify API or an rate limit has been reached.' + error_message = items_title + error_data = max_items elif request.path == '/playlists': cat = request.args.get('cat', None) if cat is not None: data, max_items, items_title = functions.getCategoryPlaylists(category=cat, offset=offset) + if not data: # Check if data is empty + error_message = items_title # Set the error message from the function + error_data = max_items additional_query += f"&cat={cat}" else: data, max_items, items_title = functions.getFeaturedPlaylists(country=country, offset=offset) + if not data: # Check if data is empty + error_message = items_title # Set the error message from the function + error_data = max_items elif request.path == '/categories': - data, max_items, items_title = functions.getCategories(country=country, offset=offset) + try: + data, max_items, items_title = functions.getCategories(country=country, offset=offset) + except Exception as e: + app.logger.error(f"Error fetching categories: {e}") + data, max_items, items_title = [], e, f'Error: Could not load categories. Please try again later. ' + error_message = items_title + error_data = max_items + next_offset = offset + len(data) total_items = max_items @@ -175,8 +190,10 @@ def loaditems(): 'total_items': total_items, 'endpoint': request.path, 'items_title': items_title, - 'items_subtitle' : items_subtitle, - 'additional_query': additional_query + 'items_subtitle': items_subtitle, + 'additional_query': additional_query, + 'error_message': error_message, # Pass error message to the template + 'error_data': error_data, # Pass error message to the template } if request.headers.get('HX-Request'): # Check if the request is from HTMX diff --git a/app/tasks.py b/app/tasks.py index 7fd7bf7..2256ca2 100644 --- a/app/tasks.py +++ b/app/tasks.py @@ -127,31 +127,34 @@ def download_missing_tracks(self): processed_tracks+=1 continue - # region search with fingerprinting - preview_url = spotify_track.get('preview_url') - if not preview_url: - app.logger.error(f"Preview URL not found for track {track.name}.") - # Decide whether to skip or proceed to download - # For now, we'll proceed to download - else: - # Get the list of Spotify artist names - spotify_artists = [artist['name'] for artist in spotify_track['artists']] - - # Perform the search in Jellyfin - match_found, jellyfin_file_path = jellyfin.search_track_in_jellyfin( - session_token=jellyfin_admin_token, - preview_url=preview_url, - song_name=track.name, - artist_names=spotify_artists - ) - if match_found: - app.logger.info(f"Match found in Jellyfin for track {track.name}. Skipping download.") - track.downloaded = True - track.filesystem_path = jellyfin_file_path - db.session.commit() - continue + # region search with fingerprinting + if spotify_track: + preview_url = spotify_track.get('preview_url') + if not preview_url: + app.logger.error(f"Preview URL not found for track {track.name}.") + # Decide whether to skip or proceed to download + # For now, we'll proceed to download else: - app.logger.info(f"No match found in Jellyfin for track {track.name}. Proceeding to download.") + # Get the list of Spotify artist names + spotify_artists = [artist['name'] for artist in spotify_track['artists']] + + # Perform the search in Jellyfin + match_found, jellyfin_file_path = jellyfin.search_track_in_jellyfin( + session_token=jellyfin_admin_token, + preview_url=preview_url, + song_name=track.name, + artist_names=spotify_artists + ) + if match_found: + app.logger.info(f"Match found in Jellyfin for track {track.name}. Skipping download.") + track.downloaded = True + track.filesystem_path = jellyfin_file_path + db.session.commit() + continue + else: + app.logger.info(f"No match found in Jellyfin for track {track.name}. Proceeding to download.") + else: + app.logger.warning(f"spotify_track not set, see previous log messages") #endregion #endregion diff --git a/templates/items.html b/templates/items.html index 7effd26..b8e95c2 100644 --- a/templates/items.html +++ b/templates/items.html @@ -1,6 +1,16 @@ {% extends "base.html" %} {% block content %} +{% if error_message %} + +{% else %}

{{ items_title }}

{{ items_subtitle }}
@@ -8,5 +18,6 @@
+{% endif %} {% endblock %} \ No newline at end of file diff --git a/templates/jellyfin_playlists.html b/templates/jellyfin_playlists.html index 7696eb8..6c4c974 100644 --- a/templates/jellyfin_playlists.html +++ b/templates/jellyfin_playlists.html @@ -1,6 +1,16 @@ {% extends "base.html" %} {% block content %} +{% if error_message %} + +{% else %}

Your subscribed Jellyfin Playlists

@@ -10,6 +20,6 @@ {% endfor %}
- +{%endif%} {% endblock %}