diff --git a/src/main.rs b/src/main.rs index 0f80121..6b7ef74 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,10 @@ mod download_error; mod client; +mod process; use std::{ - env::{current_dir, set_current_dir}, ffi::OsStr, - io::ErrorKind, - path::{Path, PathBuf}, - process::Command, + path::PathBuf, time::Duration }; @@ -14,12 +12,12 @@ use anyhow::anyhow; use clap::Parser; use env_logger::Env; use http::Uri; -use shellwords::escape; -use tokio::{fs::{create_dir, remove_dir_all}, task::JoinSet}; -use which::which; +use tokio::task::JoinSet; use log::{log, Level}; +use process::{enter_download_dir, ffmpeg, remove_download_dir}; + #[allow(dead_code)] #[derive(Debug)] @@ -65,20 +63,6 @@ async fn main() -> anyhow::Result<()> { return Err(anyhow!("Only filenames with .mp4 extension are allowed")); } - let basename = name.file_stem() - . ok_or(anyhow!("No valid filename given"))?; - - if let Err(error) = create_dir(basename).await { - if ErrorKind::AlreadyExists == error.kind() { - if ! Path::new(basename).is_dir() { - return Err(anyhow!("Unable to create directory at {:?}", basename)); - } - } else { - return Err(anyhow!("Unable to create directory at {:?}", basename)); - } - } - set_current_dir(basename)?; - let concurrency = args.concurrency.unwrap_or(10); let timeout = args.timeout.unwrap_or(10); let timeout = Duration::from_secs(timeout); @@ -87,6 +71,10 @@ async fn main() -> anyhow::Result<()> { let m3u8_path_and_query = m3u8_uri.path_and_query() . ok_or(anyhow!("Problem path and query in m3u8 uri"))?; + log!(Level::Info, "Create and chdir into temporary download dir..."); + + let basename = enter_download_dir(&name).await?; + log!(Level::Info, "Creating an HTTP client with Tower layers..."); let mut state = client::State::new(&m3u8_uri, concurrency, timeout)?; @@ -127,45 +115,15 @@ async fn main() -> anyhow::Result<()> { } } - let outfile = current_dir()?; - let outfile = outfile.parent().ok_or(anyhow!("Can't get output dir"))?; - let mut outfile = outfile.canonicalize()?; - outfile.push(name); - let outfile = outfile.as_os_str(); - - let ffmpeg = which("ffmpeg")?; - let ffmpeg = ffmpeg.as_path().as_os_str(); - - let index = Path::new(m3u8_uri.path()).file_name() - . ok_or(anyhow!("unable to get index filename from url"))?; - let index = Path::new(index).canonicalize()?; - let index = index.as_os_str(); - - log!(Level::Debug, "ffmpeg: {:?}", ffmpeg); - log!(Level::Debug, "index: {:?}", index); - log!(Level::Debug, "outfile: {:?}", outfile); - - log!( Level::Info - , "execute: {} -allowed_extensions ALL -i {} -c copy {}" - , escape(ffmpeg.try_into()?) - , escape(index.try_into()?) - , escape(outfile.try_into()?) ); - let mut child = Command::new(ffmpeg) - . arg("-allowed_extensions") - . arg("ALL") - . arg("-i") - . arg(index) - . arg("-c") - . arg("copy") - . arg(outfile) - . spawn()?; - - let status = child.wait()?; + log!(Level::Info, "Call ffmpeg to join ts files to single mp4..."); + + let status = ffmpeg(&name, &m3u8_uri).await?; log!(Level::Info, "ffmpeg status: {}", status); - set_current_dir("..")?; - remove_dir_all(basename).await?; + log!(Level::Info, "Leave and remove temporary download dir..."); + + remove_download_dir(&basename).await?; Ok(()) } diff --git a/src/process.rs b/src/process.rs new file mode 100644 index 0000000..033f12b --- /dev/null +++ b/src/process.rs @@ -0,0 +1,82 @@ +use std::{ + env::{current_dir, + set_current_dir}, + ffi::OsString, + io::ErrorKind, + path::Path, + process::{Command, ExitStatus} +}; + +use anyhow::anyhow; +use http::Uri; +use shellwords::escape; +use tokio::fs::{create_dir, remove_dir_all}; +use which::which; + +use log::{log, Level}; + + +pub(super) async fn enter_download_dir(name: &dyn AsRef) -> anyhow::Result { + let basename = name.as_ref().file_stem() + . ok_or(anyhow!("No valid filename given"))?; + + if let Err(error) = create_dir(basename).await { + if ErrorKind::AlreadyExists == error.kind() { + if ! Path::new(basename).is_dir() { + return Err(anyhow!("Unable to create directory at {:?}", basename)); + } + } else { + return Err(anyhow!("Unable to create directory at {:?}", basename)); + } + } + set_current_dir(basename)?; + + Ok(basename.into()) +} + +pub(super) async fn remove_download_dir(name: &dyn AsRef) -> anyhow::Result<()> { + let name = name.as_ref().parent() + . ok_or(anyhow!("Failed to get parent of download dir"))?; + set_current_dir(name)?; + remove_dir_all(name).await?; + + Ok(()) +} + +pub(super) async fn ffmpeg(name: &dyn AsRef, uri: &Uri) -> anyhow::Result { + let outfile = current_dir()?; + let outfile = outfile.parent().ok_or(anyhow!("Can't get output dir"))?; + let mut outfile = outfile.canonicalize()?; + outfile.push(name); + let outfile = outfile.as_os_str(); + + let which = which("ffmpeg"); + let ffmpeg = which?; + let ffmpeg = ffmpeg.as_os_str(); + + let index = Path::new(uri.path()).file_name() + . ok_or(anyhow!("unable to get index filename from url"))?; + let index = Path::new(index).canonicalize()?; + let index = index.as_os_str(); + + log!(Level::Debug, "ffmpeg: {:?}", ffmpeg); + log!(Level::Debug, "index: {:?}", index); + log!(Level::Debug, "outfile: {:?}", outfile); + + log!( Level::Debug + , "execute: {} -allowed_extensions ALL -i {} -c copy {}" + , escape(ffmpeg.try_into()?) + , escape(index.try_into()?) + , escape(outfile.try_into()?) ); + let mut child = Command::new(ffmpeg) + . arg("-allowed_extensions") + . arg("ALL") + . arg("-i") + . arg(index) + . arg("-c") + . arg("copy") + . arg(outfile) + . spawn()?; + + Ok(child.wait()?) +}