#![feature(box_syntax)] use crate::frontend::{pages::MyError, style::CSS_BUNDLE}; use database::Database; use frontend::pages::{home::page_home, node::page_library_node}; use jellyremuxer::{RemuxerContext, SendWriter}; use library::Library; use log::{debug, warn}; use rocket::{ get, http::ContentType, launch, response::stream::{ByteStream, ReaderStream}, routes, State, }; use std::{fs::read_to_string, sync::Arc}; use tokio::{ io::{duplex, DuplexStream}, sync::mpsc, }; 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( selection: String, state: &State, ) -> Result<(ContentType, ReaderStream![DuplexStream]), MyError> { let (a, b) = duplex(8196); let item = state.library.nested("mili-bento-box-bivouac")?.get_item()?; let remuxer = state.remuxer.clone(); let b = SyncIoBridge::new(b); tokio::task::spawn_blocking(move || { if let Err(e) = remuxer.generate_into( b, item.fs_path.parent().unwrap().to_path_buf(), item.data.clone(), selection.split(",").map(|e| e.parse().unwrap()).collect(), ) { 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], ) }