From 8a7ee5fbfc20d8724923fc0deb8f4d90e69c40ca Mon Sep 17 00:00:00 2001 From: haha114514 <47912037+haha114514@users.noreply.github.com> Date: Fri, 17 Mar 2023 10:58:56 +1100 Subject: [PATCH] =?UTF-8?q?=E4=B8=80=E7=B3=BB=E5=88=97bug=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E4=B8=8E=E4=BC=98=E5=8C=96=20(#384)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修复优选CDN与直播流不生效的问题 * 优化streamlink下载参数 * Update __init__.py * Update CHANGELOG.md * 完善依赖列表 * Update CHANGELOG.md --- CHANGELOG.md | 6 ++ biliup/__init__.py | 2 +- biliup/engine/download.py | 6 +- biliup/plugins/bilibili.py | 120 ++++++++++++++++++------------------- pyproject.toml | 4 +- 5 files changed, 69 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 936543d..be885fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ - 🔧已修复的问题 - ⚠️需要手动操作的更新信息 +## 0.4.12 +- 更新时间:2023.03.17 +- 🔧修复优选CDN与直播流不生效的问题。[@haha114514](https://github.com/haha114514) +- 🔧优化streamlink下载参数。[@haha114514](https://github.com/haha114514) +- 🔧完善依赖列表,将最低的yt-dlp版本要求升级到2023.3.3,解决2022年版本已无法解析YouTube视频的问题。并且将streamlink最低要求版本升级到5.3.0,提升hls流录制稳定性。[@haha114514](https://github.com/haha114514) + ## 0.4.11 - 更新时间:2023.03.07 - 🔧修复上一版本的封面上传问题。[@haha114514](https://github.com/haha114514) diff --git a/biliup/__init__.py b/biliup/__init__.py index f7647bf..392bbad 100644 --- a/biliup/__init__.py +++ b/biliup/__init__.py @@ -1,7 +1,7 @@ import logging import sys -__version__ = "0.4.11" +__version__ = "0.4.12" LOG_CONF = { 'version': 1, diff --git a/biliup/engine/download.py b/biliup/engine/download.py index ffbebbb..f97afe6 100644 --- a/biliup/engine/download.py +++ b/biliup/engine/download.py @@ -78,8 +78,8 @@ class DownloadBase: else: self.ffmpeg_download(fmtname) - def streamlink_download(self, filename): - streamlink_input_args = ['--stream-segment-threads', '10'] + def streamlink_download(self, filename): #streamlink+ffmpeg混合下载模式,适用于下载hls流 + streamlink_input_args = ['--stream-segment-threads', '5'] streamlink_cmd = ['streamlink', *streamlink_input_args, self.raw_stream_url, 'best', '-O'] ffmpeg_input_args = ['-reconnect_streamed', '1', '-reconnect_delay_max', '20', '-rw_timeout', '20000000'] ffmpeg_cmd = ['ffmpeg', '-re', '-i', 'pipe:0', '-y',*ffmpeg_input_args, *self.default_output_args, *self.opt_args, '-c', 'copy', '-f', self.suffix] @@ -93,7 +93,7 @@ class DownloadBase: ffmpeg_proc = subprocess.Popen(ffmpeg_cmd, stdin=streamlink_proc.stdout, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) try: with ffmpeg_proc.stdout as stdout: - for line in iter(stdout.readline, b''): # b'\n'-separated lines + for line in iter(stdout.readline, b''): decode_line = line.decode(errors='ignore') print(decode_line, end='', file=sys.stderr) logger.debug(decode_line.rstrip()) diff --git a/biliup/plugins/bilibili.py b/biliup/plugins/bilibili.py index 9c8ef13..0a42e25 100644 --- a/biliup/plugins/bilibili.py +++ b/biliup/plugins/bilibili.py @@ -60,7 +60,7 @@ class Bilibili(DownloadBase): live_start_time = room_info['data']['room_info']['live_start_time'] uname = room_info['data']['anchor_info']['base_info']['uname'] room_title = room_info['data']['room_info']['title'] - if self.use_live_cover is True: # 获取直播封面并保存到cover目录下 + if self.use_live_cover is True: # 获取直播封面并保存到cover目录下 try: self.live_cover_path = get_live_cover(uname, room_id, room_title, live_start_time, cover_url) except: @@ -69,7 +69,7 @@ class Bilibili(DownloadBase): self.room_title = room_info['data']['room_info']['title'] custom_api = config.get('bili_liveapi') is not None # 当 Cookie 存在,并且自定义APi使用Cookie开关关闭时,仅使用官方 Api - if config.get('user', {}).get('bili_cookie') and self.customAPI_use_cookie is not True: + if config.get('user', {}).get('bili_cookie') and self.customAPI_use_cookie is not True: custom_api = False play_info = get_play_info(s, custom_api, official_api_host, params) if play_info['code'] != 0: @@ -78,78 +78,72 @@ class Bilibili(DownloadBase): streams = play_info['data']['playurl_info']['playurl']['stream'] live_start_time = room_info['data']['room_info']['live_start_time'] if protocol == "stream": - stream = streams[0] + stream = streams[0] stream_info = stream['format'][0]['codec'][0] - elif protocol == "hls_ts" or "hls": - stream = streams[1] + elif protocol == "hls_ts" or protocol == "hls": + stream = streams[1] stream_info = stream['format'][0]['codec'][0] elif protocol == "hls_fmp4": try: - stream = streams[1] - stream_info = stream['format'][1]['codec'][0] - logger.error(f"获取到{uname}房间的fmp4流") + stream = streams[1] + stream_info = stream['format'][1]['codec'][0] + logger.debug(f"获取到{uname}房间的fmp4流") except: now_timestamp = int(time.time()) - if now_timestamp - live_start_time <= 45: #等待45s,如果还没有fmp4流就回退到flv流 + if now_timestamp - live_start_time <= 45: # 等待45s,如果还没有fmp4流就回退到flv流 return False - else: - stream = streams[0] - stream_info = stream['format'][0]['codec'][0] - logger.error(f"获取{uname}房间fmp4流失败,回退到flv流") - self.raw_stream_url = stream_info['url_info'][0]['host'] + stream_info['base_url'] \ - + stream_info['url_info'][0]['extra'] - find = False - for url_info in stream_info['url_info']: - # 跳过p2pCDN - if 'mcdn' in url_info['host']: - continue - # 哔哩哔哩直播强制原画(仅限HLS_ts流的 cn-gotcha01 CDN). 并且仅当主播有二压的时候才自动去掉m3u8的_bluray前缀,避免stream-gears的疯狂分段bug - if force_source is True and "cn-gotcha01" in url_info['extra'] and "_bluray.m3u8" in stream_info['base_url']: - stream_info['base_url'] = re.sub(r'_bluray(?=.*m3u8)', "", stream_info['base_url']) - find = True - # 强制替换cn-gotcha01的节点为指定节点 注意:只有大陆ip才能获取到cn-gotcha01的节点。 - if force_cn01_domain and "cn-gotcha01" in perf_cdn: - i = 0 - while i < 6: # 测试随机到的节点是否可用 - random_choice = random.choice(force_cn01_domain.split(",")) - i += 1 - try: # 发起 HEAD 请求,并获取响应状态码 - status_code = s.get(f"https://{random_choice}{stream_info['base_url']}{url_info['extra']}", - stream=True).status_code - if status_code == 200: # 如果响应状态码是 200,跳出循环 - break - except requests.exceptions.ConnectionError: # 如果发生连接异常,继续下一次循环 - logger.debug(f"随机到的域名 {random_choice} 无法访问,尝试重新发起随机。") - continue else: - logger.error(f"强制替换hls流的cn-gotcha01的节点为指定节点失败啦") - return False - logger.debug(f"随机到的域名 {random_choice} 返回了 200 状态码。") - url_info['host'] = "https://" + random_choice - find = True - # 强制替换ov05 302redirect之后的真实地址为指定的域名或ip达到自选ov05节点的目的 - if force_ov05_ip and "ov-gotcha05" in url_info['host']: - response = requests.get(url_info['host'] + stream_info['base_url'] + url_info['extra']) - self.raw_stream_url = re.sub(r"https://([a-z0-9]+)\.ourdvsss\.com", f"http://{force_ov05_ip}", - response.url) - logger.debug(f"将ov-gotcha05的节点ip替换为了{force_ov05_ip}") - break - # 匹配PerfCDN + stream = streams[0] + stream_info = stream['format'][0]['codec'][0] + logger.debug(f"获取{uname}房间fmp4流失败,回退到flv流") + found_perf_cdn = False #优选CDN节点 + for url_info in stream_info['url_info']: if perf_cdn and perf_cdn in url_info['extra']: - find = True - logger.debug(f"获取到{url_info['host']}节点,找到了prefCDN") - self.raw_stream_url = url_info['host'] + stream_info['base_url'] + url_info['extra'] - if find: + found_perf_cdn = True + logger.debug(f"找到了perfCDN{url_info['extra']}") break - if self.bili_cdn_fallback is False: - return True - stream_info['url_info'].reverse() - # 检查直播流是否可用以倒叙尝试回退 - for stream_url in stream_info['url_info']: - self.raw_stream_url = stream_url['host'] + stream_info['base_url'] + stream_url['extra'] - if s.get(self.raw_stream_url, stream=True).status_code == 404: + if not found_perf_cdn: #没找到优选CDN就回到第一个节点 + url_info['host'] = stream_info['url_info'][0]['host'] + url_info['extra'] = stream_info['url_info'][0]['extra'] + logger.debug(f"没找到perfCDN哦") + # 哔哩哔哩直播强制原画(仅限HLS_ts流的 cn-gotcha01 CDN). 并且仅当主播有二压的时候才自动去掉m3u8的_bluray前缀,避免stream-gears的疯狂分段bug + if force_source is True and "cn-gotcha01" in url_info['extra'] and "_bluray.m3u8" in stream_info['base_url']: + stream_info['base_url'] = re.sub(r'_bluray(?=.*m3u8)', "", stream_info['base_url']) + # 强制替换cn-gotcha01的节点为指定节点 注意:只有大陆ip才能获取到cn-gotcha01的节点。 + if force_cn01_domain and "cn-gotcha01" in url_info['extra']: + i = 0 + while i < 6: # 测试随机到的节点是否可用 + random_choice = random.choice(force_cn01_domain.split(",")) + i += 1 + try: # 发起 GET 请求,并获取响应状态码 + status_code = s.get(f"https://{random_choice}{stream_info['base_url']}{url_info['extra']}", + stream=True).status_code + if status_code == 200: # 如果响应状态码是 200,跳出循环 + break + except requests.exceptions.ConnectionError: # 如果发生连接异常,继续下一次循环 + logger.debug(f"随机到的域名 {random_choice} 无法访问,尝试重新发起随机。") continue - break + else: + logger.error(f"强制替换hls流的cn-gotcha01的节点为指定节点失败啦") + return False + logger.debug(f"随机到的域名 {random_choice} 返回了 200 状态码。") + url_info['host'] = "https://" + random_choice + # 强制替换ov05 302redirect之后的真实地址为指定的域名或ip达到自选ov05节点的目的 + if force_ov05_ip and "ov-gotcha05" in url_info['host']: + response = requests.get(url_info['host'] + stream_info['base_url'] + url_info['extra']) + self.raw_stream_url = re.sub(r"https://([a-z0-9]+)\.ourdvsss\.com", f"http://{force_ov05_ip}", + response.url) + logger.debug(f"将ov-gotcha05的节点ip替换为了{force_ov05_ip}") + self.raw_stream_url = url_info['host'] + stream_info['base_url'] + url_info['extra'] + if self.bili_cdn_fallback is False: + return True + stream_info['url_info'].reverse() + # 检查直播流是否可用以倒叙尝试回退 + for stream_url in stream_info['url_info']: + self.raw_stream_url = stream_url['host'] + stream_info['base_url'] + stream_url['extra'] + if s.get(self.raw_stream_url, stream=True).status_code == 404: + continue + break return True async def danmaku_download_start(self, filename): diff --git a/pyproject.toml b/pyproject.toml index daffe96..6c5b45d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,12 +14,12 @@ dependencies = [ "stream-gears >= 0.1.15", "psutil >= 5.4.6", "aiofiles >= 22.1.0", - "yt_dlp >= 2022.3.8.2", + "yt_dlp >= 2023.3.3", "Pillow >= 5.2.0", "aiohttp[speedups]", "Requests >= 2.20.0", "PyYAML >= 4.2b1", - "streamlink >= 0.14.2", + "streamlink >= 5.3.0", "ykdl >= 1.8.0", "rsa >= 4.6", 'importlib-resources; python_version<"3.9"',