aboutsummaryrefslogtreecommitdiff
path: root/server/src/main.rs
blob: 6243b3f1d4ad5853204e18082ad914586c39f09c (plain)
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,
            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<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],
    )
}