begin working on proper stdout / stderr piping because we need it for yt-dlp file output and proper stderr parsing on the fly (for progress bars in future)
This commit is contained in:
parent
6ac64055cc
commit
a456da66a4
2 changed files with 81 additions and 4 deletions
|
|
@ -1,12 +1,18 @@
|
|||
use core::fmt;
|
||||
use std::process::Output;
|
||||
use std::str::Utf8Error;
|
||||
use std::{fs::OpenOptions, process::Stdio};
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::process::Command;
|
||||
use tracing::{event, Level};
|
||||
|
||||
use super::tmpfile::TmpFile;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SpawnError {
|
||||
CommandError(std::io::Error),
|
||||
NoStdErr,
|
||||
PipeError(std::io::Error),
|
||||
UtfError(Utf8Error),
|
||||
ErrorMessage(String),
|
||||
}
|
||||
|
|
@ -28,6 +34,8 @@ impl fmt::Display for SpawnError {
|
|||
use SpawnError as FE;
|
||||
match self {
|
||||
FE::CommandError(e) => write!(f, "Command::new - {}", e),
|
||||
FE::NoStdErr => write!(f, "spawned process has closed stderr!"),
|
||||
FE::PipeError(e) => write!(f, "pipe error - {}", e),
|
||||
FE::UtfError(_) => write!(f, "Error while decoding UTF8"),
|
||||
FE::ErrorMessage(msg) => write!(f, "ffmpeg error - {}", msg),
|
||||
}
|
||||
|
|
@ -51,3 +59,67 @@ pub async fn spawn(program: &str, args: &[&str]) -> Result<Output, SpawnError> {
|
|||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub async fn spawn_pipe(
|
||||
program: &str,
|
||||
args: &[&str],
|
||||
output_file: &TmpFile,
|
||||
) -> Result<(), SpawnError> {
|
||||
{
|
||||
let cmd_args = args.join(" ");
|
||||
event!(Level::INFO, "{} {}", program, cmd_args);
|
||||
}
|
||||
|
||||
let output_file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(&output_file.path)
|
||||
.map_err(|e| SpawnError::PipeError(e))?;
|
||||
|
||||
let mut process = Command::new(program)
|
||||
.args(args)
|
||||
.stdout(output_file)
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
let mut stderr = process.stderr.take().ok_or(SpawnError::NoStdErr)?;
|
||||
|
||||
let result = process.wait().await?;
|
||||
|
||||
if !result.success() {
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
stderr.read_to_end(&mut data).await?;
|
||||
|
||||
let message = std::str::from_utf8(&data)?;
|
||||
return Err(SpawnError::ErrorMessage(message.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::dl::spawn::{spawn_pipe, SpawnError};
|
||||
use crate::dl::tmpfile::TmpFile;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_spawn_pipe() {
|
||||
let stdout = TmpFile::new("stdout.test").unwrap();
|
||||
let result = spawn_pipe(
|
||||
"python",
|
||||
&[
|
||||
"-c",
|
||||
"import sys; print(file=sys.stderr, 'stderr test'); sys.exit(1)",
|
||||
],
|
||||
&stdout,
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(true, result.is_err());
|
||||
if let Err(e) = result {
|
||||
match e {
|
||||
SpawnError::ErrorMessage(msg) => assert_eq!("stderr test", msg),
|
||||
_ => panic!("SpawnError is not ErrorMessage!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use super::spawn::{spawn, SpawnError};
|
||||
use super::spawn::{spawn, spawn_pipe, SpawnError};
|
||||
use super::tmpfile::{TmpFile, TmpFileError};
|
||||
use core::fmt;
|
||||
use ordered_float::OrderedFloat;
|
||||
|
|
@ -254,21 +254,26 @@ impl YtDlp {
|
|||
}
|
||||
|
||||
pub async fn download(url: &str, info: &YtDlpInfo) -> Result<TmpFile, YtDlpError> {
|
||||
let file = TmpFile::new(&format!("{}.bin", info.id))?;
|
||||
let file = TmpFile::new(&info.id)?;
|
||||
|
||||
spawn(
|
||||
// since yt-dlp tend to randomly choose filename we can't rely on it,
|
||||
// and instead output to stdout and then pipe to our file
|
||||
// that way we can avoid bugs related to filename confusion
|
||||
let output = spawn_pipe(
|
||||
"python",
|
||||
&[
|
||||
"-m",
|
||||
"yt_dlp",
|
||||
url,
|
||||
"-o",
|
||||
&file.path,
|
||||
"-",
|
||||
"--force-overwrites",
|
||||
"--no-exec",
|
||||
],
|
||||
&file,
|
||||
)
|
||||
.await?;
|
||||
dbg!(output);
|
||||
|
||||
match file.exists() {
|
||||
true => Ok(file),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue