commit 4be8622bcd31cc6411300f7f9e5bae7723c66b5b Author: Kamil Date: Fri Oct 11 15:02:37 2024 +0000 init init diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..82801c4 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,189 @@ +import atexit +from logging.handlers import RotatingFileHandler +import os +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate +import spotipy +from spotipy.oauth2 import SpotifyOAuth, SpotifyClientCredentials +from celery import Celery +from config import Config +from jellyfin.client import JellyfinClient +import logging +from spotdl import Spotdl +from spotdl.utils.config import DEFAULT_CONFIG + +app = Flask(__name__, template_folder="../templates") +app.config.from_object(Config) +sp = spotipy.Spotify(auth_manager= SpotifyClientCredentials(client_id=app.config['SPOTIPY_CLIENT_ID'], client_secret=app.config['SPOTIPY_CLIENT_SECRET'])) +jellyfin = JellyfinClient(app.config['JELLYFIN_SERVER_URL']) +spotdl_config = DEFAULT_CONFIG + +spotdl_config['cookie_file'] = '/jellyplist/cookies.txt' +spotdl_config['output'] = '/storage/media/music/_spotify_playlists/{track-id}' +spotdl_config['threads'] = 12 +spotdl = Spotdl( app.config['SPOTIPY_CLIENT_ID'],app.config['SPOTIPY_CLIENT_SECRET'], downloader_settings=spotdl_config) + + + +# Configurations +app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://jellyplist:jellyplist@192.168.178.14/jellyplist' +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + +db = SQLAlchemy(app) +migrate = Migrate(app, db) + +# Configure Logging +log_file = 'app.log' +handler = RotatingFileHandler(log_file, maxBytes=100000, backupCount=3) # 100KB per file, with 3 backups +handler.setLevel(logging.INFO) + +app.logger.info('Application started') + +from app import routes, models +from app.models import JellyfinUser,Track,Playlist +from apscheduler.schedulers.background import BackgroundScheduler + + + +# Initialize the scheduler +scheduler = BackgroundScheduler() + +def update_all_playlists_track_status(): + """ + Update track_count and tracks_available for all playlists in the database. + For each track, check if the file exists on the filesystem. If not, reset the downloaded flag and filesystem_path. + """ + with app.app_context(): + playlists = Playlist.query.all() + + if not playlists: + app.logger.info("No playlists found.") + return + + for playlist in playlists: + total_tracks = 0 + available_tracks = 0 + + for track in playlist.tracks: + total_tracks += 1 + + # Check if the file exists + if track.filesystem_path and os.path.exists(track.filesystem_path): + available_tracks += 1 + else: + # If the file doesn't exist, reset the 'downloaded' flag and 'filesystem_path' + track.downloaded = False + track.filesystem_path = None + db.session.commit() + + # Update playlist fields + playlist.track_count = total_tracks + playlist.tracks_available = available_tracks + + db.session.commit() + + app.logger.info("All playlists' track statuses updated.") + +def download_missing_tracks(): + app.logger.info("Starting track download job...") + with app.app_context(): + # Get all tracks that are not downloaded + undownloaded_tracks = Track.query.filter_by(downloaded=False).all() + + if not undownloaded_tracks: + app.logger.info("No undownloaded tracks found.") + return + + for track in undownloaded_tracks: + app.logger.info(f"Trying to downloading track: {track.name} ({track.spotify_track_id})") + + try: + # Download track using spotDL + s_url = f"https://open.spotify.com/track/{track.spotify_track_id}" + search = spotdl.search([s_url]) + if search: + song = search[0] + dl_request = spotdl.download(song) + # Assuming spotDL downloads files to the './downloads' directory, set the filesystem path + file_path = dl_request[1].__str__() # Adjust according to naming conventions + + if os.path.exists(file_path): + # Update the track's downloaded status and filesystem path + track.downloaded = True + track.filesystem_path = file_path + db.session.commit() + + app.logger.info(f"Track {track.name} downloaded successfully to {file_path}.") + else: + app.logger.error(f"Download failed for track {track.name}: file not found.") + else: + app.logger.warning(f"{track.name} ({track.spotify_track_id}) not Found") + + except Exception as e: + app.logger.error(f"Error downloading track {track.name}: {str(e)}") + + app.logger.info("Track download job finished.") + update_all_playlists_track_status() + +def check_for_playlist_updates(): + app.logger.info('Starting playlist update check...') + with app.app_context(): + try: + playlists = Playlist.query.all() # Get all users + for playlist in playlists: + app.logger.info(f'Checking updates for playlist: {playlist.name}') + try: + # Fetch the latest data from the Spotify API + playlist_data = sp.playlist(playlist.spotify_playlist_id) + spotify_tracks = {track['track']['id']: track['track'] for track in playlist_data['tracks']['items']} + existing_tracks = {track.spotify_track_id: track for track in playlist.tracks} + + # Tracks to add + tracks_to_add = [] + for track_id, track_info in spotify_tracks.items(): + if track_id not in existing_tracks: + track = Track.query.filter_by(spotify_track_id=track_id).first() + if not track: + track = Track(name=track_info['name'], spotify_track_id=track_id, spotify_uri=track_info['uri'],downloaded= False) + db.session.add(track) + db.session.commit() + app.logger.info(f'Added new track: {track.name}') + tracks_to_add.append(track) + + # Tracks to remove + tracks_to_remove = [existing_tracks[track_id] for track_id in existing_tracks if track_id not in spotify_tracks] + + if tracks_to_add: + for track in tracks_to_add: + playlist.tracks.append(track) + db.session.commit() + app.logger.info(f'Added {len(tracks_to_add)} tracks to playlist: {playlist.name}') + + if tracks_to_remove: + for track in tracks_to_remove: + playlist.tracks.remove(track) + db.session.commit() + app.logger.info(f'Removed {len(tracks_to_remove)} tracks from playlist: {playlist.name}') + + except Exception as e: + app.logger.error(f"Error updating playlist {playlist.name}: {str(e)}") + + except Exception as e: + app.logger.error(f"Error in check_for_playlist_updates: {str(e)}") + + app.logger.info('Finished playlist update check.') + update_all_playlists_track_status() + + + +# Add the job to run every 10 minutes (customize the interval as needed) +#scheduler.add_job(download_missing_tracks, 'interval', seconds=30, max_instances=1) +#scheduler.add_job(check_for_playlist_updates, 'interval', minutes=10, max_instances=1) +#download_missing_tracks() +#check_for_playlist_updates() +# Start the scheduler +scheduler.start() + +# Ensure the scheduler shuts down properly when the app stops +atexit.register(lambda: scheduler.shutdown()) \ No newline at end of file