160 lines
4.7 KiB
Python
160 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
NFOGuard - Automated NFO file management for Radarr and Sonarr
|
|
Modular architecture with webhook processing and intelligent date handling
|
|
"""
|
|
import os
|
|
import sys
|
|
import signal
|
|
from pathlib import Path
|
|
from datetime import datetime, timezone
|
|
|
|
import uvicorn
|
|
from fastapi import FastAPI
|
|
|
|
# Import configuration first
|
|
from config.settings import config
|
|
from utils.logging import _log
|
|
|
|
# Import core components
|
|
from core.database import NFOGuardDatabase
|
|
from core.nfo_manager import NFOManager
|
|
from core.path_mapper import PathMapper
|
|
|
|
# Import clients
|
|
from clients.external_clients import ExternalClientManager
|
|
|
|
# Import processors
|
|
from processors.tv_processor import TVProcessor
|
|
from processors.movie_processor import MovieProcessor
|
|
|
|
# Import webhook handling
|
|
from webhooks.webhook_batcher import WebhookBatcher
|
|
|
|
# Import API routes
|
|
from api.routes import register_routes
|
|
|
|
|
|
def get_version() -> str:
|
|
"""Get application version"""
|
|
try:
|
|
version = (Path(__file__).parent / "VERSION").read_text().strip()
|
|
except:
|
|
version = "0.1.0"
|
|
|
|
# Check if running from dev branch (detect at runtime)
|
|
try:
|
|
# Try to read git branch from .git/HEAD
|
|
git_head_path = Path(__file__).parent / ".git" / "HEAD"
|
|
if git_head_path.exists():
|
|
head_content = git_head_path.read_text().strip()
|
|
if "ref: refs/heads/dev" in head_content:
|
|
version = f"{version}-dev"
|
|
elif head_content.startswith("ref: refs/heads/"):
|
|
# Extract branch name for other branches
|
|
branch = head_content.split("refs/heads/")[-1]
|
|
if branch != "main":
|
|
version = f"{version}-{branch}"
|
|
except Exception:
|
|
# If git detection fails, that's fine - use base version
|
|
pass
|
|
|
|
# Check for build source (only add -gitea for local Gitea builds)
|
|
build_source = os.environ.get("BUILD_SOURCE", "")
|
|
if build_source == "gitea":
|
|
if "gitea" not in version: # Don't double-add gitea suffix
|
|
version = f"{version}-gitea"
|
|
|
|
return version
|
|
|
|
|
|
def create_app() -> FastAPI:
|
|
"""Create and configure the FastAPI application"""
|
|
version = get_version()
|
|
|
|
app = FastAPI(
|
|
title="NFOGuard",
|
|
description="Webhook server for preserving media import dates",
|
|
version=version
|
|
)
|
|
|
|
return app
|
|
|
|
|
|
def initialize_components():
|
|
"""Initialize all application components"""
|
|
start_time = datetime.now(timezone.utc)
|
|
|
|
# Initialize core components
|
|
db = NFOGuardDatabase(config.db_path)
|
|
nfo_manager = NFOManager(config.manager_brand, config.debug)
|
|
path_mapper = PathMapper(config)
|
|
|
|
# Initialize processors
|
|
tv_processor = TVProcessor(db, nfo_manager, path_mapper)
|
|
movie_processor = MovieProcessor(db, nfo_manager, path_mapper)
|
|
|
|
# Initialize webhook batcher with nfo_manager for comprehensive IMDb detection
|
|
batcher = WebhookBatcher(nfo_manager)
|
|
batcher.set_processors(tv_processor, movie_processor)
|
|
|
|
return {
|
|
"db": db,
|
|
"nfo_manager": nfo_manager,
|
|
"path_mapper": path_mapper,
|
|
"tv_processor": tv_processor,
|
|
"movie_processor": movie_processor,
|
|
"batcher": batcher,
|
|
"start_time": start_time,
|
|
"config": config,
|
|
"version": get_version()
|
|
}
|
|
|
|
|
|
def signal_handler(signum, frame):
|
|
"""Handle shutdown signals gracefully"""
|
|
_log("INFO", f"Received signal {signum}, shutting down gracefully...")
|
|
sys.exit(0)
|
|
|
|
|
|
def main():
|
|
"""Main application entry point"""
|
|
# Register signal handlers for graceful shutdown
|
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
version = get_version()
|
|
|
|
_log("INFO", "Starting NFOGuard")
|
|
_log("INFO", f"Version: {version}")
|
|
_log("INFO", f"TV paths: {[str(p) for p in config.tv_paths]}")
|
|
_log("INFO", f"Movie paths: {[str(p) for p in config.movie_paths]}")
|
|
_log("INFO", f"Database: {config.db_path}")
|
|
_log("INFO", f"Config: manage_nfo={config.manage_nfo}, fix_mtimes={config.fix_dir_mtimes}")
|
|
_log("INFO", f"Movie priority: {config.movie_priority}")
|
|
|
|
# Create FastAPI app
|
|
app = create_app()
|
|
|
|
# Initialize components
|
|
dependencies = initialize_components()
|
|
|
|
# Register routes
|
|
register_routes(app, dependencies)
|
|
|
|
try:
|
|
uvicorn.run(
|
|
app,
|
|
host="0.0.0.0",
|
|
port=int(os.environ.get("PORT", "8080")),
|
|
reload=False
|
|
)
|
|
except KeyboardInterrupt:
|
|
_log("INFO", "NFOGuard stopped by user")
|
|
except Exception as e:
|
|
_log("ERROR", f"NFOGuard crashed: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |