/* 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 anyhow::{anyhow, Result}; use jellybase::{permission::PermissionSetExt, CONF}; use jellycommon::{ jhls::{JhlsMetadata, JhlsTrack}, stream::StreamSpec, user::{PermissionSet, UserPermission}, Node, TrackSource, }; use tokio::io::{AsyncWriteExt, DuplexStream}; pub async fn jhls_stream( node: Node, track_sources: &[TrackSource], _spec: StreamSpec, mut b: DuplexStream, perms: &PermissionSet, ) -> Result<()> { let track_sources = track_sources.to_vec(); let media = node.public.media.clone().unwrap(); let tracks = tokio::task::spawn_blocking(move || { media .tracks .iter() .enumerate() .filter_map(|(i, t)| { // TODO can we maybe stream the subtitles as .mks in the future? that would be cool // subtitles can only be supported by snippet_index if we relax constraints on the contents of each cluster if matches!(t.kind, jellycommon::SourceTrackKind::Subtitles) { return Some(Ok(JhlsTrack { info: t.to_owned(), segments: vec![], // clients need to ignore this })); } match jellyremuxer::snippet::snippet_index( &CONF.library_path, &node.public, match &track_sources[i] { TrackSource::Local(x) => x, // TODO fetch seek index from the remote and create a single session to be sent in jhls TrackSource::Remote(_) => { return Some(Err(anyhow!("remote tracks dont work yet"))) } }, ) { Ok(segments) => Some(Ok::<_, anyhow::Error>(JhlsTrack { info: t.to_owned(), segments, })), Err(e) => Some(Err(e)), } }) .try_collect::>() }) .await??; let out = serde_json::to_string(&JhlsMetadata { tracks, extra_profiles: if perms.check(&UserPermission::Transcode) { CONF.transcoding_profiles.clone() } else { vec![] }, duration: media.duration, })?; tokio::spawn(async move { b.write_all(out.as_bytes()).await }); Ok(()) }