85 lines
2.3 KiB
Python
85 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import asyncio
|
|
import logging
|
|
import signal
|
|
import sys
|
|
from typing import List
|
|
|
|
from src.config import AppConfig
|
|
from src.jellyfin.client import JellyfinClient
|
|
from src.player.mpv_controller import MpvController
|
|
from src.remotes.kodi_rpc import KodiRpcServer
|
|
from src.remotes.rest_api import RestApiServer
|
|
|
|
logger = logging.getLogger("jellyfin_client")
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description="Jellyfin HTPC Client & YouTube Player")
|
|
parser.add_argument("-c", "--config", help="Path to config.yaml file")
|
|
parser.add_argument("-d", "--debug", action="store_true", help="Enable debug logging")
|
|
return parser.parse_args()
|
|
|
|
|
|
async def run_app(config: AppConfig) -> None:
|
|
player = MpvController(config.player)
|
|
jellyfin = JellyfinClient(config.jellyfin, player)
|
|
kodi_rpc = KodiRpcServer(config.kodi_rpc, player)
|
|
rest_api = RestApiServer(config.web_api, player)
|
|
|
|
# Start services
|
|
await player.start()
|
|
await jellyfin.start()
|
|
await kodi_rpc.start()
|
|
await rest_api.start()
|
|
|
|
logger.info("Jellyfin HTPC Client is fully initialized and ready.")
|
|
|
|
# Setup signal handling for graceful shutdown
|
|
loop = asyncio.get_running_loop()
|
|
stop_event = asyncio.Event()
|
|
|
|
def _sig_handler() -> None:
|
|
logger.info("Shutdown signal received")
|
|
stop_event.set()
|
|
|
|
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
try:
|
|
loop.add_signal_handler(sig, _sig_handler)
|
|
except NotImplementedError:
|
|
pass # Windows or non-main thread
|
|
|
|
await stop_event.wait()
|
|
|
|
logger.info("Shutting down services...")
|
|
await rest_api.shutdown()
|
|
await kodi_rpc.shutdown()
|
|
await jellyfin.shutdown()
|
|
await player.shutdown()
|
|
logger.info("Graceful shutdown complete.")
|
|
|
|
|
|
def main() -> None:
|
|
args = parse_args()
|
|
log_level = logging.DEBUG if args.debug else logging.INFO
|
|
logging.basicConfig(
|
|
level=log_level,
|
|
format="%(asctime)s [%(levelname)s] (%(name)s) %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
)
|
|
|
|
config = AppConfig.load(args.config)
|
|
try:
|
|
asyncio.run(run_app(config))
|
|
except KeyboardInterrupt:
|
|
pass
|
|
except Exception as e:
|
|
logger.critical("Fatal error: %s", e, exc_info=True)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|