use anyhow::{anyhow, bail}; use clap::Parser; use jellycommon::{ItemInfo, Source, SourceTrack, SourceTrackKind}; use jellyebml::{ matroska::MatroskaTag, read::EbmlReader, unflatten::{Unflat, Unflatten}, Master, }; use log::error; use std::{collections::BTreeMap, fs::File, io::Write, path::PathBuf}; #[derive(Parser)] struct Args { #[clap(short = 'I', long)] identifier: String, #[clap(short = 'O', long)] write_json: bool, #[clap(short, long)] title: String, #[clap(short = 'i', long)] input: PathBuf, } fn main() -> anyhow::Result<()> { env_logger::init_from_env("LOG"); let args = Args::parse(); let mut tracks = BTreeMap::new(); let input = File::open(args.input.clone()).unwrap(); let mut input = EbmlReader::new(input); // TODO dont traverse the entire file, if the tracks are listed at the end while let Some(item) = input.next() { let item = item?; match item { MatroskaTag::Tracks(_) => { let mut iter = Unflatten::new(&mut input); while let Some(Ok(Unflat { children, item })) = iter.next() { if !matches!(item, MatroskaTag::TrackEntry(_)) { panic!("no") } 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, ) = (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::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), _ => (), } } } _ => (), } } tracks.insert( index.unwrap(), SourceTrack { name: name.unwrap_or_else(|| "unnamed".to_string()), codec: codec.unwrap(), language: language.unwrap_or_else(|| "none".to_string()), kind: match kind.ok_or(anyhow!("track type required"))? { 1 => SourceTrackKind::Video { fps: 0.0, // TODO width: width.unwrap(), height: height.unwrap(), }, 2 => SourceTrackKind::Audio { bit_depth: 0, // TODO channels: channels.unwrap(), sample_rate: sample_rate.unwrap(), }, 17 => SourceTrackKind::Subtitles, _ => bail!("invalid track type"), }, }, ); } error!("break!"); drop(iter); error!("break done!"); break; } _ => (), } } let k = serde_json::to_string_pretty(&ItemInfo { title: args.title, source: Source { path: args.input.clone(), tracks, }, })?; if args.write_json { let mut f = File::create(format!("{}.json", args.identifier))?; f.write_all(k.as_bytes())?; } else { println!("{k}") } Ok(()) }