diff --git a/app/ui/plugins/global.tsx b/app/ui/plugins/global.tsx
index d46e80a..dd68dc0 100644
--- a/app/ui/plugins/global.tsx
+++ b/app/ui/plugins/global.tsx
@@ -42,7 +42,7 @@ const Global: React.FC = () => {
3. stream-gears(默认。防 FLV 流花屏)
4. sync-downloader(流式边录边传,需先为主播设定上传模板。不受
- pool2/threads/segment_time 控制,默认 3 线程上传,请确保上传带宽充足。需安装 FFmpeg)
+ pool2/threads/segment_time 控制,默认 3 线程上传,请确保上传带宽充足。需安装 FFmpeg)详见 Wiki 点击查看
}
style={{ width: '100%' }}
diff --git a/biliup/engine/download.py b/biliup/engine/download.py
index 38845fb..1788451 100644
--- a/biliup/engine/download.py
+++ b/biliup/engine/download.py
@@ -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__ 的参数名
diff --git a/biliup/engine/sync_downloader.py b/biliup/engine/sync_downloader.py
index e6bd51c..bb882d9 100644
--- a/biliup/engine/sync_downloader.py
+++ b/biliup/engine/sync_downloader.py
@@ -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",
diff --git a/biliup/plugins/huya.py b/biliup/plugins/huya.py
index f3f9f53..fb29ed0 100644
--- a/biliup/plugins/huya.py
+++ b/biliup/plugins/huya.py
@@ -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())})"
\ No newline at end of file
+ headers['User-Agent'] = f"HYSDK(Windows, {int(time.time())})"