#![feature(box_syntax)] use crate::frontend::{pages::MyError, style::CSS_BUNDLE}; use anyhow::{anyhow, Context}; use database::Database; use frontend::pages::{home::page_home, node::page_library_node}; use jellyremuxer::RemuxerContext; use library::Library; use log::{debug, warn}; use rocket::{get, http::ContentType, launch, response::stream::ReaderStream, routes, State}; use std::{fs::read_to_string, path::PathBuf, sync::Arc}; use tokio::io::{duplex, DuplexStream}; use tokio_util::io::SyncIoBridge; pub mod database; pub mod frontend; pub mod library; #[get("/assets/style.css")] async fn assets_style() -> (ContentType, String) { ( ContentType::CSS, if cfg!(debug_assertions) { read_to_string("server/src/frontend/style/layout.css").unwrap() } else { CSS_BUNDLE.to_string() }, ) } #[get("/stream/?")] fn stream( path: PathBuf, selection: String, state: &State, ) -> Result<(ContentType, ReaderStream![DuplexStream]), MyError> { let (a, b) = duplex(1024); let path = path.to_str().unwrap().to_string(); let item = state .library .nested(&path) .context("retrieving library node")? .get_item()?; let remuxer = state.remuxer.clone(); let selection = selection .split(",") .map(|e| e.parse().map_err(|_| anyhow!("invalid number"))) .into_iter() .collect::, _>>()?; let b = SyncIoBridge::new(b); tokio::task::spawn_blocking(move || { if let Err(e) = remuxer.generate_into( b, offset, item.fs_path.parent().unwrap().to_path_buf(), item.data.clone(), selection, ) { warn!("stream stopped: {e}") } }); debug!("starting stream"); Ok((ContentType::WEBM, ReaderStream::one(a))) } pub struct AppState { pub database: Database, pub library: Library, pub remuxer: Arc, } #[launch] fn rocket() -> _ { env_logger::init_from_env("LOG"); let db_path = std::env::var("DB_PATH").unwrap_or("data/db".to_string()); let lib_path = std::env::var("LIB_PATH").unwrap_or("data/library".to_string()); let state = AppState { remuxer: RemuxerContext::new(), library: Library::open(&lib_path).unwrap(), database: Database::open(&db_path).unwrap(), }; rocket::build().manage(state).mount( "/", routes![page_home, page_library_node, assets_style, stream], ) }