Files
2026-07-17 09:24:47 +08:00

125 lines
4.2 KiB
Python

"""Build frontier-im WebSocket URL (DouYin_Spider douyin_recv_msg logic)."""
import hashlib
import logging
from typing import Optional
import requests
from .auth import DouyinAuth
from .dy_util import generate_a_bogus, generate_msToken, generate_webid, splice_url
from .session import DouyinImSession, is_frontier_ws_url
logger = logging.getLogger("douyin_im.frontier")
APP_KEY = "e1bd35ec9db7b8d846de66ed140b1ad9"
FP_ID = "9"
def build_frontier_ws_url(session: DouyinImSession, device_id: str) -> Optional[str]:
token = session.cookies.get("sessionid") or session.cookies.get("sessionid_ss") or ""
if not token or not device_id:
return None
access_key_raw = f"{FP_ID}{APP_KEY}{device_id}f8a69f1719916z"
access_key = hashlib.md5(access_key_raw.encode("utf-8")).hexdigest()
params = {
"aid": "6383",
"device_platform": "douyin_pc",
"fpid": FP_ID,
"device_id": device_id,
"token": token,
"access_key": access_key,
}
query = "&".join(f"{k}={v}" for k, v in params.items())
return f"wss://frontier-im.douyin.com/ws/v2?{query}"
def fetch_device_id(session: DouyinImSession) -> str:
"""Call Douyin user query API to obtain device/web id."""
auth = DouyinAuth()
auth.perepare_auth(
session.cookie_header(),
session.web_protect_str,
session.keys_str,
)
url = "https://www.douyin.com/aweme/v1/web/query/user"
headers = {
"User-Agent": session.user_agent,
"Referer": "https://www.douyin.com/discover",
"Accept": "application/json, text/plain, */*",
"Cookie": session.cookie_header(),
}
params = {
"device_platform": "webapp",
"aid": "6383",
"channel": "channel_pc_web",
"publish_video_strategy_type": "2",
"verifyFp": auth.cookie.get("s_v_web_id", "") if auth.cookie else "",
"fp": auth.cookie.get("s_v_web_id", "") if auth.cookie else "",
"webid": generate_webid(auth, "https://www.douyin.com/discover"),
"msToken": generate_msToken(),
}
query = splice_url(params)
params["a_bogus"] = generate_a_bogus(query, user_agent=session.user_agent)
try:
resp = requests.get(
url,
params=params,
headers=headers,
cookies=auth.cookie,
verify=False,
timeout=15,
)
data = resp.json()
device_id = str(data.get("id") or data.get("device_id") or "")
if device_id.isdigit():
logger.info(f"Fetched device_id: {device_id[:20]}...")
return device_id
logger.warning(f"query/user returned non-numeric id: {device_id[:32]!r}")
except Exception as e:
logger.warning(f"fetch_device_id failed: {e}")
return ""
def resolve_frontier_device_id(session: DouyinImSession) -> str:
"""Frontier WS requires numeric device_id from Douyin query/user API."""
current = str(session.device_id or session.web_id or "")
if current.isdigit():
return current
fetched = fetch_device_id(session)
if fetched and str(fetched).isdigit():
session.device_id = str(fetched)
logger.info(f"Using numeric device_id for frontier WS: {fetched[:16]}...")
return str(fetched)
logger.warning(
f"Invalid frontier device_id={current[:24]!r}; "
"expected numeric id from query/user API"
)
return ""
def ensure_frontier_ws(session: DouyinImSession) -> Optional[str]:
"""Ensure session has a usable frontier WebSocket URL."""
session.sanitize_ws_urls()
# 方案 A:若已有浏览器抓到的“真实” frontier URL(带 sdk_cert/ts_sign),
# 直接沿用,不要用我们自己推导 token/access_key 的版本去覆盖它。
for url in session.ws_urls:
if is_frontier_ws_url(url) and "sdk_cert=" in url:
session.ws_urls = [url]
logger.info("Using captured real frontier WS URL (with sdk_cert)")
return url
device_id = resolve_frontier_device_id(session)
if not device_id:
session.ws_urls = []
return None
built = build_frontier_ws_url(session, device_id)
if built:
session.ws_urls = [built]
logger.info("Built frontier WS URL from cookie session")
return built
return None