diff options
author | metamuffin <metamuffin@disroot.org> | 2023-01-18 19:13:36 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2023-01-18 19:13:36 +0100 |
commit | e24a026c58c07a2800662b9f5f4fd3f61d53c1d1 (patch) | |
tree | 91c158ff294508f903f2489f204e313700d42828 /server/src/routes | |
parent | 46ef75431e1d34c63a690726a8ef584d175ddd30 (diff) | |
download | jellything-e24a026c58c07a2800662b9f5f4fd3f61d53c1d1.tar jellything-e24a026c58c07a2800662b9f5f4fd3f61d53c1d1.tar.bz2 jellything-e24a026c58c07a2800662b9f5f4fd3f61d53c1d1.tar.zst |
player config works
Diffstat (limited to 'server/src/routes')
-rw-r--r-- | server/src/routes/mod.rs | 4 | ||||
-rw-r--r-- | server/src/routes/stream.rs | 28 | ||||
-rw-r--r-- | server/src/routes/ui/error.rs | 2 | ||||
-rw-r--r-- | server/src/routes/ui/mod.rs | 1 | ||||
-rw-r--r-- | server/src/routes/ui/node.rs | 11 | ||||
-rw-r--r-- | server/src/routes/ui/player.rs | 95 | ||||
-rw-r--r-- | server/src/routes/ui/style/layout.css | 23 |
7 files changed, 143 insertions, 21 deletions
diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs index 4491e81..87d3051 100644 --- a/server/src/routes/mod.rs +++ b/server/src/routes/mod.rs @@ -4,6 +4,7 @@ use stream::r_stream; use ui::error::r_not_found; use ui::home::r_home; use ui::node::r_library_node; +use ui::player::r_player; use ui::style::{r_assets_font, r_assets_style}; pub mod stream; @@ -20,7 +21,8 @@ pub fn build_rocket(state: AppState) -> Rocket<Build> { r_library_node, r_assets_style, r_assets_font, - r_stream + r_stream, + r_player, ], ) } diff --git a/server/src/routes/stream.rs b/server/src/routes/stream.rs index 6ff6982..c49f51d 100644 --- a/server/src/routes/stream.rs +++ b/server/src/routes/stream.rs @@ -1,7 +1,6 @@ use super::ui::error::MyError; use crate::AppState; -use anyhow::anyhow; -use anyhow::Context; +use anyhow::{anyhow, Context}; use log::debug; use log::warn; use rocket::{get, http::ContentType, response::stream::ReaderStream, State}; @@ -9,10 +8,23 @@ use std::path::PathBuf; use tokio::io::{duplex, DuplexStream}; use tokio_util::io::SyncIoBridge; -#[get("/stream/<path..>?<selection>")] +pub fn stream_uri(path: &PathBuf, tracks: &Vec<u64>) -> String { + format!( + "/stream/{}?tracks={}", + path.to_str().unwrap().to_string(), + tracks + .iter() + .map(|v| format!("{v}")) + .collect::<Vec<_>>() + .join(",") + ) +} + +#[get("/stream/<path..>?<tracks>&<webm>")] pub fn r_stream( path: PathBuf, - selection: String, + webm: Option<bool>, + tracks: String, state: &State<AppState>, ) -> Result<(ContentType, ReaderStream![DuplexStream]), MyError> { let (a, b) = duplex(1024); @@ -23,20 +35,22 @@ pub fn r_stream( .context("retrieving library node")? .get_item()?; let remuxer = state.remuxer.clone(); - let selection = selection + let tracks = tracks .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, + item.info.clone(), + tracks, + webm.unwrap_or(false), ) { warn!("stream stopped: {e}") } diff --git a/server/src/routes/ui/error.rs b/server/src/routes/ui/error.rs index 2a1f8e6..0b24b62 100644 --- a/server/src/routes/ui/error.rs +++ b/server/src/routes/ui/error.rs @@ -20,6 +20,8 @@ pub fn r_not_found<'a>(status: Status, _request: &Request) -> HtmlTemplate<marku ) } +pub type MyResult<T> = Result<T, MyError>; + #[derive(Debug)] pub struct MyError(anyhow::Error); diff --git a/server/src/routes/ui/mod.rs b/server/src/routes/ui/mod.rs index aa0259d..1cad72c 100644 --- a/server/src/routes/ui/mod.rs +++ b/server/src/routes/ui/mod.rs @@ -12,6 +12,7 @@ pub mod home; pub mod layout; pub mod node; pub mod style; +pub mod player; pub struct HtmlTemplate<T>(pub String, pub T); diff --git a/server/src/routes/ui/node.rs b/server/src/routes/ui/node.rs index b6d0d30..ea23291 100644 --- a/server/src/routes/ui/node.rs +++ b/server/src/routes/ui/node.rs @@ -1,5 +1,5 @@ -use super::super::stream::rocket_uri_macro_r_stream; use super::error::MyError; +use super::player::player_uri; use crate::{ library::{Directory, Item, Node}, routes::ui::HtmlTemplate, @@ -14,10 +14,9 @@ pub async fn r_library_node( path: PathBuf, state: &State<AppState>, ) -> Result<HtmlTemplate<markup::DynRender>, MyError> { - let path = path.to_str().unwrap().to_string(); let node = state .library - .nested(&path) + .nested_path(&path) .context("retrieving library node")? .clone(); Ok(HtmlTemplate( @@ -52,10 +51,10 @@ markup::define! { } } ItemCard(item: Arc<Item>) { - span { a[href=&uri!(r_library_node(&item.lib_path)).to_string()] { @item.data.title } } + span { a[href=&uri!(r_library_node(&item.lib_path)).to_string()] { @item.info.title } } } ItemPage(item: Arc<Item>) { - h1 { @item.data.title } - video[src=&uri!(r_stream(&item.lib_path, "1,2")).to_string(), controls] {} + h1 { @item.info.title } + a[href=&player_uri(&item.lib_path)] { "Watch now" } } } diff --git a/server/src/routes/ui/player.rs b/server/src/routes/ui/player.rs new file mode 100644 index 0000000..69f6e7f --- /dev/null +++ b/server/src/routes/ui/player.rs @@ -0,0 +1,95 @@ +use super::HtmlTemplate; +use crate::routes::stream::stream_uri; +use crate::{library::Item, routes::ui::error::MyResult, AppState}; +use jellycommon::SourceTrackKind; +use log::warn; +use rocket::{get, FromForm, State}; +use std::{path::PathBuf, sync::Arc}; + +pub fn player_uri(path: &PathBuf) -> String { + format!("/player/{}", path.to_str().unwrap()) +} + +#[derive(FromForm, Default, Clone, Debug)] +pub struct PlayerConfig { + pub a: Option<u64>, + pub v: Option<u64>, + pub s: Option<u64>, + pub webm: bool, +} + +#[get("/player/<path..>?<conf..>", rank = 4)] +pub fn r_player( + state: &State<AppState>, + path: PathBuf, + conf: PlayerConfig, +) -> MyResult<HtmlTemplate<markup::DynRender<'_>>> { + warn!("{conf:?}"); + let item = state.library.nested_path(&path)?.get_item()?; + if conf.a.is_none() && conf.v.is_none() && conf.s.is_none() { + return player_conf(item.clone()); + } + + let tracks = conf + .a + .into_iter() + .chain(conf.v.into_iter()) + .chain(conf.s.into_iter()) + .collect::<Vec<_>>(); + + Ok(HtmlTemplate( + "Configure Player".to_string(), + markup::new! { + video[src=stream_uri(&item.lib_path, &tracks), controls]; + }, + )) +} + +pub fn player_conf<'a>(item: Arc<Item>) -> MyResult<HtmlTemplate<markup::DynRender<'a>>> { + let mut audio_tracks = vec![]; + let mut video_tracks = vec![]; + let mut sub_tracks = vec![]; + for (tid, track) in item.info.tracks.clone() { + match &track.kind { + SourceTrackKind::Audio { .. } => audio_tracks.push((tid, track)), + SourceTrackKind::Video { .. } => video_tracks.push((tid, track)), + SourceTrackKind::Subtitles { .. } => sub_tracks.push((tid, track)), + } + } + + Ok(HtmlTemplate( + "Configure Player".to_string(), + markup::new! { + h2 { "Watch: " @item.info.title } + form[method = "GET", action = ""] { + h3 { "Select tracks" } + + label[for="select-v"] { "Video: " } + select[name="v", id="select-v"] { + @for (tid, track) in &video_tracks { + option[value=tid] { @format!("{track}") } + } + } + br; + + label[for="select-a"] { "Audio: " } + select[name="a", id="select-a"] { + @for (tid, track) in &audio_tracks { + option[value=tid] { @format!("{track}") } + } + } + br; + + label[for="select-s"] { "Subtitles: " } + select[name="s", id="select-s"] { + @for (tid, track) in &sub_tracks { + option[value=tid] { @format!("{track}") } + } + } + br; + + input[type="submit", value="Start playback"]; + } + }, + )) +} diff --git a/server/src/routes/ui/style/layout.css b/server/src/routes/ui/style/layout.css index 0612a7e..2e54b1b 100644 --- a/server/src/routes/ui/style/layout.css +++ b/server/src/routes/ui/style/layout.css @@ -1,18 +1,18 @@ @font-face { - font-family: 'Cantarell'; - src: url(/assets/cantarell.woff2) format('woff2'); + font-family: "Cantarell"; + src: url(/assets/cantarell.woff2) format("woff2"); } * { - color: white; + color: rgb(218, 218, 218); font-family: "Cantarell", sans-serif; - font-weight: 300; + font-weight: 500; margin: 0px; padding: 0px; } body { - background-color: #1a1a1a; + background-color: #0f0f0f; width: 100vw; } @@ -23,7 +23,7 @@ nav { padding: 1em; width: calc(100vw - 2em); height: 2em; - background-color: #41414144; + background-color: #27272744; } nav h1 { @@ -42,4 +42,13 @@ nav h1 { padding: 1em; color: rgb(255, 117, 117); font-family: monospace; -}
\ No newline at end of file +} + +select, +input { + color: white; + background-color: black; +} +option { + font-family: "Cantarell", sans-serif; +} |