/* 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::CONF; use jellycommon::{ stream::{StreamFormat, StreamSpec}, LocalTrack, Node, }; use std::{fmt::Write, ops::Range}; use tokio::{ io::{AsyncWriteExt, DuplexStream}, task::spawn_blocking, }; pub async fn hls_master_stream( _node: Node, _local_tracks: Vec, spec: StreamSpec, mut b: DuplexStream, ) -> Result<()> { let mut out = String::new(); writeln!(out, "#EXTM3U")?; writeln!(out, "#EXT-X-VERSION:4")?; writeln!(out, "#EXT-X-INDEPENDENT-SEGMENTS")?; for t in spec.tracks { let uri = format!( "stream?{}", StreamSpec { tracks: vec![t], format: StreamFormat::HlsVariant, ..Default::default() } .to_query() ); // writeln!(out,"#EXT-X-MEDIA:NAME=\"track {t}\", TYPE=AUDIO, GROUP-ID=\"track{t}\", DEFAULT=YES, AUTOSELECT=YES, URI=\"{uri}\"")?; writeln!(out, "{uri}")?; } tokio::spawn(async move { b.write_all(out.as_bytes()).await }); Ok(()) } pub async fn hls_variant_stream( node: Node, local_tracks: Vec, mut spec: StreamSpec, mut b: DuplexStream, ) -> Result<()> { let snips = spawn_blocking(move || { jellyremuxer::snippet::snippet_index( &CONF.library_path, &node.public, local_tracks.get(0).ok_or(anyhow!("no track"))?, ) }) .await??; let mut out = String::new(); writeln!(out, "#EXTM3U")?; writeln!(out, "#EXT-X-VERSION:4")?; spec.format = StreamFormat::Snippet; for (i, Range { start, end }) in snips.iter().enumerate() { writeln!(out, "#EXTINF:{},", end - start)?; spec.index = Some(i); writeln!(out, "stream?{}", spec.to_query())?; } tokio::spawn(async move { b.write_all(out.as_bytes()).await }); Ok(()) }