changed "spotify" to "provider"

This commit is contained in:
Kamil
2024-11-29 22:50:10 +00:00
parent 56aaec603b
commit 94d401a99f
5 changed files with 217 additions and 107 deletions

View File

@@ -1,4 +1,7 @@
import json
from typing import Optional
from flask import flash, redirect, session, url_for from flask import flash, redirect, session, url_for
import requests
from app.models import JellyfinUser, Playlist,Track from app.models import JellyfinUser, Playlist,Track
from app import sp, cache, app, jellyfin ,jellyfin_admin_token, jellyfin_admin_id,device_id, cache from app import sp, cache, app, jellyfin ,jellyfin_admin_token, jellyfin_admin_id,device_id, cache
from functools import wraps from functools import wraps
@@ -57,40 +60,46 @@ def prepPlaylistData(data):
for playlist_data in data['playlists']['items']: for playlist_data in data['playlists']['items']:
# Fetch the playlist from the database if it exists # Fetch the playlist from the database if it exists
db_playlist = Playlist.query.filter_by(spotify_playlist_id=playlist_data['id']).first() if playlist_data:
db_playlist = Playlist.query.filter_by(provider_playlist_id=playlist_data['id']).first()
if db_playlist: if db_playlist:
# If the playlist is in the database, use the stored values # If the playlist is in the database, use the stored values
if playlist_data.get('tracks'):
if isinstance(playlist_data['tracks'],list): if isinstance(playlist_data['tracks'],list):
track_count = len(playlist_data['tracks'] ) track_count = len(playlist_data['tracks'] )
else: else:
track_count = playlist_data['tracks']['total'] or 0 track_count = playlist_data['tracks']['total'] or 0
else:
track_count = 0
tracks_available = db_playlist.tracks_available or 0 tracks_available = db_playlist.tracks_available or 0
tracks_linked = len([track for track in db_playlist.tracks if track.jellyfin_id]) or 0 tracks_linked = len([track for track in db_playlist.tracks if track.jellyfin_id]) or 0
percent_available = (tracks_available / track_count * 100) if track_count > 0 else 0 percent_available = (tracks_available / track_count * 100) if track_count > 0 else 0
# Determine playlist status # Determine playlist status
if not playlist_data.get('status'):
if tracks_available == track_count and track_count > 0: if tracks_available == track_count and track_count > 0:
status = 'green' # Fully available playlist_data['status'] = 'green' # Fully available
elif tracks_available > 0: elif tracks_available > 0:
status = 'yellow' # Partially available playlist_data['status'] = 'yellow' # Partially available
else: else:
status = 'red' # Not available playlist_data['status'] = 'red' # Not available
else: else:
# If the playlist is not in the database, initialize with 0 # If the playlist is not in the database, initialize with 0
track_count = 0 track_count = 0
tracks_available = 0 tracks_available = 0
tracks_linked = 0 tracks_linked = 0
percent_available = 0 percent_available = 0
status = 'red' # Not requested yet playlist_data['status'] = 'red' # Not requested yet
# Append playlist data to the list # Append playlist data to the list
playlists.append({ playlists.append({
'name': playlist_data['name'], 'name': playlist_data['name'],
'description': playlist_data['description'], 'description': playlist_data['description'],
'image': playlist_data['images'][0]['url'] if playlist_data['images'] else 'default-image.jpg', 'image': playlist_data['images'][0]['url'] if playlist_data.get('images') else '/static/images/placeholder.png',
'url': playlist_data['external_urls']['spotify'], 'url': playlist_data['external_urls']['spotify'] if playlist_data.get('external_urls') else '',
'id': playlist_data['id'], 'id': playlist_data['id'] if playlist_data['id'] else '',
'jellyfin_id': db_playlist.jellyfin_id if db_playlist else '', 'jellyfin_id': db_playlist.jellyfin_id if db_playlist else '',
'can_add': (db_playlist not in jellyfin_user.playlists) if db_playlist else True, 'can_add': (db_playlist not in jellyfin_user.playlists) if db_playlist else True,
'can_remove' : (db_playlist in jellyfin_user.playlists) if db_playlist else False, 'can_remove' : (db_playlist in jellyfin_user.playlists) if db_playlist else False,
@@ -100,7 +109,7 @@ def prepPlaylistData(data):
'track_count': track_count, 'track_count': track_count,
'tracks_linked': tracks_linked, 'tracks_linked': tracks_linked,
'percent_available': percent_available, 'percent_available': percent_available,
'status': status # Red, yellow, or green based on availability 'status': playlist_data['status'] # Red, yellow, or green based on availability
}) })
return playlists return playlists
@@ -115,24 +124,57 @@ def get_cached_spotify_playlists(playlist_ids):
spotify_data = {'playlists': {'items': []}} spotify_data = {'playlists': {'items': []}}
for playlist_id in playlist_ids: for playlist_id in playlist_ids:
playlist_data = None
not_found = False
try:
playlist_data = get_cached_spotify_playlist(playlist_id) playlist_data = get_cached_spotify_playlist(playlist_id)
except SpotifyException as e:
app.logger.error(f"Error Fetching Playlist {playlist_id}: {e}")
not_found = 'http status: 404' in str(e)
if not_found:
playlist_data = {
'status':'red',
'description': 'Playlist has most likely been removed. You can keep it, but won´t receive Updates.',
'id': playlist_id,
'name' : ''
}
if playlist_data: if playlist_data:
spotify_data['playlists']['items'].append(playlist_data) spotify_data['playlists']['items'].append(playlist_data)
else:
app.logger.warning(f"Playlist data for ID {playlist_id} could not be retrieved.")
return spotify_data return spotify_data
@cache.memoize(timeout=3600) @cache.memoize(timeout=3600)
def get_cached_spotify_playlist(playlist_id): def get_cached_playlist(playlist_id):
""" """
Fetches a Spotify playlist by its ID, utilizing caching to minimize API calls. Fetches a Spotify playlist by its ID, utilizing caching to minimize API calls.
:param playlist_id: The Spotify playlist ID. :param playlist_id: The Spotify playlist ID.
:return: Playlist data as a dictionary, or None if an error occurs. :return: Playlist data as a dictionary, or None if an error occurs.
""" """
playlist_data = sp.playlist(playlist_id) # Fetch data from Spotify API # When the playlist_id starts with 37i9dQZF1, we need to use the new function
# as the standard Spotify API endpoints are deprecated for these playlists.
# Reference: https://github.com/kamilkosek/jellyplist/issues/25
if playlist_id.startswith("37i9dQZF1"):
app.logger.warning(f"Algorithmic or Spotify-owned editorial playlist, using custom Implementation to fetch details")
# Use the custom implementation for these playlists
try:
data = fetch_spotify_playlist(playlist_id)
return transform_playlist_response(data)
except Exception as e:
print(f"Error fetching playlist with custom method: {e}")
return None
# Otherwise, use the standard Spotipy API
try:
playlist_data = sp.playlist(playlist_id) # Fetch data using Spotipy
return playlist_data return playlist_data
except Exception as e:
print(f"Error fetching playlist with Spotipy: {e}")
return None
@cache.memoize(timeout=3600*24*10) @cache.memoize(timeout=3600*24*10)
def get_cached_spotify_track(track_id): def get_cached_spotify_track(track_id):
@@ -189,7 +231,7 @@ def getFeaturedPlaylists(country: str, offset: int):
def getCategoryPlaylists(category: str, offset: int): def getCategoryPlaylists(category: str, offset: int):
try: try:
playlists_data = sp.category_playlists(category_id=category, limit=16, offset=offset) playlists_data = sp.category_playlists(category_id=category, country=app.config['SPOTIFY_COUNTRY_CODE'], limit=16, offset=offset)
return prepPlaylistData(playlists_data), playlists_data['playlists']['total'], f"Category {playlists_data['message']}" return prepPlaylistData(playlists_data), playlists_data['playlists']['total'], f"Category {playlists_data['message']}"
except SpotifyException as e: except SpotifyException as e:
app.logger.error(f"Spotify API error in getCategoryPlaylists: {e}") app.logger.error(f"Spotify API error in getCategoryPlaylists: {e}")
@@ -215,14 +257,14 @@ def get_tracks_for_playlist(data):
tracks = [] tracks = []
is_admin = session.get('is_admin', False) is_admin = session.get('is_admin', False)
for idx, item in enumerate(results['tracks']): for idx, item in enumerate(results['tracks']['items']):
track_data = item['track'] track_data = item['track']
if track_data: if track_data:
duration_ms = track_data['duration_ms'] duration_ms = track_data['duration_ms']
minutes = duration_ms // 60000 minutes = duration_ms // 60000
seconds = (duration_ms % 60000) // 1000 seconds = (duration_ms % 60000) // 1000
track_db = Track.query.filter_by(spotify_track_id=track_data['id']).first() track_db = Track.query.filter_by(provider_track_id=track_data['id']).first()
if track_db: if track_db:
downloaded = track_db.downloaded downloaded = track_db.downloaded

