Compare commits

2 Commits
Author SHA1 Message Date
Your Name d46960433d 更新 2026-07-20 09:57:57 +08:00
Your Name 2f2150d2f6 更新: 2026-07-17 10:21:18 +08:00
23 changed files with 854 additions and 249 deletions
@@ -818,7 +818,22 @@ class DouyinImHttpClient:
"""通过 IM API 发送 Protobuf 编码的私信(带接口签名) """通过 IM API 发送 Protobuf 编码的私信(带接口签名)
content 可为纯文本,或 JSON 格式的结构化回复(文本/网址/卡片)。 content 可为纯文本,或 JSON 格式的结构化回复(文本/网址/卡片)。
所有出站发送(自动回复/手动 API/脚本)都经过全局限流器,
防止大量托管账号同时发送占满出站带宽或触发平台级风控。
""" """
from .rate_limit import get_send_limiter
async with get_send_limiter():
return await self._send_text_message_unthrottled(
conversation_id, content, conversation_short_id
)
async def _send_text_message_unthrottled(
self,
conversation_id: str,
content: str,
conversation_short_id: str = "",
) -> bool:
from .auth import DouyinAuth from .auth import DouyinAuth
from .proto_builder import ProtoBuilder from .proto_builder import ProtoBuilder
from .reply_payload import ( from .reply_payload import (
+117
View File
@@ -0,0 +1,117 @@
"""全局出站发送限流器。
目的:托管账号数量多(如 1000 个)时,避免自动回复/手动发送在同一时刻
大量并发打向抖音接口,占满服务器出站带宽或触发平台级风控。
机制(两层,先到先过):
1. 并发上限(Semaphore):同时在途的发送请求数不超过 KEFU_SEND_MAX_CONCURRENCY
2. 令牌桶(QPS):平均发送速率不超过 KEFU_SEND_RATE_PER_SEC,允许 KEFU_SEND_BURST 的突发。
限流器按事件循环惰性创建(backend 主循环一个实例;独立脚本各自一个),
所有账号共享同一个实例,因此是跨账号的全局闸门。
环境变量:
KEFU_SEND_MAX_CONCURRENCY 同时在途发送数上限,默认 20,<=0 表示不限
KEFU_SEND_RATE_PER_SEC 平均每秒发送数上限,默认 10,<=0 表示不限
KEFU_SEND_BURST 令牌桶容量(突发上限),默认与并发上限相同
"""
from __future__ import annotations
import asyncio
import logging
import os
import time
logger = logging.getLogger("douyin_im.rate_limit")
def _env_float(name: str, default: float) -> float:
try:
return float(os.getenv(name, "") or default)
except ValueError:
return default
class GlobalSendLimiter:
"""并发上限 + 令牌桶。用法:async with limiter: await 发送。"""
def __init__(
self,
max_concurrency: int = 20,
rate_per_sec: float = 10.0,
burst: float | None = None,
):
self.max_concurrency = int(max_concurrency)
self.rate_per_sec = float(rate_per_sec)
self.burst = float(burst if burst is not None else max(1, max_concurrency))
self._sem = (
asyncio.Semaphore(self.max_concurrency) if self.max_concurrency > 0 else None
)
self._tokens = self.burst
self._last_refill = time.monotonic()
self._token_lock = asyncio.Lock()
# 统计:便于日志观察限流是否生效
self._waited_total = 0.0
self._acquired_count = 0
async def _take_token(self) -> None:
if self.rate_per_sec <= 0:
return
async with self._token_lock:
while True:
now = time.monotonic()
self._tokens = min(
self.burst, self._tokens + (now - self._last_refill) * self.rate_per_sec
)
self._last_refill = now
if self._tokens >= 1.0:
self._tokens -= 1.0
return
await asyncio.sleep((1.0 - self._tokens) / self.rate_per_sec)
async def __aenter__(self) -> "GlobalSendLimiter":
start = time.monotonic()
if self._sem is not None:
await self._sem.acquire()
try:
await self._take_token()
except BaseException:
if self._sem is not None:
self._sem.release()
raise
waited = time.monotonic() - start
self._waited_total += waited
self._acquired_count += 1
if waited > 1.0:
logger.info(
"send throttled: waited %.1fs (in-flight cap=%s, rate=%s/s)",
waited, self.max_concurrency or "", self.rate_per_sec or "",
)
return self
async def __aexit__(self, *exc) -> None:
if self._sem is not None:
self._sem.release()
# 每个事件循环一个实例(backend 主循环即全局唯一;脚本自用循环互不影响)
_limiters: dict[int, GlobalSendLimiter] = {}
def get_send_limiter() -> GlobalSendLimiter:
loop = asyncio.get_running_loop()
key = id(loop)
limiter = _limiters.get(key)
if limiter is None:
max_conc = int(_env_float("KEFU_SEND_MAX_CONCURRENCY", 20))
rate = _env_float("KEFU_SEND_RATE_PER_SEC", 10.0)
burst = _env_float("KEFU_SEND_BURST", max(1, max_conc))
limiter = GlobalSendLimiter(max_conc, rate, burst)
_limiters[key] = limiter
logger.info(
"global send limiter ready: concurrency=%s, rate=%s/s, burst=%s",
max_conc if max_conc > 0 else "unlimited",
rate if rate > 0 else "unlimited",
burst,
)
return limiter
+21 -1
View File
@@ -1,5 +1,7 @@
import asyncio import asyncio
import logging import logging
import os
import random
import time import time
from typing import Awaitable, Callable, Optional from typing import Awaitable, Callable, Optional
@@ -374,6 +376,22 @@ class DouyinImService:
) )
await self._ws_client.start() await self._ws_client.start()
# 轮询错峰:多账号同时托管时,若所有账号按同一节奏轮询,请求会在同一
# 时刻叠峰。这里给每个账号随机相位偏移 + 每轮 ±20% 抖动,把请求摊平。
# WS 可用时轮询只是兜底,可以适当放缓(KEFU_IM_POLL_INTERVAL_SECONDS 可调)。
try:
poll_interval = float(os.getenv("KEFU_IM_POLL_INTERVAL_SECONDS", "") or 15)
except ValueError:
poll_interval = 15.0
poll_interval = max(5.0, poll_interval)
if has_ws:
poll_interval = max(poll_interval, 30.0)
# 首轮轮询前的随机延迟(相位偏移),批量启动时错开各账号的首波请求
await asyncio.sleep(random.uniform(0.5, min(10.0, poll_interval)))
if not self._running:
return
try: try:
await self._poll_conversations() await self._poll_conversations()
except Exception as e: except Exception as e:
@@ -387,10 +405,12 @@ class DouyinImService:
) )
loop_count = 0 loop_count = 0
next_poll = time.monotonic() + poll_interval * random.uniform(0.8, 1.2)
while self._running: while self._running:
loop_count += 1 loop_count += 1
try: try:
if loop_count % 3 == 1: if time.monotonic() >= next_poll:
next_poll = time.monotonic() + poll_interval * random.uniform(0.8, 1.2)
await self._poll_conversations() await self._poll_conversations()
if loop_count % 6 == 1: if loop_count % 6 == 1:
logger.info(f"IM direct tick #{loop_count} account={self.account_id}") logger.info(f"IM direct tick #{loop_count} account={self.account_id}")
+33
View File
@@ -49,6 +49,36 @@ async def _launch_chromium(pw, args: list[str], headless: Optional[bool] = None)
return await pw.chromium.launch(**launch_kwargs) return await pw.chromium.launch(**launch_kwargs)
# ---------------------------------------------------------------------------
# 启动错峰门:批量启动大量账号时,把各 worker 的启动时刻按固定间隔排开,
# 避免同一瞬间大量凭证校验/WS 建连/首轮拉取叠峰。
# 空闲时单个账号启动无需等待;只有短时间内大量启动才会排队。
# KEFU_WORKER_START_INTERVAL_SECONDS:相邻两个 worker 启动的最小间隔,默认 1.5s,<=0 关闭。
# ---------------------------------------------------------------------------
_start_gate = {"lock": None, "next_at": 0.0}
async def _startup_stagger(account_id: int) -> None:
try:
interval = float(os.getenv("KEFU_WORKER_START_INTERVAL_SECONDS", "") or 1.5)
except ValueError:
interval = 1.5
if interval <= 0:
return
if _start_gate["lock"] is None:
_start_gate["lock"] = asyncio.Lock()
async with _start_gate["lock"]:
now = time.monotonic()
wait = max(0.0, _start_gate["next_at"] - now)
_start_gate["next_at"] = max(now, _start_gate["next_at"]) + interval
if wait > 0:
if wait > 5:
logger.info(
f"Account {account_id}: start queued, waiting {wait:.1f}s to smooth batch startup"
)
await asyncio.sleep(wait)
def format_error(exc: BaseException) -> str: def format_error(exc: BaseException) -> str:
message = str(exc).strip() message = str(exc).strip()
if "Target page, context or browser has been closed" in message: if "Target page, context or browser has been closed" in message:
@@ -995,6 +1025,9 @@ class DouyinWorker:
logger.info(f"Starting worker loop for account {self.account_id}") logger.info(f"Starting worker loop for account {self.account_id}")
try: try:
await _startup_stagger(self.account_id)
if self.stopping:
return
storage_state = await self._load_storage_state() storage_state = await self._load_storage_state()
cookie_info = analyze_cookie( cookie_info = analyze_cookie(
json.dumps(storage_state, ensure_ascii=False) if storage_state else None json.dumps(storage_state, ensure_ascii=False) if storage_state else None
+9 -1
View File
@@ -139,9 +139,17 @@ def open_logged_in_browser(
log(f"页面加载提示:{e}(可在浏览器里手动刷新)") log(f"页面加载提示:{e}(可在浏览器里手动刷新)")
log("浏览器已打开,登录态已注入。关闭浏览器窗口即可结束。") log("浏览器已打开,登录态已注入。关闭浏览器窗口即可结束。")
# 保活等待必须用 Playwright 的 wait_for_timeout(内部持续处理浏览器事件),
# 不能用 time.sleepsleep 会卡住 Playwright 客户端线程,导致页面里
# 新开的标签页(如抖音「视频管理」)永远停在 about:blank 无法加载。
try: try:
while browser.is_connected() and context.pages: while browser.is_connected() and context.pages:
time.sleep(1) pg = context.pages[0]
try:
pg.wait_for_timeout(1000)
except Exception:
# 该页面刚被用户关闭等瞬态情况,稍候重查
time.sleep(0.2)
except Exception: except Exception:
pass pass
try: try:
@@ -1,5 +1,5 @@
(['D:\\file\\kefu\\douyin-login-launcher\\desktop_app.py'], (['D:\\file\\douyin\\douyin-login-launcher\\desktop_app.py'],
['D:\\file\\kefu\\douyin-login-launcher'], ['D:\\file\\douyin\\douyin-login-launcher'],
['playwright', ['playwright',
'playwright.__main__', 'playwright.__main__',
'playwright._impl', 'playwright._impl',
@@ -2485,7 +2485,7 @@
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\rthooks\\pyi_rth_cryptography_openssl.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\rthooks\\pyi_rth_cryptography_openssl.py',
'PYSOURCE'), 'PYSOURCE'),
('desktop_app', ('desktop_app',
'D:\\file\\kefu\\douyin-login-launcher\\desktop_app.py', 'D:\\file\\douyin\\douyin-login-launcher\\desktop_app.py',
'PYSOURCE')], 'PYSOURCE')],
[('pkg_resources', [('pkg_resources',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\pkg_resources\\__init__.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\pkg_resources\\__init__.py',
@@ -2559,9 +2559,6 @@
('gettext', ('gettext',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\gettext.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\gettext.py',
'PYMODULE'), 'PYMODULE'),
('urllib',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\__init__.py',
'PYMODULE'),
('email.charset', ('email.charset',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\charset.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\email\\charset.py',
'PYMODULE'), 'PYMODULE'),
@@ -3130,30 +3127,6 @@
('xml.sax.saxutils', ('xml.sax.saxutils',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\xml\\sax\\saxutils.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\xml\\sax\\saxutils.py',
'PYMODULE'), 'PYMODULE'),
('urllib.request',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\request.py',
'PYMODULE'),
('getpass',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\getpass.py',
'PYMODULE'),
('nturl2path',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\nturl2path.py',
'PYMODULE'),
('ftplib',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ftplib.py',
'PYMODULE'),
('netrc',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\netrc.py',
'PYMODULE'),
('http.cookiejar',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\http\\cookiejar.py',
'PYMODULE'),
('urllib.response',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\response.py',
'PYMODULE'),
('urllib.error',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\error.py',
'PYMODULE'),
('xml.sax', ('xml.sax',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\xml\\sax\\__init__.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\xml\\sax\\__init__.py',
'PYMODULE'), 'PYMODULE'),
@@ -3346,6 +3319,9 @@
('setuptools._distutils.command.register', ('setuptools._distutils.command.register',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools\\_distutils\\command\\register.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools\\_distutils\\command\\register.py',
'PYMODULE'), 'PYMODULE'),
('getpass',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\getpass.py',
'PYMODULE'),
('setuptools._distutils.command.py37compat', ('setuptools._distutils.command.py37compat',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools\\_distutils\\command\\py37compat.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools\\_distutils\\command\\py37compat.py',
'PYMODULE'), 'PYMODULE'),
@@ -4969,31 +4945,47 @@
('playwright', ('playwright',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\playwright\\__init__.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\playwright\\__init__.py',
'PYMODULE'), 'PYMODULE'),
('stringprep',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\stringprep.py',
'PYMODULE'),
('tracemalloc', ('tracemalloc',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\tracemalloc.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\tracemalloc.py',
'PYMODULE'), 'PYMODULE'),
('_py_abc', ('_py_abc',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_py_abc.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_py_abc.py',
'PYMODULE'), 'PYMODULE'),
('stringprep', ('urllib.request',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\stringprep.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\request.py',
'PYMODULE'), 'PYMODULE'),
('json', ('nturl2path',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\__init__.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\nturl2path.py',
'PYMODULE'), 'PYMODULE'),
('json.encoder', ('ftplib',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\encoder.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ftplib.py',
'PYMODULE'), 'PYMODULE'),
('json.decoder', ('netrc',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\decoder.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\netrc.py',
'PYMODULE'), 'PYMODULE'),
('json.scanner', ('http.cookiejar',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\scanner.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\http\\cookiejar.py',
'PYMODULE'),
('urllib.response',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\response.py',
'PYMODULE'),
('urllib.error',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\error.py',
'PYMODULE'),
('urllib',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\__init__.py',
'PYMODULE'),
('version',
'D:\\file\\douyin\\douyin-login-launcher\\version.py',
'PYMODULE'),
('updater',
'D:\\file\\douyin\\douyin-login-launcher\\updater.py',
'PYMODULE'), 'PYMODULE'),
('version', 'D:\\file\\kefu\\douyin-login-launcher\\version.py', 'PYMODULE'),
('updater', 'D:\\file\\kefu\\douyin-login-launcher\\updater.py', 'PYMODULE'),
('browser_open', ('browser_open',
'D:\\file\\kefu\\douyin-login-launcher\\browser_open.py', 'D:\\file\\douyin\\douyin-login-launcher\\browser_open.py',
'PYMODULE'), 'PYMODULE'),
('webview', ('webview',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\webview\\__init__.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\webview\\__init__.py',
@@ -5175,6 +5167,18 @@
('subprocess', ('subprocess',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\subprocess.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\subprocess.py',
'PYMODULE'), 'PYMODULE'),
('json',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\__init__.py',
'PYMODULE'),
('json.encoder',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\encoder.py',
'PYMODULE'),
('json.decoder',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\decoder.py',
'PYMODULE'),
('json.scanner',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\scanner.py',
'PYMODULE'),
('ctypes', ('ctypes',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ctypes\\__init__.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ctypes\\__init__.py',
'PYMODULE'), 'PYMODULE'),
@@ -7270,15 +7274,6 @@
('webview\\window.py', ('webview\\window.py',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\webview\\window.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\webview\\window.py',
'DATA'), 'DATA'),
('cryptography-48.0.0.dist-info\\WHEEL',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\WHEEL',
'DATA'),
('cryptography-48.0.0.dist-info\\METADATA',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\METADATA',
'DATA'),
('cryptography-48.0.0.dist-info\\INSTALLER',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\INSTALLER',
'DATA'),
('cryptography-48.0.0.dist-info\\sboms\\cryptography-rust.cyclonedx.json', ('cryptography-48.0.0.dist-info\\sboms\\cryptography-rust.cyclonedx.json',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\sboms\\cryptography-rust.cyclonedx.json', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\sboms\\cryptography-rust.cyclonedx.json',
'DATA'), 'DATA'),
@@ -7288,17 +7283,44 @@
('cryptography-48.0.0.dist-info\\RECORD', ('cryptography-48.0.0.dist-info\\RECORD',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\RECORD', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\RECORD',
'DATA'), 'DATA'),
('cryptography-48.0.0.dist-info\\licenses\\LICENSE.APACHE', ('cryptography-48.0.0.dist-info\\METADATA',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\licenses\\LICENSE.APACHE', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\METADATA',
'DATA'),
('cryptography-48.0.0.dist-info\\INSTALLER',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\INSTALLER',
'DATA'), 'DATA'),
('cryptography-48.0.0.dist-info\\licenses\\LICENSE', ('cryptography-48.0.0.dist-info\\licenses\\LICENSE',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\licenses\\LICENSE', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\licenses\\LICENSE',
'DATA'), 'DATA'),
('cryptography-48.0.0.dist-info\\WHEEL',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\WHEEL',
'DATA'),
('cryptography-48.0.0.dist-info\\licenses\\LICENSE.BSD', ('cryptography-48.0.0.dist-info\\licenses\\LICENSE.BSD',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\licenses\\LICENSE.BSD', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\licenses\\LICENSE.BSD',
'DATA'), 'DATA'),
('attrs-26.1.0.dist-info\\METADATA', ('cryptography-48.0.0.dist-info\\licenses\\LICENSE.APACHE',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\METADATA', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\cryptography-48.0.0.dist-info\\licenses\\LICENSE.APACHE',
'DATA'),
('wheel-0.45.1.dist-info\\WHEEL',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\WHEEL',
'DATA'),
('attrs-26.1.0.dist-info\\INSTALLER',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\INSTALLER',
'DATA'),
('wheel-0.45.1.dist-info\\RECORD',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\RECORD',
'DATA'),
('setuptools-65.5.0.dist-info\\WHEEL',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\WHEEL',
'DATA'),
('wheel-0.45.1.dist-info\\LICENSE.txt',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\LICENSE.txt',
'DATA'),
('setuptools-65.5.0.dist-info\\INSTALLER',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\INSTALLER',
'DATA'),
('setuptools-65.5.0.dist-info\\RECORD',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\RECORD',
'DATA'), 'DATA'),
('wheel-0.45.1.dist-info\\REQUESTED', ('wheel-0.45.1.dist-info\\REQUESTED',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\REQUESTED', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\REQUESTED',
@@ -7309,129 +7331,48 @@
('setuptools-65.5.0.dist-info\\LICENSE', ('setuptools-65.5.0.dist-info\\LICENSE',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\LICENSE', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\LICENSE',
'DATA'), 'DATA'),
('wheel-0.45.1.dist-info\\entry_points.txt',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\entry_points.txt',
'DATA'),
('wheel-0.45.1.dist-info\\RECORD',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\RECORD',
'DATA'),
('attrs-26.1.0.dist-info\\INSTALLER',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\INSTALLER',
'DATA'),
('wheel-0.45.1.dist-info\\LICENSE.txt',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\LICENSE.txt',
'DATA'),
('attrs-26.1.0.dist-info\\RECORD',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\RECORD',
'DATA'),
('setuptools-65.5.0.dist-info\\INSTALLER',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\INSTALLER',
'DATA'),
('setuptools-65.5.0.dist-info\\WHEEL',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\WHEEL',
'DATA'),
('wheel-0.45.1.dist-info\\METADATA', ('wheel-0.45.1.dist-info\\METADATA',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\METADATA', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\METADATA',
'DATA'), 'DATA'),
('wheel-0.45.1.dist-info\\INSTALLER',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\INSTALLER',
'DATA'),
('setuptools-65.5.0.dist-info\\METADATA',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\METADATA',
'DATA'),
('wheel-0.45.1.dist-info\\WHEEL',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\WHEEL',
'DATA'),
('setuptools-65.5.0.dist-info\\entry_points.txt', ('setuptools-65.5.0.dist-info\\entry_points.txt',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\entry_points.txt', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\entry_points.txt',
'DATA'), 'DATA'),
('setuptools-65.5.0.dist-info\\RECORD',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\RECORD',
'DATA'),
('setuptools-65.5.0.dist-info\\top_level.txt', ('setuptools-65.5.0.dist-info\\top_level.txt',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\top_level.txt', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\top_level.txt',
'DATA'), 'DATA'),
('wheel-0.45.1.dist-info\\entry_points.txt',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\entry_points.txt',
'DATA'),
('attrs-26.1.0.dist-info\\licenses\\LICENSE', ('attrs-26.1.0.dist-info\\licenses\\LICENSE',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\licenses\\LICENSE', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\licenses\\LICENSE',
'DATA'), 'DATA'),
('attrs-26.1.0.dist-info\\RECORD',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\RECORD',
'DATA'),
('setuptools-65.5.0.dist-info\\METADATA',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\setuptools-65.5.0.dist-info\\METADATA',
'DATA'),
('wheel-0.45.1.dist-info\\INSTALLER',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\wheel-0.45.1.dist-info\\INSTALLER',
'DATA'),
('attrs-26.1.0.dist-info\\METADATA',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\METADATA',
'DATA'),
('attrs-26.1.0.dist-info\\WHEEL', ('attrs-26.1.0.dist-info\\WHEEL',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\WHEEL', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\WHEEL',
'DATA'), 'DATA'),
('base_library.zip', ('base_library.zip',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\base_library.zip', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\base_library.zip',
'DATA')], 'DATA')],
[('_weakrefset', [('functools',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_weakrefset.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\functools.py',
'PYMODULE'), 'PYMODULE'),
('copyreg', ('locale',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\copyreg.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\locale.py',
'PYMODULE'),
('posixpath',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\posixpath.py',
'PYMODULE'),
('stat',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\stat.py',
'PYMODULE'),
('warnings',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\warnings.py',
'PYMODULE'),
('traceback',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\traceback.py',
'PYMODULE'),
('reprlib',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\reprlib.py',
'PYMODULE'),
('re._parser',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_parser.py',
'PYMODULE'),
('re._constants',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_constants.py',
'PYMODULE'),
('re._compiler',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_compiler.py',
'PYMODULE'),
('re._casefix',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_casefix.py',
'PYMODULE'),
('re',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\__init__.py',
'PYMODULE'),
('ntpath',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ntpath.py',
'PYMODULE'),
('enum',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\enum.py',
'PYMODULE'),
('abc',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\abc.py',
'PYMODULE'),
('_collections_abc',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_collections_abc.py',
'PYMODULE'),
('keyword',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\keyword.py',
'PYMODULE'),
('sre_constants',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_constants.py',
'PYMODULE'),
('heapq',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\heapq.py',
'PYMODULE'),
('sre_compile',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_compile.py',
'PYMODULE'),
('sre_parse',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_parse.py',
'PYMODULE'), 'PYMODULE'),
('genericpath', ('genericpath',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\genericpath.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\genericpath.py',
'PYMODULE'), 'PYMODULE'),
('collections.abc',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\collections\\abc.py',
'PYMODULE'),
('collections',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\collections\\__init__.py',
'PYMODULE'),
('encodings.zlib_codec', ('encodings.zlib_codec',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\zlib_codec.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\zlib_codec.py',
'PYMODULE'), 'PYMODULE'),
@@ -7798,30 +7739,93 @@
('encodings', ('encodings',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\__init__.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\__init__.py',
'PYMODULE'), 'PYMODULE'),
('locale', ('sre_parse',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\locale.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_parse.py',
'PYMODULE'), 'PYMODULE'),
('types', ('warnings',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\types.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\warnings.py',
'PYMODULE'), 'PYMODULE'),
('linecache', ('re._parser',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\linecache.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_parser.py',
'PYMODULE'), 'PYMODULE'),
('io', ('re._constants',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\io.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_constants.py',
'PYMODULE'), 'PYMODULE'),
('weakref', ('re._compiler',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\weakref.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_compiler.py',
'PYMODULE'), 'PYMODULE'),
('functools', ('re._casefix',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\functools.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_casefix.py',
'PYMODULE'),
('re',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\__init__.py',
'PYMODULE'), 'PYMODULE'),
('operator', ('operator',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\operator.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\operator.py',
'PYMODULE'), 'PYMODULE'),
('stat',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\stat.py',
'PYMODULE'),
('posixpath',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\posixpath.py',
'PYMODULE'),
('enum',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\enum.py',
'PYMODULE'),
('reprlib',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\reprlib.py',
'PYMODULE'),
('heapq',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\heapq.py',
'PYMODULE'),
('abc',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\abc.py',
'PYMODULE'),
('sre_constants',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_constants.py',
'PYMODULE'),
('copyreg',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\copyreg.py',
'PYMODULE'),
('_weakrefset',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_weakrefset.py',
'PYMODULE'),
('types',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\types.py',
'PYMODULE'),
('sre_compile',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_compile.py',
'PYMODULE'),
('_collections_abc',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_collections_abc.py',
'PYMODULE'),
('ntpath',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ntpath.py',
'PYMODULE'),
('codecs', ('codecs',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\codecs.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\codecs.py',
'PYMODULE'), 'PYMODULE'),
('linecache',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\linecache.py',
'PYMODULE'),
('weakref',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\weakref.py',
'PYMODULE'),
('traceback',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\traceback.py',
'PYMODULE'),
('io',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\io.py',
'PYMODULE'),
('keyword',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\keyword.py',
'PYMODULE'),
('collections.abc',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\collections\\abc.py',
'PYMODULE'),
('collections',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\collections\\__init__.py',
'PYMODULE'),
('os', ('os',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\os.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\os.py',
'PYMODULE')]) 'PYMODULE')])
@@ -1,5 +1,5 @@
([('DouyinDesktop.exe', ([('DouyinDesktop.exe',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\DouyinDesktop.exe', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\DouyinDesktop.exe',
'EXECUTABLE'), 'EXECUTABLE'),
('Pythonwin\\mfc140u.dll', ('Pythonwin\\mfc140u.dll',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\Pythonwin\\mfc140u.dll', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\Pythonwin\\mfc140u.dll',
@@ -71,7 +71,7 @@
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\licenses\\LICENSE', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\attrs-26.1.0.dist-info\\licenses\\LICENSE',
'DATA'), 'DATA'),
('base_library.zip', ('base_library.zip',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\base_library.zip', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\base_library.zip',
'DATA'), 'DATA'),
('bcrypt\\_bcrypt.pyd', ('bcrypt\\_bcrypt.pyd',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\bcrypt\\_bcrypt.pyd', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\bcrypt\\_bcrypt.pyd',
+11 -11
View File
@@ -1,8 +1,8 @@
('D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\DouyinDesktop.exe', ('D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\DouyinDesktop.exe',
False, False,
False, False,
True, True,
['D:\\file\\kefu\\douyin-login-launcher\\app.ico'], ['D:\\file\\douyin\\douyin-login-launcher\\app.ico'],
None, None,
False, False,
False, False,
@@ -29,25 +29,25 @@
None, None,
None, None,
None, None,
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\DouyinDesktop.pkg', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\DouyinDesktop.pkg',
[('pyi-contents-directory _internal', '', 'OPTION'), [('pyi-contents-directory _internal', '', 'OPTION'),
('PYZ-00.pyz', ('PYZ-00.pyz',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\PYZ-00.pyz', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\PYZ-00.pyz',
'PYZ'), 'PYZ'),
('struct', ('struct',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\localpycs\\struct.pyc', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\localpycs\\struct.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod01_archive', ('pyimod01_archive',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod01_archive.pyc', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod01_archive.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod02_importers', ('pyimod02_importers',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod02_importers.pyc', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod02_importers.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod03_ctypes', ('pyimod03_ctypes',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod03_ctypes.pyc', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod03_ctypes.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod04_pywin32', ('pyimod04_pywin32',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod04_pywin32.pyc', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod04_pywin32.pyc',
'PYMODULE'), 'PYMODULE'),
('pyiboot01_bootstrap', ('pyiboot01_bootstrap',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
@@ -77,12 +77,12 @@
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\rthooks\\pyi_rth_cryptography_openssl.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\rthooks\\pyi_rth_cryptography_openssl.py',
'PYSOURCE'), 'PYSOURCE'),
('desktop_app', ('desktop_app',
'D:\\file\\kefu\\douyin-login-launcher\\desktop_app.py', 'D:\\file\\douyin\\douyin-login-launcher\\desktop_app.py',
'PYSOURCE')], 'PYSOURCE')],
[], [],
False, False,
False, False,
1782438587, 1784257587,
[('runw.exe', [('runw.exe',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\runw.exe', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\runw.exe',
'EXECUTABLE')], 'EXECUTABLE')],
@@ -1,4 +1,4 @@
('D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\DouyinDesktop.pkg', ('D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\DouyinDesktop.pkg',
{'BINARY': True, {'BINARY': True,
'DATA': True, 'DATA': True,
'EXECUTABLE': True, 'EXECUTABLE': True,
@@ -10,22 +10,22 @@
'SYMLINK': False}, 'SYMLINK': False},
[('pyi-contents-directory _internal', '', 'OPTION'), [('pyi-contents-directory _internal', '', 'OPTION'),
('PYZ-00.pyz', ('PYZ-00.pyz',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\PYZ-00.pyz', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\PYZ-00.pyz',
'PYZ'), 'PYZ'),
('struct', ('struct',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\localpycs\\struct.pyc', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\localpycs\\struct.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod01_archive', ('pyimod01_archive',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod01_archive.pyc', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod01_archive.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod02_importers', ('pyimod02_importers',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod02_importers.pyc', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod02_importers.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod03_ctypes', ('pyimod03_ctypes',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod03_ctypes.pyc', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod03_ctypes.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod04_pywin32', ('pyimod04_pywin32',
'D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod04_pywin32.pyc', 'D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\localpycs\\pyimod04_pywin32.pyc',
'PYMODULE'), 'PYMODULE'),
('pyiboot01_bootstrap', ('pyiboot01_bootstrap',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
@@ -55,7 +55,7 @@
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\rthooks\\pyi_rth_cryptography_openssl.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\_pyinstaller_hooks_contrib\\rthooks\\pyi_rth_cryptography_openssl.py',
'PYSOURCE'), 'PYSOURCE'),
('desktop_app', ('desktop_app',
'D:\\file\\kefu\\douyin-login-launcher\\desktop_app.py', 'D:\\file\\douyin\\douyin-login-launcher\\desktop_app.py',
'PYSOURCE')], 'PYSOURCE')],
'python311.dll', 'python311.dll',
True, True,
Binary file not shown.
@@ -1,4 +1,4 @@
('D:\\file\\kefu\\douyin-login-launcher\\build\\desktop\\PYZ-00.pyz', ('D:\\file\\douyin\\douyin-login-launcher\\build\\desktop\\PYZ-00.pyz',
[('PyInstaller', [('PyInstaller',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\__init__.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\__init__.py',
'PYMODULE'), 'PYMODULE'),
@@ -294,7 +294,7 @@
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\bottle.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\bottle.py',
'PYMODULE'), 'PYMODULE'),
('browser_open', ('browser_open',
'D:\\file\\kefu\\douyin-login-launcher\\browser_open.py', 'D:\\file\\douyin\\douyin-login-launcher\\browser_open.py',
'PYMODULE'), 'PYMODULE'),
('bz2', ('bz2',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\bz2.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\bz2.py',
@@ -2346,7 +2346,9 @@
('unittest.util', ('unittest.util',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\unittest\\util.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\unittest\\util.py',
'PYMODULE'), 'PYMODULE'),
('updater', 'D:\\file\\kefu\\douyin-login-launcher\\updater.py', 'PYMODULE'), ('updater',
'D:\\file\\douyin\\douyin-login-launcher\\updater.py',
'PYMODULE'),
('urllib', ('urllib',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\__init__.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\urllib\\__init__.py',
'PYMODULE'), 'PYMODULE'),
@@ -2365,7 +2367,9 @@
('uuid', ('uuid',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\uuid.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\uuid.py',
'PYMODULE'), 'PYMODULE'),
('version', 'D:\\file\\kefu\\douyin-login-launcher\\version.py', 'PYMODULE'), ('version',
'D:\\file\\douyin\\douyin-login-launcher\\version.py',
'PYMODULE'),
('webbrowser', ('webbrowser',
'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\webbrowser.py', 'C:\\Users\\pc\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\webbrowser.py',
'PYMODULE'), 'PYMODULE'),
Binary file not shown.
@@ -15,11 +15,8 @@ IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
tracking down the missing module yourself. Thanks! tracking down the missing module yourself. Thanks!
missing module named pyimod02_importers - imported by C:\Users\pc\AppData\Local\Programs\Python\Python311\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed), C:\Users\pc\AppData\Local\Programs\Python\Python311\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgres.py (delayed) missing module named pyimod02_importers - imported by C:\Users\pc\AppData\Local\Programs\Python\Python311\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed), C:\Users\pc\AppData\Local\Programs\Python\Python311\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgres.py (delayed)
missing module named 'org.python' - imported by copy (optional), xml.sax (delayed, conditional) missing module named org - imported by copy (optional)
missing module named org - imported by pickle (optional) missing module named 'org.python' - imported by pickle (optional), xml.sax (delayed, conditional)
missing module named urllib.unquote - imported by urllib (conditional), bottle (conditional)
missing module named urllib.quote - imported by urllib (conditional), bottle (conditional)
missing module named urllib.urlencode - imported by urllib (conditional), bottle (conditional)
missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), http.server (delayed, optional), webbrowser (delayed), distutils.util (delayed, conditional, optional), distutils.archive_util (optional), netrc (delayed, conditional), getpass (delayed), setuptools._distutils.archive_util (optional), setuptools._distutils.util (delayed, conditional, optional) missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), http.server (delayed, optional), webbrowser (delayed), distutils.util (delayed, conditional, optional), distutils.archive_util (optional), netrc (delayed, conditional), getpass (delayed), setuptools._distutils.archive_util (optional), setuptools._distutils.util (delayed, conditional, optional)
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), distutils.archive_util (optional), setuptools._distutils.archive_util (optional) missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), distutils.archive_util (optional), setuptools._distutils.archive_util (optional)
missing module named posix - imported by os (conditional, optional), posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional) missing module named posix - imported by os (conditional, optional), posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional)
@@ -42,7 +39,6 @@ missing module named _winreg - imported by platform (delayed, optional), pkg_res
missing module named pkg_resources.extern.packaging - imported by pkg_resources.extern (top-level), pkg_resources (top-level) missing module named pkg_resources.extern.packaging - imported by pkg_resources.extern (top-level), pkg_resources (top-level)
missing module named pkg_resources.extern.appdirs - imported by pkg_resources.extern (top-level), pkg_resources (top-level) missing module named pkg_resources.extern.appdirs - imported by pkg_resources.extern (top-level), pkg_resources (top-level)
missing module named 'pkg_resources.extern.jaraco' - imported by pkg_resources (top-level), pkg_resources._vendor.jaraco.text (top-level) missing module named 'pkg_resources.extern.jaraco' - imported by pkg_resources (top-level), pkg_resources._vendor.jaraco.text (top-level)
missing module named _scproxy - imported by urllib.request (conditional)
missing module named 'java.lang' - imported by platform (delayed, optional), xml.sax._exceptions (conditional) missing module named 'java.lang' - imported by platform (delayed, optional), xml.sax._exceptions (conditional)
missing module named vms_lib - imported by platform (delayed, optional) missing module named vms_lib - imported by platform (delayed, optional)
missing module named java - imported by platform (delayed) missing module named java - imported by platform (delayed)
@@ -104,6 +100,9 @@ missing module named ConfigParser - imported by bottle (conditional)
missing module named StringIO - imported by bottle (conditional) missing module named StringIO - imported by bottle (conditional)
missing module named cPickle - imported by bottle (conditional) missing module named cPickle - imported by bottle (conditional)
missing module named Cookie - imported by bottle (conditional) missing module named Cookie - imported by bottle (conditional)
missing module named urllib.unquote - imported by urllib (conditional), bottle (conditional)
missing module named urllib.quote - imported by urllib (conditional), bottle (conditional)
missing module named urllib.urlencode - imported by urllib (conditional), bottle (conditional)
missing module named urlparse - imported by bottle (conditional) missing module named urlparse - imported by bottle (conditional)
missing module named thread - imported by bottle (conditional), cffi.lock (conditional, optional), cffi.cparser (conditional, optional), sortedcontainers.sortedlist (conditional, optional) missing module named thread - imported by bottle (conditional), cffi.lock (conditional, optional), cffi.cparser (conditional, optional), sortedcontainers.sortedlist (conditional, optional)
missing module named httplib - imported by bottle (conditional) missing module named httplib - imported by bottle (conditional)
@@ -173,4 +172,5 @@ missing module named collections.Set - imported by collections (optional), sorte
missing module named collections.MutableSequence - imported by collections (optional), sortedcontainers.sortedlist (optional) missing module named collections.MutableSequence - imported by collections (optional), sortedcontainers.sortedlist (optional)
missing module named _typeshed - imported by trio._file_io (conditional), trio._path (conditional) missing module named _typeshed - imported by trio._file_io (conditional), trio._path (conditional)
missing module named OpenSSL - imported by trio._dtls (delayed, conditional) missing module named OpenSSL - imported by trio._dtls (delayed, conditional)
missing module named _scproxy - imported by urllib.request (conditional)
missing module named fcntl - imported by subprocess (optional) missing module named fcntl - imported by subprocess (optional)
@@ -15,7 +15,7 @@
<div class="node"> <div class="node">
<a name="desktop_app.py"></a> <a name="desktop_app.py"></a>
<a target="code" href="///D:/file/kefu/douyin-login-launcher/desktop_app.py" type="text/plain"><tt>desktop_app.py</tt></a> <a target="code" href="///D:/file/douyin/douyin-login-launcher/desktop_app.py" type="text/plain"><tt>desktop_app.py</tt></a>
<span class="moduletype">Script</span> <div class="import"> <span class="moduletype">Script</span> <div class="import">
imports: imports:
<a href="#__future__">__future__</a> <a href="#__future__">__future__</a>
@@ -262,10 +262,14 @@ imports:
&#8226; <a href="#stat">stat</a> &#8226; <a href="#stat">stat</a>
&#8226; <a href="#subprocess">subprocess</a> &#8226; <a href="#subprocess">subprocess</a>
&#8226; <a href="#threading">threading</a> &#8226; <a href="#threading">threading</a>
&#8226; <a href="#time">time</a>
&#8226; <a href="#traceback">traceback</a> &#8226; <a href="#traceback">traceback</a>
&#8226; <a href="#types">types</a> &#8226; <a href="#types">types</a>
&#8226; <a href="#updater">updater</a> &#8226; <a href="#updater">updater</a>
&#8226; <a href="#urllib">urllib</a>
&#8226; <a href="#urllib.error">urllib.error</a>
&#8226; <a href="#urllib.parse">urllib.parse</a> &#8226; <a href="#urllib.parse">urllib.parse</a>
&#8226; <a href="#urllib.request">urllib.request</a>
&#8226; <a href="#version">version</a> &#8226; <a href="#version">version</a>
&#8226; <a href="#warnings">warnings</a> &#8226; <a href="#warnings">warnings</a>
&#8226; <a href="#weakref">weakref</a> &#8226; <a href="#weakref">weakref</a>
@@ -683,7 +687,7 @@ imported by:
<a target="code" href="" type="text/plain"><tt>'org.python'</tt></a> <a target="code" href="" type="text/plain"><tt>'org.python'</tt></a>
<span class="moduletype">MissingModule</span> <div class="import"> <span class="moduletype">MissingModule</span> <div class="import">
imported by: imported by:
<a href="#copy">copy</a> <a href="#pickle">pickle</a>
&#8226; <a href="#xml.sax">xml.sax</a> &#8226; <a href="#xml.sax">xml.sax</a>
</div> </div>
@@ -4941,7 +4945,7 @@ imported by:
<div class="node"> <div class="node">
<a name="browser_open"></a> <a name="browser_open"></a>
<a target="code" href="///D:/file/kefu/douyin-login-launcher/browser_open.py" type="text/plain"><tt>browser_open</tt></a> <a target="code" href="///D:/file/douyin/douyin-login-launcher/browser_open.py" type="text/plain"><tt>browser_open</tt></a>
<span class="moduletype">SourceModule</span> <div class="import"> <span class="moduletype">SourceModule</span> <div class="import">
imports: imports:
<a href="#__future__">__future__</a> <a href="#__future__">__future__</a>
@@ -6783,8 +6787,8 @@ imported by:
<a target="code" href="///C:/Users/pc/AppData/Local/Programs/Python/Python311/Lib/copy.py" type="text/plain"><tt>copy</tt></a> <a target="code" href="///C:/Users/pc/AppData/Local/Programs/Python/Python311/Lib/copy.py" type="text/plain"><tt>copy</tt></a>
<span class="moduletype">SourceModule</span> <div class="import"> <span class="moduletype">SourceModule</span> <div class="import">
imports: imports:
<a href="#'org.python'">'org.python'</a> <a href="#copyreg">copyreg</a>
&#8226; <a href="#copyreg">copyreg</a> &#8226; <a href="#org">org</a>
&#8226; <a href="#types">types</a> &#8226; <a href="#types">types</a>
&#8226; <a href="#weakref">weakref</a> &#8226; <a href="#weakref">weakref</a>
@@ -16230,7 +16234,7 @@ imported by:
<a target="code" href="" type="text/plain"><tt>org</tt></a> <a target="code" href="" type="text/plain"><tt>org</tt></a>
<span class="moduletype">MissingModule</span> <div class="import"> <span class="moduletype">MissingModule</span> <div class="import">
imported by: imported by:
<a href="#pickle">pickle</a> <a href="#copy">copy</a>
</div> </div>
@@ -17235,14 +17239,14 @@ imported by:
<a target="code" href="///C:/Users/pc/AppData/Local/Programs/Python/Python311/Lib/pickle.py" type="text/plain"><tt>pickle</tt></a> <a target="code" href="///C:/Users/pc/AppData/Local/Programs/Python/Python311/Lib/pickle.py" type="text/plain"><tt>pickle</tt></a>
<span class="moduletype">SourceModule</span> <div class="import"> <span class="moduletype">SourceModule</span> <div class="import">
imports: imports:
<a href="#_compat_pickle">_compat_pickle</a> <a href="#'org.python'">'org.python'</a>
&#8226; <a href="#_compat_pickle">_compat_pickle</a>
&#8226; <a href="#_pickle">_pickle</a> &#8226; <a href="#_pickle">_pickle</a>
&#8226; <a href="#codecs">codecs</a> &#8226; <a href="#codecs">codecs</a>
&#8226; <a href="#copyreg">copyreg</a> &#8226; <a href="#copyreg">copyreg</a>
&#8226; <a href="#functools">functools</a> &#8226; <a href="#functools">functools</a>
&#8226; <a href="#io">io</a> &#8226; <a href="#io">io</a>
&#8226; <a href="#itertools">itertools</a> &#8226; <a href="#itertools">itertools</a>
&#8226; <a href="#org">org</a>
&#8226; <a href="#pprint">pprint</a> &#8226; <a href="#pprint">pprint</a>
&#8226; <a href="#re">re</a> &#8226; <a href="#re">re</a>
&#8226; <a href="#struct">struct</a> &#8226; <a href="#struct">struct</a>
@@ -27029,6 +27033,7 @@ imported by:
&#8226; <a href="#browser_open">browser_open</a> &#8226; <a href="#browser_open">browser_open</a>
&#8226; <a href="#concurrent.futures._base">concurrent.futures._base</a> &#8226; <a href="#concurrent.futures._base">concurrent.futures._base</a>
&#8226; <a href="#datetime">datetime</a> &#8226; <a href="#datetime">datetime</a>
&#8226; <a href="#desktop_app.py">desktop_app.py</a>
&#8226; <a href="#email._parseaddr">email._parseaddr</a> &#8226; <a href="#email._parseaddr">email._parseaddr</a>
&#8226; <a href="#email.generator">email.generator</a> &#8226; <a href="#email.generator">email.generator</a>
&#8226; <a href="#email.utils">email.utils</a> &#8226; <a href="#email.utils">email.utils</a>
@@ -30095,7 +30100,7 @@ imported by:
<div class="node"> <div class="node">
<a name="updater"></a> <a name="updater"></a>
<a target="code" href="///D:/file/kefu/douyin-login-launcher/updater.py" type="text/plain"><tt>updater</tt></a> <a target="code" href="///D:/file/douyin/douyin-login-launcher/updater.py" type="text/plain"><tt>updater</tt></a>
<span class="moduletype">SourceModule</span> <div class="import"> <span class="moduletype">SourceModule</span> <div class="import">
imports: imports:
<a href="#__future__">__future__</a> <a href="#__future__">__future__</a>
@@ -30129,6 +30134,7 @@ imports:
<div class="import"> <div class="import">
imported by: imported by:
<a href="#bottle">bottle</a> <a href="#bottle">bottle</a>
&#8226; <a href="#desktop_app.py">desktop_app.py</a>
&#8226; <a href="#email._header_value_parser">email._header_value_parser</a> &#8226; <a href="#email._header_value_parser">email._header_value_parser</a>
&#8226; <a href="#playwright._impl._network">playwright._impl._network</a> &#8226; <a href="#playwright._impl._network">playwright._impl._network</a>
&#8226; <a href="#updater">updater</a> &#8226; <a href="#updater">updater</a>
@@ -30153,7 +30159,8 @@ imports:
</div> </div>
<div class="import"> <div class="import">
imported by: imported by:
<a href="#urllib.request">urllib.request</a> <a href="#desktop_app.py">desktop_app.py</a>
&#8226; <a href="#urllib.request">urllib.request</a>
</div> </div>
@@ -30263,6 +30270,7 @@ imports:
imported by: imported by:
<a href="#PyInstaller.lib.modulegraph.modulegraph">PyInstaller.lib.modulegraph.modulegraph</a> <a href="#PyInstaller.lib.modulegraph.modulegraph">PyInstaller.lib.modulegraph.modulegraph</a>
&#8226; <a href="#clr_loader.util.coreclr_errors">clr_loader.util.coreclr_errors</a> &#8226; <a href="#clr_loader.util.coreclr_errors">clr_loader.util.coreclr_errors</a>
&#8226; <a href="#desktop_app.py">desktop_app.py</a>
&#8226; <a href="#http.cookiejar">http.cookiejar</a> &#8226; <a href="#http.cookiejar">http.cookiejar</a>
&#8226; <a href="#setuptools._distutils.command.register">setuptools._distutils.command.register</a> &#8226; <a href="#setuptools._distutils.command.register">setuptools._distutils.command.register</a>
&#8226; <a href="#setuptools._distutils.command.upload">setuptools._distutils.command.upload</a> &#8226; <a href="#setuptools._distutils.command.upload">setuptools._distutils.command.upload</a>
@@ -30385,7 +30393,7 @@ imported by:
<div class="node"> <div class="node">
<a name="version"></a> <a name="version"></a>
<a target="code" href="///D:/file/kefu/douyin-login-launcher/version.py" type="text/plain"><tt>version</tt></a> <a target="code" href="///D:/file/douyin/douyin-login-launcher/version.py" type="text/plain"><tt>version</tt></a>
<span class="moduletype">SourceModule</span> <div class="import"> <span class="moduletype">SourceModule</span> <div class="import">
imported by: imported by:
<a href="#desktop_app.py">desktop_app.py</a> <a href="#desktop_app.py">desktop_app.py</a>
+388 -12
View File
@@ -1,10 +1,12 @@
"""抖音托管客服 · 桌面整合版 """抖音托管客服 · 桌面整合版
一个软件搞定一切: 一个软件搞定一切:
1) 启动后默认打开云端网站(dev.zhenyangtang.com.cn); 1) 启动后先显示「站点选择」界面(内置 dev / dev1,可自行添加更多站点,
2) 页面右下角自动出现「一键本地登录」悬浮按钮 可勾选“记住选择”下次直接进入)
3) 点它 → 弹出你的托管账号列表 → 选一个 → 本机直接打开一个 2) 进入站点后,页面右下角自动出现「一键本地登录」悬浮按钮,
已登录该托管账号的浏览器,进入抖音。 以及「切换站点」按钮可随时换环境;
3) 点「一键本地登录」→ 弹出你的托管账号列表 → 选一个 → 本机直接打开
一个已登录该托管账号的浏览器,进入抖音。
原理:桌面壳(pywebview)把云端网页装进原生窗口,并注入一段脚本。该脚本 原理:桌面壳(pywebview)把云端网页装进原生窗口,并注入一段脚本。该脚本
用网页里已有的登录令牌(localStorage.kefu_token)调用云端接口取账号与凭证, 用网页里已有的登录令牌(localStorage.kefu_token)调用云端接口取账号与凭证,
@@ -18,6 +20,7 @@
from __future__ import annotations from __future__ import annotations
import ctypes import ctypes
import json
import os import os
import subprocess import subprocess
import threading import threading
@@ -29,10 +32,276 @@ from browser_open import normalize_storage_state, open_logged_in_browser
from updater import check_update, download_installer from updater import check_update, download_installer
from version import __version__ from version import __version__
CLOUD_URL = "https://dev.zhenyangtang.com.cn/"
CLOUD_HOST = urlparse(CLOUD_URL).hostname or ""
WINDOW_TITLE = f"抖音托管客服 · 桌面版 v{__version__}" WINDOW_TITLE = f"抖音托管客服 · 桌面版 v{__version__}"
# ---------------------------------------------------------------------------
# 多站点配置:内置站点 + 用户自定义站点(保存在用户目录,升级软件不丢失)。
# 以后要新增内置环境,往 BUILTIN_SITES 里加一行即可。
# ---------------------------------------------------------------------------
BUILTIN_SITES = [
{"name": "节点1", "url": "https://dev.zhenyangtang.com.cn/"},
{"name": "节点2", "url": "https://dev1.zhenyangtang.com.cn/"},
]
CONFIG_PATH = os.path.join(os.path.expanduser("~"), ".douyin_desktop_config.json")
def load_config() -> dict:
try:
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
data = json.load(f)
return data if isinstance(data, dict) else {}
except Exception: # noqa: BLE001
return {}
def save_config(cfg: dict) -> None:
try:
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
json.dump(cfg, f, ensure_ascii=False, indent=2)
except Exception: # noqa: BLE001
pass
def normalize_site_url(url: str) -> str:
url = (url or "").strip()
if not url:
return ""
if not url.startswith(("http://", "https://")):
url = "https://" + url
if not url.endswith("/"):
url += "/"
return url
def get_all_sites() -> list[dict]:
"""内置站点 + 自定义站点(去重,按 URL)。"""
cfg = load_config()
sites: list[dict] = []
seen: set[str] = set()
for s in BUILTIN_SITES:
u = normalize_site_url(s["url"])
sites.append({"name": s["name"], "url": u, "builtin": True})
seen.add(u)
for s in cfg.get("custom_sites", []):
u = normalize_site_url(s.get("url", ""))
if u and u not in seen:
sites.append({"name": s.get("name") or urlparse(u).hostname, "url": u, "builtin": False})
seen.add(u)
return sites
def site_hosts() -> set[str]:
return {urlparse(s["url"]).hostname or "" for s in get_all_sites()} - {""}
# ---------------------------------------------------------------------------
# 站点选择界面(软件启动首页)。与注入浮层同一套视觉语言:#fe2c55 主色 + 玻璃拟态。
# ---------------------------------------------------------------------------
PICKER_HTML = r"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>选择站点</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;
font-family:'PingFang SC','Microsoft YaHei',system-ui,-apple-system,'Segoe UI',sans-serif;}
html,body{height:100%;}
body{display:flex;align-items:center;justify-content:center;padding:28px;
background:#0f1023;
background-image:radial-gradient(900px 500px at 15% -10%,rgba(254,44,85,.28),transparent 60%),
radial-gradient(800px 500px at 110% 110%,rgba(99,102,241,.25),transparent 60%);}
.card{width:520px;max-width:100%;max-height:92vh;display:flex;flex-direction:column;
background:rgba(255,255,255,.06);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);
border:1px solid rgba(255,255,255,.14);border-radius:20px;overflow:hidden;
box-shadow:0 30px 80px rgba(0,0,0,.45);}
.head{padding:26px 28px 18px;}
.brand{display:flex;align-items:center;gap:12px;}
.logo{width:42px;height:42px;border-radius:12px;flex:none;display:flex;align-items:center;justify-content:center;
background:linear-gradient(135deg,#fe2c55,#ff7a59);box-shadow:0 8px 20px rgba(254,44,85,.4);}
.logo svg{width:22px;height:22px;color:#fff;}
h1{font-size:18px;font-weight:600;color:#fff;line-height:1.3;}
.sub{margin-top:3px;font-size:12.5px;color:rgba(255,255,255,.55);}
.body{padding:4px 20px 8px;overflow:auto;}
.site{display:flex;align-items:center;gap:12px;width:100%;text-align:left;
padding:13px 14px;margin-bottom:10px;border:1px solid rgba(255,255,255,.12);
border-radius:14px;background:rgba(255,255,255,.04);cursor:pointer;
transition:border-color .18s ease,background .18s ease,transform .18s ease;}
.site:hover{border-color:#fe2c55;background:rgba(254,44,85,.12);transform:translateY(-1px);}
.site:focus-visible{outline:2px solid #fe2c55;outline-offset:2px;}
.dot{width:36px;height:36px;border-radius:10px;flex:none;display:flex;align-items:center;justify-content:center;
background:rgba(255,255,255,.08);color:rgba(255,255,255,.85);font-weight:600;font-size:15px;}
.site:hover .dot{background:rgba(254,44,85,.9);color:#fff;}
.si{flex:1;min-width:0;}
.sn{font-size:14.5px;font-weight:600;color:#fff;display:flex;align-items:center;gap:8px;}
.tag{font-size:10.5px;font-weight:500;padding:1px 7px;border-radius:999px;
background:rgba(255,255,255,.1);color:rgba(255,255,255,.6);}
.su{margin-top:3px;font-size:12px;color:rgba(255,255,255,.5);
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.ping{flex:none;display:inline-flex;align-items:center;gap:5px;padding:3px 9px;
border-radius:999px;font-size:11px;font-weight:500;
background:rgba(255,255,255,.08);color:rgba(255,255,255,.55);}
.ping i{width:7px;height:7px;border-radius:50%;background:rgba(255,255,255,.35);flex:none;}
.ping.fast{background:rgba(34,197,94,.15);color:#4ade80;}
.ping.fast i{background:#22c55e;}
.ping.mid{background:rgba(245,158,11,.15);color:#fbbf24;}
.ping.mid i{background:#f59e0b;}
.ping.slow{background:rgba(239,68,68,.15);color:#f87171;}
.ping.slow i{background:#ef4444;}
.ping.fail{background:rgba(239,68,68,.15);color:#f87171;}
.ping.fail i{background:#ef4444;}
@keyframes blink{50%{opacity:.35;}}
.ping.wait i{animation:blink 1s ease infinite;}
.arrow{flex:none;color:rgba(255,255,255,.3);transition:color .18s ease,transform .18s ease;}
.site:hover .arrow{color:#fff;transform:translateX(2px);}
.arrow svg{width:18px;height:18px;display:block;}
.del{flex:none;border:none;background:transparent;color:rgba(255,255,255,.35);cursor:pointer;
width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;
transition:background .18s ease,color .18s ease;}
.del:hover{background:rgba(239,68,68,.2);color:#f87171;}
.del svg{width:15px;height:15px;}
.foot{padding:6px 20px 22px;}
.remember{display:flex;align-items:center;gap:8px;padding:6px 2px 14px;
font-size:12.5px;color:rgba(255,255,255,.65);cursor:pointer;user-select:none;}
.remember input{width:15px;height:15px;accent-color:#fe2c55;cursor:pointer;}
.addbar{display:flex;gap:8px;}
.addbar input{flex:1;min-width:0;padding:10px 12px;font-size:13px;color:#fff;
background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.14);border-radius:10px;outline:none;
transition:border-color .18s ease;}
.addbar input::placeholder{color:rgba(255,255,255,.35);}
.addbar input:focus{border-color:#fe2c55;}
.addbar input.name{flex:0 0 120px;}
.addbar button{flex:none;padding:10px 16px;font-size:13px;font-weight:600;color:#fff;
background:#fe2c55;border:none;border-radius:10px;cursor:pointer;
transition:background .18s ease,box-shadow .18s ease;}
.addbar button:hover{background:#e0214a;box-shadow:0 6px 18px rgba(254,44,85,.4);}
.err{margin-top:8px;font-size:12px;color:#f87171;min-height:16px;}
.empty{padding:20px;text-align:center;font-size:13px;color:rgba(255,255,255,.45);}
</style>
</head>
<body>
<div class="card">
<div class="head">
<div class="brand">
<div class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/>
<line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg></div>
<div>
<h1>抖音托管客服 · 桌面版</h1>
<div class="sub">请选择要进入的站点,也可以在下方添加新站点</div>
</div>
</div>
</div>
<div class="body" id="list"><div class="empty">正在加载站点…</div></div>
<div class="foot">
<label class="remember"><input type="checkbox" id="remember">记住选择,下次启动直接进入(可在站点内切换)</label>
<div class="addbar">
<input class="name" id="addName" placeholder="名称(选填)" maxlength="20">
<input id="addUrl" placeholder="https://xxx.zhenyangtang.com.cn/" spellcheck="false">
<button id="addBtn">添加</button>
</div>
<div class="err" id="err"></div>
</div>
</div>
<script>
(function () {
var CHEV = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>';
var X = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>';
function esc(s) {
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c];
});
}
function api() { return window.pywebview && window.pywebview.api; }
function setErr(msg) { document.getElementById('err').textContent = msg || ''; }
function pingSite(url, badge) {
if (!badge) return;
api().ping_site(url).then(function (r) {
if (!r || !r.ok) {
badge.className = 'ping fail';
badge.innerHTML = '<i></i>不可达';
return;
}
var ms = r.ms;
var cls = ms <= 300 ? 'fast' : (ms <= 800 ? 'mid' : 'slow');
badge.className = 'ping ' + cls;
badge.innerHTML = '<i></i>' + ms + ' ms';
}).catch(function () {
badge.className = 'ping fail';
badge.innerHTML = '<i></i>检测失败';
});
}
function render(sites) {
var list = document.getElementById('list');
if (!sites || !sites.length) {
list.innerHTML = '<div class="empty">暂无站点,请在下方添加</div>';
return;
}
list.innerHTML = '';
sites.forEach(function (s) {
var host = '';
try { host = new URL(s.url).hostname; } catch (e) { host = s.url; }
var initial = esc((s.name || host || '?').trim().charAt(0).toUpperCase());
var el = document.createElement('button');
el.className = 'site';
el.type = 'button';
el.innerHTML = '<span class="dot">' + initial + '</span>'
+ '<span class="si"><span class="sn">' + esc(s.name || host)
+ (s.builtin ? '<span class="tag">内置</span>' : '') + '</span>'
+ '<span class="su">' + esc(s.url) + '</span></span>'
+ '<span class="ping wait"><i></i>检测中</span>'
+ (s.builtin ? '' : '<span class="del" title="删除该站点" role="button">' + X + '</span>')
+ '<span class="arrow">' + CHEV + '</span>';
pingSite(s.url, el.querySelector('.ping'));
el.addEventListener('click', function (e) {
var del = e.target.closest && e.target.closest('.del');
if (del) {
e.stopPropagation();
api().remove_site(s.url).then(function (r) { render(r.sites); });
return;
}
var remember = document.getElementById('remember').checked;
el.style.opacity = '.6';
api().open_site(s.url, remember);
});
list.appendChild(el);
});
}
function addSite() {
var name = document.getElementById('addName').value.trim();
var url = document.getElementById('addUrl').value.trim();
if (!url) { setErr('请填写站点网址'); return; }
setErr('');
api().add_site(name, url).then(function (r) {
if (r.error) { setErr(r.error); return; }
document.getElementById('addName').value = '';
document.getElementById('addUrl').value = '';
render(r.sites);
});
}
function init() {
api().get_sites().then(function (r) {
render(r.sites);
document.getElementById('remember').checked = !!r.remember;
});
document.getElementById('addBtn').addEventListener('click', addSite);
document.getElementById('addUrl').addEventListener('keydown', function (e) {
if (e.key === 'Enter') addSite();
});
}
if (api()) init();
else window.addEventListener('pywebviewready', init);
})();
</script>
</body>
</html>
"""
# 注入到云端页面的脚本:加悬浮按钮 + 账号选择浮层,调用本机桥打开浏览器。 # 注入到云端页面的脚本:加悬浮按钮 + 账号选择浮层,调用本机桥打开浏览器。
# 设计:Glassmorphism 浮层 + SVG 图标(无 emoji)+ 悬浮过渡 + 可访问性(焦点/Esc/减少动效)。 # 设计:Glassmorphism 浮层 + SVG 图标(无 emoji)+ 悬浮过渡 + 可访问性(焦点/Esc/减少动效)。
@@ -47,7 +316,8 @@ INJECT_JS = r"""
close: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>', close: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',
chevron: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>', chevron: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>',
monitor: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>', monitor: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>',
alert: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>' alert: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
swap: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>'
}; };
var ROOT = null; // Shadow root,隔离宿主站点 CSS,避免错位/层级冲突 var ROOT = null; // Shadow root,隔离宿主站点 CSS,避免错位/层级冲突
@@ -91,6 +361,16 @@ INJECT_JS = r"""
.${NS}-btn:focus-visible{outline:3px solid rgba(254,44,85,.4);outline-offset:2px;} .${NS}-btn:focus-visible{outline:3px solid rgba(254,44,85,.4);outline-offset:2px;}
.${NS}-btn svg{width:18px;height:18px;} .${NS}-btn svg{width:18px;height:18px;}
.${NS}-switch{position:fixed;right:24px;bottom:76px;z-index:2147483646;display:inline-flex;
align-items:center;gap:6px;background:rgba(15,23,42,.72);color:#fff;
border:1px solid rgba(255,255,255,.18);border-radius:999px;padding:8px 14px 8px 12px;
font-size:12.5px;font-weight:500;cursor:pointer;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
box-shadow:0 6px 18px rgba(15,23,42,.3);transition:background .2s ease,transform .2s ease;}
.${NS}-switch:hover{background:rgba(15,23,42,.88);}
.${NS}-switch:active{transform:translateY(1px);}
.${NS}-switch:focus-visible{outline:2px solid #fe2c55;outline-offset:2px;}
.${NS}-switch svg{width:14px;height:14px;}
.${NS}-mask{position:fixed;inset:0;z-index:2147483647;background:rgba(15,23,42,.45); .${NS}-mask{position:fixed;inset:0;z-index:2147483647;background:rgba(15,23,42,.45);
display:flex;align-items:center;justify-content:center;padding:20px; display:flex;align-items:center;justify-content:center;padding:20px;
animation:${NS}-fade .18s ease;} animation:${NS}-fade .18s ease;}
@@ -316,7 +596,7 @@ INJECT_JS = r"""
function ensureButton() { function ensureButton() {
var root = ensureRoot(); var root = ensureRoot();
if (root.querySelector('.' + NS + '-btn')) return; if (!root.querySelector('.' + NS + '-btn')) {
var btn = document.createElement('button'); var btn = document.createElement('button');
btn.className = NS + '-btn'; btn.className = NS + '-btn';
btn.innerHTML = ICON.login + '<span>一键本地登录</span>'; btn.innerHTML = ICON.login + '<span>一键本地登录</span>';
@@ -324,6 +604,18 @@ INJECT_JS = r"""
btn.addEventListener('click', showPanel); btn.addEventListener('click', showPanel);
root.appendChild(btn); root.appendChild(btn);
} }
if (!root.querySelector('.' + NS + '-switch')) {
var sw = document.createElement('button');
sw.className = NS + '-switch';
sw.innerHTML = ICON.swap + '<span>切换站点</span>';
sw.title = '返回站点选择界面';
sw.addEventListener('click', function () {
var a = api();
if (a && a.go_picker) a.go_picker();
});
root.appendChild(sw);
}
}
// 供 Python 端回报本地登录错误:右下角弹出可关闭的提示条 // 供 Python 端回报本地登录错误:右下角弹出可关闭的提示条
window.__dylNotify = function (msg, ok) { window.__dylNotify = function (msg, ok) {
@@ -415,6 +707,76 @@ class Api:
self._window = None self._window = None
self._update_info: dict | None = None self._update_info: dict | None = None
# ---- 站点选择 ----
def get_sites(self) -> dict:
cfg = load_config()
return {"sites": get_all_sites(), "remember": bool(cfg.get("default_url"))}
def add_site(self, name: str, url: str) -> dict:
u = normalize_site_url(url)
host = urlparse(u).hostname if u else None
if not u or not host or "." not in host:
return {"error": "网址格式不对,示例:https://dev1.zhenyangtang.com.cn/", "sites": get_all_sites()}
if any(s["url"] == u for s in get_all_sites()):
return {"error": "该站点已存在", "sites": get_all_sites()}
cfg = load_config()
custom = cfg.get("custom_sites", [])
custom.append({"name": (name or "").strip() or host, "url": u})
cfg["custom_sites"] = custom
save_config(cfg)
return {"sites": get_all_sites()}
def remove_site(self, url: str) -> dict:
u = normalize_site_url(url)
cfg = load_config()
cfg["custom_sites"] = [
s for s in cfg.get("custom_sites", []) if normalize_site_url(s.get("url", "")) != u
]
if normalize_site_url(cfg.get("default_url", "")) == u:
cfg.pop("default_url", None)
save_config(cfg)
return {"sites": get_all_sites()}
def ping_site(self, url: str) -> dict:
"""测量站点响应时间(毫秒)。服务器有响应即算可达,包括 4xx/5xx。"""
import time
from urllib import error as _err, request as _req
u = normalize_site_url(url)
if not u:
return {"ok": False}
req = _req.Request(u, method="HEAD", headers={"User-Agent": "Mozilla/5.0"})
start = time.perf_counter()
try:
with _req.urlopen(req, timeout=8):
pass
except _err.HTTPError:
pass # 服务器已响应(如 403/405),延迟有效
except Exception: # noqa: BLE001
return {"ok": False}
return {"ok": True, "ms": int((time.perf_counter() - start) * 1000)}
def open_site(self, url: str, remember: bool = False) -> dict:
u = normalize_site_url(url)
cfg = load_config()
if remember:
cfg["default_url"] = u
else:
cfg.pop("default_url", None)
save_config(cfg)
if self._window:
self._window.load_url(u)
return {"ok": True}
def go_picker(self) -> dict:
"""从站点页面返回选择界面,并取消“记住选择”。"""
cfg = load_config()
cfg.pop("default_url", None)
save_config(cfg)
if self._window:
self._window.load_html(PICKER_HTML)
return {"ok": True}
# ---- 本地登录 ---- # ---- 本地登录 ----
def _notify(self, msg: str, ok: bool = False) -> None: def _notify(self, msg: str, ok: bool = False) -> None:
if not self._window: if not self._window:
@@ -547,10 +909,23 @@ def _check_update_async(window, api) -> None:
def main() -> None: def main() -> None:
api = Api() api = Api()
# 直接打开云端主界面,更新检测放到后台线程做,避免启动时同步联网卡住窗口 # 上次勾选了“记住选择”则直接进该站点,否则先显示站点选择界面
# 更新检测放到后台线程做,避免启动时同步联网卡住窗口。
cfg = load_config()
default_url = normalize_site_url(cfg.get("default_url", ""))
if default_url and any(s["url"] == default_url for s in get_all_sites()):
window = webview.create_window( window = webview.create_window(
WINDOW_TITLE, WINDOW_TITLE,
CLOUD_URL, default_url,
js_api=api,
width=1280,
height=860,
text_select=True,
)
else:
window = webview.create_window(
WINDOW_TITLE,
html=PICKER_HTML,
js_api=api, js_api=api,
width=1280, width=1280,
height=860, height=860,
@@ -560,12 +935,13 @@ def main() -> None:
_update_checked = {"done": False} _update_checked = {"done": False}
def on_loaded(): def on_loaded():
# 仅在云端站点页面注入悬浮按钮脚本;about:blank 等不注入。 # 仅在已配置站点页面注入悬浮按钮脚本;选择页/about:blank 等不注入。
try: try:
current = window.get_current_url() or "" current = window.get_current_url() or ""
except Exception: # noqa: BLE001 except Exception: # noqa: BLE001
current = "" current = ""
if CLOUD_HOST and CLOUD_HOST in current: host = urlparse(current).hostname or ""
if host and host in site_hosts():
try: try:
window.evaluate_js(INJECT_JS) window.evaluate_js(INJECT_JS)
except Exception as e: # noqa: BLE001 except Exception as e: # noqa: BLE001
Binary file not shown.
+1 -1
View File
@@ -5,4 +5,4 @@
2) 同步修改 installer.iss 顶部的 AppVersion 2) 同步修改 installer.iss 顶部的 AppVersion
3) 重新打包,把新的 installer 与 latest.json 上传到服务器 /desktop/ 目录。 3) 重新打包,把新的 installer 与 latest.json 上传到服务器 /desktop/ 目录。
""" """
__version__ = "1.0.0" __version__ = "1.1.0"
+9 -4
View File
@@ -11,10 +11,15 @@
【推荐:桌面整合版 desktop_app.py】——一个软件搞定 【推荐:桌面整合版 desktop_app.py】——一个软件搞定
==================================================================== ====================================================================
效果: 效果:
打开软件 → 默认加载云端网站 https://dev.zhenyangtang.com.cn/ 打开软件 → 先显示「站点选择」界面,内置两个站点:
→ 你在里面正常登录后台 - 节点1 https://dev.zhenyangtang.com.cn/
→ 页面右下角出现「🚀 一键本地登录」按钮 - 节点2 https://dev1.zhenyangtang.com.cn/
→ 点它 → 弹出托管账号列表 → 选一个 以后有新环境,可直接在界面下方填名称+网址「添加」(保存在本机,升级不丢)。
勾选「记住选择」后,下次启动直接进入该站点,跳过选择页。
→ 选一个站点进入 → 你在里面正常登录后台
→ 页面右下角出现「一键本地登录」按钮(上方还有「切换站点」按钮,
点击可随时返回站点选择界面,并自动取消“记住选择”)
→ 点「一键本地登录」→ 弹出托管账号列表 → 选一个
→ 本机直接打开一个“已登录该托管账号”的浏览器进入抖音。 → 本机直接打开一个“已登录该托管账号”的浏览器进入抖音。
运行方式一(推荐,无黑窗): 运行方式一(推荐,无黑窗):
+20 -5
View File
@@ -19,18 +19,33 @@ echo [3/4] Building exe (windowed, no cmd window)...
if errorlevel 1 ( echo [ERROR] PyInstaller build failed & pause & exit /b 1 ) if errorlevel 1 ( echo [ERROR] PyInstaller build failed & pause & exit /b 1 )
echo [4/4] Building installer... echo [4/4] Building installer...
set "ISCC=%LOCALAPPDATA%\Programs\Inno Setup 6\ISCC.exe" call :find_iscc
if not exist "%ISCC%" set "ISCC=C:\Program Files (x86)\Inno Setup 6\ISCC.exe" if not exist "%ISCC%" (
if not exist "%ISCC%" set "ISCC=C:\Program Files\Inno Setup 6\ISCC.exe" echo [setup] Inno Setup not found; installing via winget...
winget install JRSoftware.InnoSetup --accept-source-agreements --accept-package-agreements --silent --disable-interactivity
call :find_iscc
)
if exist "%ISCC%" ( if exist "%ISCC%" (
"%ISCC%" installer.iss "%ISCC%" installer.iss
if errorlevel 1 ( echo [ERROR] Installer build failed & pause & exit /b 1 )
echo. echo.
echo [OK] Installer: installer\DouyinHostedDesktop-Setup.exe echo [OK] Installer: installer\DouyinHostedDesktop-Setup.exe
) else ( ) else (
echo [WARN] Inno Setup not found. echo [ERROR] Inno Setup still not found. Install manually:
echo Install it: winget install JRSoftware.InnoSetup echo winget install JRSoftware.InnoSetup
echo Or just distribute the folder: dist\DouyinDesktop\ echo Or just distribute the folder: dist\DouyinDesktop\
pause
exit /b 1
) )
goto :after_installer
:find_iscc
set "ISCC=%LOCALAPPDATA%\Programs\Inno Setup 6\ISCC.exe"
if not exist "%ISCC%" set "ISCC=C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
if not exist "%ISCC%" set "ISCC=C:\Program Files\Inno Setup 6\ISCC.exe"
exit /b 0
:after_installer
echo. echo.
echo [OK] Portable app folder: dist\DouyinDesktop\ (run DouyinDesktop.exe) echo [OK] Portable app folder: dist\DouyinDesktop\ (run DouyinDesktop.exe)
pause pause