Compare commits
1 commit
master
...
accuwau/ch
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2496539fde |
7 changed files with 121 additions and 101 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -1,6 +1,6 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
|
@ -252,7 +252,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "n-mb"
|
name = "n-mb"
|
||||||
version = "1.1.3"
|
version = "1.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "n-mb"
|
name = "n-mb"
|
||||||
authors = ["Djkáťo <djkatovfx@gmail.com>"]
|
authors = ["Djkáťo <djkatovfx@gmail.com>"]
|
||||||
version = "1.1.3"
|
version = "1.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Simple ffmpeg wrapper to parse files to the most efficient formats within a set size"
|
description = "Simple ffmpeg wrapper to parse files to the most efficient formats within a set size"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[toolchain]
|
|
||||||
channel = "nightly"
|
|
201
src/encoder.rs
201
src/encoder.rs
|
@ -267,10 +267,8 @@ impl FFMPEGCommand {
|
||||||
.to_str()
|
.to_str()
|
||||||
.context("missing or bad path")?,
|
.context("missing or bad path")?,
|
||||||
]);
|
]);
|
||||||
#[cfg(debug_assertions)]
|
// dbg!(&command);
|
||||||
dbg!(&command);
|
// dbg!(&command2);
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
dbg!(&command2);
|
|
||||||
Ok(FFMPEGCommand {
|
Ok(FFMPEGCommand {
|
||||||
file_name: path.file_name().unwrap().to_str().unwrap().to_owned(),
|
file_name: path.file_name().unwrap().to_str().unwrap().to_owned(),
|
||||||
resolution: None,
|
resolution: None,
|
||||||
|
@ -343,19 +341,17 @@ pub enum EncodingStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn parse_ffprobe(path: &PathBuf) -> anyhow::Result<MediaData> {
|
async fn parse_ffprobe(path: &PathBuf) -> anyhow::Result<MediaData> {
|
||||||
let args = [
|
|
||||||
"-v",
|
|
||||||
"error",
|
|
||||||
"-select_streams",
|
|
||||||
"v:0",
|
|
||||||
"-show_entries",
|
|
||||||
"stream=width,height,duration,bit_rate",
|
|
||||||
"-of",
|
|
||||||
"csv=s=,:p=0",
|
|
||||||
];
|
|
||||||
|
|
||||||
let ffprobe = Command::new("ffprobe")
|
let ffprobe = Command::new("ffprobe")
|
||||||
.args(args)
|
.args([
|
||||||
|
"-v",
|
||||||
|
"error",
|
||||||
|
"-select_streams",
|
||||||
|
"v:0",
|
||||||
|
"-show_entries",
|
||||||
|
"stream=width,height,duration,bit_rate",
|
||||||
|
"-of",
|
||||||
|
"csv=s=,:p=0",
|
||||||
|
])
|
||||||
.arg(path)
|
.arg(path)
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.output()
|
.output()
|
||||||
|
@ -372,69 +368,6 @@ async fn parse_ffprobe(path: &PathBuf) -> anyhow::Result<MediaData> {
|
||||||
let width = mem.first().and_then(|v| v.parse::<u16>().ok());
|
let width = mem.first().and_then(|v| v.parse::<u16>().ok());
|
||||||
let height = mem.get(1).and_then(|v| v.parse::<u16>().ok());
|
let height = mem.get(1).and_then(|v| v.parse::<u16>().ok());
|
||||||
|
|
||||||
let duration = match mem.get(2).and_then(|v| v.parse::<f32>().ok()) {
|
|
||||||
Some(d) => d,
|
|
||||||
None => {
|
|
||||||
let metadata = get_attribute_from_meta("duration", path)
|
|
||||||
.await
|
|
||||||
.context("can't find duration anywhere")?;
|
|
||||||
let res = metadata.parse::<f32>();
|
|
||||||
//see if metadatat had seconds directly
|
|
||||||
if let Ok(d) = res {
|
|
||||||
d
|
|
||||||
} else {
|
|
||||||
// try to convert 00:00:00:00 to 0.000s
|
|
||||||
if !metadata.contains(":") {
|
|
||||||
return Err(anyhow::anyhow!("can't find duration of media anywhere"));
|
|
||||||
} else {
|
|
||||||
let mut res = 0.;
|
|
||||||
let mut iter = metadata.split(':').rev();
|
|
||||||
let secs = iter.next().map(|n| n.parse::<f32>().ok()).flatten();
|
|
||||||
let mins = iter
|
|
||||||
.next()
|
|
||||||
.map(|n| n.parse::<f32>().ok().map(|m| m * 60.))
|
|
||||||
.flatten();
|
|
||||||
let hrs = iter
|
|
||||||
.next()
|
|
||||||
.map(|n| n.parse::<f32>().ok().map(|h| h * 3600.))
|
|
||||||
.flatten();
|
|
||||||
let days = iter
|
|
||||||
.next()
|
|
||||||
.map(|n| n.parse::<f32>().ok().map(|d| d * 24. * 3600.))
|
|
||||||
.flatten();
|
|
||||||
if let Some(s) = secs {
|
|
||||||
res = res + s
|
|
||||||
};
|
|
||||||
if let Some(m) = mins {
|
|
||||||
res = res + m
|
|
||||||
};
|
|
||||||
if let Some(h) = hrs {
|
|
||||||
res = res + h
|
|
||||||
};
|
|
||||||
if let Some(d) = days {
|
|
||||||
res = res + d
|
|
||||||
};
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
dbg!(&duration);
|
|
||||||
let old_kbit_rate = mem
|
|
||||||
.get(3)
|
|
||||||
.and_then(|v| v.parse::<u32>().ok().map(|v| v / 1000));
|
|
||||||
|
|
||||||
let resolution = width.zip(height);
|
|
||||||
|
|
||||||
Ok(MediaData {
|
|
||||||
duration,
|
|
||||||
resolution,
|
|
||||||
old_kbit_rate,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_attribute_from_meta(attr: &str, path: &PathBuf) -> Option<String> {
|
|
||||||
let ffprobe = Command::new("ffprobe")
|
let ffprobe = Command::new("ffprobe")
|
||||||
.args([
|
.args([
|
||||||
"-v",
|
"-v",
|
||||||
|
@ -442,18 +375,114 @@ async fn get_attribute_from_meta(attr: &str, path: &PathBuf) -> Option<String> {
|
||||||
"-select_streams",
|
"-select_streams",
|
||||||
"v:0",
|
"v:0",
|
||||||
"-show_entries",
|
"-show_entries",
|
||||||
&format!("stream={attr}:stream_args={attr}"),
|
"format=duration,bit_rate",
|
||||||
"-of",
|
"-of",
|
||||||
"csv=s=,:p=0",
|
"csv=s=,:p=0",
|
||||||
])
|
])
|
||||||
.arg(path)
|
.arg(path)
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await?;
|
||||||
.ok()?;
|
ffprobe
|
||||||
ffprobe.status.exit_ok().ok()?;
|
.status
|
||||||
|
.exit_ok()
|
||||||
|
.context("Failed to run ffprobe. Make sure ffprobe is installed and file exists")?;
|
||||||
|
|
||||||
|
|
||||||
std::str::from_utf8(&ffprobe.stdout)
|
|
||||||
.ok()
|
let text = std::str::from_utf8(&ffprobe.stdout)?;
|
||||||
.map(|v| v.to_string())
|
|
||||||
|
let mem = text.split(',').collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let duration = mem
|
||||||
|
.get(0)
|
||||||
|
.and_then(|v| v.parse::<f32>().ok())
|
||||||
|
.context("missing duration")?;
|
||||||
|
let old_kbit_rate = mem
|
||||||
|
.get(1)
|
||||||
|
.and_then(|v| v.parse::<u32>().ok().map(|v| v / 1000));
|
||||||
|
|
||||||
|
let resolution = width.zip(height);
|
||||||
|
// if let Ok(dur) = parse_duration(text) {
|
||||||
|
// duration = dur
|
||||||
|
// } else {
|
||||||
|
// bail!("FFProbe missing duration in media. Is file corrupted or non-existent?")
|
||||||
|
// }
|
||||||
|
// let old_kbit_rate = parse_bitrate(text).ok();
|
||||||
|
//
|
||||||
|
// let mut resolution = None;
|
||||||
|
// if text.contains("Stream") {
|
||||||
|
// resolution = parse_resolution(text).ok();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
Ok(MediaData {
|
||||||
|
duration,
|
||||||
|
resolution,
|
||||||
|
old_kbit_rate,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_duration(text: &str) -> anyhow::Result<f32> {
|
||||||
|
let text = text[text.find("Duration").unwrap()..].to_owned();
|
||||||
|
let dur_text = text[text
|
||||||
|
.find(':')
|
||||||
|
.context("something wrong with the ffprobe output")?
|
||||||
|
+ 2
|
||||||
|
..text
|
||||||
|
.find(',')
|
||||||
|
.context("something wrong with the ffprobe output")?]
|
||||||
|
.to_owned();
|
||||||
|
let durs_text: Vec<&str> = dur_text.split(':').collect();
|
||||||
|
let mut durs_text_iter = durs_text.into_iter();
|
||||||
|
let h = durs_text_iter
|
||||||
|
.next()
|
||||||
|
.context("something wrong with the ffprobe output")?
|
||||||
|
.parse::<f32>()?;
|
||||||
|
let m = durs_text_iter
|
||||||
|
.next()
|
||||||
|
.context("something wrong with the ffprobe output")?
|
||||||
|
.parse::<f32>()?;
|
||||||
|
let s = durs_text_iter
|
||||||
|
.next()
|
||||||
|
.context("something wrong with the ffprobe output")?
|
||||||
|
.parse::<f32>()?;
|
||||||
|
Ok(h * 60. * 60. + m * 60. + s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn parse_bitrate(text: &str) -> anyhow::Result<u16> {
|
||||||
|
// let text = text[text.find("bitrate").unwrap()..].to_owned();
|
||||||
|
// let bitrate_text = text[9..text.find('/').unwrap() - 2].to_owned();
|
||||||
|
//
|
||||||
|
// Ok(bitrate_text.parse::<u16>()?)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn parse_resolution(text: &str) -> anyhow::Result<(u16, u16)> {
|
||||||
|
// let text = text[text.find("Stream").unwrap()..].to_owned();
|
||||||
|
// let sar_i = text
|
||||||
|
// .find("[SAR ")
|
||||||
|
// .context("something wrong with the ffprobe output")?
|
||||||
|
// - 1;
|
||||||
|
//
|
||||||
|
// let rb_b4_sar_i = text[..sar_i]
|
||||||
|
// .rfind(',')
|
||||||
|
// .context("something wrong with the ffprobe output")?
|
||||||
|
// + 1;
|
||||||
|
//
|
||||||
|
// let res_text = text[rb_b4_sar_i..sar_i].to_owned();
|
||||||
|
// let res_text = res_text.trim().to_owned();
|
||||||
|
//
|
||||||
|
// let width = res_text[..res_text
|
||||||
|
// .find('x')
|
||||||
|
// .context("something wrong with ffprobe output")?]
|
||||||
|
// .to_owned()
|
||||||
|
// .parse::<u16>()?;
|
||||||
|
//
|
||||||
|
// let height = res_text[res_text
|
||||||
|
// .find('x')
|
||||||
|
// .context("something wrong with ffprobe output")?
|
||||||
|
// + 1..]
|
||||||
|
// .to_owned()
|
||||||
|
// .parse::<u16>()?;
|
||||||
|
//
|
||||||
|
// Ok((width, height))
|
||||||
|
// }
|
||||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -150,14 +150,13 @@ async fn main() -> anyhow::Result<()> {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::time::interval;
|
use tokio::time::interval;
|
||||||
let commands_ref = commands.clone();
|
let commands_ref = commands.clone();
|
||||||
let mut intv = interval(Duration::from_millis(10));
|
let mut intv = interval(Duration::from_millis(50));
|
||||||
|
|
||||||
command_spawns.push(tokio::spawn(async move {
|
command_spawns.push(tokio::spawn(async move {
|
||||||
intv.tick().await;
|
intv.tick().await;
|
||||||
|
|
||||||
'line: while let Ok(Some(line)) = buff_reader.1.next_line().await {
|
'line: while let Ok(Some(line)) = buff_reader.1.next_line().await {
|
||||||
#[cfg(debug_assertions)]
|
// dbg!(&line);
|
||||||
dbg!(&line);
|
|
||||||
if let Some(time_start) = line.find("out_time=") {
|
if let Some(time_start) = line.find("out_time=") {
|
||||||
let time: Vec<String> = line[time_start + 10..]
|
let time: Vec<String> = line[time_start + 10..]
|
||||||
.split(':')
|
.split(':')
|
||||||
|
@ -170,13 +169,9 @@ async fn main() -> anyhow::Result<()> {
|
||||||
if let Ok(number) = part.parse::<f32>() {
|
if let Ok(number) = part.parse::<f32>() {
|
||||||
parsed_time.push(number)
|
parsed_time.push(number)
|
||||||
} else {
|
} else {
|
||||||
// parsed_time.push(0.);
|
break 'line;
|
||||||
// break 'line;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if parsed_time.len() == 0 {
|
|
||||||
parsed_time.append(&mut vec![0., 0., 0.]);
|
|
||||||
}
|
|
||||||
let time = parsed_time[0] * 3600. + parsed_time[1] * 60. + parsed_time[2];
|
let time = parsed_time[0] * 3600. + parsed_time[1] * 60. + parsed_time[2];
|
||||||
|
|
||||||
let mut command = commands_ref.lock().await;
|
let mut command = commands_ref.lock().await;
|
||||||
|
@ -186,8 +181,6 @@ async fn main() -> anyhow::Result<()> {
|
||||||
command.progressed_time = time;
|
command.progressed_time = time;
|
||||||
}
|
}
|
||||||
if let Some(progress_i) = line.find("progress=") {
|
if let Some(progress_i) = line.find("progress=") {
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
println!("found progress!, {}", &line[progress_i + 9..]);
|
|
||||||
let mut command = commands_ref.lock().await;
|
let mut command = commands_ref.lock().await;
|
||||||
let command = command.get_mut(buff_reader.0).unwrap();
|
let command = command.get_mut(buff_reader.0).unwrap();
|
||||||
|
|
||||||
|
|
BIN
test.avi
BIN
test.avi
Binary file not shown.
BIN
test.mkv
BIN
test.mkv
Binary file not shown.
Loading…
Reference in a new issue