feat: add baidu and weibo live record, fix douyu record

This commit is contained in:
ihmily 2024-04-23 21:53:16 +08:00
parent 7f1618b7de
commit a7e0a5779f
6 changed files with 261 additions and 72 deletions

View File

@ -34,6 +34,8 @@
- [x] FlexTV
- [x] PopkonTV
- [x] TwitCasting
- [x] 百度直播
- [x] 微博直播
- [ ] 更多平台正在更新中
</div>
@ -148,6 +150,12 @@ https://www.popkontv.com/live/view?castId=wjfal007&partnerCode=P-00117
TwitCasting:
https://twitcasting.tv/c:uonq
百度直播:
https://live.baidu.com/m/media/pclive/pchome/live.html?room_id=9175031377&tab_category
微博直播:
https://weibo.com/l/wblive/p/show/1022:2321325026370190442592
```
直播间分享地址和网页端长地址都能正常进行录制抖音尽量用长链接避免因短链接转换失效导致不能正常录制而且需要有nodejs环境否则无法转换
@ -280,11 +288,18 @@ docker-compose stop
## ⏳提交日志
- 20240423
- 新增百度直播录制、微博直播录制
- 修复斗鱼录制直播回放的问题
- 新增直播源地址显示以及输出到日志文件设置
- 20240311
- 修复海外平台录制bug增加画质选择增强录制稳定性
- 修复虎牙录制bug (虎牙`一起看`频道 有特殊限制,有时无法录制)
- 20240309
- 修复虎牙直播、小红书直播和B站直播录制
- 新增5个直播平台录制包括winktv、flextv、look、popkontv、twitcasting

View File

@ -8,6 +8,7 @@
循环时间(秒) = 120
排队读取网址时间(秒) = 0
是否显示循环秒数 =
是否显示直播源地址 =
分段录制是否开启 =
视频分段时间(秒) = 1800
ts录制完成后自动转为mp4格式 =
@ -50,6 +51,8 @@ winktv_cookie =
flextv_cookie =
look_cookie =
twitcasting_cookie =
baidu_cookie =
weibo_cookie =
[Authorization]
popkontv_token =

View File

@ -2,5 +2,23 @@
from loguru import logger
# 每天日志自动分文件
logger.add("./logs/DouyinLiveRecorder.log", rotation="12:00")
logger.add(
"./logs/PlayURL.log",
level="INFO",
format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {message}",
filter=lambda i: i["level"].name == "INFO",
serialize=False,
enqueue=True,
rotation="12:00",
retention="10 days",
)
logger.add(
"./logs/DouyinLiveRecorder.log",
level="WARNING",
serialize=False,
enqueue=True,
rotation="12:00",
retention="10 days",
)

130
main.py
View File

@ -4,7 +4,7 @@
Author: Hmily
GitHub: https://github.com/ihmily
Date: 2023-07-17 23:52:05
Update: 2024-04-12 18:54:27
Update: 2024-04-23 21:34:55
Copyright (c) 2023-2024 by Hmily, All Rights Reserved.
Function: Record live stream video.
"""
@ -49,7 +49,9 @@ from spider import (
get_flextv_stream_data,
get_looklive_stream_url,
get_popkontv_stream_url,
get_twitcasting_stream_url
get_twitcasting_stream_url,
get_baidu_stream_data,
get_weibo_stream_url
)
from web_rid import (
@ -62,8 +64,8 @@ from utils import (
)
from msg_push import dingtalk, xizhi, tg_bot
version = "v3.0.2"
platforms = "\n国内站点:抖音|快手|虎牙|斗鱼|YY|B站|小红书|bigo直播|blued直播|网易CC|千度热播|猫耳FM|Look直播|TwitCasting" \
version = "v3.0.3"
platforms = "\n国内站点:抖音|快手|虎牙|斗鱼|YY|B站|小红书|bigo直播|blued直播|网易CC|千度热播|猫耳FM|Look直播|TwitCasting|百度直播|微博直播" \
"\n海外站点TikTok|AfreecaTV|PandaTV|WinkTV|FlexTV|PopkonTV"
# --------------------------全局变量-------------------------------------
@ -153,7 +155,7 @@ def display_info():
else:
start_display_time = now_time
except Exception as e:
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.error(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
def update_file(file_path: str, old_str: str, new_str: str, start_str: str = None):
@ -484,6 +486,9 @@ def get_huya_stream_url(json_data: dict, video_quality: str) -> dict:
@trace_error_decorator
def get_douyu_stream_url(json_data: dict, cookies: str, video_quality: str, proxy_address: str) -> dict:
# TODO: 获取斗鱼直播源地址
if not json_data["is_live"]:
return json_data
video_quality_options = {
"原画": '0',
"蓝光": '0',
@ -492,25 +497,15 @@ def get_douyu_stream_url(json_data: dict, cookies: str, video_quality: str, prox
"标清": '1'
}
room_info = json_data.get('pageContext', json_data)['pageProps']['room']['roomInfo']['roomInfo']
anchor_name = room_info.get('nickname', '')
status = room_info.get('isLive', False)
result = {
"anchor_name": anchor_name,
"is_live": False,
}
# 如果status值为1则正在直播
# 这边有个bug就是如果是直播回放状态也是在直播 待优化
if status == 1:
rid = str(room_info['rid'])
rate = video_quality_options.get(video_quality, '0') # 默认为原画
flv_data = get_douyu_stream_data(rid, rate, cookies=cookies, proxy_addr=proxy_address)
flv_url = flv_data['data'].get('url', None)
if flv_url:
result['flv_url'] = flv_url
result['is_live'] = True
result['record_url'] = flv_url
return result
rid = str(json_data["room_id"])
json_data.pop("room_id", None)
rate = video_quality_options.get(video_quality, '0') # 默认为原画只有登录后才能获取最高画质需要配置cookie
flv_data = get_douyu_stream_data(rid, rate, cookies=cookies, proxy_addr=proxy_address)
flv_url = flv_data['data'].get('url', None)
if flv_url:
json_data['flv_url'] = flv_url
json_data['record_url'] = flv_url
return json_data
@trace_error_decorator
@ -599,6 +594,7 @@ def get_bilibili_stream_url(json_data: dict, video_quality: str) -> dict:
@trace_error_decorator
def get_afreecatv_stream_url(json_data: dict, video_quality: str) -> dict:
# TODO: 获取afreecatv直播源地址
if not json_data['is_live']:
return json_data
@ -620,6 +616,7 @@ def get_afreecatv_stream_url(json_data: dict, video_quality: str) -> dict:
@trace_error_decorator
def get_netease_stream_url(json_data: dict, video_quality: str) -> dict:
# TODO: 获取netease直播源地址
if not json_data['is_live']:
return json_data
stream_list = json_data['stream_list']['resolution']
@ -642,6 +639,7 @@ def get_netease_stream_url(json_data: dict, video_quality: str) -> dict:
@trace_error_decorator
def get_pandatv_stream_url(json_data: dict, video_quality: str) -> dict:
# TODO: 获取pandatv直播源地址
if not json_data['is_live']:
return json_data
@ -663,6 +661,7 @@ def get_pandatv_stream_url(json_data: dict, video_quality: str) -> dict:
@trace_error_decorator
def get_winktv_stream_url(json_data: dict, video_quality: str) -> dict:
# TODO: 获取winktv直播源地址
if not json_data['is_live']:
return json_data
@ -684,6 +683,7 @@ def get_winktv_stream_url(json_data: dict, video_quality: str) -> dict:
@trace_error_decorator
def get_flextv_stream_url(json_data: dict, video_quality: str) -> dict:
# TODO: 获取flextv直播源地址
if not json_data['is_live']:
return json_data
@ -703,6 +703,28 @@ def get_flextv_stream_url(json_data: dict, video_quality: str) -> dict:
}
@trace_error_decorator
def get_baidu_stream_url(json_data: dict, video_quality: str) -> dict:
# TODO: 获取百度直播源地址
if not json_data['is_live']:
return json_data
play_url_list = json_data['play_url_list']
quality_list = {'原画': 0, '蓝光': 0, '超清': 1, '高清': 2, '标清': 3}
while len(play_url_list) < 4:
play_url_list.append(play_url_list[-1])
selected_quality = quality_list[video_quality]
m3u8_url = play_url_list[selected_quality]
return {
"anchor_name": json_data['anchor_name'],
"is_live": True,
"m3u8_url": m3u8_url,
"record_url": m3u8_url
}
def push_message(content: str):
push_pts = []
if '微信' in live_status_push:
@ -794,7 +816,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
cookies=tiktok_cookie)
port_info = get_tiktok_stream_url(json_data, record_quality)
else:
logger.warning(f"错误信息: 网络异常请检查网络是否能正常访问TikTok平台")
logger.error(f"错误信息: 网络异常请检查网络是否能正常访问TikTok平台")
elif record_url.find("https://live.kuaishou.com/") > -1:
platform = '快手直播'
@ -869,7 +891,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
)
port_info = get_afreecatv_stream_url(json_data, record_quality)
else:
logger.warning(f"错误信息: 网络异常请检查本网络是否能正常访问AfreecaTV平台")
logger.error(f"错误信息: 网络异常请检查本网络是否能正常访问AfreecaTV平台")
elif record_url.find("cc.163.com/") > -1:
platform = '网易CC直播'
@ -894,7 +916,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
)
port_info = get_pandatv_stream_url(json_data, record_quality)
else:
logger.warning(f"错误信息: 网络异常请检查本网络是否能正常访问PandaTV直播平台")
logger.error(f"错误信息: 网络异常请检查本网络是否能正常访问PandaTV直播平台")
elif record_url.find("fm.missevan.com/") > -1:
platform = '猫耳FM直播'
@ -912,7 +934,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
cookies=winktv_cookie)
port_info = get_winktv_stream_url(json_data, record_quality)
else:
logger.warning(f"错误信息: 网络异常请检查本网络是否能正常访问WinkTV直播平台")
logger.error(f"错误信息: 网络异常请检查本网络是否能正常访问WinkTV直播平台")
elif record_url.find("www.flextv.co.kr/") > -1:
platform = 'FlexTV'
@ -927,7 +949,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
)
port_info = get_flextv_stream_url(json_data, record_quality)
else:
logger.warning(f"错误信息: 网络异常请检查本网络是否能正常访问FlexTV直播平台")
logger.error(f"错误信息: 网络异常请检查本网络是否能正常访问FlexTV直播平台")
elif record_url.find("look.163.com/") > -1:
platform = 'Look直播'
@ -949,7 +971,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
partner_code=popkontv_partner_code
)
else:
logger.warning(f"错误信息: 网络异常请检查本网络是否能正常访问PopkonTV直播平台")
logger.error(f"错误信息: 网络异常请检查本网络是否能正常访问PopkonTV直播平台")
elif record_url.find("twitcasting.tv/") > -1:
platform = 'TwitCasting'
@ -962,8 +984,23 @@ def start_record(url_data: tuple, count_variable: int = -1):
password=twitcasting_password
)
elif record_url.find("live.baidu.com/") > -1:
platform = '百度直播'
with semaphore:
json_data = get_baidu_stream_data(
url=record_url,
proxy_addr=proxy_address,
cookies=baidu_cookie)
port_info = get_baidu_stream_url(json_data, record_quality)
elif record_url.find("weibo.com/") > -1:
platform = '微博直播'
with semaphore:
port_info = get_weibo_stream_url(
url=record_url, proxy_addr=proxy_address, cookies=weibo_cookie)
else:
logger.warning(f'{record_url} 未知直播地址')
logger.error(f'{record_url} 未知直播地址')
return
if anchor_name:
@ -1037,10 +1074,10 @@ def start_record(url_data: tuple, count_variable: int = -1):
if not os.path.exists(full_path):
os.makedirs(full_path)
except Exception as e:
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.error(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
if not os.path.exists(full_path):
logger.warning(
logger.error(
"错误信息: 保存路径不存在,不能生成录制.请避免把本程序放在c盘,桌面,下载文件夹,qq默认传输目录.请重新检查设置")
user_agent = ("Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-G973U) AppleWebKit/537.36 ("
@ -1090,6 +1127,8 @@ def start_record(url_data: tuple, count_variable: int = -1):
recording_time_list[record_name] = [start_record_time, record_quality]
rec_info = f"\r{anchor_name} 录制视频中: {full_path}"
filename_short = full_path + '/' + anchor_name + '_' + now
if show_url:
logger.info(f"{platform} | {anchor_name} | 直播源地址: {port_info['record_url']}")
if video_save_type == "FLV":
filename = anchor_name + '_' + now + '.flv'
@ -1112,7 +1151,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
raise Exception('该直播无flv直播流请切换视频保存类型')
except Exception as e:
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.error(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
warning_count += 1
no_error = False
@ -1159,7 +1198,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
_output = subprocess.check_output(ffmpeg_command, stderr=subprocess.STDOUT)
record_finished = True
except subprocess.CalledProcessError as e:
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.error(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
warning_count += 1
no_error = False
@ -1203,7 +1242,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
_output = subprocess.check_output(ffmpeg_command, stderr=subprocess.STDOUT)
record_finished = True
except subprocess.CalledProcessError as e:
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.error(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
warning_count += 1
no_error = False
@ -1255,7 +1294,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
threading.Thread(target=converts_m4a, args=(save_file_path,)).start()
except subprocess.CalledProcessError as e:
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.error(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
warning_count += 1
no_error = False
@ -1304,7 +1343,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
threading.Thread(target=converts_m4a, args=(save_file_path,)).start()
except subprocess.CalledProcessError as e:
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.error(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
warning_count += 1
no_error = False
@ -1339,7 +1378,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
record_finished = True
except subprocess.CalledProcessError as e:
logger.warning(
logger.error(
f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
warning_count += 1
no_error = False
@ -1375,7 +1414,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
if ts_to_m4a:
threading.Thread(target=converts_m4a, args=(save_file_path,)).start()
except subprocess.CalledProcessError as e:
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.error(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
warning_count += 1
no_error = False
@ -1397,7 +1436,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
record_finished_2 = False
except Exception as e:
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.error(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
warning_count += 1
num = random.randint(-5, 5) + delay_default # 生成-5到5的随机数加上delay_default
@ -1430,7 +1469,7 @@ def start_record(url_data: tuple, count_variable: int = -1):
if loop_time:
print('\r检测直播间中...', end="")
except Exception as e:
logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
logger.error(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
warning_count += 1
time.sleep(2)
@ -1597,6 +1636,7 @@ while True:
delay_default = int(read_config_value(config, '录制设置', '循环时间(秒)', 120))
local_delay_default = int(read_config_value(config, '录制设置', '排队读取网址时间(秒)', 0))
loop_time = options.get(read_config_value(config, '录制设置', '是否显示循环秒数', ""), False)
show_url = options.get(read_config_value(config, '录制设置', '是否显示直播源地址', ""), False)
split_video_by_time = options.get(read_config_value(config, '录制设置', '分段录制是否开启', ""), False)
split_time = str(read_config_value(config, '录制设置', '视频分段时间(秒)', 1800))
ts_to_mp4 = options.get(read_config_value(config, '录制设置', 'ts录制完成后自动转为mp4格式', ""),
@ -1651,6 +1691,8 @@ while True:
flextv_cookie = read_config_value(config, 'Cookie', 'flextv_cookie', '')
look_cookie = read_config_value(config, 'Cookie', 'look_cookie', '')
twitcasting_cookie = read_config_value(config, 'Cookie', 'twitcasting_cookie', '')
baidu_cookie = read_config_value(config, 'Cookie', 'baidu_cookie', '')
weibo_cookie = read_config_value(config, 'Cookie', 'weibo_cookie', '')
if len(video_save_type) > 0:
if video_save_type.upper().lower() == "FLV".lower():
@ -1735,6 +1777,8 @@ while True:
'fm.missevan.com',
'look.163.com',
'twitcasting.tv',
'live.baidu.com',
'weibo.com',
]
overseas_platform_host = [
'www.tiktok.com',
@ -1793,7 +1837,7 @@ while True:
first_start = False
except Exception as err:
logger.warning(f"错误信息: {err} 发生错误的行数: {err.__traceback__.tb_lineno}")
logger.error(f"错误信息: {err} 发生错误的行数: {err.__traceback__.tb_lineno}")
if first_run:
t = threading.Thread(target=display_info, args=(), daemon=True)

157
spider.py
View File

@ -4,12 +4,13 @@
Author: Hmily
GitHub:https://github.com/ihmily
Date: 2023-07-15 23:15:00
Update: 2024-04-12 19:14:00
Update: 2024-04-23 21:14:21
Copyright (c) 2023 by Hmily, All Rights Reserved.
Function: Get live stream data.
"""
import hashlib
import random
import time
import urllib.parse
import urllib.error
@ -27,21 +28,19 @@ from utils import (
)
import http.cookiejar
no_proxy_handler = urllib.request.ProxyHandler({})
opener = urllib.request.build_opener(no_proxy_handler)
def get_req(
url: str,
proxy_addr: Union[str, None] = None,
headers: Union[dict, None] = None,
data: Union[dict, bytes, None] = None,
json_data: dict = None,
timeout: int = 20,
abroad: bool = False
url: str,
proxy_addr: Union[str, None] = None,
headers: Union[dict, None] = None,
data: Union[dict, bytes, None] = None,
json_data: dict = None,
timeout: int = 20,
abroad: bool = False
) -> Union[str, Any]:
if headers is None:
headers = {}
try:
@ -51,7 +50,8 @@ def get_req(
'https': proxy_addr
}
if data or json_data:
response = requests.post(url, data=data, json=json_data, headers=headers, proxies=proxies, timeout=timeout)
response = requests.post(url, data=data, json=json_data, headers=headers, proxies=proxies,
timeout=timeout)
else:
response = requests.get(url, headers=headers, proxies=proxies, timeout=timeout)
resp_str = response.text
@ -89,7 +89,6 @@ def get_req(
def get_partner_code(url, params):
parsed_url = urllib.parse.urlparse(url)
query_params = urllib.parse.parse_qs(parsed_url.query)
@ -99,6 +98,19 @@ def get_partner_code(url, params):
return None
def jsonp_to_json(jsonp_str):
pattern = r'(\w+)\((.*)\);?$'
match = re.search(pattern, jsonp_str)
if match:
_, json_str = match.groups()
json_obj = json.loads(json_str)
return json_obj
else:
print("No JSON data found in JSONP response.")
return None
def get_play_url_list(m3u8: str, proxy: Union[str, None] = None, header: Union[dict, None] = None) -> list:
resp = get_req(url=m3u8, proxy_addr=proxy, headers=header, abroad=True)
play_url_list = []
@ -321,15 +333,21 @@ def get_douyu_info_data(url: str, proxy_addr: Union[str, None] = None) -> Dict[s
else:
rid = re.search('douyu.com/(.*?)(?=\?|$)', url).group(1)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
'referer': 'https://www.douyu.com/7644887?dyshid=0-40f7c4a06aae9dc5bede316000031701&dyshci=181',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0',
}
url2 = f'https://m.douyu.com/{rid}'
html_str = get_req(url=url2, proxy_addr=proxy_addr, headers=headers)
json_str = re.search('<script id="vike_pageContext" type="application/json">(.*?)</script>',
html_str).group(1)
url2 = f'https://www.douyu.com/betard/{rid}'
json_str = get_req(url=url2, proxy_addr=proxy_addr, headers=headers)
json_data = json.loads(json_str)
return json_data
result = {
"anchor_name": json_data['room']['nickname'],
"is_live": False
}
if json_data['room']['videoLoop'] == 0 and json_data['room']['show_status'] ==1:
result["is_live"] = True
result["room_id"] = json_data['room']['room_id']
return result
@trace_error_decorator
@ -1329,7 +1347,8 @@ def get_popkontv_stream_url(
status_msg = json_data["statusMsg"]
if json_data['statusCd'] == "L000A":
print('获取直播源失败,', status_msg)
raise RuntimeError('你是未认证会员。登录popkontv官方网站后在“我的页面”>“修改我的信息”底部进行手机认证后可用')
raise RuntimeError(
'你是未认证会员。登录popkontv官方网站后在“我的页面”>“修改我的信息”底部进行手机认证后可用')
elif json_data['statusCd'] == "L0001":
cast_start_date_code = int(cast_start_date_code) - 1
json_str = fetch_data(headers, partner_code)
@ -1350,7 +1369,6 @@ def get_popkontv_stream_url(
def login_twitcasting(
username: str, password: str, proxy_addr: Union[str, None] = None, cookies: Union[str, None] = None
) -> Union[str, None]:
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
@ -1408,7 +1426,6 @@ def get_twitcasting_stream_url(
username: Union[str, None] = None,
password: Union[str, None] = None,
) -> Dict[str, Any]:
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
@ -1423,7 +1440,7 @@ def get_twitcasting_stream_url(
def get_data(header):
html_str = get_req(url, proxy_addr=proxy_addr, headers=header)
anchor = re.search("<title>(.*?)\(@(.*?)\) 's Live - Twit",html_str)
anchor = re.search("<title>(.*?)\(@(.*?)\) 's Live - Twit", html_str)
status = re.search('data-is-onlive="(.*?)"\n\s+data-view-mode', html_str)
movie_id = re.search('data-movie-id="(.*?)" data-audience-id', html_str)
return f'{anchor.group(1).strip()}-{anchor.group(2)}-{movie_id.group(1)}', status.group(1)
@ -1451,6 +1468,94 @@ def get_twitcasting_stream_url(
return result
@trace_error_decorator
def get_baidu_stream_data(url: str, proxy_addr: Union[str, None] = None, cookies: Union[str, None] = None) -> \
Dict[str, Any]:
headers = {
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Connection': 'keep-alive',
'Referer': 'https://live.baidu.com/',
'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36 Edg/121.0.0.0',
}
if cookies:
headers['Cookie'] = cookies
uid = random.choice([
'h5-683e85bdf741bf2492586f7ca39bf465',
'h5-c7c6dc14064a136be4215b452fab9eea',
'h5-4581281f80bb8968bd9a9dfba6050d3a'
])
room_id = re.search('room_id=(.*?)&', url).group(1)
params = {
'cmd': '371',
'action': 'star',
'service': 'bdbox',
'osname': 'baiduboxapp',
'data': '{"data":{"room_id":"' + room_id + '","device_id":"h5-683e85bdf741bf2492586f7ca39bf465","source_type":0,"osname":"baiduboxapp"},"replay_slice":0,"nid":"","schemeParams":{"src_pre":"pc","src_suf":"other","bd_vid":"","share_uid":"","share_cuk":"","share_ecid":"","zb_tag":"","shareTaskInfo":"{\\"room_id\\":\\"9175031377\\"}","share_from":"","ext_params":"","nid":""}}',
'ua': '360_740_ANDROID_0',
'bd_vid': '',
'uid': uid,
'_': str(int(time.time() * 1000)),
'callback': '__jsonp_callback_1__',
}
app_api = f'https://mbd.baidu.com/searchbox?{urllib.parse.urlencode(params)}'
jsonp_str = get_req(url=app_api, proxy_addr=proxy_addr, headers=headers)
json_data = jsonp_to_json(jsonp_str)
key = list(json_data['data'].keys())[0]
data = json_data['data'][key]
anchor_name = data['host']['name']
result = {
"anchor_name": anchor_name,
"is_live": False,
}
live_status = data['video']['stream']
if live_status == 1:
play_url_list = data['video']['url_clarity_list']
url_list = []
prefix = 'https://hls.liveshow.bdstatic.com/live/'
for i in play_url_list:
url_list.append(prefix + i['urls']['flv'].rsplit('.', maxsplit=1)[0].rsplit('/', maxsplit=1)[1]+'.m3u8')
if play_url_list:
result['play_url_list'] = url_list
result['is_live'] = True
return result
@trace_error_decorator
def get_weibo_stream_url(url: str, proxy_addr: Union[str, None] = None, cookies: Union[str, None] = None) -> \
Dict[str, Any]:
headers = {
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Connection': 'keep-alive',
'Referer': 'https://live.baidu.com/',
'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36 Edg/121.0.0.0',
}
if cookies:
headers['Cookie'] = cookies
room_id = url.split('?')[0].split('show/')[1]
app_api = f'https://weibo.com/l/pc/anchor/live?live_id={room_id}'
# app_api = f'https://weibo.com/l/!/2/wblive/room/show_pc_live.json?live_id={room_id}'
json_str = get_req(url=app_api, proxy_addr=proxy_addr, headers=headers)
json_data = json.loads(json_str)
anchor_name = json_data['data']['user_info']['name']
result = {
"anchor_name": anchor_name,
"is_live": False,
}
live_status = json_data['data']['item']['status']
if live_status == 1:
result["is_live"] = True
play_url_list = json_data['data']['item']['stream_info']['pull']
result['m3u8_url'] = play_url_list['live_origin_hls_url']
result['flv_url'] = play_url_list['live_origin_flv_url']
result['record_url'] = play_url_list['live_origin_hls_url']
return result
if __name__ == '__main__':
# 尽量用自己的cookie以避免默认的不可用导致无法获取数据
# 以下示例链接不保证时效性,请自行查看链接是否能正常访问
@ -1481,6 +1586,8 @@ if __name__ == '__main__':
# room_url = 'https://look.163.com/live?id=65108820&position=3' # Look直播
# room_url = 'https://www.popkontv.com/live/view?castId=wjfal007&partnerCode=P-00117' # popkontv
# room_url = 'https://twitcasting.tv/c:uonq' # TwitCasting
# room_url = 'https://live.baidu.com/m/media/pclive/pchome/live.html?room_id=9175031377&tab_category' # 百度直播
# room_url = 'https://weibo.com/l/wblive/p/show/1022:2321325026370190442592' # 微博直播
print(get_douyin_stream_data(room_url, proxy_addr=''))
# print(get_tiktok_stream_data(room_url, proxy_addr=''))
@ -1502,4 +1609,6 @@ if __name__ == '__main__':
# print(get_flextv_stream_data(room_url,proxy_addr='', username='', password=''))
# print(get_looklive_stream_url(room_url, proxy_addr=''))
# print(get_popkontv_stream_url(room_url, proxy_addr='', username='', password=''))
# print(get_twitcasting_stream_url(room_url, proxy_addr='', username='', password=''))
# print(get_twitcasting_stream_url(room_url, proxy_addr='', username='', password=''))
# print(get_baidu_stream_data(room_url, proxy_addr=''))
# print(get_weibo_stream_url(room_url, proxy_addr=''))

View File

@ -15,7 +15,7 @@ def trace_error_decorator(func):
except Exception as e:
error_line = traceback.extract_tb(e.__traceback__)[-1].lineno
error_info = f"错误信息: type: {type(e).__name__}, {str(e)} in function {func.__name__} at line: {error_line}"
logger.warning(error_info)
logger.error(error_info)
return []
return wrapper