View File

@@ -16,25 +16,37 @@ def jellyfin_playlists():
try: try:
# Fetch playlists from Jellyfin # Fetch playlists from Jellyfin
playlists = jellyfin.get_playlists(session_token=functions._get_token_from_sessioncookie()) playlists = jellyfin.get_playlists(session_token=functions._get_token_from_sessioncookie())
spotify_data = {'playlists': {'items': []}}
# Extract Spotify playlist IDs from the database # Extract Spotify playlist IDs from the database
spotify_playlist_ids = []
for pl in playlists: for pl in playlists:
# Retrieve the playlist from the database using Jellyfin ID # Retrieve the playlist from the database using Jellyfin ID
from_db = Playlist.query.filter_by(jellyfin_id=pl['Id']).first() from_db = Playlist.query.filter_by(jellyfin_id=pl['Id']).first()
if from_db and from_db.spotify_playlist_id: playlist_data = None
spotify_playlist_ids.append(from_db.spotify_playlist_id) not_found = False
if from_db and from_db.provider_playlist_id:
pl_id = from_db.provider_playlist_id
try:
playlist_data = functions.get_cached_spotify_playlist(pl_id)
except SpotifyException as e:
app.logger.error(f"Error Fetching Playlist {pl_id}: {e}")
not_found = 'http status: 404' in str(e)
if not_found:
playlist_data = {
'status':'red',
'description': 'Playlist has most likely been removed. You can keep it, but won´t receive Updates.',
'id': from_db.provider_playlist_id,
'name' : from_db.name
}
if playlist_data:
spotify_data['playlists']['items'].append(playlist_data)
else: else:
app.logger.warning(f"No database entry found for Jellyfin playlist ID: {pl['Id']}") app.logger.warning(f"No database entry found for Jellyfin playlist ID: {pl['Id']}")
if not spotify_playlist_ids:
flash('No Spotify playlists found to display.', 'warning')
return render_template('jellyfin_playlists.html', playlists=functions.prepPlaylistData({'playlists': {'items': []}}))
# Use the cached function to fetch Spotify playlists
spotify_data = functions.get_cached_spotify_playlists(spotify_playlist_ids)
# Prepare the data for the template
prepared_data = functions.prepPlaylistData(spotify_data) prepared_data = functions.prepPlaylistData(spotify_data)
return render_template('jellyfin_playlists.html', playlists=prepared_data) return render_template('jellyfin_playlists.html', playlists=prepared_data)
@@ -63,13 +75,13 @@ def add_playlist():
playlist_data = functions.get_cached_spotify_playlist(playlist_id) playlist_data = functions.get_cached_spotify_playlist(playlist_id)
# Check if playlist already exists in the database # Check if playlist already exists in the database
playlist = Playlist.query.filter_by(spotify_playlist_id=playlist_id).first() playlist = Playlist.query.filter_by(provider_playlist_id=playlist_id).first()
if not playlist: if not playlist:
# Add new playlist if it doesn't exist # Add new playlist if it doesn't exist
# create the playlist via api key, with the first admin as 'owner' # create the playlist via api key, with the first admin as 'owner'
fromJellyfin = jellyfin.create_music_playlist(functions._get_api_token(),playlist_data['name'],[],functions._get_admin_id())['Id'] fromJellyfin = jellyfin.create_music_playlist(functions._get_api_token(),playlist_data['name'],[],functions._get_admin_id())['Id']
playlist = Playlist(name=playlist_data['name'], spotify_playlist_id=playlist_id,spotify_uri=playlist_data['uri'],track_count = playlist_data['tracks']['total'], tracks_available=0, jellyfin_id = fromJellyfin) playlist = Playlist(name=playlist_data['name'], provider_playlist_id=playlist_id,provider_uri=playlist_data['uri'],track_count = playlist_data['tracks']['total'], tracks_available=0, jellyfin_id = fromJellyfin)
db.session.add(playlist) db.session.add(playlist)
db.session.commit() db.session.commit()
if app.config['START_DOWNLOAD_AFTER_PLAYLIST_ADD']: if app.config['START_DOWNLOAD_AFTER_PLAYLIST_ADD']:
@@ -83,7 +95,7 @@ def add_playlist():
spotify_tracks = {} spotify_tracks = {}
offset = 0 offset = 0
while True: while True:
playlist_items = sp.playlist_items(playlist.spotify_playlist_id, offset=offset, limit=100) playlist_items = sp.playlist_items(playlist.provider_playlist_id, offset=offset, limit=100)
items = playlist_items['items'] items = playlist_items['items']
spotify_tracks.update({offset + idx: track['track'] for idx, track in enumerate(items) if track['track']}) spotify_tracks.update({offset + idx: track['track'] for idx, track in enumerate(items) if track['track']})
@@ -94,11 +106,11 @@ def add_playlist():
track_info = track_data track_info = track_data
if not track_info: if not track_info:
continue continue
track = Track.query.filter_by(spotify_track_id=track_info['id']).first() track = Track.query.filter_by(provider_track_id=track_info['id']).first()
if not track: if not track:
# Add new track if it doesn't exist # Add new track if it doesn't exist
track = Track(name=track_info['name'], spotify_track_id=track_info['id'], spotify_uri=track_info['uri'], downloaded=False) track = Track(name=track_info['name'], provider_track_id=track_info['id'], provider_uri=track_info['uri'], downloaded=False)
db.session.add(track) db.session.add(track)
db.session.commit() db.session.commit()
elif track.downloaded: elif track.downloaded:
@@ -158,7 +170,7 @@ def delete_playlist(playlist_id):
flash('Playlist removed') flash('Playlist removed')
item = { item = {
"name" : playlist.name, "name" : playlist.name,
"id" : playlist.spotify_playlist_id, "id" : playlist.provider_playlist_id,
"can_add":True, "can_add":True,
"can_remove":False, "can_remove":False,
"jellyfin_id" : playlist.jellyfin_id "jellyfin_id" : playlist.jellyfin_id
@@ -182,7 +194,7 @@ def wipe_playlist(playlist_id):
if playlist: if playlist:
# Delete the playlist # Delete the playlist
name = playlist.name name = playlist.name
id = playlist.spotify_playlist_id id = playlist.provider_playlist_id
jf_id = playlist.jellyfin_id jf_id = playlist.jellyfin_id
db.session.delete(playlist) db.session.delete(playlist)
db.session.commit() db.session.commit()

View File

@@ -22,8 +22,8 @@ user_playlists = db.Table('user_playlists',
class Playlist(db.Model): class Playlist(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(150), nullable=False) name = db.Column(db.String(150), nullable=False)
spotify_playlist_id = db.Column(db.String(120), unique=True, nullable=False) provider_playlist_id = db.Column(db.String(120), unique=True, nullable=False)
spotify_uri = db.Column(db.String(120), unique=True, nullable=False) provider_uri = db.Column(db.String(120), unique=True, nullable=False)
# Relationship with Tracks # Relationship with Tracks
tracks = db.relationship('Track', secondary='playlist_tracks', back_populates='playlists') tracks = db.relationship('Track', secondary='playlist_tracks', back_populates='playlists')
@@ -37,7 +37,7 @@ class Playlist(db.Model):
users = db.relationship('JellyfinUser', secondary=user_playlists, back_populates='playlists') users = db.relationship('JellyfinUser', secondary=user_playlists, back_populates='playlists')
def __repr__(self): def __repr__(self):
return f'<Playlist {self.name}:{self.spotify_playlist_id}>' return f'<Playlist {self.name}:{self.provider_playlist_id}>'
# Association table between Playlists and Tracks # Association table between Playlists and Tracks
playlist_tracks = db.Table('playlist_tracks', playlist_tracks = db.Table('playlist_tracks',
@@ -50,8 +50,8 @@ playlist_tracks = db.Table('playlist_tracks',
class Track(db.Model): class Track(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(150), nullable=False) name = db.Column(db.String(150), nullable=False)
spotify_track_id = db.Column(db.String(120), unique=True, nullable=False) provider_track_id = db.Column(db.String(120), unique=True, nullable=False)
spotify_uri = db.Column(db.String(120), unique=True, nullable=False) provider_uri = db.Column(db.String(120), unique=True, nullable=False)
downloaded = db.Column(db.Boolean()) downloaded = db.Column(db.Boolean())
filesystem_path = db.Column(db.String(), nullable=True) filesystem_path = db.Column(db.String(), nullable=True)
jellyfin_id = db.Column(db.String(120), nullable=True) # Add Jellyfin track ID field jellyfin_id = db.Column(db.String(120), nullable=True) # Add Jellyfin track ID field
@@ -60,4 +60,4 @@ class Track(db.Model):
# Many-to-Many relationship with Playlists # Many-to-Many relationship with Playlists
playlists = db.relationship('Playlist', secondary=playlist_tracks, back_populates='tracks') playlists = db.relationship('Playlist', secondary=playlist_tracks, back_populates='tracks')
def __repr__(self): def __repr__(self):
return f'<Track {self.name}:{self.spotify_track_id}>' return f'<Track {self.name}:{self.provider_track_id}>'

View File

@@ -1,10 +1,29 @@
from flask import Flask, Response, jsonify, render_template, request, redirect, url_for, session, flash import json
import re
from flask import Flask, Response, jsonify, render_template, request, redirect, url_for, session, flash, Blueprint, g
from app import app, db, functions, sp, jellyfin, celery, jellyfin_admin_token, jellyfin_admin_id,device_id, cache, read_dev_build_file, tasks from app import app, db, functions, sp, jellyfin, celery, jellyfin_admin_token, jellyfin_admin_id,device_id, cache, read_dev_build_file, tasks
from app.models import JellyfinUser,Playlist,Track from app.models import JellyfinUser,Playlist,Track
from celery.result import AsyncResult from celery.result import AsyncResult
from app.providers import base
from app.providers.base import MusicProviderClient
from app.providers.spotify import SpotifyClient
from app.registry.music_provider_registry import MusicProviderRegistry
from .version import __version__ from .version import __version__
from spotipy.exceptions import SpotifyException from spotipy.exceptions import SpotifyException
pl_bp = Blueprint('playlist', __name__)
@pl_bp.before_request
def set_active_provider():
"""
Middleware to select the active provider based on request parameters.
"""
provider_id = request.args.get('provider', 'Spotify') # Default to Spotify
try:
g.music_provider = MusicProviderRegistry.get_provider(provider_id)
except ValueError as e:
return {"error": str(e)}, 400
@app.context_processor @app.context_processor
def add_context(): def add_context():
unlinked_track_count = len(Track.query.filter_by(downloaded=True,jellyfin_id=None).all()) unlinked_track_count = len(Track.query.filter_by(downloaded=True,jellyfin_id=None).all())
@@ -45,7 +64,7 @@ def link_issues():
unlinked_tracks = Track.query.filter_by(downloaded=True,jellyfin_id=None).all() unlinked_tracks = Track.query.filter_by(downloaded=True,jellyfin_id=None).all()
tracks = [] tracks = []
for ult in unlinked_tracks: for ult in unlinked_tracks:
sp_track = functions.get_cached_spotify_track(ult.spotify_track_id) sp_track = functions.get_cached_spotify_track(ult.provider_track_id)
duration_ms = sp_track['duration_ms'] duration_ms = sp_track['duration_ms']
minutes = duration_ms // 60000 minutes = duration_ms // 60000
seconds = (duration_ms % 60000) // 1000 seconds = (duration_ms % 60000) // 1000
@@ -117,7 +136,7 @@ def login():
db.session.add(new_user) db.session.add(new_user)
db.session.commit() db.session.commit()
return redirect('/playlists') return redirect('/')
except: except:
flash('Login failed. Please check your Jellyfin credentials and try again.', 'error') flash('Login failed. Please check your Jellyfin credentials and try again.', 'error')
return redirect(url_for('login')) return redirect(url_for('login'))
@@ -130,6 +149,32 @@ def logout():
session.pop('jellyfin_access_token', None) session.pop('jellyfin_access_token', None)
return redirect(url_for('login')) return redirect(url_for('login'))
@app.route('/add_single',methods=['GET'])
@functions.jellyfin_login_required
def add_single():
playlist = request.args.get('playlist')
error = None
errdata= None
if playlist:
parsed = sp._get_id(type='playlist',id=playlist)
if parsed:
try:
functions.get_cached_spotify_playlist(parsed)
return redirect(f'/playlist/view/{parsed}')
except SpotifyException as e:
url_match = re.search(sp._regex_spotify_url, playlist)
if url_match is not None:
resp = functions.fetch_spotify_playlist(playlist,None)
parsed_data = functions.parse_spotify_playlist_html(resp)
error = (f'Playlist can´t be fetched')
errdata = str(e)
return render_template('index.html',error_message = error, error_data = errdata)
@app.route('/playlists') @app.route('/playlists')
@app.route('/categories') @app.route('/categories')
@@ -147,8 +192,13 @@ def loaditems():
try: try:
db_playlists = db.session.query(Playlist).offset(offset).limit(limit).all() db_playlists = db.session.query(Playlist).offset(offset).limit(limit).all()
max_items = db.session.query(Playlist).count() 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)) provider_playlist_ids = [playlist.provider_playlist_id for playlist in db_playlists]
spotify_data = functions.get_cached_spotify_playlists(tuple(provider_playlist_ids))
for x in spotify_data['playlists']['items']:
for from_db in db_playlists:
if x['id'] == from_db.provider_playlist_id:
x['name'] = from_db.name
data = functions.prepPlaylistData(spotify_data) data = functions.prepPlaylistData(spotify_data)
items_title = "Monitored Playlists" 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." items_subtitle = "These playlists are already monitored by the Server. If you add one to your Jellyfin account, they will be available immediately."
@@ -209,11 +259,11 @@ def searchResults():
context = {} context = {}
if query: if query:
# Add your logic here to perform the search on Spotify (or Jellyfin) # Add your logic here to perform the search on Spotify (or Jellyfin)
search_result = sp.search(q = query, type= 'track,album,artist,playlist') search_result = sp.search(q = query, type= 'playlist',limit= 50, market=app.config['SPOTIFY_COUNTRY_CODE'])
context = { context = {
'artists' : functions.prepArtistData(search_result ), #'artists' : functions.prepArtistData(search_result ),
'playlists' : functions.prepPlaylistData(search_result ), 'playlists' : functions.prepPlaylistData(search_result ),
'albums' : functions.prepAlbumData(search_result ), #'albums' : functions.prepAlbumData(search_result ),
'query' : query 'query' : query
} }
return render_template('search.html', **context) return render_template('search.html', **context)
@@ -221,14 +271,14 @@ def searchResults():
return render_template('search.html', query=None, results={}) return render_template('search.html', query=None, results={})
@app.route('/playlist/view/<playlist_id>') @pl_bp.route('/playlist/view/<playlist_id>')
@functions.jellyfin_login_required @functions.jellyfin_login_required
def get_playlist_tracks(playlist_id): def get_playlist_tracks(playlist_id):
# Hol dir alle Tracks für die Playlist provider: MusicProviderClient = g.music_provider # Explicit type hint for g.music_provider
data = functions.get_full_playlist_data(playlist_id) # Diese neue Funktion holt alle Tracks der Playlist playlist: base.Playlist = provider.get_playlist(playlist_id)
tracks = functions.get_tracks_for_playlist(data) # Deine Funktion, um Tracks zu holen tracks = functions.get_tracks_for_playlist(playlist.tracks) # Deine Funktion, um Tracks zu holen
# Berechne die gesamte Dauer der Playlist # Berechne die gesamte Dauer der Playlist
total_duration_ms = sum([track['track']['duration_ms'] for track in data['tracks'] if track['track']]) total_duration_ms = sum([track['track']['duration_ms'] for track in data['tracks']['items'] if track['track']])
# Konvertiere die Gesamtdauer in ein lesbares Format # Konvertiere die Gesamtdauer in ein lesbares Format
hours, remainder = divmod(total_duration_ms // 1000, 3600) hours, remainder = divmod(total_duration_ms // 1000, 3600)
@@ -263,7 +313,7 @@ def associate_track():
flash('Missing Jellyfin or Spotify ID') flash('Missing Jellyfin or Spotify ID')
# Retrieve the track by Spotify ID # Retrieve the track by Spotify ID
track = Track.query.filter_by(spotify_track_id=spotify_id).first() track = Track.query.filter_by(provider_track_id=spotify_id).first()
if not track: if not track:
flash('Track not found') flash('Track not found')
@@ -296,4 +346,10 @@ def unlock_key():
@app.route('/test') @app.route('/test')
def test(): def test():
playlist_id = "37i9dQZF1DX12qgyzUprB6"
client = SpotifyClient(cookie_file='/jellyplist/open.spotify.com_cookies.txt')
client.authenticate()
pl = client.get_playlist(playlist_id=playlist_id)
browse = client.browse_all()
page = client.browse_page(browse[0].items[12])
return '' return ''

View File

@@ -49,7 +49,7 @@ def update_all_playlists_track_status(self):
for playlist in playlists: for playlist in playlists:
total_tracks = 0 total_tracks = 0
available_tracks = 0 available_tracks = 0
app.logger.debug(f"Current Playlist: {playlist.name} [{playlist.id}:{playlist.spotify_playlist_id}]" ) app.logger.debug(f"Current Playlist: {playlist.name} [{playlist.id}:{playlist.provider_playlist_id}]" )
for track in playlist.tracks: for track in playlist.tracks:
total_tracks += 1 total_tracks += 1
if track.filesystem_path and os.path.exists(track.filesystem_path): if track.filesystem_path and os.path.exists(track.filesystem_path):
@@ -106,21 +106,21 @@ def download_missing_tracks(self):
processed_tracks = 0 processed_tracks = 0
failed_downloads = 0 failed_downloads = 0
for track in undownloaded_tracks: for track in undownloaded_tracks:
app.logger.info(f"Processing track: {track.name} [{track.spotify_track_id}]") app.logger.info(f"Processing track: {track.name} [{track.provider_track_id}]")
# Check if the track already exists in the output directory # Check if the track already exists in the output directory
file_path = f"{output_dir.replace('{track-id}', track.spotify_track_id)}.mp3" file_path = f"{output_dir.replace('{track-id}', track.provider_track_id)}.mp3"
# region search before download # region search before download
if search_before_download: if search_before_download:
app.logger.info(f"Searching for track in Jellyfin: {track.name}") app.logger.info(f"Searching for track in Jellyfin: {track.name}")
spotify_track = functions.get_cached_spotify_track(track.spotify_track_id) spotify_track = functions.get_cached_spotify_track(track.provider_track_id)
# at first try to find the track without fingerprinting it # at first try to find the track without fingerprinting it
best_match = find_best_match_from_jellyfin(track) best_match = find_best_match_from_jellyfin(track)
if best_match: if best_match:
track.downloaded = True track.downloaded = True
if track.jellyfin_id != best_match['Id']: if track.jellyfin_id != best_match['Id']:
track.jellyfin_id = best_match['Id'] track.jellyfin_id = best_match['Id']
app.logger.info(f"Updated Jellyfin ID for track: {track.name} ({track.spotify_track_id})") app.logger.info(f"Updated Jellyfin ID for track: {track.name} ({track.provider_track_id})")
if track.filesystem_path != best_match['Path']: if track.filesystem_path != best_match['Path']:
track.filesystem_path = best_match['Path'] track.filesystem_path = best_match['Path']
@@ -171,8 +171,8 @@ def download_missing_tracks(self):
# Attempt to download the track using spotdl # Attempt to download the track using spotdl
try: try:
app.logger.info(f"Trying to download track: {track.name} ({track.spotify_track_id}), spotdl timeout = 90") app.logger.info(f"Trying to download track: {track.name} ({track.provider_track_id}), spotdl timeout = 90")
s_url = f"https://open.spotify.com/track/{track.spotify_track_id}" s_url = f"https://open.spotify.com/track/{track.provider_track_id}"
command = [ command = [
"spotdl", "download", s_url, "spotdl", "download", s_url,
@@ -251,7 +251,7 @@ def check_for_playlist_updates(self):
for playlist in playlists: for playlist in playlists:
playlist.last_updated = datetime.now( timezone.utc) playlist.last_updated = datetime.now( timezone.utc)
sp_playlist = sp.playlist(playlist.spotify_playlist_id) sp_playlist = sp.playlist(playlist.provider_playlist_id)
full_update = True full_update = True
app.logger.info(f'Checking updates for playlist: {playlist.name}, s_snapshot = {sp_playlist['snapshot_id']}') app.logger.info(f'Checking updates for playlist: {playlist.name}, s_snapshot = {sp_playlist['snapshot_id']}')
db.session.commit() db.session.commit()
@@ -266,7 +266,7 @@ def check_for_playlist_updates(self):
offset = 0 offset = 0
playlist.snapshot_id = sp_playlist['snapshot_id'] playlist.snapshot_id = sp_playlist['snapshot_id']
while True: while True:
playlist_data = sp.playlist_items(playlist.spotify_playlist_id, offset=offset, limit=100) playlist_data = sp.playlist_items(playlist.provider_playlist_id, offset=offset, limit=100)
items = playlist_data['items'] items = playlist_data['items']
spotify_tracks.update({offset + idx: track['track'] for idx, track in enumerate(items) if track['track']}) spotify_tracks.update({offset + idx: track['track'] for idx, track in enumerate(items) if track['track']})
@@ -274,7 +274,7 @@ def check_for_playlist_updates(self):
break break
offset += 100 # Move to the next batch offset += 100 # Move to the next batch
existing_tracks = {track.spotify_track_id: track for track in playlist.tracks} existing_tracks = {track.provider_track_id: track for track in playlist.tracks}
# Determine tracks to add and remove # Determine tracks to add and remove
tracks_to_add = [] tracks_to_add = []
@@ -282,9 +282,9 @@ def check_for_playlist_updates(self):
if track_info: if track_info:
track_id = track_info['id'] track_id = track_info['id']
if track_id not in existing_tracks: if track_id not in existing_tracks:
track = Track.query.filter_by(spotify_track_id=track_id).first() track = Track.query.filter_by(provider_track_id=track_id).first()
if not track: if not track:
track = Track(name=track_info['name'], spotify_track_id=track_id, spotify_uri=track_info['uri'], downloaded=False) track = Track(name=track_info['name'], provider_track_id=track_id, provider_uri=track_info['uri'], downloaded=False)
db.session.add(track) db.session.add(track)
db.session.commit() db.session.commit()
app.logger.info(f'Added new track: {track.name}') app.logger.info(f'Added new track: {track.name}')
@@ -382,10 +382,10 @@ def update_jellyfin_id_for_downloaded_tracks(self):
track.downloaded = True track.downloaded = True
if track.jellyfin_id != best_match['Id']: if track.jellyfin_id != best_match['Id']:
track.jellyfin_id = best_match['Id'] track.jellyfin_id = best_match['Id']
app.logger.info(f"Updated Jellyfin ID for track: {track.name} ({track.spotify_track_id})") app.logger.info(f"Updated Jellyfin ID for track: {track.name} ({track.provider_track_id})")
if track.filesystem_path != best_match['Path']: if track.filesystem_path != best_match['Path']:
track.filesystem_path = best_match['Path'] track.filesystem_path = best_match['Path']
app.logger.info(f"Updated filesystem_path for track: {track.name} ({track.spotify_track_id})") app.logger.info(f"Updated filesystem_path for track: {track.name} ({track.provider_track_id})")
@@ -422,10 +422,10 @@ def find_best_match_from_jellyfin(track: Track):
for result in search_results: for result in search_results:
app.logger.debug(f"Processing search result: {result['Id']}") app.logger.debug(f"Processing search result: {result['Id']}, Path = {result['Path']}")
quality_score = compute_quality_score(result, app.config['FIND_BEST_MATCH_USE_FFPROBE']) quality_score = compute_quality_score(result, app.config['FIND_BEST_MATCH_USE_FFPROBE'])
try: try:
spotify_track = functions.get_cached_spotify_track(track.spotify_track_id) spotify_track = functions.get_cached_spotify_track(track.provider_track_id)
spotify_track_name = spotify_track['name'].lower() spotify_track_name = spotify_track['name'].lower()
spotify_artists = [artist['name'].lower() for artist in spotify_track['artists']] spotify_artists = [artist['name'].lower() for artist in spotify_track['artists']]
except Exception as e: except Exception as e: