Add support for sendcm
- Change the special url resolver code to better (still not optimally) support different services besides zippyshare - Implement support for sendcm
This commit is contained in:
parent
e2c4d3572b
commit
a46bc063ff
@ -2,7 +2,7 @@
|
|||||||
name = "ffdl"
|
name = "ffdl"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
authors = ["daniel m <danielm@dnml.de>"]
|
authors = ["daniel m <danielm@dnml.de>"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
description = "Download files fast"
|
description = "Download files fast"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
26
src/integrations.rs
Normal file
26
src/integrations.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
mod zippy;
|
||||||
|
mod sendcm;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
pub enum IntegratedService {
|
||||||
|
ZippyShare,
|
||||||
|
SendCm,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_integrated_url(url: &str) -> Option<IntegratedService> {
|
||||||
|
if zippy::is_zippyshare_url(url) {
|
||||||
|
Some(IntegratedService::ZippyShare)
|
||||||
|
} else if sendcm::is_sendcm_url(url) {
|
||||||
|
Some(IntegratedService::SendCm)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn resolve_integrated_url(url: &str, service: IntegratedService) -> Result<String> {
|
||||||
|
match service {
|
||||||
|
IntegratedService::ZippyShare => zippy::resolve_link(url).await,
|
||||||
|
IntegratedService::SendCm => sendcm::resolve_link(url).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/integrations/sendcm.rs
Normal file
84
src/integrations/sendcm.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use std::io::{Error, ErrorKind};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
pub fn is_sendcm_url(url: &str) -> bool {
|
||||||
|
Regex::new(r"^https?://send\.cm/(?:d/)?[0-9a-zA-Z]+$")
|
||||||
|
.unwrap()
|
||||||
|
.is_match(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Updated: 01.04.2022
|
||||||
|
Link generation code:
|
||||||
|
- A post request is sent to the server using the form described below
|
||||||
|
- The id field is the value which is used to generate the link
|
||||||
|
- If the id is not found, the link is not generated
|
||||||
|
- The id is the same as the url suffix when NOT using a /d/ prefix url
|
||||||
|
- The reponse to the post request is a 302 redirect to the generated link
|
||||||
|
|
||||||
|
```
|
||||||
|
<form name="F1" method="POST" action="https://send.cm">
|
||||||
|
<input type="hidden" name="op" value="download2">
|
||||||
|
<input type="hidden" name="id" value="xxxxxxxxxx">
|
||||||
|
<input type="hidden" name="rand" value="">
|
||||||
|
<input type="hidden" name="referer" value="">
|
||||||
|
<input type="hidden" name="method_free" value="">
|
||||||
|
<input type="hidden" name="method_premium" value="">
|
||||||
|
......
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
pub async fn resolve_link(url: &str) -> Result<String> {
|
||||||
|
let user_agent = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:77.0) Gecko/20100101 Firefox/77.0";
|
||||||
|
let accept =
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8";
|
||||||
|
|
||||||
|
// Add a few extra headers to the request in order to be less suspicious
|
||||||
|
let body = reqwest::Client::new()
|
||||||
|
.get(url)
|
||||||
|
.header("User-Agent", user_agent)
|
||||||
|
.header("Accept", accept)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.text()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let re_link = Regex::new(r#"<input type="hidden" name="id" value="([0-9a-zA-Z]+)">"#)?;
|
||||||
|
|
||||||
|
let cap_link = match re_link.captures(&body) {
|
||||||
|
Some(cap) => cap,
|
||||||
|
None => return Err(Error::new(ErrorKind::Other, "Link not found").into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = &match cap_link.get(1) {
|
||||||
|
Some(id) => id.as_str(),
|
||||||
|
None => return Err(Error::new(ErrorKind::Other, "Link not found").into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let resp = reqwest::ClientBuilder::new()
|
||||||
|
.redirect(reqwest::redirect::Policy::none())
|
||||||
|
.build()?
|
||||||
|
.post("https://send.cm")
|
||||||
|
.header("User-Agent", user_agent)
|
||||||
|
.header("Accept", accept)
|
||||||
|
.form(&[
|
||||||
|
("op", "download2"),
|
||||||
|
("id", id),
|
||||||
|
("rand", ""),
|
||||||
|
("referer", ""),
|
||||||
|
("method_free", ""),
|
||||||
|
("method_premium", ""),
|
||||||
|
])
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if resp.status().is_redirection() {
|
||||||
|
match resp.headers().get(reqwest::header::LOCATION) {
|
||||||
|
Some(location) => Ok(location.to_str()?.to_string()),
|
||||||
|
None => Err(Error::new(ErrorKind::Other, "Location header not found").into()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::new(ErrorKind::Other, "Link not found").into())
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/main.rs
29
src/main.rs
@ -15,15 +15,15 @@ use crate::args::CLIArgs;
|
|||||||
use crate::clireporter::cli_print_reports;
|
use crate::clireporter::cli_print_reports;
|
||||||
use crate::dlreport::{DlReport, DlReporter};
|
use crate::dlreport::{DlReport, DlReporter};
|
||||||
use crate::download::{download_feedback, download_feedback_multi, http_file_info};
|
use crate::download::{download_feedback, download_feedback_multi, http_file_info};
|
||||||
use crate::zippy::is_zippyshare_url;
|
use crate::integrations::{is_integrated_url, resolve_integrated_url};
|
||||||
|
|
||||||
mod args;
|
mod args;
|
||||||
mod clireporter;
|
mod clireporter;
|
||||||
mod dlreport;
|
mod dlreport;
|
||||||
mod download;
|
mod download;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
mod integrations;
|
||||||
mod misc;
|
mod misc;
|
||||||
mod zippy;
|
|
||||||
|
|
||||||
struct DlRequest {
|
struct DlRequest {
|
||||||
id: usize,
|
id: usize,
|
||||||
@ -113,20 +113,21 @@ async fn download_job(urls: SyncQueue, reporter: UnboundedSender<DlReport>, cli_
|
|||||||
let reporter = DlReporter::new(dlreq.id as u32, reporter.clone());
|
let reporter = DlReporter::new(dlreq.id as u32, reporter.clone());
|
||||||
|
|
||||||
// Resolve the zippy url to the direct download url if necessary
|
// Resolve the zippy url to the direct download url if necessary
|
||||||
let url = if is_zippyshare_url(&dlreq.url) {
|
let url = match is_integrated_url(&dlreq.url) {
|
||||||
match zippy::resolve_link(&dlreq.url).await {
|
Some(service) => {
|
||||||
Ok(url) => url,
|
match resolve_integrated_url(&dlreq.url, service).await {
|
||||||
Err(_e) => {
|
Ok(url) => url,
|
||||||
report_msg!(
|
Err(_e) => {
|
||||||
reporter,
|
report_msg!(
|
||||||
"Zippyshare link could not be resolved, skipping: {}",
|
reporter,
|
||||||
dlreq.url
|
"Zippyshare link could not be resolved, skipping: {}",
|
||||||
);
|
dlreq.url
|
||||||
continue;
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
None => dlreq.url,
|
||||||
dlreq.url.to_string()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let info = match http_file_info(&url).await {
|
let info = match http_file_info(&url).await {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user