Skip to content

Commit

Permalink
feat: dark mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Tomio committed May 11, 2022
1 parent a5b20d8 commit 0ca4555
Show file tree
Hide file tree
Showing 12 changed files with 114 additions and 83 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/continuous-delivery.yml
Expand Up @@ -90,15 +90,15 @@ jobs:
run: |
cd target/${{ matrix.target }}/release
strip website-screenshot.exe
7z a ../../../${{ matrix.name }} website-screenshot.exe ../../evasions
7z a ../../../${{ matrix.name }} website-screenshot.exe
cd -
- name: Post Build | Prepare artifacts [-nix]
if: matrix.os != 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
strip website-screenshot || true
tar czvf ../../../${{ matrix.name }} website-screenshot ../../evasions
tar czvf ../../../${{ matrix.name }} website-screenshot
cd -
- name: Deploy | Upload artifacts
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -7,6 +7,7 @@ categories = ["caching", "command-line-utilities", "database-implementations", "
edition = "2021"
include = [
"src/**/*",
"evasions/*",
"build.rs",
"Cross.toml",
"LICENSE-APACHE",
Expand Down
1 change: 0 additions & 1 deletion Dockerfile
Expand Up @@ -47,6 +47,5 @@ RUN fleet build --release
FROM base as runner

COPY --from=builder /usr/src/app/target/release/website-screenshot /usr/local/bin/website-screenshot
COPY --from=builder /usr/src/app/evasions evasions/

ENTRYPOINT ["/usr/local/bin/website-screenshot"]
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -32,6 +32,7 @@
- `CHECK_IF_NSFW` - if set, it will check if the url is marked as NSFW (optional)
- `GOOGLE_CHROME_PATH` - the path to the chrome executable (optional)
- `CHROMEDRIVER_PATH` - the path to the chromedriver executable (optional)
- `DARK_MODE` - if set, it will take screenshots in dark mode, if the website supports it (optional)

### Railway

Expand Down
4 changes: 2 additions & 2 deletions build.rs
Expand Up @@ -18,8 +18,8 @@ fn main() {
None => Version::parse("1.0.0").unwrap().to_mmp(),
};

if version.0 != 1 && version.1 < 60 {
panic!("Minimum rust version required is 1.60, please update your rust version via `rustup update`.");
if version.0 != 1 && version.1 < 62 {
panic!("Minimum rust version required is nightly 1.62, please update your rust version via `rustup update`.");
}

let features = get_features!(
Expand Down
17 changes: 0 additions & 17 deletions evasions/hairline.fix.js

This file was deleted.

2 changes: 1 addition & 1 deletion install.sh
Expand Up @@ -207,7 +207,7 @@ detect_platform() {
# use the statically compiled musl bins on linux to avoid linking issues.
linux) platform="unknown-linux-musl" ;;
darwin) platform="apple-darwin" ;;
freebsd) platform="unknown-freebsd" ;;
# freebsd) platform="unknown-freebsd" ;;
esac

printf '%s' "${platform}"
Expand Down
Binary file modified screenshots/abcdefghijk.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions src/evasions.rs
@@ -0,0 +1,36 @@
use fantoccini::Client;
use serde_json::{json, Value};

use crate::cdp::ChromeCommand;

pub const CHROME_APP: &str = include_str!("../evasions/chrome.app.js");
pub const CHROME_CSI: &str = include_str!("../evasions/chrome.csi.js");
pub const CHROME_LOADTIMES: &str = include_str!("../evasions/chrome.loadTimes.js");
pub const CHROME_RUNTIME: &str = include_str!("../evasions/chrome.runtime.js");
pub const IFRAME_CONTENTWINDOW: &str = include_str!("../evasions/iframe.contentWindow.js");
pub const MEDIA_CODECS: &str = include_str!("../evasions/media.codecs.js");
pub const NAVIGATOR_HARDWARECONCURRENCY: &str =
include_str!("../evasions/navigator.hardwareConcurrency.js");
pub const NAVIGATOR_LANGUAGES: &str = include_str!("../evasions/navigator.languages.js");
pub const NAVIGATOR_PERMISSIONS: &str = include_str!("../evasions/navigator.permissions.js");
pub const NAVIGATOR_PLUGINS: &str = include_str!("../evasions/navigator.plugins.js");
pub const NAVIGATOR_VENDOR: &str = include_str!("../evasions/navigator.vendor.js");
pub const NAVIGATOR_WEBDRIVER: &str = include_str!("../evasions/navigator.webdriver.js");
pub const UTILS: &str = include_str!("../evasions/utils.js");
pub const WEBGL_VENDOR: &str = include_str!("../evasions/webgl.vendor.js");
pub const WINDOW_OUTERDIMENSIONS: &str = include_str!("../evasions/window.outerdimensions.js");

pub async fn evaluate_on_new_document<'a>(client: &Client, js: &'a str, args: Vec<Value>) {
let expr = format!(
"({js})({})",
args.into_iter().map(|arg| arg.to_string()).collect::<Vec<_>>().join(", ")
);

client
.issue_cmd(ChromeCommand::ExecuteCdpCommand(
"Page.addScriptToEvaluateOnNewDocument".to_owned(),
json!({ "source": expr }),
))
.await
.expect("Failed issuing cmd");
}
85 changes: 26 additions & 59 deletions src/main.rs
Expand Up @@ -3,15 +3,15 @@
#[macro_use]
extern crate tracing;

use std::env;
use std::process::Stdio;
use std::sync::Arc;
use std::time::Duration;
use std::{env, fs, thread};

use actix_governor::{Governor, GovernorConfigBuilder};
use actix_web::middleware::Compress;
use actix_web::{web, App, Error, HttpServer};
use cdp::ChromeCommand;
use evasions::*;
use fantoccini::{Client, ClientBuilder};
use providers::{Provider, Storage};
use reqwest::Client as ReqwestClient;
Expand All @@ -20,10 +20,11 @@ use tokio::process::Command;
use tokio_process_stream::ProcessLineStream;
use tokio_stream::StreamExt;
use tracing_actix_web::TracingLogger;
use util::{evaluate_on_new_document, initialize_tracing, load_env};
use util::{initialize_tracing, load_env};

pub mod cdp;
pub mod error;
pub mod evasions;
pub mod middlewares;
pub mod providers;
pub mod routes;
Expand Down Expand Up @@ -60,9 +61,6 @@ async fn main() -> anyhow::Result<()> {
}
});

