From b486391e6900020930d89e7b134e11fc1645d4a8 Mon Sep 17 00:00:00 2001 From: XuanRan_Nyan <31968665+xxxxuanran@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:17:39 +0800 Subject: [PATCH] =?UTF-8?q?fix(douyin):=20=E4=BF=AE=E5=A4=8D=E6=8A=96?= =?UTF-8?q?=E9=9F=B3=E7=9B=B4=E6=92=AD=E6=B5=81=E8=8E=B7=E5=8F=96=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20(#1378)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(douyin): 修复抖音直播流获取问题 * fix(douyin): 添加缺少的默认值 --- .gitignore | 3 ++- biliup/plugins/douyin.py | 46 +++++++++++++++++++++++++++------------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 845eb25..0ea9e25 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,5 @@ out downloads/ target _internal -.trae/ \ No newline at end of file +.trae/ +pnpm-lock.yaml \ No newline at end of file diff --git a/biliup/plugins/douyin.py b/biliup/plugins/douyin.py index 5a1450c..c627ad1 100644 --- a/biliup/plugins/douyin.py +++ b/biliup/plugins/douyin.py @@ -1,11 +1,11 @@ -import json from typing import Optional from urllib.parse import unquote, urlparse, parse_qs, urlencode, urlunparse import requests +import random from biliup.common.util import client -from . import logger, match1, random_user_agent +from . import logger, match1, random_user_agent, json_loads from biliup.config import config from biliup.Danmaku import DanmakuClient from ..common.tools import NamedLock @@ -92,7 +92,7 @@ class Douyin(DownloadBase): self.__sec_uid = _room_info['data']['user']['sec_uid'] # PCWeb 端无流 或 没有提供 web_rid if not _room_info.get('data', {}).get('data'): - _room_info = await self.get_room_info(self.__sec_uid, self.__room_id) + _room_info = await self.get_h5_room_info(self.__sec_uid, self.__room_id) if _room_info['data'].get('room', {}).get('owner'): self.__web_rid = _room_info['data']['room']['owner']['web_rid'] try: @@ -120,9 +120,10 @@ class Douyin(DownloadBase): pull_data = room_info['stream_url']['live_core_sdk_data']['pull_data'] if room_info['stream_url'].get('pull_datas') and self.douyin_double_screen: pull_data = next(iter(room_info['stream_url']['pull_datas'].values())) - stream_data = json.loads(pull_data['stream_data'])['data'] + stream_data = json_loads(pull_data['stream_data'])['data'] except: logger.exception(f"{self.plugin_msg}: 加载直播流失败") + logger.debug(f"{self.plugin_msg}: room_info {room_info}") return False # 抖音FLV真原画 @@ -135,8 +136,12 @@ class Douyin(DownloadBase): # and # self.raw_stream_url.find('_or4.flv') != -1 # or4(origin) ): - self.raw_stream_url = stream_data['ao']['main']['flv'].replace('&only_audio=1', '') - else: + try: + self.raw_stream_url = stream_data['ao']['main']['flv'].replace('&only_audio=1', '') + except KeyError: + logger.debug(f"{self.plugin_msg}: 未找到 ao 流 {stream_data}") + + if not self.raw_stream_url: # 原画origin 蓝光uhd 超清hd 高清sd 标清ld 流畅md 仅音频ao quality_items = ['origin', 'uhd', 'hd', 'sd', 'ld', 'md'] quality = self.douyin_quality @@ -197,15 +202,24 @@ class Douyin(DownloadBase): except: pass - async def get_web_room_info(self, web_rid) -> dict: - target_url = DouyinUtils.build_request_url(f"https://live.douyin.com/webcast/room/web/enter/?web_rid={web_rid}") - web_info = (await client.get(target_url, headers=self.fake_headers)).json() + async def get_web_room_info(self, web_rid: str) -> dict: + query = { + 'web_rid': web_rid, + # 2025-08-01 服务端暂不校验以下参数的值,只校验参数存在 + 'enter_from': random.choice(['link_share', 'web_live']), + 'a_bogus': '0', + } + target_url = DouyinUtils.build_request_url(f"https://live.douyin.com/webcast/room/web/enter/", query) + logger.debug(f"{self.plugin_msg}: get_web_room_info {target_url}") + web_info = await client.get(target_url, headers=self.fake_headers) + web_info = json_loads(web_info.text) + logger.debug(f"{self.plugin_msg}: get_web_room_info {web_info}") return web_info - async def get_room_info(self, sec_user_id, room_id) -> dict: + async def get_h5_room_info(self, sec_user_id: str, room_id: str) -> dict: if not sec_user_id: raise ValueError("sec_user_id is None") - params = { + query = { 'type_id': 0, 'live_id': 1, 'version_code': '99.99.99', @@ -213,8 +227,10 @@ class Douyin(DownloadBase): 'room_id': room_id if room_id else 2, # 必要但不校验 'sec_user_id': sec_user_id } - info = (await client.get("https://webcast.amemv.com/webcast/room/reflow/info/", - params=params, headers=self.fake_headers)).json() + info = await client.get("https://webcast.amemv.com/webcast/room/reflow/info/", + params=query, headers=self.fake_headers) + info = json_loads(info.text) + logger.debug(f"{self.plugin_msg}: get_h5_room_info {info}") return info @@ -237,9 +253,9 @@ class DouyinUtils: return DouyinUtils._douyin_ttwid @staticmethod - def build_request_url(url: str) -> str: + def build_request_url(url: str, query: Optional[dict] = None) -> str: parsed_url = urlparse(url) - existing_params = parse_qs(parsed_url.query) + existing_params = query or parse_qs(parsed_url.query) existing_params['aid'] = ['6383'] existing_params['device_platform'] = ['web'] existing_params['browser_language'] = ['zh-CN']