From d2379ed6ccd07ee125aec5ca68c8388fe0e45370 Mon Sep 17 00:00:00 2001 From: Rixuan Shao <2023311022@bipt.edu.cn> Date: Tue, 25 Aug 2026 14:01:02 +0800 Subject: [PATCH] feat: make direct Douyin traffic the branch default --- .env.example | 4 +- CHANGELOG.md | 2 + DouYinSparkFlow/core/browser.py | 37 ++++++++++++++---- .../tests/test_deployment_contract.py | 22 ++++++++--- DouYinSparkFlow/tests/test_douyin_network.py | 38 +++++++++++++++++++ DouYinSparkFlow/utils/config.py | 2 + DouYinSparkFlow/webui/app.py | 3 ++ .../webui/templates/dashboard.html | 2 + README.md | 6 +-- docker-compose.yml | 26 +------------ docs/usage.md | 4 +- 11 files changed, 101 insertions(+), 45 deletions(-) create mode 100644 DouYinSparkFlow/tests/test_douyin_network.py diff --git a/.env.example b/.env.example index 5ad6d11..2b65176 100644 --- a/.env.example +++ b/.env.example @@ -15,8 +15,8 @@ LOGIN_DESKTOP_PIDS_LIMIT=256 LOGIN_DESKTOP_IDLE_TIMEOUT_SECONDS=1800 LOGIN_DESKTOP_STOP_AFTER_EXPORT_SECONDS=60 LOGIN_DESKTOP_STATUS_CACHE_SECONDS=15 -# Login browser uses direct access first; Mihomo is a fallback when direct access fails. -LOGIN_DESKTOP_PROXY_MODE=auto +# Login browser uses direct access by default; Mihomo is an advanced option. +LOGIN_DESKTOP_PROXY_MODE=direct LOGIN_DESKTOP_PROXY=http://proxy:7890 LOGIN_DESKTOP_PREFLIGHT_TIMEOUT_SECONDS=15 LOGIN_DESKTOP_NETWORK_CACHE_SECONDS=30 diff --git a/CHANGELOG.md b/CHANGELOG.md index e27438e..b373111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## 2026-08-25 +- Made the experimental branch default to direct Douyin business traffic; Mihomo is now an explicit advanced option for browser traffic. + - Made login-browser networking direct-first with Mihomo fallback and explicit preflight errors. # Changelog diff --git a/DouYinSparkFlow/core/browser.py b/DouYinSparkFlow/core/browser.py index f501ccc..d36f171 100644 --- a/DouYinSparkFlow/core/browser.py +++ b/DouYinSparkFlow/core/browser.py @@ -8,7 +8,7 @@ from pathlib import Path from playwright.async_api import async_playwright from rich.console import Console -from utils.config import DEBUG, Environment, get_environment +from utils.config import DEBUG, Environment, get_app_settings, get_environment console = Console() @@ -52,6 +52,30 @@ def _browser_args(): ] +def _douyin_browser_proxy(): + """Return an explicit proxy for Douyin browser traffic, or None for direct.""" + settings = get_app_settings(force_reload=True) + mode = str( + os.getenv("SPARKFLOW_DOUYIN_NETWORK_MODE") + or settings.get("douyin_network_mode", "direct") + ).strip().lower() + if mode != "mihomo": + return None + return str( + os.getenv("SPARKFLOW_DOUYIN_PROXY_URL") + or settings.get("douyin_proxy_url", "http://proxy:7890") + ).strip() or None + + +def _browser_launch_options(GUI=False): + args = _browser_args() + proxy = _douyin_browser_proxy() + if proxy: + return {"headless": _headless_for(GUI), "args": args, "proxy": {"server": proxy}} + args.append("--no-proxy-server") + return {"headless": _headless_for(GUI), "args": args} + + def sanitize_profile_name(value): raw = str(value or "").strip() if not raw: @@ -83,10 +107,7 @@ async def get_browser(GUI=False): try: playwright = await async_playwright().start() - browser = await playwright.chromium.launch( - headless=_headless_for(GUI), - args=_browser_args(), - ) + browser = await playwright.chromium.launch(**_browser_launch_options(GUI)) return playwright, browser except Exception as exc: if "Executable doesn't exist" in str(exc) and get_environment() != Environment.GITHUBACTION: @@ -105,11 +126,11 @@ async def get_persistent_browser_context(profile_name, GUI=False, root=None): try: playwright = await async_playwright().start() + launch_options = _browser_launch_options(GUI) + launch_options["viewport"] = {"width": 1600, "height": 1000} context = await playwright.chromium.launch_persistent_context( str(profile_dir), - headless=_headless_for(GUI), - viewport={"width": 1600, "height": 1000}, - args=_browser_args(), + **launch_options, ) return playwright, context, profile_dir except Exception as exc: diff --git a/DouYinSparkFlow/tests/test_deployment_contract.py b/DouYinSparkFlow/tests/test_deployment_contract.py index 6a12647..4ba3006 100644 --- a/DouYinSparkFlow/tests/test_deployment_contract.py +++ b/DouYinSparkFlow/tests/test_deployment_contract.py @@ -72,9 +72,14 @@ class DeploymentContractTests(unittest.TestCase): compose = (REPO_ROOT / "docker-compose.yml").read_text(encoding="utf-8") self.assertIn("# Build proxies must never leak", dockerfile) self.assertIn("http_proxy=", dockerfile) - self.assertGreaterEqual(compose.count("http_proxy: http://proxy:7890"), 3) - self.assertGreaterEqual(compose.count("https_proxy: http://proxy:7890"), 3) - self.assertGreaterEqual(compose.count("no_proxy:"), 3) + self.assertIn("HTTP_PROXY: ${HTTP_PROXY_BUILD:-}", compose) + self.assertIn("HTTPS_PROXY: ${HTTPS_PROXY_BUILD:-}", compose) + self.assertIn("ALL_PROXY: ${ALL_PROXY_BUILD:-}", compose) + for service in ("web", "scheduler", "task"): + block = compose.split(f" {service}:", 1)[1] + block = block.split("\n ", 1)[0] + self.assertNotIn("HTTP_PROXY: http://proxy:7890", block) + self.assertNotIn("http_proxy: http://proxy:7890", block) def test_sensitive_ports_bind_to_loopback_by_default(self): text = (REPO_ROOT / "docker-compose.yml").read_text(encoding="utf-8") @@ -119,14 +124,21 @@ class DeploymentContractTests(unittest.TestCase): env_example = (REPO_ROOT / ".env.example").read_text(encoding="utf-8") server = (SOURCE_ROOT / "login_desktop_server.py").read_text(encoding="utf-8") login_block = compose.split(" login-desktop:", 1)[1].split(" scheduler:", 1)[0] - self.assertIn("LOGIN_DESKTOP_PROXY_MODE: ${LOGIN_DESKTOP_PROXY_MODE:-auto}", login_block) + self.assertIn("LOGIN_DESKTOP_PROXY_MODE: ${LOGIN_DESKTOP_PROXY_MODE:-direct}", login_block) self.assertIn("LOGIN_DESKTOP_PROXY: ${LOGIN_DESKTOP_PROXY:-http://proxy:7890}", login_block) self.assertNotIn("HTTP_PROXY: http://proxy:7890", login_block) - self.assertIn("LOGIN_DESKTOP_PROXY_MODE=auto", env_example) + self.assertIn("LOGIN_DESKTOP_PROXY_MODE=direct", env_example) self.assertIn('candidates.append(("direct", None))', server) self.assertIn('candidates.append(("proxy", LOGIN_PROXY_SERVER))', server) self.assertIn('"--no-proxy-server"', server) self.assertIn('"/preflight"', server) + self.assertIn('LOGIN_DESKTOP_PROXY_MODE: ${LOGIN_DESKTOP_PROXY_MODE:-direct}', login_block) + dashboard = (SOURCE_ROOT / "webui" / "templates" / "dashboard.html").read_text(encoding="utf-8") + self.assertIn('name="douyin_network_mode"', dashboard) + self.assertIn('name="douyin_proxy_url"', dashboard) + browser = (SOURCE_ROOT / "core" / "browser.py").read_text(encoding="utf-8") + self.assertIn("--no-proxy-server", browser) + self.assertIn("SPARKFLOW_DOUYIN_NETWORK_MODE", browser) def test_login_desktop_resource_controls_are_configured(self): compose = (REPO_ROOT / "docker-compose.yml").read_text(encoding="utf-8") diff --git a/DouYinSparkFlow/tests/test_douyin_network.py b/DouYinSparkFlow/tests/test_douyin_network.py new file mode 100644 index 0000000..4418b16 --- /dev/null +++ b/DouYinSparkFlow/tests/test_douyin_network.py @@ -0,0 +1,38 @@ +import unittest +from unittest.mock import patch + +from core import browser +from utils import config as config_module + + +class DouyinNetworkRouteTests(unittest.TestCase): + def test_default_route_is_direct(self): + with patch.object(browser, "get_app_settings", return_value={}): + with patch.dict(browser.os.environ, {}, clear=False): + browser.os.environ.pop("SPARKFLOW_DOUYIN_NETWORK_MODE", None) + browser.os.environ.pop("SPARKFLOW_DOUYIN_PROXY_URL", None) + options = browser._browser_launch_options(False) + self.assertNotIn("proxy", options) + self.assertIn("--no-proxy-server", options["args"]) + + def test_mihomo_route_is_explicit(self): + with patch.object(browser, "get_app_settings", return_value={}): + with patch.dict( + browser.os.environ, + { + "SPARKFLOW_DOUYIN_NETWORK_MODE": "mihomo", + "SPARKFLOW_DOUYIN_PROXY_URL": "http://127.0.0.1:7890", + }, + clear=False, + ): + options = browser._browser_launch_options(False) + self.assertEqual({"server": "http://127.0.0.1:7890"}, options["proxy"]) + self.assertNotIn("--no-proxy-server", options["args"]) + + def test_default_app_settings_keep_direct_route(self): + self.assertEqual("direct", config_module.DEFAULT_APP_SETTINGS["douyin_network_mode"]) + self.assertEqual("http://proxy:7890", config_module.DEFAULT_APP_SETTINGS["douyin_proxy_url"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/DouYinSparkFlow/utils/config.py b/DouYinSparkFlow/utils/config.py index 1e7cf57..3d2354d 100644 --- a/DouYinSparkFlow/utils/config.py +++ b/DouYinSparkFlow/utils/config.py @@ -85,6 +85,8 @@ DEFAULT_APP_SETTINGS = { "proxy_refresh_script": "/opt/douyin-sparkflow/refresh_proxy.sh", "local_login_helper_url": "http://127.0.0.1:18765", "login_desktop_api_url": "http://127.0.0.1:18090", + "douyin_network_mode": "direct", + "douyin_proxy_url": "http://proxy:7890", "login_desktop_public_url": "", "login_desktop_public_scheme": "http", "login_desktop_public_port": 8788, diff --git a/DouYinSparkFlow/webui/app.py b/DouYinSparkFlow/webui/app.py index 098b31e..9e12318 100644 --- a/DouYinSparkFlow/webui/app.py +++ b/DouYinSparkFlow/webui/app.py @@ -1035,6 +1035,9 @@ def create_app(): settings["login_desktop_api_url"] = str( form.get("login_desktop_api_url", settings.get("login_desktop_api_url", "http://127.0.0.1:18090")) ).strip() + network_mode = str(form.get("douyin_network_mode", settings.get("douyin_network_mode", "direct"))).strip().lower() + settings["douyin_network_mode"] = network_mode if network_mode in {"direct", "mihomo"} else "direct" + settings["douyin_proxy_url"] = str(form.get("douyin_proxy_url", settings.get("douyin_proxy_url", "http://proxy:7890"))).strip() settings["ui_port"] = int(form.get("ui_port", settings.get("ui_port", 8787))) save_app_settings(settings) diff --git a/DouYinSparkFlow/webui/templates/dashboard.html b/DouYinSparkFlow/webui/templates/dashboard.html index 5bdd833..987304e 100644 --- a/DouYinSparkFlow/webui/templates/dashboard.html +++ b/DouYinSparkFlow/webui/templates/dashboard.html @@ -578,6 +578,8 @@ + +
diff --git a/README.md b/README.md index 2dc3a26..e681846 100644 --- a/README.md +++ b/README.md @@ -225,8 +225,8 @@ WEB_PORT=8787 LOGIN_DESKTOP_BIND_ADDRESS=127.0.0.1 LOGIN_DESKTOP_WEB_PORT=8788 LOGIN_DESKTOP_PUBLIC_URL=/login-desktop/proxy/vnc.html?autoconnect=1&resize=scale&view_only=0&path=login-desktop/proxy/websockify -# 登录浏览器默认先直连抖音,直连失败时再尝试 Mihomo -LOGIN_DESKTOP_PROXY_MODE=auto +# 登录浏览器默认直连;Mihomo 仅作为高级选项 +LOGIN_DESKTOP_PROXY_MODE=direct LOGIN_DESKTOP_PROXY=http://proxy:7890 # Mihomo 代理和控制端口默认仅绑定本机 @@ -236,7 +236,7 @@ PROXY_CONTROLLER_PORT=9090 PROXY_SUB_URL= ``` -登录工作区的网络路径与发送任务分开处理。`LOGIN_DESKTOP_PROXY_MODE=auto` 时,登录浏览器会从 `login-desktop` 容器内先直连 `creator.douyin.com`;只有直连预检失败时才使用 `LOGIN_DESKTOP_PROXY`。发送任务容器仍可独立使用 Mihomo。若两条路径都不可用,登录页面会显示明确的网络错误,不会无限等待二维码。 +默认抖音业务网络使用直连。登录、好友刷新和浏览器发送会显式禁用环境代理,避免未配置的 Mihomo 影响正常使用。高级用户可在 Web UI「系统设置」中选择 Mihomo 并填写代理地址;登录浏览器仍可通过 `LOGIN_DESKTOP_PROXY_MODE=proxy` 强制使用代理。 #### `config.example.json` 与 `config.json` - 应用配置 diff --git a/docker-compose.yml b/docker-compose.yml index 1dd56f7..e6b3993 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,14 +38,6 @@ services: - login-desktop environment: TZ: ${TZ:-Asia/Shanghai} - HTTP_PROXY: http://proxy:7890 - HTTPS_PROXY: http://proxy:7890 - ALL_PROXY: socks5://proxy:7890 - http_proxy: http://proxy:7890 - https_proxy: http://proxy:7890 - all_proxy: socks5://proxy:7890 - NO_PROXY: localhost,127.0.0.1,login-desktop,douyin.com,amemv.com,snssdk.com,bytedance.com,pstatp.com,volccdn.com,bytescm.com,byted.net,douyinstatic.com,bytecdn.cn,byteimg.com,bytegoofy.com,toutiaostatic.com - no_proxy: localhost,127.0.0.1,login-desktop,douyin.com,amemv.com,snssdk.com,bytedance.com,pstatp.com,volccdn.com,bytescm.com,byted.net,douyinstatic.com,bytecdn.cn,byteimg.com,bytegoofy.com,toutiaostatic.com SPARKFLOW_LOGIN_DESKTOP_API_URL: http://login-desktop:18090 LOGIN_DESKTOP_PUBLIC_PORT: ${LOGIN_DESKTOP_WEB_PORT:-8788} SPARKFLOW_LOGIN_DESKTOP_PUBLIC_URL: ${LOGIN_DESKTOP_PUBLIC_URL:-/login-desktop/proxy/vnc.html?autoconnect=1&resize=scale&view_only=0&path=login-desktop/proxy/websockify} @@ -77,7 +69,7 @@ services: LOGIN_DESKTOP_IDLE_TIMEOUT_SECONDS: ${LOGIN_DESKTOP_IDLE_TIMEOUT_SECONDS:-1800} LOGIN_DESKTOP_STOP_AFTER_EXPORT_SECONDS: ${LOGIN_DESKTOP_STOP_AFTER_EXPORT_SECONDS:-60} LOGIN_DESKTOP_STATUS_CACHE_SECONDS: ${LOGIN_DESKTOP_STATUS_CACHE_SECONDS:-15} - LOGIN_DESKTOP_PROXY_MODE: ${LOGIN_DESKTOP_PROXY_MODE:-auto} + LOGIN_DESKTOP_PROXY_MODE: ${LOGIN_DESKTOP_PROXY_MODE:-direct} LOGIN_DESKTOP_PROXY: ${LOGIN_DESKTOP_PROXY:-http://proxy:7890} LOGIN_DESKTOP_PREFLIGHT_TIMEOUT_SECONDS: ${LOGIN_DESKTOP_PREFLIGHT_TIMEOUT_SECONDS:-15} LOGIN_DESKTOP_NETWORK_CACHE_SECONDS: ${LOGIN_DESKTOP_NETWORK_CACHE_SECONDS:-30} @@ -101,14 +93,6 @@ services: environment: TZ: ${TZ:-Asia/Shanghai} PYTHONUNBUFFERED: "1" - HTTP_PROXY: http://proxy:7890 - HTTPS_PROXY: http://proxy:7890 - ALL_PROXY: socks5://proxy:7890 - http_proxy: http://proxy:7890 - https_proxy: http://proxy:7890 - all_proxy: socks5://proxy:7890 - NO_PROXY: localhost,127.0.0.1,douyin.com,amemv.com,snssdk.com,bytedance.com,pstatp.com,volccdn.com,bytescm.com,byted.net,douyinstatic.com,bytecdn.cn,byteimg.com,bytegoofy.com,toutiaostatic.com - no_proxy: localhost,127.0.0.1,douyin.com,amemv.com,snssdk.com,bytedance.com,pstatp.com,volccdn.com,bytescm.com,byted.net,douyinstatic.com,bytecdn.cn,byteimg.com,bytegoofy.com,toutiaostatic.com command: python /app/scripts/cron_runner.py /host-spool-cron/root volumes: - ./DouYinSparkFlow:/app @@ -125,14 +109,6 @@ services: environment: TZ: ${TZ:-Asia/Shanghai} PYTHONUNBUFFERED: "1" - HTTP_PROXY: http://proxy:7890 - HTTPS_PROXY: http://proxy:7890 - ALL_PROXY: socks5://proxy:7890 - http_proxy: http://proxy:7890 - https_proxy: http://proxy:7890 - all_proxy: socks5://proxy:7890 - NO_PROXY: localhost,127.0.0.1,douyin.com,amemv.com,snssdk.com,bytedance.com,pstatp.com,volccdn.com,bytescm.com,byted.net,douyinstatic.com,bytecdn.cn,byteimg.com,bytegoofy.com,toutiaostatic.com - no_proxy: localhost,127.0.0.1,douyin.com,amemv.com,snssdk.com,bytedance.com,pstatp.com,volccdn.com,bytescm.com,byted.net,douyinstatic.com,bytecdn.cn,byteimg.com,bytegoofy.com,toutiaostatic.com command: python main.py --doTask volumes: - ./DouYinSparkFlow:/app diff --git a/docs/usage.md b/docs/usage.md index 305ed52..929c9db 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -79,7 +79,7 @@ ssh -L 8788:127.0.0.1:8788 @ ### 登录工作区打不开 -先确认 SSH 隧道仍在运行,再检查 `login-desktop` 容器。默认不需要把 8788 暴露到公网。登录浏览器默认优先直连抖音,代理只作为备用路径。 +先确认 SSH 隧道仍在运行,再检查 `login-desktop` 容器。默认不需要把 8788 暴露到公网。登录浏览器、好友刷新和发送浏览器默认使用直连。Mihomo 是高级选项,可在 Web UI「系统设置」中手动启用。 ```bash docker compose ps @@ -87,7 +87,7 @@ docker compose logs -f login-desktop docker compose exec login-desktop curl -fsS http://127.0.0.1:18090/preflight ``` -如果预检失败,页面会显示直连和代理各自的检查结果;通常不需要修改发送任务使用的 Mihomo 配置。 +如果直连遇到网络问题,再在「系统设置」中选择 Mihomo 并填写代理地址。未配置 Mihomo 不会影响默认直连模式。 ### 账号保存后没有好友列表