Fix 修复一些问题 (#1207)

* fix

* 修复虎牙拉流失败异常

* 添加边录边传下载器日志记录

* 修复边录边传投稿标题概率异常

* Update biliup/plugins/huya.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* 添加边录边传的说明链接

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
XiaoMiku01andCopilot authored and GitHub committed 2025-03-30 11:13:55 +00:00
1 parent 58dd823faa
commit eb6b35c93f
4 files changed
+47 -31

No files matched your search

+1 -1
View File
@@ -42,7 +42,7 @@ const Global: React.FC = () => {
3. stream-gears(默认。防 FLV 流花屏)
<br />
4. sync-downloader(流式边录边传,需先为主播设定上传模板。不受
pool2/threads/segment_time 控制,默认 3 线程上传,请确保上传带宽充足。需安装 FFmpeg)
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>
</div>
}
style={{ width: '100%' }}
+1 -1
View File
@@ -537,7 +537,7 @@ def sync_download(stream_url, headers, segment_duration=60, max_file_size=100, o
def upload(video_queue, stream_info, stop_event: threading.Event):
with SessionLocal() as db:
data = get_stream_info(db, f"{stream_info['name']}")
data, _ = fmt_title_and_desc({**data, "name": stream_info['name']})
data, _ = fmt_title_and_desc({**data, "name": stream_info['name'], "title": stream_info['title']})
stream_info.update(data)
logger.info(f"stream_info: {stream_info}")
# 获取 BiliWebAsync.__init__ 的参数名
+36 -19
View File
@@ -67,36 +67,49 @@ class SyncDownloader:
def run_ffmpeg_with_url(self, ffmpeg_cmd, output_filename):
with subprocess.Popen(ffmpeg_cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE) as ffmpeg_proc:
# print("[run] 启动 ffmpeg...")
logging.info("[run] 启动 ffmpeg...")
logger.info("[run] 启动 ffmpeg...")
if output_filename == "-":
data = ffmpeg_proc.stdout.read(self.read_block_size) # 读取第一个 data
if not data: # 如果第一个 data 为空
logging.info("[run] ffmpeg 没有输出数据,返回 False")
logger.info("[run] ffmpeg 没有输出数据,返回 False")
err = ffmpeg_proc.stderr.read()
if err:
logger.error("[run] ffmpeg err " + err.decode("utf-8", errors="replace"))
return False
self.video_queue.put(data) # 将第一个数据放入队列
while True:
data = ffmpeg_proc.stdout.read(self.read_block_size)
if not data:
logging.info("[run] ffmpeg stdout 已到达 EOF。结束本段写入。")
logger.info("[run] ffmpeg stdout 已到达 EOF。结束本段写入。")
break
self.video_queue.put(data)
ffmpeg_proc.wait()
# 输出 ffmpeg 的错误信息(如果有的话)
err = ffmpeg_proc.stderr.read()
if err:
logger.error("[run] ffmpeg err " + err.decode("utf-8", errors="replace"))
return True # 如果正常执行,返回 True
def run_streamlink_with_ffmpeg(self, streamlink_cmd, ffmpeg_cmd, output_filename):
with subprocess.Popen(streamlink_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as streamlink_proc:
logging.info("[run] 启动 streamlink...")
with subprocess.Popen(ffmpeg_cmd, stdin=streamlink_proc.stdout, stdout=subprocess.PIPE) as ffmpeg_proc:
logging.info("[run] 启动 ffmpeg...")
logger.info("[run] 启动 streamlink...")
with subprocess.Popen(ffmpeg_cmd, stdin=streamlink_proc.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as ffmpeg_proc:
logger.info("[run] 启动 ffmpeg...")
if output_filename == "-":
logging.info("[run] 读取 ffmpeg stdout...")
logger.info("[run] 读取 ffmpeg stdout...")
# 读取第一个数据
data = ffmpeg_proc.stdout.read(self.read_block_size)
if not data: # 如果第一个数据为空
logging.info("[run] ffmpeg 没有输出数据,返回 False")
logger.info("[run] ffmpeg 没有输出数据,返回 False")
streamlink_proc.kill() # 终止 streamlink 进程
ffmpeg_proc.kill() # 终止 ffmpeg 进程
# 打印错误输出
ffmpeg_err = ffmpeg_proc.stderr.read()
if ffmpeg_err:
logger.error("[run] ffmpeg err " + ffmpeg_err.decode("utf-8", errors="replace"))
streamlink_err = streamlink_proc.stderr.read()
if streamlink_err:
logger.error("[run] streamlink err " + streamlink_err.decode("utf-8", errors="replace"))
return False
self.video_queue.put(data) # 将第一个数据放入队列
@@ -104,22 +117,26 @@ class SyncDownloader:
while True:
data = ffmpeg_proc.stdout.read(self.read_block_size)
if not data:
logging.info("[run] ffmpeg stdout 已到达 EOF。结束本段写入。")
logger.info("[run] ffmpeg stdout 已到达 EOF。结束本段写入。")
break
self.video_queue.put(data)
ffmpeg_proc.wait()
logging.info("[run] ffmpeg 已到达输出大小并退出。结束本段写入。")
# streamlink_proc.kill() # 确保终止 streamlink 进程
# streamlink_proc.wait()
logger.info("[run] ffmpeg 已到达输出大小并退出。结束本段写入。")
# 打印 ffmpeg 子进程的错误输出
ffmpeg_err = ffmpeg_proc.stderr.read()
if ffmpeg_err:
logger.error("[run] ffmpeg err " + ffmpeg_err.decode("utf-8", errors="replace"))
# 打印 streamlink 子进程的错误输出
streamlink_err = streamlink_proc.stderr.read()
if streamlink_err:
logger.error("[run] streamlink err " + streamlink_err.decode("utf-8", errors="replace"))
return True # 如果一切正常,返回 True
def build_ffmpeg_cmd(self, input_source, output_filename, headers, segment_duration):
cmd = [
"ffmpeg",
"-loglevel", "quiet",
"-loglevel", "error",
# "-" # 覆盖输出文件
]
if headers:
@@ -160,12 +177,12 @@ class SyncDownloader:
output_filename = f"{self.output_prefix}{file_index:03d}.mkv"
# print(f"\n[run] ========== 准备录制第 {file_index} 段:{output_filename} ==========")
# logging.info(f"\n[run] == 当前下载流地址:{self.stream_url} ==")
logging.info(f"\n[run] == 准备录制第 {file_index} 段:{output_filename} ==")
logger.info(f"\n[run] == 准备录制第 {file_index} 段:{output_filename} ==")
output_filename = "-"
is_hls = '.m3u8' in urlparse(self.stream_url).path
if not is_hls:
# print("[run] 输入源不是 HLS 地址,将直接使用 ffmpeg 进行录制。", self.stream_url)
logging.info("[run] 输入源不是 HLS 地址,将直接使用 ffmpeg 进行录制。")
logger.info("[run] 输入源不是 HLS 地址,将直接使用 ffmpeg 进行录制。")
ffmpeg_cmd = self.build_ffmpeg_cmd(self.stream_url, output_filename,
self.headers, self.segment_duration)
if not self.run_ffmpeg_with_url(ffmpeg_cmd, output_filename):
@@ -174,7 +191,7 @@ class SyncDownloader:
continue
else:
# print("[run] 输入源是 HLS 地址,将使用 streamlink + ffmpeg 进行录制。")
logging.info("[run] 输入源是 HLS 地址,将使用 streamlink + ffmpeg 进行录制。")
logger.info("[run] 输入源是 HLS 地址,将使用 streamlink + ffmpeg 进行录制。")
streamlink_cmd = [
"streamlink",
"--stream-segment-threads", "3",
+9 -10
View File
@@ -16,6 +16,7 @@ from ..plugins import logger, match1, random_user_agent
HUYA_WEB_BASE_URL = "https://www.huya.com"
HUYA_MOBILE_BASE_URL = "https://m.huya.com"
@Plugin.download(regexp=r'https?://(?:(?:www|m)\.)?huya\.com')
class Huya(DownloadBase):
def __init__(self, fname, url, suffix='flv'):
@@ -25,7 +26,7 @@ class Huya(DownloadBase):
self.__room_id = url.split('huya.com/')[1].split('?')[0]
self.huya_danmaku = config.get('huya_danmaku', False)
self.huya_max_ratio = config.get('huya_max_ratio', 0)
self.huya_cdn = config.get('huya_cdn', "").upper() # 不填写时使用主播的CDN优先级
self.huya_cdn = config.get('huya_cdn', "").upper() # 不填写时使用主播的CDN优先级
self.huya_protocol = 'Hls' if config.get('huya_protocol') == 'Hls' else 'Flv'
self.huya_imgplus = config.get('huya_imgplus', True)
self.huya_cdn_fallback = config.get('huya_cdn_fallback', False)
@@ -123,12 +124,10 @@ class Huya(DownloadBase):
self.raw_stream_url += f"&ratio={record_ratio}"
return True
def danmaku_init(self):
if self.huya_danmaku:
self.danmaku = DanmakuClient(self.url, self.gen_download_filename())
async def get_room_profile(self, use_api=False) -> dict:
if use_api:
params = {
@@ -137,8 +136,8 @@ class Huya(DownloadBase):
'roomid': self.__room_id,
'showSecret': 1,
}
resp = (await client.get(f"https://mp.huya.com/cache.php", \
headers=self.fake_headers, params=params)).json()
resp = (await client.get(f"https://mp.huya.com/cache.php",
headers=self.fake_headers, params=params)).json()
if resp['status'] != 200:
raise Exception(f"{resp['message']}")
return resp['data']
@@ -148,13 +147,12 @@ class Huya(DownloadBase):
raise Exception(f"找不到这个主播")
return json.loads(html.split('stream: ')[1].split('};')[0])
async def get_stream_urls(self, protocol, use_api=False, allow_imgplus=True, is_xingxiu=False) -> dict:
'''
返回指定协议的所有CDN流
'''
streams = {}
weights = {} # https://cdnweb.huya.com/getUidsDomainList?anchor_uid={anchor_uid}
weights = {} # https://cdnweb.huya.com/getUidsDomainList?anchor_uid={anchor_uid}
room_profile = await self.get_room_profile(use_api=use_api)
if not use_api:
try:
@@ -170,8 +168,8 @@ class Huya(DownloadBase):
if not allow_imgplus:
stream_name = stream_name.replace('-imgplus', '')
anti_code = anti_code + "&codec=264" \
if is_xingxiu else \
self.__build_query(stream_name, anti_code, _get_uid(self.fake_headers['cookie'], stream_name))
if is_xingxiu else \
self.__build_query(stream_name, anti_code, _get_uid(self.fake_headers.get('cookie', ''), stream_name))
for stream in stream_info:
# 优先级<0代表不可用
priority = stream['iWebPriorityRate']
@@ -273,5 +271,6 @@ def _get_uid(cookie: str, stream_name: str) -> int:
# }
# )['data']['uid']
def update_user_agent(headers: dict):
headers['User-Agent'] = f"HYSDK(Windows, {int(time.time())})"
headers['User-Agent'] = f"HYSDK(Windows, {int(time.time())})"