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"
|
||||
version = "0.1.2"
|
||||
authors = ["daniel m <danielm@dnml.de>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
description = "Download files fast"
|
||||
|
||||
[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::dlreport::{DlReport, DlReporter};
|
||||
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 clireporter;
|
||||
mod dlreport;
|
||||
mod download;
|
||||
mod errors;
|
||||
mod integrations;
|
||||
mod misc;
|
||||
mod zippy;
|
||||
|
||||
struct DlRequest {
|
||||
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());
|
||||
|
||||
// Resolve the zippy url to the direct download url if necessary
|
||||
let url = if is_zippyshare_url(&dlreq.url) {
|
||||
match zippy::resolve_link(&dlreq.url).await {
|
||||
Ok(url) => url,
|
||||
Err(_e) => {
|
||||
report_msg!(
|
||||
reporter,
|
||||
"Zippyshare link could not be resolved, skipping: {}",
|
||||
dlreq.url
|
||||
);
|
||||
continue;
|
||||
let url = match is_integrated_url(&dlreq.url) {
|
||||
Some(service) => {
|
||||
match resolve_integrated_url(&dlreq.url, service).await {
|
||||
Ok(url) => url,
|
||||
Err(_e) => {
|
||||
report_msg!(
|
||||
reporter,
|
||||
"Zippyshare link could not be resolved, skipping: {}",
|
||||
dlreq.url
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dlreq.url.to_string()
|
||||
None => dlreq.url,
|
||||
};
|
||||
|
||||
let info = match http_file_info(&url).await {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user