From e5019fcd71487b5a6f7db4ff27a60714ef84208c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=81=A24=2E435km/s?= <54017765+CoolZxp@users.noreply.github.com> Date: Tue, 5 Sep 2023 23:46:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8A=96=E9=9F=B3=E7=9B=B4?= =?UTF-8?q?=E6=92=AD=E5=8F=8A=E5=BC=B9=E5=B9=95=E5=BD=95=E5=88=B6=20(#626)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修复抖音直播及弹幕录制 * 修复抖音直播及弹幕录制 --- biliup/plugins/Danmaku/douyin.py | 26 +++++-------- biliup/plugins/douyin.py | 67 +++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 30 deletions(-) diff --git a/biliup/plugins/Danmaku/douyin.py b/biliup/plugins/Danmaku/douyin.py index 4f146a6..e374756 100644 --- a/biliup/plugins/Danmaku/douyin.py +++ b/biliup/plugins/Danmaku/douyin.py @@ -2,7 +2,6 @@ # 2023.07.14:KNaiFen:这部分代码参考了https://github.com/SmallPeaches/DanmakuRender import gzip -import time import aiohttp import json @@ -15,9 +14,6 @@ from google.protobuf import json_format class Douyin: headers = { - 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', - 'Accept-Encoding': 'gzip, deflate', - 'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36', 'Referer': 'https://live.douyin.com/', 'Cookie': config.get('user', {}).get('douyin_cookie', '') @@ -28,6 +24,7 @@ class Douyin: @staticmethod async def get_ws_info(url): async with aiohttp.ClientSession() as session: + from biliup.plugins.douyin import DouyinUtils if "/user/" in url: async with session.get(url, headers=Douyin.headers, timeout=5) as resp: user_page = await resp.text() @@ -36,22 +33,19 @@ class Douyin: room_id = match1(user_page_data, r'"web_rid":"([^"]+)"') else: room_id = url.split('douyin.com/')[1].split('/')[0].split('?')[0] + if room_id[0] == "+": room_id = room_id[1:] - if room_id.isdigit(): - room_id = f"+{room_id}" - async with session.get(f'https://live.douyin.com/{room_id}', headers=Douyin.headers, timeout=5) as resp: - page = await resp.text() - if "ttwid" not in Douyin.headers['Cookie'] and "ttwid" in resp.cookies: - Douyin.headers['Cookie'] = f'ttwid={resp.cookies.get("ttwid").value};{Douyin.headers["Cookie"]}' - data = json.loads( - unquote( - page.split('')[0])) - real_rid = data['app']['initialState']['roomStore']['roomInfo']['roomId'] - user_unique_id = data['app']['odin']['user_unique_id'] + if "ttwid" not in Douyin.headers['Cookie']: + Douyin.headers['Cookie'] = f'ttwid={DouyinUtils.get_ttwid()};{Douyin.headers["Cookie"]}' - url = f"wss://webcast3-ws-web-lf.douyin.com/webcast/im/push/v2/?app_name=douyin_web&version_code=180800&webcast_sdk_version=1.3.0&update_version_code=1.3.0&compress=gzip&internal_ext=internal_src:dim|wss_push_room_id:{real_rid}|wss_push_did:{user_unique_id}|dim_log_id:2023011316221327ACACF0E44A2C0E8200|fetch_time:${int(time.time())}123|seq:1|wss_info:0-1673598133900-0-0|wrds_kvs:WebcastRoomRankMessage-1673597852921055645_WebcastRoomStatsMessage-1673598128993068211&cursor=u-1_h-1_t-1672732684536_r-1_d-1&host=https://live.douyin.com&aid=6383&live_id=1&did_rule=3&debug=false&endpoint=live_pc&support_wrds=1&im_path=/webcast/im/fetch/&device_platform=web&cookie_enabled=true&screen_width=1228&screen_height=691&browser_language=zh-CN&browser_platform=Win32&browser_name=Mozilla&browser_version=5.0%20Mozilla/5.0%20(Windows%20NT%2010.0;%20Win64;%20x64)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/92.0.4515.159%20Safari/537.36&browser_online=true&tz_name=Asia/Shanghai&identity=audience&room_id={real_rid}&heartbeatDuration=0&signature=00000000" + async with session.get( + DouyinUtils.build_request_url(f"https://live.douyin.com/webcast/room/web/enter/?web_rid={room_id}"), + headers=Douyin.headers, timeout=5) as resp: + room_info = json.loads(await resp.text())['data']['data'][0] + url = DouyinUtils.build_request_url( + f"wss://webcast3-ws-web-lf.douyin.com/webcast/im/push/v2/?room_id={room_info['id_str']}&compress=gzip&signature=00000000") return url, [] @staticmethod diff --git a/biliup/plugins/douyin.py b/biliup/plugins/douyin.py index 7e84bb6..18efd8f 100644 --- a/biliup/plugins/douyin.py +++ b/biliup/plugins/douyin.py @@ -1,12 +1,14 @@ import json -from urllib.parse import unquote +from typing import Optional +from urllib.parse import unquote, urlparse, parse_qs, urlencode, urlunparse import requests from . import logger, match1 from biliup.config import config +from .Danmaku import DanmakuClient +from ..common.tools import NamedLock from ..engine.decorators import Plugin from ..engine.download import DownloadBase -from biliup.plugins.Danmaku import DanmakuClient @Plugin.download(regexp=r'(?:https?://)?(?:(?:www|m|live)\.)?douyin\.com') @@ -27,8 +29,11 @@ class Douyin(DownloadBase): if room_id is None or not room_id: logger.debug(f"{Douyin.__name__}: {self.url}: 未开播") return False + except (KeyError, IndexError): + logger.warning(f"{Douyin.__name__}: {self.url}: 获取房间ID失败,请检查Cookie设置") + return False except: - logger.warning(f"{Douyin.__name__}: {self.url}: 获取房间ID错误") + logger.warning(f"{Douyin.__name__}: {self.url}: 获取房间ID失败") return False else: try: @@ -41,19 +46,19 @@ class Douyin(DownloadBase): if room_id[0] == "+": room_id = room_id[1:] - if room_id.isdigit(): - room_id = f"+{room_id}" - try: - page = requests.get(f"https://live.douyin.com/{room_id}", headers=self.fake_headers, timeout=5).text - page_data = unquote( - page.split('')[0]) - room_info = json.loads(page_data)['app']['initialState']['roomStore']['roomInfo']['room'] - except (KeyError, IndexError): - logger.warning(f"{Douyin.__name__}: {self.url}: 获取错误,请检查Cookie设置") - return False + if "ttwid" not in self.fake_headers['Cookie']: + self.fake_headers['Cookie'] = f'ttwid={DouyinUtils.get_ttwid()};{self.fake_headers["Cookie"]}' + page = requests.get( + DouyinUtils.build_request_url(f"https://live.douyin.com/webcast/room/web/enter/?web_rid={room_id}"), + headers=self.fake_headers, timeout=5).text + room_info = json.loads(page)['data']['data'] + if len(room_info) > 0: + room_info = room_info[0] + else: + room_info = {} except: - logger.warning(f"{Douyin.__name__}: {self.url}: 获取错误") + logger.warning(f"{Douyin.__name__}: {self.url}: 获取失败") return False try: @@ -111,3 +116,37 @@ class Douyin(DownloadBase): def close(self): if self.danmaku: self.danmaku.stop() + + +class DouyinUtils: + # 抖音ttwid + _douyin_ttwid: Optional[str] = None + + @staticmethod + def get_ttwid() -> Optional[str]: + with NamedLock("douyin_ttwid_get"): + if not DouyinUtils._douyin_ttwid: + page = requests.get("https://live.douyin.com/1-2-3-4-5-6-7-8-9-0", timeout=5) + DouyinUtils._douyin_ttwid = page.cookies.get("ttwid") + return DouyinUtils._douyin_ttwid + + @staticmethod + def build_request_url(url: str) -> str: + parsed_url = urlparse(url) + existing_params = parse_qs(parsed_url.query) + existing_params['aid'] = ['6383'] + existing_params['device_platform'] = ['web'] + existing_params['browser_language'] = ['zh-CN'] + existing_params['browser_platform'] = ['Win32'] + existing_params['browser_name'] = ['Chrome'] + existing_params['browser_version'] = ['92.0.4515.159'] + new_query_string = urlencode(existing_params, doseq=True) + new_url = urlunparse(( + parsed_url.scheme, + parsed_url.netloc, + parsed_url.path, + parsed_url.params, + new_query_string, + parsed_url.fragment + )) + return new_url