1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
#![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/<path..>?<selection>")]
fn stream(
path: PathBuf,
selection: String,
state: &State<AppState>,
) -> 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::<Result<Vec<_>, _>>()?;
let b = SyncIoBridge::new(b);
tokio::task::spawn_blocking(move || {
if let Err(e) = remuxer.generate_into(
b,
0,
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<RemuxerContext>,
}
#[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],
)
}
|