refactor!: move downloader to seperate crate

This commit is contained in:
ThetaDev 2022-12-01 00:25:47 +01:00
parent a741a61a30
commit e063c04821
8 changed files with 96 additions and 83 deletions

View file

@ -10,7 +10,7 @@ keywords = ["youtube", "video", "music"]
include = ["/src", "README.md", "LICENSE", "!snapshots"] include = ["/src", "README.md", "LICENSE", "!snapshots"]
[workspace] [workspace]
members = [".", "codegen", "cli"] members = [".", "codegen", "downloader", "cli"]
[features] [features]
default = ["default-tls"] default = ["default-tls"]
@ -34,9 +34,8 @@ reqwest = { version = "0.11.11", default-features = false, features = [
"json", "json",
"gzip", "gzip",
"brotli", "brotli",
"stream",
] } ] }
tokio = { version = "1.20.0", features = ["macros", "time", "fs", "process"] } tokio = { version = "1.20.0", features = ["macros", "time"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.82" serde_json = "1.0.82"
serde_with = { version = "2.0.0", features = ["json"] } serde_with = { version = "2.0.0", features = ["json"] }
@ -47,8 +46,6 @@ time = { version = "0.3.15", features = [
"serde-well-known", "serde-well-known",
] } ] }
futures = "0.3.21" futures = "0.3.21"
indicatif = "0.17.0"
filenamify = "0.1.0"
ress = "0.11.4" ress = "0.11.4"
phf = "0.11.1" phf = "0.11.1"
base64 = "0.13.0" base64 = "0.13.0"

View file

@ -4,11 +4,16 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
rustypipe = {path = "../", default_features = false, features = ["rustls-tls-native-roots"]} rustypipe = { path = "../", default_features = false, features = [
reqwest = {version = "0.11.11", default_features = false} "rustls-tls-native-roots",
tokio = {version = "1.20.0", features = ["macros", "rt-multi-thread"]} ] }
rustypipe-downloader = { path = "../downloader", default_features = false, features = [
"rustls-tls-native-roots",
] }
reqwest = { version = "0.11.11", default_features = false }
tokio = { version = "1.20.0", features = ["macros", "rt-multi-thread"] }
indicatif = "0.17.0" indicatif = "0.17.0"
futures = "0.3.21" futures = "0.3.21"
anyhow = "1.0" anyhow = "1.0"
clap = { version = "3.2.16", features = ["derive"] } clap = { version = "3.2.16", features = ["derive"] }
env_logger = "0.9.0" env_logger = "0.10.0"

View file

