/* 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) 2024 metamuffin */ use anyhow::{anyhow, bail, Context, Result}; use jellybase::CONF; use jellycommon::{jhls::SubtitleCue, stream::StreamSpec, LocalTrack, Node}; use jellyremuxer::extract::extract_track; use jellytranscoder::subtitles::{parse_ass_blocks, parse_webvtt_blocks, write_webvtt}; use tokio::io::{AsyncWriteExt, DuplexStream}; pub async fn vtt_stream( json: bool, node: Node, local_tracks: Vec, spec: StreamSpec, mut b: DuplexStream, ) -> Result<()> { // TODO cache // TODO should use snippets too? big films take too long... let tracki = *spec.tracks.get(0).ok_or(anyhow!("no track selected"))?; let local_track = local_tracks.get(0).ok_or(anyhow!("no tracks"))?.clone(); let track = &node.public.media.unwrap().tracks[tracki]; let write = |blocks: Vec| -> anyhow::Result<()> { let output = if json { serde_json::to_string(&blocks)? } else { write_webvtt(node.public.title.clone().unwrap_or_default(), blocks) .context("writing webvtt")? }; tokio::task::spawn(async move { let _ = b.write_all(output.as_bytes()).await; }); Ok(()) }; match track.codec.as_str() { "D_WEBVTT/SUBTITLES" => { let webvtt_blocks = tokio::task::spawn_blocking(move || { extract_track(CONF.media_path.clone(), local_track) }) .await??; let subtitles = parse_webvtt_blocks(webvtt_blocks).context("parsing subtitles")?; write(subtitles)?; } "S_TEXT/UTF8" => bail!("no subrip yet"), "S_VOBSUB" => bail!("no vobsub yet"), "S_TEXT/ASS" => { let codec_private = local_track .codec_private .clone() .ok_or(anyhow!("ASS is missing required codec private data"))?; let ass_blocks = tokio::task::spawn_blocking(move || { extract_track(CONF.media_path.clone(), local_track) }) .await??; let subtitles = parse_ass_blocks(codec_private, ass_blocks).context("parsing subtitles")?; write(subtitles)?; } x => bail!("unknown sub codec {x:?}"), }; Ok(()) }