/* 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) 2025 metamuffin */ use crate::seek_index::get_seek_index; use anyhow::{anyhow, bail}; use jellycommon::LocalTrack; use jellymatroska::{block::Block, read::EbmlReader, Master, MatroskaTag}; use log::debug; use std::{fs::File, io::BufReader, path::PathBuf}; pub type TrackExtract = Vec<(u64, Option, Vec)>; pub fn extract_track(path_base: PathBuf, track_info: LocalTrack) -> anyhow::Result { let source_path = path_base.join(track_info.path); let file = File::open(&source_path)?; let mut reader = EbmlReader::new(BufReader::new(file)); let index = get_seek_index(&source_path)?; let index = index .get(&(track_info.track as u64)) .ok_or(anyhow!("track missing"))?; let mut out = Vec::new(); for b in &index.blocks { reader.seek(b.source_off, MatroskaTag::BlockGroup(Master::Start))?; let (duration, block) = read_group(&mut reader)?; assert_eq!( track_info.track, block.track as usize, "seek index is wrong" ); out.push((b.pts, duration, block.data)) } Ok(out) } pub fn read_group(segment: &mut EbmlReader) -> anyhow::Result<(Option, Block)> { let (mut dur, mut block) = (None, None); for _ in 0..10 { let (_, item) = segment.next().ok_or(anyhow!("eof"))??; match item { MatroskaTag::Void(_) => (), MatroskaTag::Crc32(_) => (), MatroskaTag::Cluster(_) => bail!("unexpected cluster"), MatroskaTag::Timestamp(_) => (), MatroskaTag::SimpleBlock(block) => { return Ok((None, block)); // HDMV/PGS does not use duration?! } MatroskaTag::BlockGroup(Master::Start) => (), MatroskaTag::BlockGroup(Master::End) => return Ok((dur, block.unwrap())), MatroskaTag::BlockDuration(duration) => dur = Some(duration), MatroskaTag::Block(blk) => block = Some(blk), MatroskaTag::Cues(_) => bail!("reached cues, this is the end"), MatroskaTag::Segment(Master::End) => bail!("extractor reached segment end"), _ => debug!("(rs) tag ignored: {item:?}"), } } bail!(".") }