use std::{path::PathBuf, process::Stdio}; // yt metadata pub fn get_youtube_metadata(path: &PathBuf) -> Option { let cmd = std::process::Command::new("mkvextract") .arg("-q") .arg("attachments") .arg(path.to_str().unwrap()) .arg("1:/proc/self/fd/1") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .unwrap(); let out = cmd.wait_with_output().unwrap(); let stdout = String::from_utf8_lossy(&out.stdout).to_string(); //let stderr = String::from_utf8_lossy(&out.stderr).to_string(); // println!("path {path:?} -- stdout {stdout} -- stderr {stderr}"); let val = serde_json::from_str(&stdout).ok()?; log::info!("Extracted YouTube Metadata from {path:?}"); Some(YouTubeMeta { inner: val }) } pub struct YouTubeMeta { inner: serde_json::Value, } impl YouTubeMeta { pub fn title(&self) -> String { self.inner .as_object() .unwrap() .get("title") .unwrap() .as_str() .unwrap() .to_owned() } pub fn youtube_id(&self) -> Option { self.inner .as_object() .unwrap() .get("id") .unwrap() .as_str() .map(std::borrow::ToOwned::to_owned) } pub fn description(&self) -> Option { self.inner .as_object() .unwrap() .get("description") .unwrap() .as_str() .map(std::borrow::ToOwned::to_owned) } pub fn uploader_name(&self) -> Option { self.inner .as_object() .unwrap() .get("uploader") .unwrap() .as_str() .map(std::borrow::ToOwned::to_owned) } pub fn uploader_id(&self) -> Option { self.inner .as_object() .unwrap() .get("channel_id") .unwrap() .as_str() .map(std::borrow::ToOwned::to_owned) } pub fn duration(&self) -> Option { self.inner .as_object() .unwrap() .get("duration") .unwrap() .as_i64() .map(|x| x.to_owned()) } pub fn views(&self) -> Option { self.inner .as_object() .unwrap() .get("view_count") .unwrap() .as_i64() .map(|x| x.to_owned()) } pub fn categories(&self) -> Option> { self.inner .as_object() .unwrap() .get("categories") .unwrap_or(&serde_json::json!(Vec::<&str>::new())) .as_array() .unwrap() .iter() .map(|x| x.as_str().map(std::borrow::ToOwned::to_owned)) .collect() } pub fn tags(&self) -> Option> { if let Some(tags) = self.inner.as_object().unwrap().get("tags") { return tags .as_array() .unwrap() .iter() .map(|x| x.as_str().map(std::borrow::ToOwned::to_owned)) .collect(); } None } pub fn upload_date(&self) -> Option { self.inner .as_object() .unwrap() .get("upload_date") .unwrap() .as_str() .map(|x| chrono::NaiveDate::parse_from_str(x, "%Y%m%d").unwrap()) } }