mangofetch_core/core/
media_processor.rs1use crate::core::hls_downloader::HlsDownloadResult;
2use tokio_util::sync::CancellationToken;
3
4pub struct MediaProcessor;
5
6impl MediaProcessor {
7 #[allow(clippy::too_many_arguments)]
8 pub async fn download_hls(
9 m3u8_url: &str,
10 output: &str,
11 referer: &str,
12 bytes_tx: Option<tokio::sync::mpsc::UnboundedSender<u64>>,
13 cancel_token: CancellationToken,
14 max_concurrent: u32,
15 max_retries: u32,
16 client: Option<reqwest::Client>,
17 ) -> anyhow::Result<HlsDownloadResult> {
18 let downloader = match client {
19 Some(c) => crate::core::hls_downloader::HlsDownloader::with_client(c),
20 None => crate::core::hls_downloader::HlsDownloader::new(),
21 };
22 downloader
23 .download(
24 m3u8_url,
25 output,
26 referer,
27 bytes_tx,
28 cancel_token,
29 max_concurrent,
30 max_retries,
31 )
32 .await
33 }
34
35 #[allow(clippy::too_many_arguments)]
36 pub async fn download_hls_with_quality(
37 m3u8_url: &str,
38 output: &str,
39 referer: &str,
40 bytes_tx: Option<tokio::sync::mpsc::UnboundedSender<u64>>,
41 cancel_token: CancellationToken,
42 max_concurrent: u32,
43 max_retries: u32,
44 client: Option<reqwest::Client>,
45 max_height: Option<u32>,
46 ) -> anyhow::Result<HlsDownloadResult> {
47 let downloader = match client {
48 Some(c) => crate::core::hls_downloader::HlsDownloader::with_client(c),
49 None => crate::core::hls_downloader::HlsDownloader::new(),
50 };
51 downloader
52 .download_with_quality(
53 m3u8_url,
54 output,
55 referer,
56 bytes_tx,
57 cancel_token,
58 max_concurrent,
59 max_retries,
60 max_height,
61 )
62 .await
63 }
64
65 pub async fn remux(input: &str, output: &str) -> anyhow::Result<()> {
66 let status = crate::core::process::command("ffmpeg")
67 .args(["-y", "-i", input, "-c", "copy", output])
68 .stdout(std::process::Stdio::null())
69 .stderr(std::process::Stdio::piped())
70 .status()
71 .await?;
72
73 if !status.success() {
74 anyhow::bail!("FFmpeg remux falhou com status {}", status);
75 }
76
77 Ok(())
78 }
79
80 pub async fn merge_audio_video(video: &str, audio: &str, output: &str) -> anyhow::Result<()> {
81 let status = crate::core::process::command("ffmpeg")
82 .args([
83 "-y", "-i", video, "-i", audio, "-map", "0:v", "-map", "1:a", "-c", "copy", output,
84 ])
85 .stdout(std::process::Stdio::null())
86 .stderr(std::process::Stdio::piped())
87 .status()
88 .await?;
89
90 if !status.success() {
91 anyhow::bail!("FFmpeg merge falhou com status {}", status);
92 }
93
94 Ok(())
95 }
96
97 pub async fn download_direct(
98 url: &str,
99 output: &str,
100 headers: &[(&str, &str)],
101 ) -> anyhow::Result<()> {
102 let mut args = vec!["-y".to_string()];
103
104 if !headers.is_empty() {
105 let header_str: String = headers
106 .iter()
107 .map(|(k, v)| format!("{}: {}\r\n", k, v))
108 .collect();
109 args.extend(["-headers".to_string(), header_str]);
110 }
111
112 args.extend([
113 "-i".to_string(),
114 url.to_string(),
115 "-c".to_string(),
116 "copy".to_string(),
117 output.to_string(),
118 ]);
119
120 let status = crate::core::process::command("ffmpeg")
121 .args(&args)
122 .stdout(std::process::Stdio::null())
123 .stderr(std::process::Stdio::piped())
124 .status()
125 .await?;
126
127 if !status.success() {
128 anyhow::bail!("FFmpeg download_direct falhou com status {}", status);
129 }
130
131 Ok(())
132 }
133}
134
135pub fn check_ffmpeg() -> bool {
136 crate::core::process::std_command("ffmpeg")
137 .arg("-version")
138 .stdout(std::process::Stdio::null())
139 .stderr(std::process::Stdio::null())
140 .status()
141 .map(|s| s.success())
142 .unwrap_or(false)
143}
144
145pub fn check_ytdlp() -> bool {
146 crate::core::process::std_command("yt-dlp")
147 .arg("--version")
148 .stdout(std::process::Stdio::null())
149 .stderr(std::process::Stdio::null())
150 .status()
151 .map(|s| s.success())
152 .unwrap_or(false)
153}
154
155pub fn check_dependencies() -> Vec<String> {
156 let mut missing = Vec::new();
157 if !check_ytdlp() {
158 missing.push("yt-dlp".into());
159 }
160 if !check_ffmpeg() {
161 missing.push("ffmpeg".into());
162 }
163 missing
164}