Add blued live record and fix yy live record

This commit is contained in:
ihmily
2023-12-07 23:38:37 +08:00
parent 74ffdb4d40
commit 9575a11467
4 changed files with 85 additions and 40 deletions

View File

@@ -5,7 +5,7 @@
![GitHub issues](https://img.shields.io/github/issues/ihmily/DouyinLiveRecorder.svg)
![Downloads](https://img.shields.io/github/downloads/ihmily/DouyinLiveRecorder/total)
一款可循环值守的直播录制工具基于FFmpeg实现多平台直播源录制支持自定义配置录制以及直播状态推送
一款可循环值守的直播录制工具基于FFmpeg实现多平台直播源录制支持自定义配置录制以及直播状态推送
</div>
@@ -20,6 +20,7 @@
- [x] B站
- [x] 小红书
- [x] bigo
- [x] blued
- [ ] 更多平台正在更新中
</div>
@@ -92,8 +93,11 @@ https://live.bilibili.com/320
小红书:
https://www.xiaohongshu.com/hina/livestream/568980065082002402?appuid=5f3f478a00000000010005b3&apptime=
bigo
bigo直播
https://www.bigo.tv/cn/716418802
buled直播
https://app.blued.cn/live?id=Mp6G2R
```
Tiktok目前只支持PC网页端地址我没下载app其他平台 app端直播间分享地址和网页端长地址都能正常进行录制抖音尽量用长链接避免因短链接转换失效导致不能正常录制
@@ -102,7 +106,7 @@ Tiktok目前只支持PC网页端地址我没下载app其他平台 app
解析接口:
该解析接口 ~~仅供演示~~(演示接口暂时停止),并且只包含抖音、快手、虎牙直播的解析,其他平台如有需要请自行添加,源码在这里 [DouyinLiveRecorder/api](https://github.com/ihmily/DouyinLiveRecorder/tree/main/api)
该解析接口 ~~仅供演示~~(演示接口暂时停止,后续再开放),并且只包含抖音、快手、虎牙直播的解析,其他平台如有需要请自行添加,源码在这里 [DouyinLiveRecorder/api](https://github.com/ihmily/DouyinLiveRecorder/tree/main/api)
```HTTP
GET https://hmily.vip/api/jx/live/?url=
@@ -134,6 +138,9 @@ GET https://hmily.vip/api/jx/live/convert.php?url=https://v.douyin.com/iQLgKSj/
## ⏳提交日志
- 20231207
- 新增blued直播录制修复YY直播录制新增直播结束消息推送
- 20231206
- 新增bigo直播录制

View File

@@ -31,3 +31,4 @@ YY_cookie =
B站cookie =
小红书cookie =
bigo_cookie =
blued_cookie =

62
main.py
View File

@@ -4,7 +4,7 @@
Author: Hmily
GitHub: https://github.com/ihmily
Date: 2023-07-17 23:52:05
Update: 2023-12-06 00:31:48
Update: 2023-12-07 01:33:19
Copyright (c) 2023 by Hmily, All Rights Reserved.
Function: Record live stream video.
"""
@@ -13,6 +13,7 @@ import random
import os
import sys
import urllib.parse
import urllib.request
import configparser
import subprocess
import threading
@@ -32,7 +33,8 @@ from spider import (
get_yy_stream_data,
get_bilibili_stream_data,
get_xhs_stream_url,
get_bigo_stream_url
get_bigo_stream_url,
get_blued_stream_url
)
from web_rid import (
@@ -46,8 +48,8 @@ from utils import (
from msg_push import dingtalk, xizhi
# 版本号
version = "v2.0.4"
platforms = "抖音|Tiktok|快手|虎牙|斗鱼|YY|B站|小红书|bigo"
version = "v2.0.5"
platforms = "抖音|Tiktok|快手|虎牙|斗鱼|YY|B站|小红书|bigo直播|blued直播"
# --------------------------全局变量-------------------------------------
recording = set()
unrecording = set()
@@ -301,7 +303,7 @@ def get_tiktok_stream_url(json_data):
stream_data = live_room.get('liveRoom', {}).get('streamData', {}).get('pull_data', {}).get('stream_data', '{}')
stream_data = json.loads(stream_data).get('data', {})
quality_list = list(stream_data.keys()) # ["origin","uhd","sd","ld"]
quality_list: list = list(stream_data.keys()) # ["origin","uhd","sd","ld"]
while len(quality_list) < 4:
quality_list.append(quality_list[-1])
video_qualities = {"原画": 0, "蓝光": 0, "超清": 1, "高清": 2, "标清": 3}
@@ -592,11 +594,15 @@ def start_record(url_tuple, count_variable=-1):
with semaphore:
port_info = get_xhs_stream_url(record_url, xhs_cookie)
elif record_url.find("https://www.bigo.tv/cn/") > -1:
elif record_url.find("https://www.bigo.tv/") > -1:
with semaphore:
port_info = get_bigo_stream_url(record_url, bigo_cookie)
anchor_name:str= port_info.get("anchor_name", '')
elif record_url.find("https://app.blued.cn/") > -1:
with semaphore:
port_info = get_blued_stream_url(record_url, blued_cookie)
anchor_name: str = port_info.get("anchor_name", '')
if not anchor_name:
print(f'序号{count_variable} 网址内容获取失败,进行重试中...获取失败的地址是:{url_tuple}')
@@ -622,8 +628,9 @@ def start_record(url_tuple, count_variable=-1):
else:
content = f"{record_name} 正在直播中..."
print(content)
# 推送通知
if live_status_push != '':
if live_status_push:
if '微信' in live_status_push:
xizhi(xizhi_api_url, content)
if '钉钉' in live_status_push:
@@ -633,7 +640,7 @@ def start_record(url_tuple, count_variable=-1):
full_path = f'{default_path}/{anchor_name}'
if real_url != "":
live_list.append(anchor_name)
now = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
now = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime(time.time()))
try:
if len(video_save_path) > 0:
if video_save_path[-1] != "/":
@@ -669,19 +676,12 @@ def start_record(url_tuple, count_variable=-1):
"-fflags", "+discardcorrupt",
"-i", real_url,
"-bufsize", "5000k",
# "-map", "0", # 不同点
"-sn", "-dn",
# "-bsf:v","h264_mp4toannexb",
# "-c","copy", # 直接用copy的话体积特别大.
# "-c:v","libx264", # 后期可以用crf来控制大小
"-reconnect_delay_max", "30",
"-reconnect_streamed", "-reconnect_at_eof",
# "-c:v", "copy", # 不同点
"-c:a", "copy",
"-max_muxing_queue_size", "64",
"-correct_ts_overflow", "1",
# "-f", "matroska", # 不同点
# "{path}".format(path=file), # 不同点
]
# 添加代理参数
@@ -723,7 +723,6 @@ def start_record(url_tuple, count_variable=-1):
print(f"\r{time.strftime('%Y-%m-%d %H:%M:%S')} {anchor_name} 未开播")
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
elif video_save_type == "MKV":
filename = anchor_name + '_' + now + ".mkv"
print(f'{rec_info}/{filename}')
@@ -754,7 +753,6 @@ def start_record(url_tuple, count_variable=-1):
print(f"{e.output} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
elif video_save_type == "MP4":
filename = anchor_name + '_' + now + ".mp4"
@@ -791,7 +789,6 @@ def start_record(url_tuple, count_variable=-1):
print(f"{e.output} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
elif video_save_type == "MKV音频":
filename = anchor_name + '_' + now + ".mkv"
print(f'{rec_info}/{filename}')
@@ -846,7 +843,7 @@ def start_record(url_tuple, count_variable=-1):
if Splitvideobysize: # 这里默认是启用/不启用视频分割功能
while True:
now = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
now = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime(time.time()))
filename = anchor_name + '_' + now + ".ts"
print(f'{rec_info}/{filename}')
file = full_path + '/' + filename
@@ -874,7 +871,7 @@ def start_record(url_tuple, count_variable=-1):
record_finished = True # 这里表示正常录制成功一次
record_finished_2 = True
count_time = time.time() # 这个记录当前时间, 用于后面 1分钟内快速2秒循环 这个值不能放到后面
count_time = time.time()
if tsconvert_to_mp4:
threading.Thread(target=converts_mp4, args=(file,)).start()
@@ -887,7 +884,6 @@ def start_record(url_tuple, count_variable=-1):
f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
break
else:
filename = anchor_name + '_' + now + ".ts"
@@ -910,8 +906,7 @@ def start_record(url_tuple, count_variable=-1):
]
ffmpeg_command.extend(command)
_output = subprocess.check_output(ffmpeg_command,
stderr=subprocess.STDOUT)
_output = subprocess.check_output(ffmpeg_command,stderr=subprocess.STDOUT)
record_finished = True
record_finished_2 = True
count_time = time.time()
@@ -921,13 +916,12 @@ def start_record(url_tuple, count_variable=-1):
if tsconvert_to_m4a:
threading.Thread(target=converts_m4a, args=(file,)).start()
except subprocess.CalledProcessError as e:
# logging.warning(str(e.output))
print(f"{e.output} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
if record_finished_2 == True:
if record_finished_2:
if record_name in recording:
recording.remove(record_name)
if anchor_name in unrecording:
@@ -935,6 +929,14 @@ def start_record(url_tuple, count_variable=-1):
print(f"\n{anchor_name} {time.strftime('%Y-%m-%d %H:%M:%S')} 直播录制完成\n")
record_finished_2 = False
# 推送通知
content = f"{record_name} 直播已结束"
if live_status_push:
if '微信' in live_status_push:
xizhi(xizhi_api_url, content)
if '钉钉' in live_status_push:
dingtalk(dingtalk_api_url, content, dingtalk_phone_num)
except Exception as e:
print(f"错误信息:{e}\r\n读取的地址为: {record_url} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
@@ -952,7 +954,7 @@ def start_record(url_tuple, count_variable=-1):
# 这里是.如果录制结束后,循环时间会暂时变成30s后检测一遍. 这样一定程度上防止主播卡顿造成少录
# 当30秒过后检测一遍后. 会回归正常设置的循环秒数
if record_finished == True:
if record_finished:
count_time_end = time.time() - count_time
if count_time_end < 60:
x = 30
@@ -985,7 +987,7 @@ def backup_file(file_path, backup_dir):
if not os.path.exists(backup_dir):
os.makedirs(backup_dir)
# 拼接备份文件名,年-月-日-时-分-秒
timestamp = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
timestamp = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
backup_file_name = os.path.basename(file_path) + '_' + timestamp
# 拷贝文件到备份目录
backup_file_path = os.path.join(backup_dir, backup_file_name)
@@ -1125,6 +1127,7 @@ while True:
bili_cookie = read_config_value(config, 'Cookie', 'B站cookie', '')
xhs_cookie = read_config_value(config, 'Cookie', '小红书cookie', '')
bigo_cookie = read_config_value(config, 'Cookie', 'bigo_cookie', '')
blued_cookie = read_config_value(config, 'Cookie', 'blued_cookie', '')
if len(video_save_type) > 0:
if video_save_type.upper().lower() == "FLV".lower():
@@ -1198,7 +1201,8 @@ while True:
'www.yy.com',
'live.bilibili.com',
'www.xiaohongshu.com',
'www.bigo.tv'
'www.bigo.tv',
'app.blued.cn'
]
if url_host in host_list:
new_line = (url, split_line[1])

View File

@@ -4,7 +4,7 @@
Author: Hmily
GitHub:https://github.com/ihmily
Date: 2023-07-15 23:15:00
Update: 2023-12-06 01:09:56
Update: 2023-12-07 23:35:47
Copyright (c) 2023 by Hmily, All Rights Reserved.
Function: Get live stream data.
"""
@@ -288,7 +288,6 @@ def get_douyu_stream_data(rid: str, rate: str = '-1', cookies: Union[str, None]
@trace_error_decorator
def get_yy_stream_data(url: str, cookies: Union[str, None] = None) -> Dict[str, Any]:
cid = re.search('yy.com/(.*?)/', url).group(1)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
@@ -302,12 +301,12 @@ def get_yy_stream_data(url: str, cookies: Union[str, None] = None) -> Dict[str,
req = urllib.request.Request(url, headers=headers)
response = opener.open(req, timeout=15)
html_str = response.read().decode('utf-8')
live_info = re.search('<div class="w-liveplayer-head__content">(.*)<i class="follow-i">', html_str, re.S).group(1)
anchor_name = re.search('<h2>(.*?)</h2>', live_info).group(1)
anchor_name = re.search('nick: "(.*?)",\n\s+logo', html_str).group(1)
cid = re.search('sid : "(.*?)",\n\s+ssid', html_str, re.S).group(1)
data = '{"head":{"seq":1691766627723,"appidstr":"0","bidstr":"121","cidstr":"' + cid + '","sidstr":"' + cid + '","uid64":0,"client_type":108,"client_ver":"5.14.13","stream_sys_ver":1,"app":"yylive_web","playersdk_ver":"5.14.13","thundersdk_ver":"0","streamsdk_ver":"5.14.13"},"client_attribute":{"client":"web","model":"","cpu":"","graphics_card":"","os":"chrome","osversion":"0","vsdk_version":"","app_identify":"","app_version":"","business":"","width":"1536","height":"864","scale":"","client_type":8,"h265":0},"avp_parameter":{"version":1,"client_type":8,"service_type":0,"imsi":0,"send_time":1691766627,"line_seq":-1,"gear":4,"ssl":1,"stream_format":0}}'
data = '{"head":{"seq":1701869217590,"appidstr":"0","bidstr":"121","cidstr":"' + cid + '","sidstr":"' + cid + '","uid64":0,"client_type":108,"client_ver":"5.17.0","stream_sys_ver":1,"app":"yylive_web","playersdk_ver":"5.17.0","thundersdk_ver":"0","streamsdk_ver":"5.17.0"},"client_attribute":{"client":"web","model":"web0","cpu":"","graphics_card":"","os":"chrome","osversion":"0","vsdk_version":"","app_identify":"","app_version":"","business":"","width":"1920","height":"1080","scale":"","client_type":8,"h265":0},"avp_parameter":{"version":1,"client_type":8,"service_type":0,"imsi":0,"send_time":1701869217,"line_seq":-1,"gear":4,"ssl":1,"stream_format":0}}'
data_bytes = data.encode('utf-8')
url2 = f'https://stream-manager.yy.com/v3/channel/streams?uid=0&cid={cid}&sid={cid}&appid=0&sequence=1691766112069&encode=json'
url2 = f'https://stream-manager.yy.com/v3/channel/streams?uid=0&cid={cid}&sid={cid}&appid=0&sequence=1701869217590&encode=json'
req = urllib.request.Request(url2, data=data_bytes, headers=headers)
response = opener.open(req, timeout=15)
json_str = response.read().decode('utf-8')
@@ -383,7 +382,7 @@ def get_bigo_stream_url(url: str, cookies: Union[str, None] = None) -> Dict[str,
data = {'siteId': room_id} # roomId
url = 'https://ta.bigo.tv/official_website/studio/getInternalStudioInfo'
data = urllib.parse.urlencode(data).encode('utf-8')
req = urllib.request.Request(url, data=data,headers=headers)
req = urllib.request.Request(url, data=data, headers=headers)
response = opener.open(req, timeout=15)
json_str = response.read().decode('utf-8')
json_data = json.loads(json_str)
@@ -402,8 +401,40 @@ def get_bigo_stream_url(url: str, cookies: Union[str, None] = None) -> Dict[str,
return result
@trace_error_decorator
def get_blued_stream_url(url: str, cookies: Union[str, None] = None) -> Dict[str, Any]:
headers = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-G973U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/14.2 Chrome/87.0.4280.141 Mobile Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
}
if cookies:
headers['Cookie'] = cookies
req = urllib.request.Request(url, headers=headers)
response = opener.open(req, timeout=15)
html_str = response.read().decode('utf-8')
json_str = re.search('decodeURIComponent\(\"(.*?)\"\)\)\,window\.Promise', html_str, re.S).group(1)
json_str = urllib.parse.unquote(json_str)
json_data = json.loads(json_str)
anchor_name = json_data['userInfo']['name']
live_status = json_data['userInfo']['onLive']
result = {
"anchor_name": anchor_name,
"is_live": False,
}
if live_status:
m3u8_url = "http:" + json_data['liveInfo']['liveUrl']
result['m3u8_url'] = m3u8_url
result['is_live'] = True
result['record_url'] = m3u8_url
return result
if __name__ == '__main__':
# 尽量用自己的cookie以避免默认的不可用导致无法获取数据
# 以下示例链接不保证时效性,请自行查看链接是否能正常访问
url = "https://live.douyin.com/745964462470" # 抖音直播
# url = "https://www.tiktok.com/@pearlgaga88/live" # Tiktok直播
# url = "https://live.kuaishou.com/u/yall1102" # 快手直播
@@ -415,9 +446,10 @@ if __name__ == '__main__':
# 小红书直播
# url = 'https://www.xiaohongshu.com/hina/livestream/568980065082002402?appuid=5f3f478a00000000010005b3&apptime='
# url = 'https://www.bigo.tv/cn/716418802' # bigo直播
# url = 'https://app.blued.cn/live?id=Mp6G2R' # blued直播
print(get_douyin_stream_data(url))
# print(get_tiktok_stream_data(url,'http://127.0.0.1:7890'))
# print(get_tiktok_stream_data(url,proxy_addr=''))
# print(get_kuaishou_stream_data(url))
# print(get_huya_stream_data(url))
# print(get_douyu_info_data(url))
@@ -426,3 +458,4 @@ if __name__ == '__main__':
# print(get_bilibili_stream_data(url))
# print(get_xhs_stream_url(url))
# print(get_bigo_stream_url(url))
# print(get_blued_stream_url(url))