@ -69,7 +69,7 @@ async fn download_single_video(
} }
} }
rustypipe::download::download_video( rustypipe_downloader::download_video(
&player_data, &player_data,
output_dir, output_dir,
output_fname, output_fname,

31
downloader/Cargo.toml Normal file
View file

@ -0,0 +1,31 @@
[package]
name = "rustypipe-downloader"
version = "0.1.0"
edition = "2021"
[features]
# Reqwest TLS
default-tls = ["reqwest/default-tls", "rustypipe/default-tls"]
rustls-tls-webpki-roots = [
"reqwest/rustls-tls-webpki-roots",
"rustypipe/rustls-tls-webpki-roots",
]
rustls-tls-native-roots = [
"reqwest/rustls-tls-native-roots",
"rustypipe/rustls-tls-native-roots",
]
[dependencies]
rustypipe = { path = "..", default-features = false }
once_cell = "1.12.0"
regex = "1.6.0"
thiserror = "1.0.36"
futures = "0.3.21"
indicatif = "0.17.0"
filenamify = "0.1.0"
log = "0.4.17"
reqwest = { version = "0.11.11", default-features = false, features = [
"stream",
] }
rand = "0.8.5"
tokio = { version = "1.20.0", features = ["macros", "fs", "process"] }

View file

@ -1,26 +1,27 @@
//! YouTube audio/video downloader //! # YouTube audio/video downloader
mod util;
use std::{borrow::Cow, cmp::Ordering, ffi::OsString, ops::Range, path::PathBuf, time::Duration}; use std::{borrow::Cow, cmp::Ordering, ffi::OsString, ops::Range, path::PathBuf, time::Duration};
use fancy_regex::Regex;
use futures::stream::{self, StreamExt}; use futures::stream::{self, StreamExt};
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use log::{debug, info}; use log::{debug, info};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use rand::Rng; use rand::Rng;
use regex::Regex;
use reqwest::{header, Client}; use reqwest::{header, Client};
use rustypipe::{
model::{AudioCodec, FileFormat, VideoCodec, VideoPlayer},
param::StreamFilter,
};
use tokio::{ use tokio::{
fs::{self, File}, fs::{self, File},
io::AsyncWriteExt, io::AsyncWriteExt,
process::Command, process::Command,
}; };
use crate::{ use util::DownloadError;
error::DownloadError,
model::{AudioCodec, FileFormat, VideoCodec, VideoPlayer},
param::StreamFilter,
util,
};
type Result<T> = core::result::Result<T, DownloadError>; type Result<T> = core::result::Result<T, DownloadError>;
@ -45,7 +46,7 @@ fn get_download_range(offset: u64, size: Option<u64>) -> Range<u64> {
fn parse_cr_header(cr_header: &str) -> Result<(u64, u64)> { fn parse_cr_header(cr_header: &str) -> Result<(u64, u64)> {
static PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r#"bytes (\d+)-(\d+)/(\d+)"#).unwrap()); static PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r#"bytes (\d+)-(\d+)/(\d+)"#).unwrap());
let captures = PATTERN.captures(cr_header).ok().flatten().ok_or_else(|| { let captures = PATTERN.captures(cr_header).ok_or_else(|| {
DownloadError::Progressive( DownloadError::Progressive(
format!( format!(
"Content-Range header '{}' does not match pattern", "Content-Range header '{}' does not match pattern",
@ -317,11 +318,9 @@ pub async fn download_video(
Some(_) => "mp4", Some(_) => "mp4",
None => match audio { None => match audio {
Some(audio) => match audio.codec { Some(audio) => match audio.codec {
AudioCodec::Unknown => {
return Err(DownloadError::Input("unknown audio codec".into()))
}
AudioCodec::Mp4a => "m4a", AudioCodec::Mp4a => "m4a",
AudioCodec::Opus => "opus", AudioCodec::Opus => "opus",
_ => return Err(DownloadError::Input("unknown audio codec".into())),
}, },
None => unreachable!(), None => unreachable!(),
}, },
@ -473,40 +472,3 @@ async fn convert_streams<P: Into<PathBuf>>(
} }
Ok(()) Ok(())
} }
/*
#[cfg(test)]
mod tests {
use crate::client::RustyTube;
use super::*;
use indicatif::{ProgressDrawTarget, ProgressStyle};
use reqwest::ClientBuilder;
// #[test_log::test(tokio::test)]
#[tokio::test]
async fn t_download_video() {
let http = ClientBuilder::new()
.user_agent(
"Mozilla/5.0 (Windows NT 10.0; Win64; rv:107.0) Gecko/20100101 Firefox/107.0",
)
.gzip(true)
.brotli(true)
.build()
.expect("unable to build the HTTP client");
// Indicatif setup
let pb = ProgressBar::new(0);
let rt = RustyTube::new();
let player_data = rt
.get_player("AbZH7XWDW_k", crate::client::ClientType::Desktop)
.await
.unwrap();
// download_video(&player_data, "tmp", "INVU", Some(1080), "ffmpeg", http, pb)
// .await
// .unwrap();
}
}
*/

42
downloader/src/util.rs Normal file
View file

@ -0,0 +1,42 @@
use std::{borrow::Cow, collections::BTreeMap};
use reqwest::Url;
/// Error from the video downloader
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum DownloadError {
/// Error from the HTTP client
#[error("http error: {0}")]
Http(#[from] reqwest::Error),
/// File IO error
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("FFmpeg error: {0}")]
Ffmpeg(Cow<'static, str>),
#[error("Progressive download error: {0}")]
Progressive(Cow<'static, str>),
#[error("input error: {0}")]
Input(Cow<'static, str>),
#[error("error: {0}")]
Other(Cow<'static, str>),
}
/// Split an URL into its base string and parameter map
///
/// Example:
///
/// `example.com/api?k1=v1&k2=v2 => example.com/api; {k1: v1, k2: v2}`
pub fn url_to_params(url: &str) -> Result<(Url, BTreeMap<String, String>), DownloadError> {
let mut parsed_url = Url::parse(url).map_err(|e| {
DownloadError::Other(format!("could not parse url `{}` err: {}", url, e).into())
})?;
let url_params: BTreeMap<String, String> = parsed_url
.query_pairs()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
parsed_url.set_query(None);
Ok((parsed_url, url_params))
}

View file

@ -10,9 +10,6 @@ pub enum Error {
/// Error from the deobfuscater /// Error from the deobfuscater
#[error("deobfuscator error: {0}")] #[error("deobfuscator error: {0}")]
Deobfuscation(#[from] DeobfError), Deobfuscation(#[from] DeobfError),
/// Error from the video downloader
#[error("download error: {0}")]
Download(#[from] DownloadError),
/// File IO error /// File IO error
#[error(transparent)] #[error(transparent)]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
@ -45,26 +42,6 @@ pub enum DeobfError {
Other(&'static str), Other(&'static str),
} }
/// Error from the video downloader
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum DownloadError {
/// Error from the HTTP client
#[error("http error: {0}")]
Http(#[from] reqwest::Error),
/// File IO error
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("FFmpeg error: {0}")]
Ffmpeg(Cow<'static, str>),
#[error("Progressive download error: {0}")]
Progressive(Cow<'static, str>),
#[error("input error: {0}")]
Input(Cow<'static, str>),
#[error("error: {0}")]
Other(Cow<'static, str>),
}
/// Error extracting content from YouTube /// Error extracting content from YouTube
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
#[non_exhaustive] #[non_exhaustive]

View file

@ -14,7 +14,6 @@ mod util;
pub mod cache; pub mod cache;
pub mod client; pub mod client;
pub mod download;
pub mod error; pub mod error;
pub mod model; pub mod model;
pub mod param; pub mod param;