use std::sync::Arc; use tokio::sync::RwLock; use axum::Router; use clap::Parser; use music_agregator::{ api, config, services::{IndexerService, MetadataService, TorrentService}, AppServices, AppState, }; use tower_http::cors::{Any, CorsLayer}; use tower_http::trace::TraceLayer; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[derive(Parser)] #[command(name = "music-agregator")] #[command(about = "Music aggregation service with torrent and metadata integration")] struct Args { #[arg(short, long, default_value = "config.yaml")] config: String, #[arg(short, long)] port: Option, } #[tokio::main] async fn main() { tracing_subscriber::registry() .with(tracing_subscriber::fmt::layer()) .with(tracing_subscriber::EnvFilter::from_default_env()) .init(); let args = Args::parse(); let config = match config::Config::load(&args.config) { Ok(cfg) => { tracing::info!("loaded config from {}", args.config); cfg } Err(e) => { tracing::error!("failed to load config: {}", e); std::process::exit(1); } }; let indexer_service = match IndexerService::from_config(&config.indexers) { Ok(svc) => { tracing::info!("initialized {} indexer(s)", config.indexers.len()); svc } Err(e) => { tracing::error!("failed to initialize indexer service: {}", e); std::process::exit(1); } }; let torrent_service = match TorrentService::from_config(&config.torrent).await { Ok(svc) => { match &config.torrent { config::TorrentConfig::QBittorrent { url, .. } => { tracing::info!("connected to qBittorrent at {}", url); } config::TorrentConfig::Stub { log_path, .. } => { tracing::info!("using stub torrent client, logging to {}", log_path); } config::TorrentConfig::None => { tracing::info!("no torrent client configured"); } } svc } Err(e) => { tracing::warn!("failed to init torrent client: {} (continuing without)", e); TorrentService::new() } }; let mut metadata_service = MetadataService::new(&config.metadata.endpoint); match metadata_service.connect().await { Ok(()) => { tracing::info!( "connected to metadata service at {}", config.metadata.endpoint ); } Err(e) => { tracing::warn!( "failed to connect to metadata service: {} (continuing without metadata)", e ); } } let state: AppState = Arc::new(RwLock::new(AppServices::new( indexer_service, torrent_service, metadata_service, args.config.clone(), ))); let cors = CorsLayer::new() .allow_origin(Any) .allow_methods(Any) .allow_headers(Any); let app = Router::new() .nest("/api", api::routes(state)) .layer(cors) .layer(TraceLayer::new_for_http()); let port = args.port.unwrap_or(config.app.port); let addr = format!("0.0.0.0:{}", port); let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); tracing::info!("listening on {}", listener.local_addr().unwrap()); axum::serve(listener, app).await.unwrap(); }