/* This file is part of jellything (https://codeberg.org/metamuffin/jellything) which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2023 metamuffin */ use super::ui::error::MyError; use crate::library::Library; use anyhow::{anyhow, Context}; use jellyremuxer::RemuxerContext; use log::debug; use log::warn; use rocket::http::Header; use rocket::response; use rocket::response::Responder; use rocket::Request; use rocket::Response; use rocket::{get, http::ContentType, response::stream::ReaderStream, State}; use std::ops::Deref; use std::path::PathBuf; use tokio::io::{duplex, DuplexStream}; use tokio_util::io::SyncIoBridge; pub fn stream_uri(path: &PathBuf, tracks: &Vec, webm: bool) -> String { format!( "/stream/{}?tracks={}&webm={}", path.to_str().unwrap().to_string(), tracks .iter() .map(|v| format!("{v}")) .collect::>() .join(","), if webm { "1" } else { "0" } ) } #[get("/stream/?&")] pub fn r_stream( path: PathBuf, webm: Option, tracks: String, remuxer: &State, library: &State, ) -> Result<(ContentType, ReaderStream![DuplexStream]), MyError> { let (a, b) = duplex(1024); let path = path.to_str().unwrap().to_string(); let item = library .nested(&path) .context("retrieving library node")? .get_item()?; let remuxer = remuxer.deref().clone(); let tracks = tracks .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, 0, item.fs_path.parent().unwrap().to_path_buf(), item.info.clone(), tracks, webm.unwrap_or(false), ) { warn!("stream stopped: {e}") } }); debug!("starting stream"); Ok((ContentType::WEBM, ReaderStream::one(a))) } pub struct StreamResponse { stream: DuplexStream, } #[rocket::async_trait] impl<'r> Responder<'r, 'static> for StreamResponse { fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { Response::build() .header(Header::new("accept-ranges", "bytes")) .header(ContentType::WEBM) .streamed_body(self.stream) .ok() } }