mirror of
https://github.com/halfwaystudent/douyin-sparkflow.git
synced 2026-08-28 19:47:03 +08:00
feat: make direct Douyin traffic the branch default
This commit is contained in:
+2
-2
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -578,6 +578,8 @@
|
||||
<label><span>任务日志文件</span><input type="text" name="ops_log_file" value="{{ app_settings.ops_log_file }}"></label>
|
||||
<label><span>代理刷新脚本</span><input type="text" name="proxy_refresh_script" value="{{ app_settings.proxy_refresh_script }}"></label>
|
||||
<label><span>登录桌面 API</span><input type="text" name="login_desktop_api_url" value="{{ app_settings.login_desktop_api_url }}"></label>
|
||||
<label><span>抖音业务网络出口</span><select name="douyin_network_mode"><option value="direct" {% if app_settings.douyin_network_mode|default("direct") == "direct" %}selected{% endif %}>直连(默认)</option><option value="mihomo" {% if app_settings.douyin_network_mode == "mihomo" %}selected{% endif %}>Mihomo(高级)</option></select></label>
|
||||
<label><span>Mihomo 代理地址</span><input type="text" name="douyin_proxy_url" value="{{ app_settings.douyin_proxy_url|default("http://proxy:7890") }}" placeholder="http://proxy:7890"><small>仅在选择 Mihomo 时使用;默认不启用。</small></label>
|
||||
<label><span>Web UI 端口</span><input type="number" min="1" max="65535" name="ui_port" value="{{ app_settings.ui_port }}"></label>
|
||||
</div>
|
||||
<div class="form-grid">
|
||||
|
||||
@@ -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` - 应用配置
|
||||
|
||||
|
||||
+1
-25
@@ -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
|
||||
|
||||
+2
-2
@@ -79,7 +79,7 @@ ssh -L 8788:127.0.0.1:8788 <user>@<server-ip>
|
||||
|
||||
### 登录工作区打不开
|
||||
|
||||
先确认 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 不会影响默认直连模式。
|
||||
|
||||
### 账号保存后没有好友列表
|
||||
|
||||
|
||||
Reference in New Issue
Block a user