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:
Daniel M 2022-04-01 00:40:42 +02:00
parent e2c4d3572b
commit a46bc063ff
5 changed files with 126 additions and 15 deletions

View File

@ -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
View 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,
}
}

View 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())
}
}

View File

@ -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,8 +113,9 @@ 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 {
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!(
@ -125,8 +126,8 @@ async fn download_job(urls: SyncQueue, reporter: UnboundedSender<DlReport>, cli_
continue;
}
}
} else {
dlreq.url.to_string()
}
None => dlreq.url,
};
let info = match http_file_info(&url).await {