Individually proxy subreddit and user icons
This commit is contained in:
parent
65e4ceff7b
commit
b3341b49c0
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1147,7 +1147,6 @@ dependencies = [
|
|||||||
"askama",
|
"askama",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"async-std",
|
"async-std",
|
||||||
"base64 0.13.0",
|
|
||||||
"cached",
|
"cached",
|
||||||
"clap",
|
"clap",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -11,7 +11,6 @@ edition = "2018"
|
|||||||
tide = { version = "0.16.0", default-features = false, features = ["h1-server", "cookies"] }
|
tide = { version = "0.16.0", default-features = false, features = ["h1-server", "cookies"] }
|
||||||
async-std = { version = "1.9.0", features = ["attributes"] }
|
async-std = { version = "1.9.0", features = ["attributes"] }
|
||||||
surf = { version = "2.1.0", default-features = false, features = ["curl-client", "encoding"] }
|
surf = { version = "2.1.0", default-features = false, features = ["curl-client", "encoding"] }
|
||||||
base64 = "0.13.0"
|
|
||||||
cached = "0.23.0"
|
cached = "0.23.0"
|
||||||
askama = { version = "0.10.5", default-features = false, features = ["config"] }
|
askama = { version = "0.10.5", default-features = false, features = ["config"] }
|
||||||
serde = { version = "1.0.123", features = ["derive"] }
|
serde = { version = "1.0.123", features = ["derive"] }
|
||||||
|
29
src/main.rs
29
src/main.rs
@ -9,6 +9,7 @@ mod utils;
|
|||||||
|
|
||||||
// Import Crates
|
// Import Crates
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
use proxy::handler;
|
||||||
use tide::{
|
use tide::{
|
||||||
utils::{async_trait, After},
|
utils::{async_trait, After},
|
||||||
Middleware, Next, Request, Response,
|
Middleware, Next, Request, Response,
|
||||||
@ -171,13 +172,27 @@ async fn main() -> tide::Result<()> {
|
|||||||
app.at("/apple-touch-icon.png/").get(iphone_logo);
|
app.at("/apple-touch-icon.png/").get(iphone_logo);
|
||||||
|
|
||||||
// Proxy media through Libreddit
|
// Proxy media through Libreddit
|
||||||
app.at("/proxy/*url/").get(proxy::handler);
|
app
|
||||||
app.at("/vid/:id/:size/").get(proxy::video);
|
.at("/vid/:id/:size/") /* */
|
||||||
app.at("/img/:id/").get(proxy::image);
|
.get(|req| handler(req, "https://v.redd.it/{}/DASH_{}", vec!["id", "size"]));
|
||||||
app.at("/thumb/:point/:id/").get(proxy::thumbnail);
|
app
|
||||||
app.at("/emoji/:id/:name/").get(proxy::emoji);
|
.at("/img/:id/") /* */
|
||||||
app.at("/preview/:location/:id/:query/").get(proxy::preview);
|
.get(|req| handler(req, "https://i.redd.it/{}", vec!["id"]));
|
||||||
app.at("/style/*path/").get(proxy::style);
|
app
|
||||||
|
.at("/thumb/:point/:id/") /* */
|
||||||
|
.get(|req| handler(req, "https://{}.thumbs.redditmedia.com/{}", vec!["point", "id"]));
|
||||||
|
app
|
||||||
|
.at("/emoji/:id/:name/") /* */
|
||||||
|
.get(|req| handler(req, "https://emoji.redditmedia.com/{}/{}", vec!["id", "name"]));
|
||||||
|
app
|
||||||
|
.at("/preview/:loc/:id/:query/")
|
||||||
|
.get(|req| handler(req, "https://{}preview.redd.it/{}?{}", vec!["loc", "id", "query"]));
|
||||||
|
app
|
||||||
|
.at("/style/*path/") /* */
|
||||||
|
.get(|req| handler(req, "https://styles.redditmedia.com/{}", vec!["path"]));
|
||||||
|
app
|
||||||
|
.at("/static/*path/") /* */
|
||||||
|
.get(|req| handler(req, "https://www.redditstatic.com/{}", vec!["path"]));
|
||||||
|
|
||||||
// Browse user profile
|
// Browse user profile
|
||||||
app.at("/u/:name/").get(user::profile);
|
app.at("/u/:name/").get(user::profile);
|
||||||
|
70
src/proxy.rs
70
src/proxy.rs
@ -1,72 +1,14 @@
|
|||||||
use base64::decode;
|
use surf::Body;
|
||||||
use surf::{Body, Url};
|
|
||||||
use tide::{Request, Response};
|
use tide::{Request, Response};
|
||||||
|
|
||||||
pub async fn handler(req: Request<()>) -> tide::Result {
|
pub async fn handler(req: Request<()>, format: &str, params: Vec<&str>) -> tide::Result {
|
||||||
let domains = vec![
|
let mut url = format.to_string();
|
||||||
// ICONS
|
|
||||||
"styles.redditmedia.com",
|
|
||||||
"www.redditstatic.com",
|
|
||||||
];
|
|
||||||
|
|
||||||
let decoded = decode(req.param("url").unwrap_or_default()).map(|bytes| String::from_utf8(bytes).unwrap_or_default());
|
for name in params {
|
||||||
|
let param = req.param(name).unwrap_or_default();
|
||||||
match decoded {
|
url = url.replacen("{}", param, 1);
|
||||||
Ok(media) => match Url::parse(media.as_str()) {
|
|
||||||
Ok(url) => {
|
|
||||||
if domains.contains(&url.domain().unwrap_or_default()) {
|
|
||||||
request(url.to_string()).await
|
|
||||||
} else {
|
|
||||||
Err(tide::Error::from_str(403, "Resource must be from Reddit"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => Err(tide::Error::from_str(400, "Can't parse base64 into URL")),
|
|
||||||
},
|
|
||||||
Err(_) => Err(tide::Error::from_str(400, "Can't decode base64")),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn video(req: Request<()>) -> tide::Result {
|
|
||||||
let id = req.param("id").unwrap_or_default();
|
|
||||||
let size = req.param("size").unwrap_or("720.mp4");
|
|
||||||
let url = format!("https://v.redd.it/{}/DASH_{}", id, size);
|
|
||||||
request(url).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn image(req: Request<()>) -> tide::Result {
|
|
||||||
let id = req.param("id").unwrap_or_default();
|
|
||||||
let url = format!("https://i.redd.it/{}", id);
|
|
||||||
request(url).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn thumbnail(req: Request<()>) -> tide::Result {
|
|
||||||
let id = req.param("id").unwrap_or_default();
|
|
||||||
let point = req.param("point").unwrap_or_default();
|
|
||||||
let url = format!("https://{}.thumbs.redditmedia.com/{}", point, id);
|
|
||||||
request(url).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn emoji(req: Request<()>) -> tide::Result {
|
|
||||||
let id = req.param("id").unwrap_or_default();
|
|
||||||
let name = req.param("name").unwrap_or_default();
|
|
||||||
let url = format!("https://emoji.redditmedia.com/{}/{}", id, name);
|
|
||||||
request(url).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn preview(req: Request<()>) -> tide::Result {
|
|
||||||
let id = req.param("id").unwrap_or_default();
|
|
||||||
let query = req.param("query").unwrap_or_default();
|
|
||||||
let prefix = match req.param("location").unwrap_or_default() {
|
|
||||||
"ext" => "external-",
|
|
||||||
_ => ""
|
|
||||||
};
|
|
||||||
let url = format!("https://{}preview.redd.it/{}?{}", prefix, id, query);
|
|
||||||
request(url).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn style(req: Request<()>) -> tide::Result {
|
|
||||||
let path = req.param("path").unwrap_or_default();
|
|
||||||
let url = format!("https://styles.redditmedia.com/{}", path);
|
|
||||||
request(url).await
|
request(url).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
31
src/utils.rs
31
src/utils.rs
@ -2,7 +2,6 @@
|
|||||||
// CRATES
|
// CRATES
|
||||||
//
|
//
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use base64::encode;
|
|
||||||
use cached::proc_macro::cached;
|
use cached::proc_macro::cached;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde_json::{from_str, Value};
|
use serde_json::{from_str, Value};
|
||||||
@ -186,7 +185,8 @@ pub fn format_url(url: &str) -> String {
|
|||||||
if url.is_empty() || url == "self" || url == "default" || url == "nsfw" || url == "spoiler" {
|
if url.is_empty() || url == "self" || url == "default" || url == "nsfw" || url == "spoiler" {
|
||||||
String::new()
|
String::new()
|
||||||
} else {
|
} else {
|
||||||
let parsed = Url::parse(url).unwrap();
|
match Url::parse(url) {
|
||||||
|
Ok(parsed) => {
|
||||||
let domain = parsed.domain().unwrap_or_default();
|
let domain = parsed.domain().unwrap_or_default();
|
||||||
|
|
||||||
let capture = |regex: &str, format: &str, levels: i16| {
|
let capture = |regex: &str, format: &str, levels: i16| {
|
||||||
@ -208,10 +208,14 @@ pub fn format_url(url: &str) -> String {
|
|||||||
"a.thumbs.redditmedia.com" => capture(r"https://a\.thumbs\.redditmedia\.com/(.*)", "/thumb/a/", 1),
|
"a.thumbs.redditmedia.com" => capture(r"https://a\.thumbs\.redditmedia\.com/(.*)", "/thumb/a/", 1),
|
||||||
"b.thumbs.redditmedia.com" => capture(r"https://b\.thumbs\.redditmedia\.com/(.*)", "/thumb/b/", 1),
|
"b.thumbs.redditmedia.com" => capture(r"https://b\.thumbs\.redditmedia\.com/(.*)", "/thumb/b/", 1),
|
||||||
"emoji.redditmedia.com" => capture(r"https://emoji\.redditmedia\.com/(.*)/(.*)", "/emoji/", 2),
|
"emoji.redditmedia.com" => capture(r"https://emoji\.redditmedia\.com/(.*)/(.*)", "/emoji/", 2),
|
||||||
"preview.redd.it" => capture(r"https://preview\.redd\.it/(.*)\?(.*)", "/preview/int/", 2),
|
"preview.redd.it" => capture(r"https://preview\.redd\.it/(.*)\?(.*)", "/preview//", 2),
|
||||||
"external-preview.redd.it" => capture(r"https://external\-preview\.redd\.it/(.*)\?(.*)", "/preview/ext/", 2),
|
"external-preview.redd.it" => capture(r"https://external\-preview\.redd\.it/(.*)\?(.*)", "/preview/external-/", 2),
|
||||||
// "styles.redditmedia.com" => capture(r"https://styles\.redditmedia\.com/(.*)", "/style/", 1),
|
"styles.redditmedia.com" => capture(r"https://styles\.redditmedia\.com/(.*)", "/style/", 1),
|
||||||
_ => format!("/proxy/{}/", encode(url).as_str()),
|
"www.redditstatic.com" => capture(r"https://www\.redditstatic\.com/(.*)", "/static/", 1),
|
||||||
|
_ => String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -510,13 +514,12 @@ pub async fn request(path: String) -> Result<Value, String> {
|
|||||||
|
|
||||||
let res = client.send(req).await;
|
let res = client.send(req).await;
|
||||||
|
|
||||||
let body = res.unwrap().take_body().into_string().await;
|
match res {
|
||||||
|
Ok(mut response) => match response.take_body().into_string().await {
|
||||||
match body {
|
|
||||||
// If response is success
|
// If response is success
|
||||||
Ok(response) => {
|
Ok(body) => {
|
||||||
// Parse the response from Reddit as JSON
|
// Parse the response from Reddit as JSON
|
||||||
match from_str(&response) {
|
match from_str(&body) {
|
||||||
Ok(json) => Ok(json),
|
Ok(json) => Ok(json),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{} - Failed to parse page JSON data: {}", url, e);
|
println!("{} - Failed to parse page JSON data: {}", url, e);
|
||||||
@ -524,6 +527,12 @@ pub async fn request(path: String) -> Result<Value, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Failed to parse body
|
||||||
|
Err(e) => {
|
||||||
|
println!("{} - Couldn't parse request body: {}", url, e);
|
||||||
|
Err("Couldn't parse request body".to_string())
|
||||||
|
}
|
||||||
|
},
|
||||||
// If failed to send request
|
// If failed to send request
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{} - Couldn't send request to Reddit: {}", url, e);
|
println!("{} - Couldn't send request to Reddit: {}", url, e);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user