use clap::{ArgAction, Parser};
use rsmp4decrypt::Mp4Decryptor;
use std::{
io::{self, Write},
path::PathBuf,
};
#[derive(Debug, Parser)]
#[command(
name = "rsmp4decrypt",
about = "Rust bindings and CLI for Bento4 mp4decrypt",
version = concat!(env!("CARGO_PKG_VERSION"), " (Bento4 v1.6.0-641)"),
after_help = "Key syntax:\n --key <id>:<key>\n <id> is either a track ID in decimal or a 128-bit KID in hex\n <key> is a 128-bit decryption key in hex\n note: KIDs are only applicable to some encryption methods like MPEG-CENC\n note: --fragments-info is typically the init segment when decrypting fMP4 fragments"
)]
struct Cli {
#[arg(long = "show-progress", action = ArgAction::SetTrue)]
show_progress: bool,
#[arg(long = "key", value_name = "ID:KEY", required = true, action = ArgAction::Append)]
keys: Vec<String>,
#[arg(long = "fragments-info", value_name = "FILE")]
fragments_info: Option<PathBuf>,
#[arg(value_name = "INPUT")]
input: PathBuf,
#[arg(value_name = "OUTPUT")]
output: PathBuf,
}
fn main() {
if let Err(error) = run() {
eprintln!("error: {error}");
std::process::exit(1);
}
}
fn run() -> Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse();
let mut builder = Mp4Decryptor::builder();
for key in &cli.keys {
builder = builder.key_spec(key)?;
}
let decryptor = builder.build()?;
if cli.show_progress {
let mut progress = |step: u32, total: u32| {
print!("\r{step}/{total}");
let _ = io::stdout().flush();
};
decryptor.decrypt_file_with_progress(
&cli.input,
&cli.output,
cli.fragments_info.as_ref(),
&mut progress,
)?;
println!();
} else {
decryptor.decrypt_file(&cli.input, &cli.output, cli.fragments_info.as_ref())?;
}
Ok(())
}