mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-06-11 17:27:54 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e98d02a585 | |||
| afa2326584 | |||
| d25d8549e4 | |||
| 662b370ed0 |
@@ -1,6 +1,24 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## v0.25.2 (2026-06-02)
|
||||
|
||||
### Refactoring
|
||||
|
||||
- cleanup
|
||||
|
||||
### Documentation
|
||||
|
||||
- update CHANGELOG.md and README.md for v0.25.1 [skip ci] (#412)
|
||||
|
||||
### Maintenance
|
||||
|
||||
- chore: simplify linux repo publish
|
||||
- chore: version bump
|
||||
- chore: copy
|
||||
- chore: update flake.nix for v0.25.1 [skip ci] (#413)
|
||||
|
||||
|
||||
## v0.25.1 (2026-06-01)
|
||||
|
||||
### Maintenance
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
| | Apple Silicon | Intel |
|
||||
|---|---|---|
|
||||
| **DMG** | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut_0.25.1_aarch64.dmg) | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut_0.25.1_x64.dmg) |
|
||||
| **DMG** | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut_0.25.2_aarch64.dmg) | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut_0.25.2_x64.dmg) |
|
||||
|
||||
Or install via Homebrew:
|
||||
|
||||
@@ -56,15 +56,15 @@ brew install --cask donut
|
||||
|
||||
### Windows
|
||||
|
||||
[Download Windows Installer (x64)](https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut_0.25.1_x64-setup.exe) · [Portable (x64)](https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut_0.25.1_x64-portable.zip)
|
||||
[Download Windows Installer (x64)](https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut_0.25.2_x64-setup.exe) · [Portable (x64)](https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut_0.25.2_x64-portable.zip)
|
||||
|
||||
### Linux
|
||||
|
||||
| Format | x86_64 | ARM64 |
|
||||
|---|---|---|
|
||||
| **deb** | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut_0.25.1_amd64.deb) | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut_0.25.1_arm64.deb) |
|
||||
| **rpm** | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut-0.25.1-1.x86_64.rpm) | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut-0.25.1-1.aarch64.rpm) |
|
||||
| **AppImage** | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut_0.25.1_amd64.AppImage) | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut_0.25.1_aarch64.AppImage) |
|
||||
| **deb** | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut_0.25.2_amd64.deb) | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut_0.25.2_arm64.deb) |
|
||||
| **rpm** | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut-0.25.2-1.x86_64.rpm) | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut-0.25.2-1.aarch64.rpm) |
|
||||
| **AppImage** | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut_0.25.2_amd64.AppImage) | [Download](https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut_0.25.2_aarch64.AppImage) |
|
||||
<!-- install-links-end -->
|
||||
|
||||
Or install via package manager:
|
||||
|
||||
@@ -96,17 +96,17 @@
|
||||
pkgConfigPath = lib.makeSearchPath "lib/pkgconfig" (
|
||||
pkgConfigLibs ++ map lib.getDev pkgConfigLibs
|
||||
);
|
||||
releaseVersion = "0.25.1";
|
||||
releaseVersion = "0.25.2";
|
||||
releaseAppImage =
|
||||
if system == "x86_64-linux" then
|
||||
pkgs.fetchurl {
|
||||
url = "https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut_0.25.1_amd64.AppImage";
|
||||
hash = "sha256-+wtKVCYUjDgXyL96oCqHC0ekWHIe9pLjn1RLBfWHamA=";
|
||||
url = "https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut_0.25.2_amd64.AppImage";
|
||||
hash = "sha256-awESxsKfrSJFMAGbTasbXjL8UnF58ziLnS8Ee0phgb8=";
|
||||
}
|
||||
else if system == "aarch64-linux" then
|
||||
pkgs.fetchurl {
|
||||
url = "https://github.com/zhom/donutbrowser/releases/download/v0.25.1/Donut_0.25.1_aarch64.AppImage";
|
||||
hash = "sha256-fEmf8OzYG3XoEHwOVLh1mONDcJEGeW3d4bb3y//6gPs=";
|
||||
url = "https://github.com/zhom/donutbrowser/releases/download/v0.25.2/Donut_0.25.2_aarch64.AppImage";
|
||||
hash = "sha256-zOUWnvf+5stknWomHwYRUw2TR0aS4/XeiVySBjHuJLA=";
|
||||
}
|
||||
else
|
||||
null;
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"name": "donutbrowser",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"version": "0.25.2",
|
||||
"version": "0.25.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack -p 12341",
|
||||
|
||||
Generated
+1
-1
@@ -1784,7 +1784,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "donutbrowser"
|
||||
version = "0.25.2"
|
||||
version = "0.25.3"
|
||||
dependencies = [
|
||||
"aes 0.9.1",
|
||||
"aes-gcm",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "donutbrowser"
|
||||
version = "0.25.2"
|
||||
version = "0.25.3"
|
||||
description = "Simple Yet Powerful Anti-Detect Browser"
|
||||
authors = ["zhom@github"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -138,6 +138,46 @@ impl WayfernManager {
|
||||
fingerprint
|
||||
}
|
||||
|
||||
/// Derive the on-screen window size Chromium should open at, from the stored
|
||||
/// fingerprint. `Wayfern.setFingerprint` only spoofs what the page *reports*
|
||||
/// for `windowOuterWidth`/`screenWidth`/etc.; it does not move or resize the
|
||||
/// real top-level window. Without `--window-size` the OS window keeps
|
||||
/// Chromium's default, so the visible window contradicts the reported
|
||||
/// dimensions — a detectable mismatch. We pass `--window-size` so the actual
|
||||
/// window matches the fingerprint.
|
||||
///
|
||||
/// Keys are the camelCase fields Wayfern uses in its fingerprint
|
||||
/// (`windowOuterWidth`, `screenAvailWidth`, …) — NOT the dotted
|
||||
/// Camoufox-style keys. Preference order, matching how the fingerprint
|
||||
/// describes the window:
|
||||
/// 1. `windowOuterWidth` / `windowOuterHeight` — the real window size.
|
||||
/// 2. `screenAvailWidth` / `screenAvailHeight` — usable screen area.
|
||||
/// 3. `screenWidth` / `screenHeight` — full screen.
|
||||
///
|
||||
/// Returns `None` when the fingerprint carries no usable dimensions, leaving
|
||||
/// Chromium's default untouched. The fingerprint JSON may be the bare object
|
||||
/// or the legacy `{ "fingerprint": {...} }` wrapper.
|
||||
fn window_size_from_fingerprint(fingerprint_json: &str) -> Option<(u32, u32)> {
|
||||
let parsed: serde_json::Value = serde_json::from_str(fingerprint_json).ok()?;
|
||||
let fp = parsed.get("fingerprint").unwrap_or(&parsed);
|
||||
let obj = fp.as_object()?;
|
||||
|
||||
// Accept both numeric and stringified numbers (Wayfern emits numbers, but a
|
||||
// CDP echo or older saved fingerprint may stringify them).
|
||||
let read = |key: &str| -> Option<u32> {
|
||||
let v = obj.get(key)?;
|
||||
v.as_u64()
|
||||
.or_else(|| v.as_str().and_then(|s| s.trim().parse::<u64>().ok()))
|
||||
.filter(|n| *n > 0)
|
||||
.map(|n| n as u32)
|
||||
};
|
||||
let pair = |w: &str, h: &str| -> Option<(u32, u32)> { Some((read(w)?, read(h)?)) };
|
||||
|
||||
pair("windowOuterWidth", "windowOuterHeight")
|
||||
.or_else(|| pair("screenAvailWidth", "screenAvailHeight"))
|
||||
.or_else(|| pair("screenWidth", "screenHeight"))
|
||||
}
|
||||
|
||||
async fn wait_for_cdp_ready(
|
||||
&self,
|
||||
port: u16,
|
||||
@@ -618,6 +658,18 @@ impl WayfernManager {
|
||||
|
||||
if headless {
|
||||
args.push("--headless=new".to_string());
|
||||
} else if let Some((w, h)) = config
|
||||
.fingerprint
|
||||
.as_deref()
|
||||
.and_then(Self::window_size_from_fingerprint)
|
||||
{
|
||||
// Size the real OS window to match the fingerprint so the visible window
|
||||
// agrees with the reported windowOuterWidth/screen dimensions. Anchor at
|
||||
// 0,0 so the window also fits within the spoofed screen origin. Skipped in
|
||||
// headless mode, where there is no on-screen window.
|
||||
log::info!("Sizing Wayfern window to fingerprint dimensions: {w}x{h}");
|
||||
args.push(format!("--window-size={w},{h}"));
|
||||
args.push("--window-position=0,0".to_string());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -1198,3 +1250,72 @@ impl WayfernManager {
|
||||
lazy_static::lazy_static! {
|
||||
static ref WAYFERN_MANAGER: WayfernManager = WayfernManager::new();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn window_size_prefers_outer_window_dimensions() {
|
||||
// Field names + values mirror a real Wayfern fingerprint (camelCase).
|
||||
let fp = r#"{"windowOuterWidth": 1268, "windowOuterHeight": 764,
|
||||
"windowInnerWidth": 1253, "windowInnerHeight": 630,
|
||||
"screenAvailWidth": 1280, "screenAvailHeight": 775,
|
||||
"screenWidth": 1280, "screenHeight": 800}"#;
|
||||
assert_eq!(
|
||||
WayfernManager::window_size_from_fingerprint(fp),
|
||||
Some((1268, 764))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_size_falls_back_to_avail_then_full_screen() {
|
||||
let avail = r#"{"screenAvailWidth": 1280, "screenAvailHeight": 775,
|
||||
"screenWidth": 1280, "screenHeight": 800}"#;
|
||||
assert_eq!(
|
||||
WayfernManager::window_size_from_fingerprint(avail),
|
||||
Some((1280, 775))
|
||||
);
|
||||
|
||||
let full = r#"{"screenWidth": 2560, "screenHeight": 1440}"#;
|
||||
assert_eq!(
|
||||
WayfernManager::window_size_from_fingerprint(full),
|
||||
Some((2560, 1440))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_size_handles_wrapper_and_stringified_numbers() {
|
||||
let wrapped = r#"{"fingerprint": {"windowOuterWidth": "1366", "windowOuterHeight": "768"}}"#;
|
||||
assert_eq!(
|
||||
WayfernManager::window_size_from_fingerprint(wrapped),
|
||||
Some((1366, 768))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_size_none_when_missing_or_invalid() {
|
||||
// No dimensions at all.
|
||||
assert_eq!(
|
||||
WayfernManager::window_size_from_fingerprint(r#"{"userAgent": "x"}"#),
|
||||
None
|
||||
);
|
||||
// A width with no matching height is not a usable pair.
|
||||
assert_eq!(
|
||||
WayfernManager::window_size_from_fingerprint(r#"{"windowOuterWidth": 1268}"#),
|
||||
None
|
||||
);
|
||||
// Zero is rejected as a degenerate size.
|
||||
assert_eq!(
|
||||
WayfernManager::window_size_from_fingerprint(
|
||||
r#"{"windowOuterWidth": 0, "windowOuterHeight": 0}"#
|
||||
),
|
||||
None
|
||||
);
|
||||
// Not valid JSON.
|
||||
assert_eq!(
|
||||
WayfernManager::window_size_from_fingerprint("not json"),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Donut",
|
||||
"version": "0.25.2",
|
||||
"version": "0.25.3",
|
||||
"identifier": "com.donutbrowser",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm copy-proxy-binary && pnpm dev",
|
||||
|
||||
Reference in New Issue
Block a user