use anyhow::{anyhow, bail, Result}; use clap::Parser; use jellycommon::{ItemInfo, SourceTrack, SourceTrackKind}; use jellymatroska::{ matroska::MatroskaTag, read::EbmlReader, unflatten::{Unflat, Unflatten}, Master, }; use log::{debug, error, info, trace, warn}; use std::{fs::File, io::Write, path::PathBuf}; #[derive(Parser)] struct Args { #[clap(short = 'I', long)] item: PathBuf, #[clap(short = 'd', long)] dry: bool, #[clap(short = 'i', long)] input: PathBuf, } fn main() -> anyhow::Result<()> { env_logger::init_from_env("LOG"); let args = Args::parse(); let mut iteminfo: ItemInfo = serde_json::from_reader(File::open(&args.item)?)?; // let iteminfo_orig = iteminfo.clone(); let input = File::open(args.input.clone()).unwrap(); let mut input = EbmlReader::new(input); read(&mut input, &mut iteminfo)?; let k = serde_json::to_string_pretty(&iteminfo)?; if args.dry { println!("{k}") } else { let mut f = File::create(args.item)?; f.write_all(k.as_bytes())?; } Ok(()) } pub fn read(input: &mut EbmlReader, iteminfo: &mut ItemInfo) -> Result<()> { // TODO dont traverse the entire file, if the tracks are listed at the end let mut sbc = 0; while let Some(item) = input.next() { let item = item?; match item { MatroskaTag::Cluster(_) => { info!("start of cluster found"); let mut iter = Unflatten::new_with_end(input, item); while let Some(Ok(Unflat { children, item })) = iter.next() { match item { MatroskaTag::BlockGroup(_) => { debug!("group"); let mut iter = children.unwrap(); while let Some(Ok(Unflat { children, item })) = iter.next() { match item { MatroskaTag::Block(_) => (), _ => trace!("{item:?}"), } } } MatroskaTag::SimpleBlock(_) => { // debug!("simple"); } _ => debug!("(rc) tag ignored: {item:?}"), } } sbc += 1; } MatroskaTag::Tags(_) => { Unflatten::new_with_end(input, item); } MatroskaTag::Cues(_) => { let mut iter = Unflatten::new_with_end(input, item); while let Some(Ok(Unflat { children, item })) = iter.next() { match item { MatroskaTag::CuePoint(_) => { let mut children = children.unwrap(); while let Some(Ok(Unflat { children, item })) = children.next() { // error!("{item:?}") } } _ => (), } } } MatroskaTag::Chapters(_) => { Unflatten::new_with_end(input, item); } MatroskaTag::Tracks(_) => { let mut iter = Unflatten::new_with_end(input, item); while let Some(Ok(Unflat { children, item })) = iter.next() { match item { MatroskaTag::TrackEntry(_) => { let mut children = children.unwrap(); let ( mut index, mut language, mut codec, mut kind, mut sample_rate, mut channels, mut width, mut height, mut name, mut fps, mut bit_depth, ) = ( None, None, None, None, None, None, None, None, None, None, None, ); while let Some(Ok(Unflat { children, item })) = children.next() { match item { MatroskaTag::CodecID(b) => codec = Some(b), MatroskaTag::Language(v) => language = Some(v), MatroskaTag::TrackNumber(v) => index = Some(v), MatroskaTag::TrackType(v) => kind = Some(v), MatroskaTag::Name(v) => name = Some(v), MatroskaTag::Audio(_) => { let mut children = children.unwrap(); while let Some(Ok(Unflat { item, .. })) = children.next() { match item { MatroskaTag::Channels(v) => { channels = Some(v as usize) } MatroskaTag::SamplingFrequency(v) => { sample_rate = Some(v) } MatroskaTag::BitDepth(v) => bit_depth = Some(v), _ => (), } } } MatroskaTag::Video(_) => { let mut children = children.unwrap(); while let Some(Ok(Unflat { item, .. })) = children.next() { match item { MatroskaTag::PixelWidth(v) => width = Some(v), MatroskaTag::PixelHeight(v) => height = Some(v), MatroskaTag::FrameRate(v) => fps = Some(v), _ => (), } } } _ => (), } } let index = index.unwrap(); let kind = match kind.ok_or(anyhow!("track type required"))? { 1 => SourceTrackKind::Video { fps: fps.unwrap_or(f64::NAN), // TODO width: width.unwrap(), height: height.unwrap(), }, 2 => SourceTrackKind::Audio { bit_depth: bit_depth.unwrap_or(0) as usize, // TODO channels: channels.unwrap(), sample_rate: sample_rate.unwrap(), }, 17 => SourceTrackKind::Subtitles, _ => bail!("invalid track type"), }; iteminfo.tracks.insert( index, SourceTrack { name: name.unwrap_or_else(|| "unnamed".to_string()), codec: codec.unwrap(), language: language.unwrap_or_else(|| "none".to_string()), kind, }, ); } _ => debug!("(rt) tag ignored: {item:?}"), } } } MatroskaTag::Segment(Master::End) => break, _ => debug!("(r) tag ignored: {item:?}"), } } info!("clusters {sbc}"); Ok(()) }