Files
jellyplist/app/__init__.py
2024-11-24 22:44:05 +00:00

165 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from logging.handlers import RotatingFileHandler
import os
import time
from flask_socketio import SocketIO
import sys
from flask import Flask, has_request_context
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from psycopg2 import OperationalError
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
from celery import Celery
from celery.schedules import crontab
from sqlalchemy import create_engine
from config import Config
from jellyfin.client import JellyfinClient
import logging
from spotdl.utils.config import DEFAULT_CONFIG
from flask_caching import Cache
from .version import __version__
def check_db_connection(db_uri, retries=5, delay=5):
"""
Check if the database is reachable.
Args:
db_uri (str): The database URI.
retries (int): Number of retry attempts.
delay (int): Delay between retries in seconds.
Raises:
SystemExit: If the database is not reachable after retries.
"""
for attempt in range(1, retries + 1):
try:
engine = create_engine(db_uri)
connection = engine.connect()
connection.close()
app.logger.info("Successfully connected to the database.")
return
except OperationalError as e:
app.logger.error(f"Database connection failed on attempt {attempt}/{retries}: {e}")
if attempt < retries:
app.logger.info(f"Retrying in {delay} seconds...")
time.sleep(delay)
else:
app.logger.critical("Could not connect to the database. Exiting application.")
sys.exit(1)
# Celery setup
def make_celery(app):
celery = Celery(
app.import_name,
backend=app.config['result_backend'],
broker=app.config['CELERY_BROKER_URL'],
include=['app.tasks']
)
celery.conf.update(app.config)
# Configure Celery Beat schedule
celery.conf.beat_schedule = {
'download-missing-tracks-schedule': {
'task': 'app.tasks.download_missing_tracks',
'schedule': crontab(minute='30'),
},
'check-playlist-updates-schedule': {
'task': 'app.tasks.check_for_playlist_updates',
'schedule': crontab(minute='25'),
},
'update_all_playlists_track_status-schedule': {
'task': 'app.tasks.update_all_playlists_track_status',
'schedule': crontab(minute='*/2'),
},
'update_jellyfin_id_for_downloaded_tracks-schedule': {
'task': 'app.tasks.update_jellyfin_id_for_downloaded_tracks',
'schedule': crontab(minute='*/10'),
}
}
celery.conf.timezone = 'UTC'
return celery
# Why this ? Because we are using the same admin login for web, worker and beat we need to distinguish the device_id´s
device_id = f'JellyPlist_{'_'.join(sys.argv)}'
# Initialize Flask app
app = Flask(__name__, template_folder="../templates", static_folder='../static')
# log_file = 'app.log'
# handler = RotatingFileHandler(log_file, maxBytes=100000, backupCount=3)
# handler.setLevel(logging.DEBUG)
# handler.setFormatter(log_formatter)
# stream_handler = logging.StreamHandler(sys.stdout)
# stream_handler.setLevel(logging.DEBUG)
# stream_handler.setFormatter(log_formatter)
# # app.logger.addHandler(handler)
# app.logger.addHandler(stream_handler)
app.config.from_object(Config)
for handler in app.logger.handlers:
app.logger.removeHandler(handler)
log_level = getattr(logging, app.config['LOG_LEVEL'], logging.INFO) # Default to DEBUG if invalid
app.logger.setLevel(log_level)
FORMAT = "[%(asctime)s][%(filename)18s:%(lineno)4s - %(funcName)20s() ] %(message)s"
logging.basicConfig(format=FORMAT)
Config.validate_env_vars()
cache = Cache(app)
# Spotify, Jellyfin, and Spotdl setup
app.logger.info(f"setting up spotipy")
sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials(
client_id=app.config['SPOTIFY_CLIENT_ID'],
client_secret=app.config['SPOTIFY_CLIENT_SECRET']
))
app.logger.info(f"setting up jellyfin client, BaseUrl = {app.config['JELLYFIN_SERVER_URL']}, timeout = {app.config['JELLYFIN_REQUEST_TIMEOUT']}")
jellyfin = JellyfinClient(app.config['JELLYFIN_SERVER_URL'], app.config['JELLYFIN_REQUEST_TIMEOUT'])
jellyfin_admin_token, jellyfin_admin_id, jellyfin_admin_name, jellyfin_admin_is_admin = jellyfin.login_with_password(
app.config['JELLYFIN_ADMIN_USER'],
app.config['JELLYFIN_ADMIN_PASSWORD'], device_id= device_id
)
# SQLAlchemy and Migrate setup
app.logger.info(f"connecting to db: {app.config['JELLYPLIST_DB_HOST']}")
check_db_connection(f'postgresql://{app.config["JELLYPLIST_DB_USER"]}:{app.config["JELLYPLIST_DB_PASSWORD"]}@{app.config["JELLYPLIST_DB_HOST"]}/jellyplist',retries=5,delay=2)
app.config['SQLALCHEMY_DATABASE_URI'] = f'postgresql://{app.config['JELLYPLIST_DB_USER']}:{app.config['JELLYPLIST_DB_PASSWORD']}@{app.config['JELLYPLIST_DB_HOST']}/jellyplist'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
app.logger.info(f"applying db migrations")
migrate = Migrate(app, db)
# Celery Configuration (Updated)
app.config.update(
CELERY_BROKER_URL=app.config['REDIS_URL'],
result_backend=app.config['REDIS_URL']
)
def read_dev_build_file(file_path="/jellyplist/DEV_BUILD"):
if os.path.exists(file_path):
with open(file_path, "r") as file:
content = file.read().strip()
return f"-{content}"
else:
return None
app.logger.info(f"initializing celery")
celery = make_celery(app)
socketio = SocketIO(app, message_queue=app.config['REDIS_URL'], async_mode='eventlet')
celery.set_default()
app.logger.info(f'Jellyplist {__version__}{read_dev_build_file()} started')
app.logger.debug(f"Debug logging active")
from app import routes
from app import jellyfin_routes, tasks
if "worker" in sys.argv:
tasks.release_lock("download_missing_tracks_lock")