import logging import logging.handlers import os from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from apscheduler.schedulers.asyncio import AsyncIOScheduler from .database import init_db from .config import get_settings from .services.file_watcher import start_file_watcher, stop_file_watcher from .services.podcast_feed import update_all_feeds def _setup_logging(): settings = get_settings() os.makedirs(settings.log_dir, exist_ok=True) fmt = logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s") root = logging.getLogger() root.setLevel(logging.INFO) # Console (bestehend) console = logging.StreamHandler() console.setFormatter(fmt) root.addHandler(console) # app.log — alle Logs app_fh = logging.handlers.RotatingFileHandler( os.path.join(settings.log_dir, "app.log"), maxBytes=5_000_000, backupCount=2, encoding="utf-8" ) app_fh.setFormatter(fmt) root.addHandler(app_fh) # matching.log — nur Matching-bezogene Logger match_fh = logging.handlers.RotatingFileHandler( os.path.join(settings.log_dir, "matching.log"), maxBytes=5_000_000, backupCount=2, encoding="utf-8" ) match_fh.setFormatter(fmt) for name in ("app.services.matcher", "app.services.matching"): logging.getLogger(name).addHandler(match_fh) _setup_logging() logger = logging.getLogger(__name__) _scheduler = AsyncIOScheduler() @asynccontextmanager async def lifespan(app: FastAPI): settings = get_settings() for d in [settings.hls_cache_dir, settings.covers_dir, settings.log_dir]: os.makedirs(d, exist_ok=True) await init_db() await start_file_watcher() _scheduler.add_job(update_all_feeds, "interval", hours=settings.podcast_update_interval_hours, id="feed_update") _scheduler.start() logger.info("Audiolib gestartet.") yield stop_file_watcher() _scheduler.shutdown(wait=False) logger.info("Audiolib gestoppt.") app = FastAPI(title="Audiolib", version="2.4.0", lifespan=lifespan) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) settings = get_settings() if os.path.exists(settings.covers_dir): app.mount("/covers", StaticFiles(directory=settings.covers_dir), name="covers") from .routers import auth, libraries, items, stream, me, users, settings as settings_router from .routers import matching, podcasts, setup, filebrowser, logs app.include_router(setup.router) app.include_router(auth.router) app.include_router(libraries.router) app.include_router(items.router) app.include_router(stream.router) app.include_router(me.router) app.include_router(users.router) app.include_router(settings_router.router) app.include_router(matching.router) app.include_router(podcasts.router) app.include_router(filebrowser.router) app.include_router(logs.router)