feat(Huya): 支持 Huya Wup 协议以获取正确直播流 (#1385)

* feat(Huya): 支持 Huya Wup 协议以获取正确直播流

* chore: 补充 webui 内容
This commit is contained in:
XuanRan_Nyan authored and GitHub committed 2025-08-02 08:59:19 +00:00
1 parent 795f2965cc
commit 8293b6cd91
41 files changed
+980 -70

No files matched your search

+1 -1
View File
@@ -221,7 +221,7 @@ const Bilibili: React.FC<Props> = props => {
/>
<Form.Switch
field="bili_anonymous_origin"
extraText="使用自定义API获取 master.m3u8 内的 hls_fmp4 原画流,无法录制特殊直播。默认关闭。"
extraText="使用自定义API获取 master playlist 内的 hls_fmp4 原画流,无法录制特殊直播。默认关闭。"
label="免登录原画(bili_anonymous_origin)"
fieldStyle={{
alignSelf: 'stretch',
+9 -3
View File
@@ -35,14 +35,18 @@ const Global: React.FC = () => {
<div style={{ fontSize: '14px' }}>
选择全局默认的下载插件, 可选:
<br />
1. streamlink(仅限 hls 流,不支持的流将回退到 ffmpeg。需安装 FFmpeg)
1. streamlink(仅限 hls 流,不支持的流将回退到 ffmpeg。非 Docker 用户需自行安装 FFmpeg)
<br />
2. ffmpeg(需安装 FFmpeg)
2. ffmpeg(非 Docker 用户需自行安装 FFmpeg)
<br />
3. stream-gears(默认。防 FLV 流花屏)
<br />
4. sync-downloader(流式边录边传,需先为主播设定上传模板。不受
pool2/threads/segment_time 控制,默认 3 线程上传,请确保上传带宽充足。需安装 FFmpeg)详见 Wiki <a href="https://github.com/biliup/biliup/wiki/%E8%BE%B9%E5%BD%95%E8%BE%B9%E4%BC%A0%E5%8A%9F%E8%83%BD" target="_blank" rel="noopener noreferrer" >点击查看</a>
pool2/threads/segment_time 控制,默认 3 线程上传,请确保上传带宽充足。非 Docker 用户需自行安装 FFmpeg)详见 Wiki <a href="https://github.com/biliup/biliup/wiki/%E8%BE%B9%E5%BD%95%E8%BE%B9%E4%BC%A0%E5%8A%9F%E8%83%BD" target="_blank" rel="noopener noreferrer" >点击查看</a>
<br />
5. ytarchive(仅适用于 Youtube Live)
<br />
{/* 6. mesio(基于 Rust 的命令行视频下载/修复器)详见 <a href="https://github.com/hua0512/rust-srec/tree/main/mesio-cli" target="_blank" rel="noopener noreferrer" >项目主页</a> */}
</div>
}
style={{ width: '100%' }}
@@ -56,6 +60,8 @@ const Global: React.FC = () => {
<Select.Option value="ffmpeg">ffmpeg</Select.Option>
<Select.Option value="stream-gears">stream-gears(默认)</Select.Option>
<Select.Option value="sync-downloader">sync-downloader(边录边传)</Select.Option>
<Select.Option value="ytarchive">ytarchive(仅适用于 Youtube Live)</Select.Option>
{/* <Select.Option value="mesio">mesio</Select.Option> */}
</Form.Select>
{formApi.getValue('downloader') === 'sync-downloader' ? (
<>
+3 -3
View File
@@ -82,12 +82,12 @@ const Huya: React.FC<Props> = props => {
>
<Select.Option value="AL">直播线路3(AL)</Select.Option>
<Select.Option value="TX">直播线路5(TX)</Select.Option>
<Select.Option value="HW">直播线路6(HW)</Select.Option>
{/* <Select.Option value="HW">直播线路6(HW)</Select.Option> */}
{/* <Select.Option value="WS">网宿(WS)</Select.Option> */}
<Select.Option value="AL13">直播线路13(AL13)</Select.Option>
<Select.Option value="HS">直播线路14(HS)</Select.Option>
<Select.Option value="TX15">直播线路15(TX15)</Select.Option>
<Select.Option value="HW16">直播线路16(HW16)</Select.Option>
{/* <Select.Option value="HW16">直播线路16(HW16)</Select.Option> */}
</Form.Select>
<Form.Switch
field="huya_cdn_fallback"
@@ -129,7 +129,7 @@ const Huya: React.FC<Props> = props => {
/>
<Form.Switch
field="huya_use_wup"
extraText="使用 WUP 协议的请求头,可能解决部分直播分区 2 分钟分段问题"
extraText="使用 WUP 协议获取直播流,可能解决部分直播分区 2 分钟分段问题"
label="使用 WUP 协议(huya_use_wup)"
initValue={entity?.hasOwnProperty('huya_use_wup') ? entity['huya_use_wup'] : true}
fieldStyle={{
+2 -2
View File
@@ -210,7 +210,7 @@ class DanmakuClient(IDanmakuClient):
except:
logger.warning(f"{DanmakuClient.__name__}:{self.__url}:弹幕处理异常", exc_info=True)
continue
# 正常弹幕记录
if msg_type == 'danmaku': # 文字弹幕
try:
@@ -341,7 +341,7 @@ class DanmakuClient(IDanmakuClient):
self.__content['uid'] = 0
logger.warning(f"{DanmakuClient.__name__}:{self.__url}: 弹幕连接异常,降级至非完整弹幕")
continue
logger.warning(f"{DanmakuClient.__name__}:{self.__url}: 弹幕连接异常,将在 30 秒后重试")
logger.warning(f"{DanmakuClient.__name__}:{self.__url}: 弹幕连接异常,将在 30 秒后重试", exc_info=True)
except:
# 记录异常不到外部处理
logger.exception(f"{DanmakuClient.__name__}:{self.__url}: 弹幕异常,将在 30 秒后重试")
+40 -13
View File
@@ -1,8 +1,11 @@
import aiohttp
from biliup.plugins import random_user_agent
from .tars import tarscore
from biliup.common.tars import tarscore
from biliup.plugins import match1
from biliup.plugins.huya_wup.wup_struct import EWebSocketCommandType
from biliup.plugins.huya_wup.wup_struct.WebSocketCommand import HuyaWebSocketCommand
from biliup.plugins.huya_wup.wup_struct.WSUserInfo import HuyaWSUserInfo
class Huya:
@@ -29,21 +32,45 @@ class Huya:
# tid = match1(room_page, r"lChannelId\":\"?(\d+)\"?")
# sid = match1(room_page, r"lSubChannelId\":\"?(\d+)\"?")
import base64
ws_user_info = HuyaWSUserInfo()
ws_user_info.iUid = uid
ws_user_info.bAnonymous = False
ws_user_info.lGroupId = uid
ws_user_info.lGroupType = 3
oos = tarscore.TarsOutputStream()
oos.write(tarscore.int64, 0, uid)
oos.write(tarscore.boolean, 1, False) # Anonymous
oos.write(tarscore.string, 2, "") # sGuid
oos.write(tarscore.string, 3, "")
oos.write(tarscore.int64, 4, 0) # tid
oos.write(tarscore.int64, 5, 0) # sid
oos.write(tarscore.int64, 6, uid)
oos.write(tarscore.int64, 7, 3)
ws_user_info.writeTo(oos, ws_user_info)
wscmd = tarscore.TarsOutputStream()
wscmd.write(tarscore.int32, 0, 1)
wscmd.write(tarscore.bytes, 1, oos.getBuffer())
# b64data = base64.b64encode(oos.getBuffer())
# print(b64data)
reg_datas.append(wscmd.getBuffer())
ws_cmd = HuyaWebSocketCommand()
ws_cmd.iCmdType = EWebSocketCommandType.EWSCmd_RegisterReq
ws_cmd.vData = oos.getBuffer()
oos = tarscore.TarsOutputStream()
ws_cmd.writeTo(oos, ws_cmd)
# oos = tarscore.TarsOutputStream()
# oos.write(tarscore.int64, 0, uid)
# oos.write(tarscore.boolean, 1, False) # Anonymous
# oos.write(tarscore.string, 2, "") # sGuid
# oos.write(tarscore.string, 3, "")
# oos.write(tarscore.int64, 4, 0) # tid
# oos.write(tarscore.int64, 5, 0) # sid
# oos.write(tarscore.int64, 6, uid)
# oos.write(tarscore.int64, 7, 3)
# b64data = base64.b64encode(oos.getBuffer())
# print(b64data)
# wscmd = tarscore.TarsOutputStream()
# wscmd.write(tarscore.int32, 0, 1)
# wscmd.write(tarscore.bytes, 1, oos.getBuffer())
# b64data = base64.b64encode(oos.getBuffer())
# print(b64data)
reg_datas.append(oos.getBuffer())
return Huya.wss_url, reg_datas
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -50,13 +50,13 @@ class RequestPacket(util.struct):
def readFrom(ios):
value = RequestPacket()
value.iVersion = ios.read(util.int16, 1, True, 0)
print(("iVersion = %d" % value.iVersion))
# print(("iVersion = %d" % value.iVersion))
value.cPacketType = ios.read(util.int8, 2, True, 0)
print(("cPackerType = %d" % value.cPacketType))
# print(("cPackerType = %d" % value.cPacketType))
value.iMessageType = ios.read(util.int32, 3, True, 0)
print(("iMessageType = %d" % value.iMessageType))
# print(("iMessageType = %d" % value.iMessageType))
value.iRequestId = ios.read(util.int32, 4, True, 0)
print(("iRequestId = %d" % value.iRequestId))
# print(("iRequestId = %d" % value.iRequestId))
value.sServantName = ios.read(util.string, 5, True, '22222222')
value.sFuncName = ios.read(util.string, 6, True, '')
value.sBuffer = ios.read(util.bytes, 7, True, value.sBuffer)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+11 -5
View File
@@ -9,13 +9,14 @@ import subprocess
import threading
import time
import shutil
import copy
from abc import ABC, abstractmethod
from typing import AsyncGenerator, List, Callable, Optional
from urllib.parse import urlparse
from datetime import datetime, timezone
import requests
from requests.utils import DEFAULT_ACCEPT_ENCODING
from requests.utils import DEFAULT_ACCEPT_ENCODING, parse_header_links
from httpx import HTTPStatusError
from biliup.common.util import client, loop, check_timerange
@@ -65,6 +66,7 @@ class DownloadBase(ABC):
'accept-language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'user-agent': random_user_agent(),
}
self.stream_headers = copy.deepcopy(self.fake_headers)
self.segment_time = config.get('segment_time', '01:00:00')
self.time_range = config.get('time_range')
self.excluded_keywords = config.get('excluded_keywords')
@@ -104,6 +106,7 @@ class DownloadBase(ABC):
return True
def download(self):
self.update_headers(self.stream_headers)
# print(f"{self.plugin_msg}: Plugin settings - {self.__dict__}")
logger.debug(f"{self.plugin_msg}: Plugin settings - {self.__dict__}")
# logger.info(f"{self.plugin_msg}: Request headers - {self.fake_headers}")
@@ -133,7 +136,7 @@ class DownloadBase(ABC):
if not self.file_size:
self.file_size = 2 * 1024 * 1024 * 1024
self.file_size = ((self.file_size + min_size - 1) // min_size) * min_size # 向上取整
sync_download(self.raw_stream_url, self.fake_headers,
sync_download(self.raw_stream_url, self.stream_headers,
max_file_size=int(self.file_size / 1024 / 1024),
output_prefix=self.gen_download_filename(True),
stream_info=stream_info,
@@ -150,7 +153,7 @@ class DownloadBase(ABC):
else:
# 其他流stream_gears会按hls保存为ts
self.suffix = 'ts'
stream_gears_download(self.raw_stream_url, self.fake_headers, self.gen_download_filename(),
stream_gears_download(self.raw_stream_url, self.stream_headers, self.gen_download_filename(),
self.segment_time,
self.file_size,
lambda file_name: self.__download_segment_callback(file_name))
@@ -231,7 +234,7 @@ class DownloadBase(ABC):
# '--http-proxy', 'http://127.0.0.1:7890',
# '--hls-live-restart',
]
for key, value in self.fake_headers.items():
for key, value in self.stream_headers.items():
streamlink_cmd.extend(['--http-header', f'{key}={value}'])
streamlink_cmd.extend([
self.raw_stream_url,
@@ -241,7 +244,7 @@ class DownloadBase(ABC):
streamlink_proc = subprocess.Popen(streamlink_cmd, stdout=subprocess.PIPE)
input_uri = 'pipe:0'
else:
input_args += ['-headers', ''.join('%s: %s\r\n' % x for x in self.fake_headers.items()),
input_args += ['-headers', ''.join('%s: %s\r\n' % x for x in self.stream_headers.items()),
'-rw_timeout', '20000000']
if '.m3u8' in urlparse(self.raw_stream_url).path:
input_args += ['-max_reload', '1000']
@@ -499,6 +502,9 @@ class DownloadBase(ABC):
else:
return filename
def update_headers(self, headers):
pass
@staticmethod
def download_file_rename(old_file_name, file_name):
try:
+215 -39
View File
@@ -1,9 +1,10 @@
import base64
import hashlib
import json
import random
import time
import html
from enum import Enum
from dataclasses import dataclass
from urllib.parse import parse_qs, unquote
from async_lru import alru_cache
from typing import (
@@ -20,9 +21,16 @@ from ..engine.decorators import Plugin
from ..engine.download import DownloadBase
from . import logger, match1, json_loads
from biliup.plugins.huya_wup import Wup, DEFAULT_TICKET_NUMBER
from biliup.plugins.huya_wup.packet import (
HuyaGetCdnTokenInfoReq,
HuyaGetCdnTokenInfoRsp
)
HUYA_WEB_BASE_URL = "https://www.huya.com"
HUYA_MOBILE_BASE_URL = "https://m.huya.com"
HUYA_MP_BASE_URL = "https://mp.huya.com"
HUYA_WUP_BASE_URL = "https://wup.huya.com"
HUYA_WEB_ROOM_DATA_REGEX = r"var TT_ROOM_DATA = (.*?);"
@Plugin.download(regexp=r'https?://(?:(?:www|m)\.)?huya\.com')
@@ -72,16 +80,12 @@ class Huya(DownloadBase):
# is_xingxiu = (room_profile['gid'] == 1663)
gid_blacklist = [1663, ]
skip_query_build = self.huya_imgplus and (self.huya_mobile_api or room_profile['gid'] in gid_blacklist)
stream_urls = self.build_stream_urls(room_profile['streams_info'], skip_query_build)
skip_query_build = room_profile['gid'] in gid_blacklist
stream_urls = await self.build_stream_urls(room_profile['streams_info'], skip_query_build)
cdn_list = list(stream_urls.keys())
if not self.huya_cdn or self.huya_cdn not in cdn_list:
self.huya_cdn = cdn_list[0]
# Thx stream-rec
if self.huya_use_wup:
self.update_headers(self.fake_headers)
try:
self.raw_stream_url = self.add_ratio(
stream_urls[self.huya_cdn],
@@ -113,7 +117,7 @@ class Huya(DownloadBase):
if not room_profile['live']:
logger.debug(f"{self.plugin_msg}: {room_profile['message']}")
return False
stream_urls = self.build_stream_urls(room_profile['streams_info'], skip_query_build)
stream_urls = await self.build_stream_urls(room_profile['streams_info'], skip_query_build)
self.raw_stream_url = self.add_ratio(
stream_urls[self.huya_cdn],
@@ -123,12 +127,10 @@ class Huya(DownloadBase):
return True
def danmaku_init(self):
if self.huya_danmaku:
self.danmaku = DanmakuClient(self.url, self.gen_download_filename())
def add_ratio(self, url: str, bitrate_info: Dict[str, Any], max_bitrate: int) -> str:
'''
添加码率
@@ -158,14 +160,12 @@ class Huya(DownloadBase):
raise KeyError(f"确定码率时发生错误") from e
return url
def get_stream_name(self, stream_name: str) -> str:
if self.huya_imgplus:
return stream_name
return stream_name.replace('-imgplus', '')
def build_stream_urls(
async def build_stream_urls(
self,
streams_info: List[Dict[str, Any]],
skip_query_build: bool
@@ -173,7 +173,7 @@ class Huya(DownloadBase):
'''
构建流地址
:param streams_info: 流信息
:param skip_query_build: 是否跳过构建anti_code
:param skip_query_build: 跳过构建anti_code
:return: 流地址
'''
proto = self.huya_protocol
@@ -187,22 +187,34 @@ class Huya(DownloadBase):
stream_name = self.get_stream_name(stream['sStreamName'])
cdn = stream['sCdnType']
suffix = stream[f's{proto}UrlSuffix']
# 默认不修改 anticode
anti_code = stream[f's{proto}AntiCode']
if not skip_query_build:
if (
# 禁用 imgplus
not self.huya_imgplus
or
# 禁用 wup ,流信息不来自移动端,不在分区黑名单中
not (self.huya_use_wup or self.huya_mobile_api or skip_query_build)
):
logger.debug(f"{self.plugin_msg}: 构建 anticode")
anti_code = self.build_query(stream_name, anti_code, self.get_uid(stream['lPresenterUid']))
# 启用 imgplus、wup 且非 mobile api
elif self.huya_use_wup and not self.huya_mobile_api:
# 使用 Wup 获取的 anti_code,必须使用 Wup UA 进行连接
anti_code = await self.get_true_anticode(cdn, stream_name, self.get_uid(stream['lPresenterUid']), proto)
anti_code = f"{anti_code}&codec={self.huya_codec}"
base_url = stream[f's{proto}Url'].replace('http://', 'https://') # 强制https
streams[cdn] = f"{base_url}/{stream_name}.{suffix}?{anti_code}"
weights[cdn] = priority
return self.__weight_sorting(streams, weights)
def extract_room_profile(self, data: Union[str, Dict[str, Any]]) -> Dict[str, Any]:
'''
ON: 直播
REPLAY: 重播
OFF: 未开播
'''
# PC web
if isinstance(data, str):
room_data = json_loads(match1(data, HUYA_WEB_ROOM_DATA_REGEX))
s = data.split('stream: ')[1].split('};')[0]
@@ -215,6 +227,7 @@ class Huya(DownloadBase):
}
live_info = s_json['data'][0]['gameLiveInfo']
streams_info = s_json['data'][0]['gameStreamInfoList']
# Mobile API(微信小程序?)
elif isinstance(data, dict):
data = data['data']
if data['liveStatus'] != 'ON' or not data.get('liveData', {}).get('bitRateInfo'):
@@ -238,7 +251,6 @@ class Huya(DownloadBase):
'streams_info': streams_info,
}
async def get_room_profile(self, use_api=False) -> dict:
'''
获取房间信息
@@ -268,6 +280,42 @@ class Huya(DownloadBase):
_raise_for_room_block(resp)
return self.extract_room_profile(resp)
async def get_true_anticode(
self,
cdn: str,
stream_name: str,
presenter_uid: int,
proto: str,
) -> str:
'''
获取 wup anti_code
:param cdn: cdn类型
:param stream_name: 流名称
:param presenter_uid: 主播uid
:param proto: 协议类型
:return: wup anti_code
'''
proto = "hls" if proto == "Hls" else "flv"
headers = {}
self.update_headers(headers)
wup_req = Wup()
wup_req.requestid = abs(DEFAULT_TICKET_NUMBER)
wup_req.servant = "liveui"
wup_req.func = "getCdnTokenInfo"
token_info_req = HuyaGetCdnTokenInfoReq()
token_info_req.cdnType = cdn
token_info_req.streamName = stream_name
token_info_req.presenterUid = presenter_uid
wup_req.put(HuyaGetCdnTokenInfoReq, "tReq", token_info_req)
data = wup_req.encode_v3()
rsp = await client.post(HUYA_WUP_BASE_URL, data=data, headers=headers)
wup_rsp = Wup()
wup_rsp.decode_v3(rsp.content)
token_info_rsp = wup_rsp.get(HuyaGetCdnTokenInfoRsp,"tRsp")
# print(token_info_rsp.as_dict())
token_info = token_info_rsp.as_dict()
logger.debug(f"{self.plugin_msg}: wup token_info {token_info}")
return token_info[f'{proto}AntiCode']
def build_query(self, stream_name, anti_code, uid: int) -> str:
'''
@@ -325,7 +373,6 @@ class Huya(DownloadBase):
return dict(sorted(data.items(), key=lambda x: weights[x[0]], reverse=True))
return {}
@staticmethod
def get_uid(uid: Union[str, int, None] = None) -> int:
try:
@@ -335,7 +382,6 @@ class Huya(DownloadBase):
pass
return uid or random.randint(1400000000000, 1499999999999)
async def get_anonymous_uid(self) -> int:
try:
rsp = await client.post(
@@ -354,40 +400,170 @@ class Huya(DownloadBase):
rsp = {}
return rsp.get('data', {}).get('uid', self.get_uid())
def update_headers(self, headers: dict):
if self.huya_use_wup:
user_agent = UAGenerator.build_user_agent(UAType.HYSDK, Platform.WINDOWS)
# user_agent = f"{Huya.get_hysdk_ua()}_APP({Huya.get_hyapp_ua()})_SDK({Huya.get_hy_trans_mod_ua()})"
headers['user-agent'] = user_agent
headers['origin'] = HUYA_WEB_BASE_URL
class UAType(Enum):
MEDIA_PLAYER = 'media_player'
HYSDK = 'hysdk'
class Platform(Enum):
ANDROID = 'adr'
HUYA_NFTV = 'huya_nftv'
WEBSOCKET = 'webh5'
WINDOWS = 'pc_exe'
class UAGenerator:
# 配置字典
HYAPP_CONFIGS = {
Platform.ANDROID: {
'platform': Platform.ANDROID,
'version': '0.0.0', # LocalVersion or "0.0.0" + hotfix_version
'channel': 'live'
},
Platform.HUYA_NFTV: {
'platform': Platform.HUYA_NFTV,
'version': '2.5.1.3141',
'channel': 'official'
},
Platform.WINDOWS: {
'platform': Platform.WINDOWS,
'version': '6090007',
'channel': 'official'
},
Platform.WEBSOCKET: { # UnUsed
'platform': Platform.WEBSOCKET,
'version': '2505091506',
'channel': 'websocket'
}
}
HYSDK_CONFIGS = {
Platform.ANDROID: {
'platform': 'Android',
'version': '30000002'
},
Platform.WINDOWS: {
'platform': 'Windows',
'version': '30000002'
}
}
TRANS_MOD_CONFIGS = {
Platform.HUYA_NFTV: {
'name': 'trans',
'version': '1.24.99-rel-tv'
},
Platform.ANDROID: {
'name': 'trans',
'version': '2.22.13-rel'
},
Platform.WINDOWS: {
'name': 'trans',
'version': '2.24.0.5157'
}
}
@staticmethod
def get_hyapp_ua() -> str:
hyapp_platform = 'huya_nftv' # adr or huya_nftv, pc_exe, webh5
hyapp_version = '2.5.1.3141' # adr/nftv: LocalVersion or "0.0.0" + hotfix_version, pc_exe: 6070100, ws: 2505091506
hyapp_channel = 'official' # adr: live, nftv/pc_exe: official, websocket
android_api_level = random.randint(28, 35) # Build.VERSION.SDK_INT
def get_hyapp_ua(platform: Platform = Platform.WINDOWS) -> str:
'''
生成 hyapp 用户代理字符串
:param platform: 平台类型
:return: 用户代理字符串
'''
config = UAGenerator.HYAPP_CONFIGS.get(platform)
if not config:
raise ValueError(f"不支持的平台: {platform}")
hyapp_platform = config['platform']
hyapp_version = config['version']
hyapp_channel = config['channel']
ua = f"{hyapp_platform}&{hyapp_version}&{hyapp_channel}"
return ua if hyapp_platform in {'pc_exe', 'webh5'} else f"{ua}&{android_api_level}"
# windows 和 websocket 不需要添加 android_api_level
if platform not in {Platform.WINDOWS, Platform.WEBSOCKET}:
android_api_level = random.randint(28, 35)
ua = f"{ua}&{android_api_level}"
return ua
@staticmethod
def get_hysdk_ua() -> str:
hysdk_platform = 'Android' # Android, Windows
hysdk_version = '30000002' # 30000002
def get_hysdk_ua(platform: Platform = Platform.WINDOWS) -> str:
'''
生成 hysdk 用户代理字符串
:param platform: 平台类型 (Android 或 Windows)
:return: 用户代理字符串
'''
config = UAGenerator.HYSDK_CONFIGS.get(platform)
if not config:
raise ValueError(f"HYSDK 不支持的平台: {platform}")
hysdk_platform = config['platform']
hysdk_version = config['version']
return f"HYSDK({hysdk_platform}, {hysdk_version})"
@staticmethod
def get_hy_media_player_ua() -> str:
hy_mp_platform = 'android' # android
hy_mp_version = '20000313' # 20000313
def get_hy_media_player_ua(platform: Platform = Platform.WINDOWS) -> str:
'''
生成 hy_media_player 用户代理字符串
:param platform: 平台类型
:return: 用户代理字符串
'''
# 目前只支持 android 平台
hy_mp_platform = 'android'
hy_mp_version = '20000313'
return f"{hy_mp_platform}, {hy_mp_version}"
@staticmethod
def get_hy_trans_mod_ua() -> str:
hy_trans_mod_name = 'trans' # trans
hy_trans_mod_version = '1.24.99-rel-tv' # nftv: 1.24.99-rel-tv, adr: 2.22.13-rel, win: 2.21.0.4784
def get_hy_trans_mod_ua(platform: Platform = Platform.WINDOWS) -> str:
'''
生成 hy_trans_mod 用户代理字符串
:param platform: 平台类型
:return: 用户代理字符串
'''
config = UAGenerator.TRANS_MOD_CONFIGS.get(platform)
if not config:
raise ValueError(f"Trans mod 不支持的平台: {platform}")
hy_trans_mod_name = config['name']
hy_trans_mod_version = config['version']
return f"{hy_trans_mod_name}&{hy_trans_mod_version}"
@staticmethod
def update_headers(headers: dict):
user_agent = f"{Huya.get_hy_media_player_ua()}_APP({Huya.get_hyapp_ua()})_SDK({Huya.get_hy_trans_mod_ua()})"
# user_agent = f"{Huya.get_hysdk_ua()}_APP({Huya.get_hyapp_ua()})_SDK({Huya.get_hy_trans_mod_ua()})"
headers['user-agent'] = user_agent
headers['origin'] = HUYA_WEB_BASE_URL
def build_user_agent(
ua_type: UAType = UAType.HYSDK,
platform: Platform = Platform.WINDOWS
) -> str:
'''
构建完整的用户代理字符串
:param ua_type: UA 类型 (MEDIA_PLAYER 或 HYSDK)
:param platform: 平台类型
:return: 完整的用户代理字符串
'''
# 获取各个组件的 UA
hyapp_ua = UAGenerator.get_hyapp_ua(platform)
trans_mod_ua = UAGenerator.get_hy_trans_mod_ua(platform)
if ua_type == UAType.MEDIA_PLAYER:
media_player_ua = UAGenerator.get_hy_media_player_ua(platform)
return f"{media_player_ua}_APP({hyapp_ua})_SDK({trans_mod_ua})"
elif ua_type == UAType.HYSDK:
sdk_platform = platform if platform in {Platform.ANDROID, Platform.HUYA_NFTV} else Platform.WINDOWS
hysdk_ua = UAGenerator.get_hysdk_ua(sdk_platform)
return f"{hysdk_ua}_APP({hyapp_ua})_SDK({trans_mod_ua})"
else:
raise ValueError(f"不支持的 UA 类型: {ua_type}")
def _raise_for_room_block(text: str):
+6
View File
@@ -0,0 +1,6 @@
from .packet import *
from .wup import Wup
__all__ = ['Wup', 'HuyaGetCdnTokenInfoReq', 'HuyaGetCdnTokenInfoRsp']
DEFAULT_TICKET_NUMBER = -1
@@ -0,0 +1,8 @@
from .getCdnTokenInfo import (
HuyaGetCdnTokenInfoReq,
HuyaGetCdnTokenInfoRsp
)
__all__ = ['HuyaGetCdnTokenInfoReq', 'HuyaGetCdnTokenInfoRsp']
+25
View File
@@ -0,0 +1,25 @@
from functools import wraps
from biliup.common.tars import tarscore
STANDARD_CHARSET = 'utf-8'
def auto_decode_fields(cls):
"""自动解码类中的bytes类型数据"""
original_read_from = cls.readFrom
@staticmethod
@wraps(original_read_from)
def wrapped_read_from(ios: tarscore.TarsInputStream):
value = original_read_from(ios)
# 遍历对象的所有属性
for attr_name, attr_value in vars(value).items():
# 如果是bytes类型
if isinstance(attr_value, bytes):
# 将其解码为字符串
setattr(value, attr_name, attr_value.decode(STANDARD_CHARSET))
return value
cls.readFrom = wrapped_read_from
return cls
@@ -0,0 +1,86 @@
from biliup.common.tars import tarscore
from .__util import auto_decode_fields
@auto_decode_fields
class HuyaGetCdnTokenInfoReq(tarscore.struct):
__tars_class__ = "Huya.GetCdnTokenInfoReq"
def __init__(self):
self.url: tarscore.string = ""
self.cdnType: tarscore.string = ""
self.streamName: tarscore.string = ""
self.presenterUid: tarscore.int32 = 0
@staticmethod
def writeTo(oos: tarscore.TarsOutputStream, value):
oos.write(tarscore.string, 0, value.url)
oos.write(tarscore.string, 1, value.cdnType)
oos.write(tarscore.string, 2, value.streamName)
oos.write(tarscore.int32, 3, value.presenterUid)
@staticmethod
def readFrom(ios: tarscore.TarsInputStream):
value = HuyaGetCdnTokenInfoReq()
value.url = ios.read(tarscore.string, 0, False)
# print(("url = %s" % value.url))
value.cdnType = ios.read(tarscore.string, 1, False)
# print(("cdnType = %s" % value.cdnType))
value.streamName = ios.read(tarscore.string, 2, False)
# print(("streamName = %s" % value.streamName))
value.presenterUid = ios.read(tarscore.int32, 3, False)
# print(("presenterUid = %d" % value.presenterUid))
return value
def as_dict(self):
return self.__dict__.copy()
@auto_decode_fields
class HuyaGetCdnTokenInfoRsp(tarscore.struct):
__tars_class__ = "Huya.GetCdnTokenInfoRsp"
def __init__(self):
self.url: tarscore.string = ""
self.cdnType: tarscore.string = ""
self.streamName: tarscore.string = ""
self.presenterUid: tarscore.int32 = 0
self.antiCode: tarscore.string = ""
self.sTime: tarscore.string = ""
self.flvAntiCode: tarscore.string = ""
self.hlsAntiCode: tarscore.string = ""
@staticmethod
def writeTo(oos: tarscore.TarsOutputStream, value):
oos.write(tarscore.string, 0, value.url)
oos.write(tarscore.string, 1, value.cdnType)
oos.write(tarscore.string, 2, value.streamName)
oos.write(tarscore.int32, 3, value.presenterUid)
oos.write(tarscore.string, 4, value.antiCode)
oos.write(tarscore.string, 5, value.sTime)
oos.write(tarscore.string, 6, value.flvAntiCode)
oos.write(tarscore.string, 7, value.hlsAntiCode)
@staticmethod
def readFrom(ios: tarscore.TarsInputStream):
value = HuyaGetCdnTokenInfoRsp()
value.url = ios.read(tarscore.string, 0, False)
# print(("url = %s" % value.url))
value.cdnType = ios.read(tarscore.string, 1, False)
# print(("cdnType = %s" % value.cdnType))
value.streamName = ios.read(tarscore.string, 2, False)
# print(("streamName = %s" % value.streamName))
value.presenterUid = ios.read(tarscore.int32, 3, False)
# print(("presenterUid = %d" % value.presenterUid))
value.antiCode = ios.read(tarscore.string, 4, False)
# print(("antiCode = %s" % value.antiCode))
value.sTime = ios.read(tarscore.string, 5, False)
# print(("sTime = %s" % value.sTime))
value.flvAntiCode = ios.read(tarscore.string, 6, False)
# print(("flvAntiCode = %s" % value.flvAntiCode))
value.hlsAntiCode = ios.read(tarscore.string, 7, False)
# print(("hlsAntiCode = %s" % value.hlsAntiCode))
return value
def as_dict(self):
return self.__dict__.copy()
@@ -0,0 +1,60 @@
from biliup.common.tars import tarscore
from .__util import auto_decode_fields
from ..wup_struct.UserId import HuyaUserId
from ..wup_struct.BeginLiveNotice import HuyaBeginLiveNotice
from ..wup_struct.StreamSettingNotice import HuyaStreamSettingNotice
class HuyaGetLivingInfoReq(tarscore.struct):
__tars_class__ = "Huya.GetLivingInfoReq"
def __init__(self):
self.tId: HuyaUserId = HuyaUserId()
self.lTopSid: tarscore.int64 = 0
self.lSubSid: tarscore.int64 = 0
self.lPresenterUid: tarscore.int64 = 0
@staticmethod
def writeTo(oos: tarscore.TarsOutputStream, value):
oos.write(HuyaUserId, 0, value.tId)
oos.write(tarscore.int64, 1, value.lTopSid)
oos.write(tarscore.int64, 2, value.lSubSid)
oos.write(tarscore.int64, 3, value.lPresenterUid)
@staticmethod
def readFrom(ios: tarscore.TarsInputStream):
value = HuyaGetLivingInfoReq()
value.tId = ios.read(HuyaUserId, 0, False)
value.lTopSid = ios.read(tarscore.int64, 1, False)
value.lSubSid = ios.read(tarscore.int64, 2, False)
value.lPresenterUid = ios.read(tarscore.int64, 3, False)
return value
class HuyaGetLivingInfoRsp(tarscore.struct):
__tars_class__ = "Huya.GetLivingInfoRsp"
def __init__(self):
self.bIsLiving: tarscore.int32 = 0
self.tNotice: HuyaBeginLiveNotice = HuyaBeginLiveNotice()
self.tStreamSettingNotice: HuyaStreamSettingNotice = HuyaStreamSettingNotice()
self.bIsSelfLiving: tarscore.int32 = 0
@staticmethod
def writeTo(oos: tarscore.TarsOutputStream, value):
oos.write(tarscore.int32, 0, value.bIsLiving)
oos.write(HuyaBeginLiveNotice, 1, value.tNotice)
oos.write(HuyaStreamSettingNotice, 2, value.tStreamSettingNotice)
oos.write(tarscore.int32, 3, value.bIsSelfLiving)
@staticmethod
def readFrom(ios: tarscore.TarsInputStream):
value = HuyaGetLivingInfoRsp()
value.bIsLiving = ios.read(tarscore.int32, 0, False)
value.tNotice = ios.read(HuyaBeginLiveNotice, 1, False)
value.tStreamSettingNotice = ios.read(HuyaStreamSettingNotice, 2, False)
value.bIsSelfLiving = ios.read(tarscore.int32, 3, False)
return value
+47
View File
@@ -0,0 +1,47 @@
from biliup.plugins.huya_wup import Wup, DEFAULT_TICKET_NUMBER
from biliup.plugins.huya_wup.packet import (
HuyaGetCdnTokenInfoReq,
HuyaGetCdnTokenInfoRsp
)
from biliup.plugins.huya_wup.packet.__util import STANDARD_CHARSET
import httpx
client = httpx.Client()
if __name__ == "__main__":
# import base64
cdn: str = "HS"
stream_name: str = "1708907089-1708907089-7339700059157561344-3417937634-10057-A-0-1-imgplus.flv"
presenter_uid: int = 1708907089
wup_url: str = "https://wup.huya.com"
huya_headers: dict = {
"user-agent": f"HYSDK(Windows, 30000002)_APP(pc_exe&6090007&official)_SDK(trans&2.24.0.5157)",
"referer": "https://www.huya.com/",
"origin": "https://www.huya.com",
}
# TupVersion3 = 3
wup_req = Wup()
# wup_req.version = TupVersion3
wup_req.requestid = abs(DEFAULT_TICKET_NUMBER)
wup_req.servant = "liveui"
wup_req.func = "getCdnTokenInfo"
token_info_req = HuyaGetCdnTokenInfoReq()
token_info_req.cdnType = cdn
token_info_req.streamName = stream_name
token_info_req.presenterUid = presenter_uid
wup_req.put(
vtype=HuyaGetCdnTokenInfoReq,
name="tReq",
value=token_info_req
)
data = wup_req.encode_v3()
rsp = client.post(wup_url, data=data, headers=huya_headers)
rsp_bytes = rsp.content
wup_rsp = Wup()
wup_rsp.decode_v3(rsp_bytes)
token_info_rsp = wup_rsp.get(
vtype=HuyaGetCdnTokenInfoRsp,
# name=bytes("tRsp", encoding=STANDARD_CHARSET)
name="tRsp"
)
print(token_info_rsp.as_dict())
+86
View File
@@ -0,0 +1,86 @@
import struct
from biliup.common.tars.__tup import TarsUniPacket
from biliup.common.tars.__packet import RequestPacket
from biliup.common.tars.__tars import (TarsInputStream, TarsOutputStream)
from biliup.common.tars.__util import util
from .packet.__util import STANDARD_CHARSET
class Wup(TarsUniPacket):
def __init__(self):
super().__init__()
self.__mapa = util.mapclass(util.string, util.bytes)
self.__new_buffer = self.__mapa()
self.__code = RequestPacket()
@property
def version(self):
return self.__code.iVersion
@version.setter
def version(self, value):
self.__code.iVersion = value
@property
def servant(self):
return self.__code.sServantName
@servant.setter
def servant(self, value):
self.__code.sServantName = value
@property
def func(self):
return self.__code.sFuncName
@func.setter
def func(self, value):
self.__code.sFuncName = value
@property
def requestid(self):
return self.__code.iRequestId
@requestid.setter
def requestid(self, value):
self.__code.iRequestId = value
def put(self, vtype, name, value):
oos = TarsOutputStream()
oos.write(vtype, 0, value)
self.__new_buffer[name] = oos.getBuffer()
def get(self, vtype, name):
if isinstance(name, str):
name = name.encode(STANDARD_CHARSET)
if (name in self.__new_buffer) == False:
raise Exception("UniAttribute not found key:%s" % name)
t = self.__new_buffer[name]
o = TarsInputStream(t)
return o.read(vtype, 0, True)
def encode_v3(self):
oos = TarsOutputStream()
oos.write(self.__mapa, 0, self.__new_buffer)
self.__code.iVersion = 3 # Force to use TarsV3
self.__code.sBuffer = oos.getBuffer()
sos = TarsOutputStream()
RequestPacket.writeTo(sos, self.__code)
return struct.pack('!i', 4 + len(sos.getBuffer())) + sos.getBuffer()
def decode_v3(self, buf):
ois = TarsInputStream(buf[4:])
self.__code = RequestPacket.readFrom(ois)
sis = TarsInputStream(self.__code.sBuffer)
self.__new_buffer = sis.read(self.__mapa, 0, True)
def clear(self):
self.__code.__init__()
@@ -0,0 +1,134 @@
from biliup.common.tars import tarscore
from .StreamInfo import HuyaStreamInfo
from .MultiStreamInfo import HuyaMultiStreamInfo
class HuyaBeginLiveNotice(tarscore.struct):
__tars_class__ = "Huya.BeginLiveNotice"
def __init__(self):
self.lPresenterUid: tarscore.int64 = 0
self.iGameId: tarscore.int32 = 0
self.sGameName: tarscore.string = ""
self.iRandomRange: tarscore.int32 = 0
self.iStreamType: tarscore.int32 = 0
self.vStreamInfo: tarscore.vector = tarscore.vector(HuyaStreamInfo) # FIXME
self.vCdnList: tarscore.vector = tarscore.vector(tarscore.string) # FIXME
self.lLiveId: tarscore.int64 = 0
self.iPCDefaultBitRate: tarscore.int32 = 0
self.iWebDefaultBitRate: tarscore.int32 = 0
self.iMobileDefaultBitRate: tarscore.int32 = 0
self.lMultiStreamFlag: tarscore.int64 = 0
self.sNick: tarscore.string = ""
self.lYYId: tarscore.int64 = 0
self.lAttendeeCount: tarscore.int64 = 0
self.iCodecType: tarscore.int32 = 0
self.iScreenType: tarscore.int32 = 0
self.vMultiStreamInfo: tarscore.vector = tarscore.vector(HuyaMultiStreamInfo) # FIXME
self.sLiveDesc: tarscore.string = ""
self.lLiveCompatibleFlag: tarscore.int64 = 0
self.sAvatarUrl: tarscore.string = ""
self.iSourceType: tarscore.int32 = 0
self.sSubchannelName: tarscore.string = ""
self.sVideoCaptureUrl: tarscore.string = ""
self.iStartTime: tarscore.int32 = 0
self.lChannelId: tarscore.int64 = 0
self.lSubChannelId: tarscore.int64 = 0
self.sLocation: tarscore.string = ""
@staticmethod
def writeTo(oos: tarscore.TarsOutputStream, value):
oos.write(tarscore.int64, 0, value.lPresenterUid)
oos.write(tarscore.int32, 1, value.iGameId)
oos.write(tarscore.string, 2, value.sGameName)
oos.write(tarscore.int32, 3, value.iRandomRange)
oos.write(tarscore.int32, 4, value.iStreamType)
oos.write(tarscore.vector, 5, value.vStreamInfo)
oos.write(tarscore.vector, 6, value.vCdnList)
oos.write(tarscore.int64, 7, value.lLiveId)
oos.write(tarscore.int32, 8, value.iPCDefaultBitRate)
oos.write(tarscore.int32, 9, value.iWebDefaultBitRate)
oos.write(tarscore.int32, 10, value.iMobileDefaultBitRate)
oos.write(tarscore.int64, 11, value.lMultiStreamFlag)
oos.write(tarscore.string, 12, value.sNick)
oos.write(tarscore.int64, 13, value.lYYId)
oos.write(tarscore.int64, 14, value.lAttendeeCount)
oos.write(tarscore.int32, 15, value.iCodecType)
oos.write(tarscore.int32, 16, value.iScreenType)
oos.write(tarscore.vector, 17, value.vMultiStreamInfo)
oos.write(tarscore.string, 18, value.sLiveDesc)
oos.write(tarscore.int64, 19, value.lLiveCompatibleFlag)
oos.write(tarscore.string, 20, value.sAvatarUrl)
oos.write(tarscore.int32, 21, value.iSourceType)
oos.write(tarscore.string, 22, value.sSubchannelName)
oos.write(tarscore.string, 23, value.sVideoCaptureUrl)
oos.write(tarscore.int32, 24, value.iStartTime)
oos.write(tarscore.int64, 25, value.lChannelId)
oos.write(tarscore.int64, 26, value.lSubChannelId)
oos.write(tarscore.string, 27, value.sLocation)
@staticmethod
def readFrom(ios: tarscore.TarsInputStream):
value = HuyaBeginLiveNotice()
value.lPresenterUid = ios.read(tarscore.int64, 0, False)
value.iGameId = ios.read(tarscore.int32, 1, False)
value.sGameName = ios.read(tarscore.string, 2, False)
value.iRandomRange = ios.read(tarscore.int32, 3, False)
value.iStreamType = ios.read(tarscore.int32, 4, False)
value.vStreamInfo = ios.read(tarscore.vector, 5, False)
value.vCdnList = ios.read(tarscore.vector, 6, False)
value.lLiveId = ios.read(tarscore.int64, 7, False)
value.iPCDefaultBitRate = ios.read(tarscore.int32, 8, False)
value.iWebDefaultBitRate = ios.read(tarscore.int32, 9, False)
value.iMobileDefaultBitRate = ios.read(tarscore.int32, 10, False)
value.lMultiStreamFlag = ios.read(tarscore.int64, 11, False)
value.sNick = ios.read(tarscore.string, 12, False)
value.lYYId = ios.read(tarscore.int64, 13, False)
value.lAttendeeCount = ios.read(tarscore.int64, 14, False)
value.iCodecType = ios.read(tarscore.int32, 15, False)
value.iScreenType = ios.read(tarscore.int32, 16, False)
value.vMultiStreamInfo = ios.read(tarscore.vector, 17, False)
value.sLiveDesc = ios.read(tarscore.string, 18, False)
value.lLiveCompatibleFlag = ios.read(tarscore.int64, 19, False)
value.sAvatarUrl = ios.read(tarscore.string, 20, False)
value.iSourceType = ios.read(tarscore.int32, 21, False)
value.sSubchannelName = ios.read(tarscore.string, 22, False)
value.sVideoCaptureUrl = ios.read(tarscore.string, 23, False)
value.iStartTime = ios.read(tarscore.int32, 24, False)
value.lChannelId = ios.read(tarscore.int64, 25, False)
value.lSubChannelId = ios.read(tarscore.int64, 26, False)
value.sLocation = ios.read(tarscore.string, 27, False)
return value
def as_dict(self):
return {
"lPresenterUid": self.lPresenterUid,
"iGameId": self.iGameId,
"sGameName": self.sGameName,
"iRandomRange": self.iRandomRange,
"iStreamType": self.iStreamType,
"vStreamInfo": self.vStreamInfo,
"vCdnList": self.vCdnList,
"lLiveId": self.lLiveId,
"iPCDefaultBitRate": self.iPCDefaultBitRate,
"iWebDefaultBitRate": self.iWebDefaultBitRate,
"iMobileDefaultBitRate": self.iMobileDefaultBitRate,
"lMultiStreamFlag": self.lMultiStreamFlag,
"sNick": self.sNick,
"lYYId": self.lYYId,
"lAttendeeCount": self.lAttendeeCount,
"iCodecType": self.iCodecType,
"iScreenType": self.iScreenType,
"vMultiStreamInfo": self.vMultiStreamInfo,
"sLiveDesc": self.sLiveDesc,
"lLiveCompatibleFlag": self.lLiveCompatibleFlag,
"sAvatarUrl": self.sAvatarUrl,
"iSourceType": self.iSourceType,
"sSubchannelName": self.sSubchannelName,
"sVideoCaptureUrl": self.sVideoCaptureUrl,
"iStartTime": self.iStartTime,
"lChannelId": self.lChannelId,
"lSubChannelId": self.lSubChannelId,
"sLocation": self.sLocation
}
@@ -0,0 +1,27 @@
from biliup.common.tars import tarscore
class HuyaMultiStreamInfo(tarscore.struct):
__tars_class__ = "Huya.MultiStreamInfo"
def __init__(self):
self.sDisplayName: tarscore.string = ""
self.iBitRate: tarscore.int32 = 0
self.iCodecType: tarscore.int32 = 0
self.iCompatibleFlag: tarscore.int32 = 0
@staticmethod
def writeTo(oos: tarscore.TarsOutputStream, value):
oos.write(tarscore.string, 0, value.sDisplayName)
oos.write(tarscore.int32, 1, value.iBitRate)
oos.write(tarscore.int32, 2, value.iCodecType)
oos.write(tarscore.int32, 3, value.iCompatibleFlag)
@staticmethod
def readFrom(ios: tarscore.TarsInputStream):
value = HuyaMultiStreamInfo()
value.sDisplayName = ios.read(tarscore.string, 0, False)
value.iBitRate = ios.read(tarscore.int32, 1, False)
value.iCodecType = ios.read(tarscore.int32, 2, False)
value.iCompatibleFlag = ios.read(tarscore.int32, 3, False)
return value
@@ -0,0 +1,69 @@
from biliup.common.tars import tarscore
class HuyaStreamInfo(tarscore.struct):
__tars_class__ = "Huya.StreamInfo"
def __init__(self):
self.sCdnType: tarscore.string = ""
self.iIsMaster: tarscore.int32 = 0
self.lChannelId: tarscore.int64 = 0
self.lSubChannelId: tarscore.int64 = 0
self.lPresenterUid: tarscore.int64 = 0
self.sStreamName: tarscore.string = ""
self.sFlvUrl: tarscore.string = ""
self.sFlvUrlSuffix: tarscore.string = ""
self.sFlvAntiCode: tarscore.string = ""
self.sHlsUrl: tarscore.string = ""
self.sHlsUrlSuffix: tarscore.string = ""
self.sHlsAntiCode: tarscore.string = ""
self.iLineIndex: tarscore.int32 = 0
self.iIsMultiStream: tarscore.int32 = 0
self.iPCPriorityRate: tarscore.int32 = 0
self.iWebPriorityRate: tarscore.int32 = 0
self.iMobilePriorityRate: tarscore.int32 = 0
@staticmethod
def writeTo(oos: tarscore.TarsOutputStream, value):
oos.write(tarscore.string, 0, value.sCdnType)
oos.write(tarscore.int32, 1, value.iIsMaster)
oos.write(tarscore.int64, 2, value.lChannelId)
oos.write(tarscore.int64, 3, value.lSubChannelId)
oos.write(tarscore.int64, 4, value.lPresenterUid)
oos.write(tarscore.string, 5, value.sStreamName)
oos.write(tarscore.string, 6, value.sFlvUrl)
oos.write(tarscore.string, 7, value.sFlvUrlSuffix)
oos.write(tarscore.string, 8, value.sFlvAntiCode)
oos.write(tarscore.string, 9, value.sHlsUrl)
oos.write(tarscore.string, 10, value.sHlsUrlSuffix)
oos.write(tarscore.string, 11, value.sHlsAntiCode)
oos.write(tarscore.int32, 12, value.iLineIndex)
oos.write(tarscore.int32, 13, value.iIsMultiStream)
oos.write(tarscore.int32, 14, value.iPCPriorityRate)
oos.write(tarscore.int32, 15, value.iWebPriorityRate)
oos.write(tarscore.int32, 16, value.iMobilePriorityRate)
@staticmethod
def readFrom(ios: tarscore.TarsInputStream):
value = HuyaStreamInfo()
value.sCdnType = ios.read(tarscore.string, 0, False)
value.iIsMaster = ios.read(tarscore.int32, 1, False)
value.lChannelId = ios.read(tarscore.int64, 2, False)
value.lSubChannelId = ios.read(tarscore.int64, 3, False)
value.lPresenterUid = ios.read(tarscore.int64, 4, False)
value.sStreamName = ios.read(tarscore.string, 5, False)
value.sFlvUrl = ios.read(tarscore.string, 6, False)
value.sFlvUrlSuffix = ios.read(tarscore.string, 7, False)
value.sFlvAntiCode = ios.read(tarscore.string, 8, False)
value.sHlsUrl = ios.read(tarscore.string, 9, False)
value.sHlsUrlSuffix = ios.read(tarscore.string, 10, False)
value.sHlsAntiCode = ios.read(tarscore.string, 11, False)
value.iLineIndex = ios.read(tarscore.int32, 12, False)
value.iIsMultiStream = ios.read(tarscore.int32, 13, False)
value.iPCPriorityRate = ios.read(tarscore.int32, 14, False)
value.iWebPriorityRate = ios.read(tarscore.int32, 15, False)
value.iMobilePriorityRate = ios.read(tarscore.int32, 16, False)
return value
@@ -0,0 +1,34 @@
from biliup.common.tars import tarscore
class HuyaStreamSettingNotice(tarscore.struct):
__tars_class__ = "Huya.StreamSettingNotice"
def __init__(self):
self.lPresenterUid: tarscore.int64 = 0
self.iBitRate: tarscore.int32 = 0
self.iResolution: tarscore.int32 = 0
self.iFrameRate: tarscore.int32 = 0
self.lLiveId: tarscore.int64 = 0
self.sDisplayName: tarscore.string = ""
@staticmethod
def writeTo(oos: tarscore.TarsOutputStream, value):
oos.write(tarscore.int64, 0, value.lPresenterUid)
oos.write(tarscore.int32, 1, value.iBitRate)
oos.write(tarscore.int32, 2, value.iResolution)
oos.write(tarscore.int32, 3, value.iFrameRate)
oos.write(tarscore.int64, 4, value.lLiveId)
oos.write(tarscore.string, 5, value.sDisplayName)
@staticmethod
def readFrom(ios: tarscore.TarsInputStream):
value = HuyaStreamSettingNotice()
value.lPresenterUid = ios.read(tarscore.int64, 0, False)
value.iBitRate = ios.read(tarscore.int32, 1, False)
value.iResolution = ios.read(tarscore.int32, 2, False)
value.iFrameRate = ios.read(tarscore.int32, 3, False)
value.lLiveId = ios.read(tarscore.int64, 4, False)
value.sDisplayName = ios.read(tarscore.string, 5, False)
return value
@@ -0,0 +1,35 @@
from biliup.common.tars import tarscore
class HuyaUserId(tarscore.struct):
__tars_class__ = "Huya.UserId"
def __init__(self):
self.lUid: tarscore.int64 = 0
self.sGuid: tarscore.string = ""
self.sToken: tarscore.string = ""
self.sHuYaUA: tarscore.string = ""
self.sCookie: tarscore.string = ""
@staticmethod
def writeTo(oos: tarscore.TarsOutputStream, value):
oos.write(tarscore.int64, 0, value.lUid)
oos.write(tarscore.string, 1, value.sGuid)
oos.write(tarscore.string, 2, value.sToken)
oos.write(tarscore.string, 3, value.sHuYaUA)
oos.write(tarscore.string, 4, value.sCookie)
@staticmethod
def readFrom(ios: tarscore.TarsInputStream):
value = HuyaUserId()
value.lUid = ios.read(tarscore.int64, 0, False)
# print(("lUid = %d" % value.lUid))
value.sGuid = ios.read(tarscore.string, 1, False)
# print(("sGuid = %s" % value.sGuid))
value.sToken = ios.read(tarscore.string, 2, False)
# print(("sToken = %s" % value.sToken))
value.sHuYaUA = ios.read(tarscore.string, 3, False)
# print(("sHuYaUA = %s" % value.sHuYaUA))
value.sCookie = ios.read(tarscore.string, 4, False)
# print(("sCookie = %s" % value.sCookie))
return value
@@ -0,0 +1,37 @@
from biliup.common.tars import tarscore
class HuyaWSUserInfo(tarscore.struct):
__tars_class__ = "Huya.WSUserInfo"
def __init__(self):
self.lUid: tarscore.int64 = 0
self.bAnonymous: tarscore.boolean = True
self.sGuid: tarscore.string = ""
self.sToken: tarscore.string = ""
self.lTid: tarscore.int64 = 0
self.lSid: tarscore.int64 = 0
self.lGroupId: tarscore.int64 = 0
self.lGroupType: tarscore.int64 = 0
@staticmethod
def writeTo(oos: tarscore.TarsOutputStream, value):
oos.write(tarscore.int64, 0, value.lUid)
oos.write(tarscore.boolean, 1, value.bAnonymous)
oos.write(tarscore.string, 2, value.sGuid)
oos.write(tarscore.string, 3, value.sToken)
oos.write(tarscore.int64, 4, value.lTid)
oos.write(tarscore.int64, 5, value.lSid)
oos.write(tarscore.int64, 6, value.lGroupId)
oos.write(tarscore.int64, 7, value.lGroupType)
@staticmethod
def readFrom(ios: tarscore.TarsInputStream):
value = HuyaWSUserInfo()
value.lUid = ios.read(tarscore.int64, 0, False)
value.bAnonymous = ios.read(tarscore.boolean, 1, False)
value.sGuid = ios.read(tarscore.string, 2, False)
value.sToken = ios.read(tarscore.string, 3, False)
value.lTid = ios.read(tarscore.int64, 4, False)
value.lSid = ios.read(tarscore.int64, 5, False)
value.lGroupId = ios.read(tarscore.int64, 6, False)
value.lGroupType = ios.read(tarscore.int64, 7, False)
@@ -0,0 +1,24 @@
from biliup.common.tars import tarscore
from biliup.common.tars.__tars import BinBuffer
class HuyaWebSocketCommand(tarscore.struct):
__tars_class__ = "Huya.WebSocketCommand"
def __init__(self):
self.iCmdType: tarscore.int32 = 0
self.vData: tarscore.bytes = b''
@staticmethod
def writeTo(oos: tarscore.TarsOutputStream, value):
oos.write(tarscore.int32, 0, value.iCmdType)
oos.write(tarscore.bytes, 1, value.vData)
# oos.write(tarscore.int64, 2, value.lRequestId)
# oos.write(tarscore.string, 3, value.traceId)
@staticmethod
def readFrom(ios: tarscore.TarsInputStream):
value = HuyaWebSocketCommand()
value.iCmdType = ios.read(tarscore.int32, 0, False)
value.vData = ios.read(tarscore.bytes, 1, False)
# value.lRequestId = ios.read(tarscore.int64, 2, False)
# value.traceId = ios.read(tarscore.string, 3, False)
@@ -0,0 +1,17 @@
from enum import IntEnum
class EWebSocketCommandType(IntEnum):
EWSCmd_NULL = 0
EWSCmd_RegisterReq = 1
EWSCmd_RegisterRsp = 2
EWSCmd_WupReq = 3
EWSCmd_WupRsp = 4
EWSCmdC2S_HeartBeat = 5
EWSCmdS2C_HeartBeatAck = 6
EWSCmdS2C_MsgPushReq = 7
EWSCmdC2S_DeregisterReq = 8
EWSCmdS2C_DeRegisterRsp = 9
EWSCmdC2S_VerifyCookieReq = 10
EWSCmdS2C_VerifyCookieRsp = 11
EWSCmdC2S_VerifyHuyaTokenReq = 12
EWSCmdS2C_VerifyHuyaTokenRsp = 13