// Chromedriver may take a while to start
thread::sleep(Duration::from_secs(3));

let mut capabilities = Map::new();
let chrome_opts = match env::var("GOOGLE_CHROME_PATH") {
Ok(path) => serde_json::json!({
Expand Down Expand Up @@ -95,62 +93,31 @@ async fn main() -> anyhow::Result<()> {

// To hide headless nature (for Cloudflare, etc.)
tokio::join!(
evaluate_on_new_document(&client, fs::read_to_string("evasions/utils.js")?, vec![]),
evaluate_on_new_document(&client, fs::read_to_string("evasions/chrome.app.js")?, vec![]),
evaluate_on_new_document(&client, fs::read_to_string("evasions/chrome.csi.js")?, vec![]),
evaluate_on_new_document(
&client,
fs::read_to_string("evasions/chrome.loadTimes.js")?,
vec![]
),
evaluate_on_new_document(&client, fs::read_to_string("evasions/chrome.runtime.js")?, vec![
Value::Bool(false)
]),
evaluate_on_new_document(
&client,
fs::read_to_string("evasions/iframe.contentWindow.js")?,
vec![]
),
evaluate_on_new_document(&client, fs::read_to_string("evasions/media.codecs.js")?, vec![]),
evaluate_on_new_document(
&client,
fs::read_to_string("evasions/navigator.hardwareConcurrency.js")?,
vec![Value::Number(4.into())]
),
evaluate_on_new_document(
&client,
fs::read_to_string("evasions/navigator.languages.js")?,
vec![Value::Array(vec!["en-US".into(), "en".into()])]
),
evaluate_on_new_document(
&client,
fs::read_to_string("evasions/navigator.permissions.js")?,
vec![]
),
evaluate_on_new_document(
&client,
fs::read_to_string("evasions/navigator.plugins.js")?,
vec![]
),
evaluate_on_new_document(
&client,
fs::read_to_string("evasions/navigator.vendor.js")?,
vec![Value::String("Google Inc.".to_owned())]
),
evaluate_on_new_document(
&client,
fs::read_to_string("evasions/navigator.webdriver.js")?,
vec![]
),
evaluate_on_new_document(&client, fs::read_to_string("evasions/webgl.vendor.js")?, vec![
evaluate_on_new_document(&client, UTILS, vec![]),
evaluate_on_new_document(&client, CHROME_APP, vec![]),
evaluate_on_new_document(&client, CHROME_CSI, vec![]),
evaluate_on_new_document(&client, CHROME_LOADTIMES, vec![]),
evaluate_on_new_document(&client, CHROME_RUNTIME, vec![Value::Bool(false)]),
evaluate_on_new_document(&client, IFRAME_CONTENTWINDOW, vec![]),
evaluate_on_new_document(&client, MEDIA_CODECS, vec![]),
evaluate_on_new_document(&client, NAVIGATOR_HARDWARECONCURRENCY, vec![Value::Number(
4.into()
)]),
evaluate_on_new_document(&client, NAVIGATOR_LANGUAGES, vec![Value::Array(vec![
"en-US".into(),
"en".into()
])]),
evaluate_on_new_document(&client, NAVIGATOR_PERMISSIONS, vec![]),
evaluate_on_new_document(&client, NAVIGATOR_PLUGINS, vec![]),
evaluate_on_new_document(&client, NAVIGATOR_VENDOR, vec![Value::String(
"Google Inc.".to_owned()
)]),
evaluate_on_new_document(&client, NAVIGATOR_WEBDRIVER, vec![]),
evaluate_on_new_document(&client, WEBGL_VENDOR, vec![
Value::String("Intel Inc.".to_owned()),
Value::String("Intel Iris OpenGL Engine".to_owned())
]),
evaluate_on_new_document(
&client,
fs::read_to_string("evasions/window.outerdimensions.js")?,
vec![]
),
evaluate_on_new_document(&client, WINDOW_OUTERDIMENSIONS, vec![]),
);

// Override user-agent
Expand Down
4 changes: 3 additions & 1 deletion src/routes/get.rs
@@ -1,3 +1,5 @@
use std::ops::Not;

use actix_web::http::header;
use actix_web::{get, web, HttpResponse};

Expand All @@ -10,7 +12,7 @@ pub async fn get_screenshot(
data: web::Data<State>,
slug: web::Path<String>,
) -> Result<HttpResponse, Error> {
if let false = data.storage.check(slug.clone()).await.expect("Failed checking slug") {
if data.storage.check(slug.clone()).await.expect("Failed checking slug").not() {
return Err(Error::ScreenshotNotFound);
}

Expand Down
42 changes: 42 additions & 0 deletions src/routes/screenshot.rs
Expand Up @@ -6,6 +6,7 @@ use fantoccini::Locator;
use serde::{Deserialize, Serialize};
use serde_json::json;

use crate::cdp::ChromeCommand;
use crate::error::Error;
use crate::providers::Provider;
use crate::util::{check_if_nsfw, check_if_url};
Expand All @@ -21,13 +22,20 @@ fn default_check_nsfw() -> bool {
env::var("CHECK_IF_NSFW").is_ok()
}

#[inline]
fn default_dark_mode() -> bool {
env::var("DARK_MODE").is_ok()
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RequestData {
url: String,
#[serde(default = "default_fullscreen")]
fullscreen: bool,
#[serde(default = "default_check_nsfw")]
check_nsfw: bool,
#[serde(default = "default_dark_mode")]
dark_mode: bool,
}

#[post("/screenshot")]
Expand Down Expand Up @@ -64,6 +72,40 @@ pub async fn screenshot(
.await
.expect("Failed hiding scrollbar");

if payload.dark_mode {
client
.issue_cmd(ChromeCommand::ExecuteCdpCommand(
"Emulation.setEmulatedMedia".to_owned(),
json!({
"features": [
{
"name": "prefers-color-scheme",
"value": "dark"
}
]
}),
))
.await
.expect("Failed issuing cmd");
} else {
client
.issue_cmd(ChromeCommand::ExecuteCdpCommand(
"Emulation.setEmulatedMedia".to_owned(),
json!({
"features": [
{
"name": "prefers-color-scheme",
"value": "light"
}
]
}),
))
.await
.expect("Failed issuing cmd");
}

client.refresh().await.expect("Failed to refresh");

let screenshot = match payload.fullscreen {
true => {
let original_size = client.get_window_size().await.expect("Failed to get window size");
Expand Down

0 comments on commit 0ca4555

Please sign in to comment.