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:
1 parent
58dd823faa
commit
eb6b35c93f
4 files changed
+47
-31
No files matched your search
@@ -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%' }}
|
||||
|
||||
@@ -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__ 的参数名
|
||||
|
||||
@@ -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
@@ -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())})"
|
||||
Reference in new issue
Block a user