Fix kuaishou and Tiktok record

This commit is contained in:
ihmily 2023-09-19 00:27:31 +08:00
parent 3808040fc7
commit 62bd6761bc
5 changed files with 288 additions and 263 deletions

View File

@ -56,6 +56,7 @@
- 当同时在录制多个直播时最好线程数设置大一些否则可能出现其中一个直播录制出错。当然设置的过大也没用要同时考虑自身电脑的配置如CPU内核数、网络带宽等限制。
- 如果想直接使用打包好的录制软件,进入[Releases](https://github.com/ihmily/DouyinLiveRecorder/releases) 下载最新发布的 zip压缩包即可可能有些电脑会报毒直接忽略即可这是pyinstaller打包的锅。
- 如果要长时间挂着软件循环监测直播最好循环时间设置长一点避免因请求频繁导致被官方封禁IP 。
- 最好使用自己的cookie切记
 
@ -120,18 +121,20 @@ GET https://hmily.vip/api/jx/live/convert.php?url=https://v.douyin.com/iQLgKSj/
## ⏳提交日志
- 20230919
- 修复了快手版本更新后录制出错的问题增加了其自动获取cookie(稳定性未知)
- 修复了Tiktok显示正在直播但不进行录制的问题
- 20230907
- 修复了因抖音官方更新了版本导致的录制出错以及短链接转换出错
- 修复B站无法录制原画视频的bug
- 修改了配置文件字段新增各平台自定义设置Cookie
- 20230903
- 修复了Tiktok录制时报644无法录制的问题
- 新增直播状态推送到钉钉和微信的功能,如有需要请看 [设置推送教程](https://d04vqdiqwr3.feishu.cn/docx/XFPwdDDvfobbzlxhmMYcvouynDh?from=from_copylink)
- 最近比较忙,其他问题有时间再更新
- 20230816
- 修复斗鱼直播(官方更新了字段)和快手直播录制出错的问题
- 20230814

View File

@ -4,7 +4,7 @@
* Author: Hmily
* Github:https://github.com/ihmily
* Date: 2023-07-20 21:06:20
* Update: 2023-09-07 23:53:07
* Update: 2023-09-17 20:23:00
* Copyright (c) 2023 by Hmily, All Rights Reserved.
* Function:Spider the live stream url
* Address:https://github.com/ihmily/DouyinLiveRecorder
@ -12,6 +12,7 @@
// 本API代码只有解析抖音、快手和虎牙的有需要其他的可自己根据源码增加
// 注意:抖音和快手的 要添加上自己的cookie才能用
header('Content-type: application/json; charset=utf-8');
if(empty($_GET['url'])){
@ -51,30 +52,23 @@ function get_douyin_json_data($url) {
'Referer: https://live.douyin.com/',
);
$cookies='ttwid=1%7CIkooT8SJQrpeYtHlSALuhz9BdcHpaaf9tHQRKHuDaYE%7C1687785070%7C6690250483b63b6482128174d0f93bd879614d76f1b6e03ca52e032cf7fbaafd; passport_csrf_token=52bece134ac246c81163cc93b72f86a6; passport_csrf_token_default=52bece134ac246c81163cc93b72f86a6; d_ticket=2b9e3eb3626216c0122f0d980f867deb7b414; n_mh=hvnJEQ4Q5eiH74-84kTFUyv4VK8xtSrpRZG1AhCeFNI; passport_auth_status=a74f300f376940d65914eb148d55ca96%2C9ca487aea255972120d502f736c5dd7b; passport_auth_status_ss=a74f300f376940d65914eb148d55ca96%2C9ca487aea255972120d502f736c5dd7b; sso_auth_status=52ecac30d95890cc7896c880366aa21a; sso_auth_status_ss=52ecac30d95890cc7896c880366aa21a; LOGIN_STATUS=1; store-region=cn-fj; store-region-src=uid; __security_server_data_status=1; __live_version__=%221.1.1.1853%22; live_can_add_dy_2_desktop=%220%22; xgplayer_user_id=528819598596; msToken=ZfXzPPa_KqQDF9wkHigKqgyUMIt33-qgLl1qqthGsAea4L69i9wxWaGH4GaQ9M_Q-eqhLpnD4v8FRGIj9KGJGIyLmjPkR1uepZ0gBaqhCkqK1KaauPXT_VK_uVgW6q4=; home_can_add_dy_2_desktop=%220%22; strategyABtestKey=%221689685952.92%22; FOLLOW_LIVE_POINT_INFO=%22MS4wLjABAAAAf6aekfyBsc4u8jMkeYbgnkFa0ksIWKWpGOywuyHXyo4%2F1689609600000%2F0%2F1689606316434%2F0%22; FOLLOW_NUMBER_YELLOW_POINT_INFO=%22MS4wLjABAAAAf6aekfyBsc4u8jMkeYbgnkFa0ksIWKWpGOywuyHXyo4%2F1689609600000%2F0%2F0%2F1689594083273%22; FORCE_LOGIN=%7B%22videoConsumedRemainSeconds%22%3A180%7D; volume_info=%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Atrue%2C%22volume%22%3A0.6%7D; device_web_cpu_core=8; device_web_memory_size=-1; webcast_local_quality=origin; csrf_session_id=0446f50cc7e08f146ad07351af90f413; __ac_nonce=064be522600a12daa29ff; __ac_signature=_02B4Z6wo00f0145FB4AAAIDCkga5P5okFMuOdAMAAIc3h1Lmbu.WZmNdgawlJBkHRSAf1yndkZFgF.zN2OHlE62.f.4ZFt740eSkTrQW8j3EM2s9s3vtK9LGh-h9jhUkgSbj4UOtYTqpCZZc88; webcast_leading_last_show_time=1690194481638; webcast_leading_total_show_times=1; odin_tt=6ebbe0a3c1b4e5bc6d333c5c7514fc88a288b3b03b1f0cf34826dee5d6d6394620f17fd4eb624b710954233f38fa3c67fd4a5338bffaa792a2cf71d1b51d837f079925497d6b372f47a577d779036a71; msToken=uHqyINCG79-ojuC5cXU6tYm0Av3BqNzqLkFGvbNw5QmSCtFY7xYHJjJ3wu-gk2Evj5QQ7D6UMsz2inlRN-aZf8xTGMAnpmgieOrygPqoK7QboFwCXR7aLi4KKcFXvFu2; tt_scid=BVvDLf3XEG4PZlY2-haad4.kR2BYWdq4X88b6-sPA2Wpg2lsSDI5M7YuZ7H-GPf.6646';
$cookies='your cookie'; # 任意抖音直播间页面的Cookie
$html_str = get_curl($url,$headers,$cookies);
// print_r($html_str);
$pattern = '/self\.__pace_f\.push(.*?)<\/script><div hidden id/Us';
$p_result = preg_match($pattern, $html_str, $matches);
$pattern2 = '/state\\\":(.*?),\\\"isAsianGames/';
$p_result2 = preg_match($pattern2, $matches[1], $matches2);
$cleaned_string = str_replace("\\", "", $matches2[1].'}]}');
$pattern = '/\{\\\"state(.*?)\"\]\)\<\/script\>\<div hidden/';
preg_match($pattern, $html_str, $matches);
$json_string = '{\"state' . explode(']\n', $matches[1])[0];
$cleaned_string = str_replace("\\", "", $json_string);
$cleaned_string = preg_replace('/bdp_log=(.*?)&bdpsum=/', '', $cleaned_string);
$replacements = array(
'"[' => '[',
']"' => ']',
'"{' => '{',
'}"' => '}',
'"{' => '{',
'}"' => '}',
'u0026' => '&'
);
$cleaned_string = strtr($cleaned_string, $replacements);
$json_str='{"state":'.$cleaned_string;
// echo $json_str;
$json_data = json_decode($json_str, true);
$json_data = json_decode($cleaned_string, true);
return $json_data;
}
@ -147,20 +141,24 @@ function get_kuaishou_stream_url($live_url){
$headers = array(
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
'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',
'Cookie:clientid=3; did=web_2e5935532808ea57f357c827b45abaf4; didv=1687682547000; client_key=65890b29; kpn=GAME_ZONE; clientid=3; did=web_2e5935532808ea57f357c827b45abaf4; client_key=65890b29; kpn=GAME_ZONE; userId=580178847; kuaishou.live.web_st=ChRrdWFpc2hvdS5saXZlLndlYi5zdBKgAQ-E2wk--dxc2ehil5tQ5PZxSAAy_WocJekwLrvD4yIZxEo4y76RBkXONqX9xSLhyX5WOyhWdsXrwH-Nj0RTgM4csF_UwnsIclnnZT6WNO2tfB3fmNHvZHAIRtqDl6hXsS1x8vwgHCii65me0EiPh1LK855TafkNx9A7wrTma6XPlCPL8L1GNKTjtiQB2L7Y0kuOp-l0zVzIidHhFvGXfFEaEgCrAu8bFEUPixNgRvVq1Nb0ZSIghKUYro_uVYFHrcP3wJ93ACmwb-oSRGVtcLhGUdaI43UoBTAB; kuaishou.live.web_ph=c371edbd1ecfdc90fb87ae8c0c8738ec6f3f; userId=580178847; kuaishou.live.bfb1s=477cb0011daca84b36b3a4676857e5a1'
);
$html_str = get_curl($live_url,$headers);
$cookies = 'your cookie'; # 任意快手直播间页面的cookie
$html_str = get_curl($live_url,$headers,$cookies);
preg_match('/__INITIAL_STATE__=(.*?);\(function/', $html_str, $matches);
$json_data = json_decode($matches[1], true);
$liveroom = $json_data['liveroom'];
$live_title=$liveroom['liveStream']['caption'];
$anchor_name = $liveroom['author']['name'];
$play_list= $json_data['liveroom']['playList'][0];
$live_title=$play_list['liveStream']['caption'];
$anchor_name = $play_list['author']['name'];
# 获取直播间状态
$status = $liveroom['isLiving']; # 直播状态True是正在直播.False是未开播
$status = $play_list['isLiving']; # 直播状态True是正在直播.False是未开播
if (!$status) {
$data=["live_status"=>'主播未开播或者直播已经结束!'];
}else{
$stream_data=$liveroom['liveStream']['playUrls'][0]['adaptationSet']['representation'];
$stream_data=$play_list['liveStream']['playUrls'][0]['adaptationSet']['representation'];
$data=["title"=>$live_title,'stream'=>$stream_data];
}
@ -252,7 +250,3 @@ function get_curl($url,$headers=array(),$cookies=''){

View File

@ -7,7 +7,6 @@
同一时间访问网络的线程数 = 3
循环时间(秒) = 60
排队读取网址时间(秒) = 0
是否显示直播地址 =
是否显示循环秒数 =
ts格式分段录制是否开启 =
视频分段大小(兆) = 1000

424
main.py
View File

@ -4,12 +4,11 @@
Author: Hmily
Github: https://github.com/ihmily
Date: 2023-07-17 23:52:05
Update: 2023-09-07 10:56:25
Update: 2023-09-19 00:16:38
Copyright (c) 2023 by Hmily, All Rights Reserved.
Function: Record live stream video.
"""
import functools
import random
import os
@ -27,7 +26,7 @@ from web_rid import *
from msg_push import *
# 版本号
version = "v1.0.8"
version = "v1.0.9"
platforms = "抖音|Tiktok|快手|虎牙|斗鱼|YY|B站"
# --------------------------log日志-------------------------------------
@ -50,6 +49,7 @@ recording = set()
unrecording = set()
warning_count = 0
max_request = 0
Monitoring = 0
runing_list = []
url_tuples_list = []
textNoRepeatUrl = []
@ -80,10 +80,11 @@ def trace_error_decorator(func):
return func(*args, **kwargs)
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}"
error_info = f"错误信息: type: {type(e).__name__}, {str(e)} in function {func.__name__} at line: {error_line}"
print(error_info)
logger.warning(error_info)
return []
return wrapper
@ -134,7 +135,7 @@ def display_info():
else:
start5_time = now_time
except Exception as e:
print("错误信息644:" + str(e) + "\r\n发生错误的行数: " + str(e.__traceback__.tb_lineno))
print("错误信息:" + str(e) + "\r\n发生错误的行数: " + str(e.__traceback__.tb_lineno))
logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
@ -249,201 +250,242 @@ def change_max_connect():
@trace_error_decorator
def get_douyin_stream_url(json_data):
# TODO: 获取抖音直播源地址
room_info = json_data['roomInfo']
anchor_name = room_info['anchor']['nickname']
status = 4
# 获取直播间状态
if 'room' in room_info:
status = room_info["room"]["status"] # 直播状态2是正在直播.4是未开播
room_info = json_data.get('roomInfo', {})
anchor_name = room_info.get('anchor', {}).get('nickname', '')
if status == 4:
data = [anchor_name, False, '', '']
else:
result = {
"anchor_name": anchor_name,
"is_live": False,
}
status = room_info.get('room', {}).get("status", 4) # 直播状态 2 是正在直播、4 是未开播
if status == 2:
stream_url = room_info['room']['stream_url']
# flv视频流链接
flv_url_list = stream_url['flv_pull_url']
# m3u8视频流链接
m3u8_url_list = stream_url['hls_pull_url_map']
# origin蓝光1080P、720超清hd、720高清sd、540标清ld
if video_quality == "原画" or video_quality == "蓝光":
m3u8_url = m3u8_url_list["FULL_HD1"]
flv_url = flv_url_list["FULL_HD1"]
elif video_quality == "超清":
m3u8_url = m3u8_url_list["HD1"]
flv_url = flv_url_list["HD1"]
elif video_quality == "高清":
m3u8_url = m3u8_url_list["SD1"]
flv_url = flv_url_list["SD1"]
elif video_quality == "标清":
m3u8_url = m3u8_url_list["SD2"]
flv_url = flv_url_list["SD2"]
video_qualities = {
"原画": "FULL_HD1",
"蓝光": "FULL_HD1",
"超清": "HD1",
"高清": "SD1",
"标清": "SD2",
}
data = [anchor_name, True, m3u8_url, flv_url]
return data
quality_key = video_qualities.get(video_quality)
if quality_key:
m3u8_url = m3u8_url_list.get(quality_key)
flv_url = flv_url_list.get(quality_key)
result['m3u8_url'] = m3u8_url
result['flv_url'] = flv_url
result['is_live'] = True
result['record_url'] = m3u8_url # 使用 m3u8 链接进行录制
return result
@trace_error_decorator
def get_tiktok_stream_url(json_data):
# TODO: 获取Tiktok直播源地址
live_room = json_data['LiveRoom']['liveRoomUserInfo']
anchor_name = live_room['user']['nickname']
status = live_room['user']["status"]
# 直播状态2是正在直播.4是未开播
if status == 4:
data = [anchor_name, False, '', '']
else:
# 画质从高到低origin>uhd>sd>sd>ld
# {origin:'原画质或蓝光',uhd:'1080P或720P',sd:'540P或480P',ld:'360P标清'}
# 上面画质对应只是一般情况,具体情况有可能不一样 可以看对应画质的sdk_params参数里面有如1080P等参数
stream_data = live_room['liveRoom']['streamData']['pull_data']['stream_data']
stream_data = json.loads(stream_data)['data']
if video_quality == "原画" or video_quality == "蓝光":
m3u8_url = stream_data["origin"]['main']['hls']
flv_url = stream_data["origin"]['main']['flv']
elif video_quality == "超清":
m3u8_url = stream_data["uhd"]['main']['hls']
flv_url = stream_data["uhd"]['main']['flv']
elif video_quality == "高清":
m3u8_url = stream_data["sd"]['main']['hls']
flv_url = stream_data["sd"]['main']['flv']
elif video_quality == "标清":
m3u8_url = stream_data["ld"]['main']['hls']
flv_url = stream_data["ld"]['main']['flv']
# 注意这里要将链接改为http协议否则无法使用ffmpeg录制原因是代理大都是http
m3u8_url = re.sub("https", "http", m3u8_url)
flv_url = re.sub("https", "http", flv_url)
data = [anchor_name, True, m3u8_url, flv_url]
return data
def get_video_quality_url(stream_data, quality_key):
return {
'hls': re.sub("https", "http", stream_data[quality_key]['main']['hls']),
'flv': re.sub("https", "http", stream_data[quality_key]['main']['flv']),
}
live_room = json_data.get('LiveRoom', {}).get('liveRoomUserInfo', {})
user = live_room.get('user', {})
anchor_name = user.get('nickname', '')
status = user.get("status", 4)
result = {
"anchor_name": anchor_name,
"is_live": False,
}
if status == 2:
stream_data = live_room.get('liveRoom', {}).get('streamData', {}).get('pull_data', {}).get('stream_data', '{}')
stream_data = json.loads(stream_data).get('data', {})
video_qualities = {
"原画": "origin",
"蓝光": "origin",
"超清": "uhd",
"高清": "sd",
"标清": "ld",
}
quality_key = video_qualities.get(video_quality)
if quality_key:
video_quality_urls = get_video_quality_url(stream_data,quality_key)
result['flv_url'] = video_quality_urls['flv']
result['m3u8_url'] = video_quality_urls['hls']
result['is_live'] = True
if result['m3u8_url']:
result['record_url'] = video_quality_urls['hls']
else:
result['record_url'] = video_quality_urls['flv']
return result
@trace_error_decorator
def get_kuaishou_stream_url(json_data):
# TODO: 获取快手直播源地址
live_room = json_data['liveroom']
anchor_name = live_room['author']['name']
# 获取直播间状态
status = live_room['isLiving']
# 直播状态True是正在直播.False是未开播
if not status:
data = [anchor_name, False, '', '']
else:
stream_data = live_room['liveStream']['playUrls'][0]['adaptationSet']['representation']
# stream_data数组中索引从小到大分别是高清、超清、蓝光4M、蓝光8M (每个直播间不一样)
quality_list = [i for i in range(len(stream_data))][::-1]
while len(quality_list) < 4:
quality_list.append(quality_list[-1])
live_room = json_data.get('liveroom', {}).get('playList')[0]
anchor_name = live_room.get('author', {}).get('name', '')
if video_quality == "原画" or video_quality == "蓝光":
flv_url = stream_data[quality_list[0]]['url']
elif video_quality == "超清":
flv_url = stream_data[quality_list[1]]['url']
elif video_quality == "高清":
flv_url = stream_data[quality_list[2]]['url']
elif video_quality == "标清":
flv_url = stream_data[quality_list[3]]['url']
result = {
"anchor_name": anchor_name,
"is_live": False,
}
data = [anchor_name, True, flv_url, flv_url] # 快手只有flv视频流
return data
status = live_room.get('isLiving', False)
if status:
stream_data = live_room.get('liveStream', {}).get('playUrls', [{}])[0].get('adaptationSet', {}).get(
'representation', [])
if stream_data:
quality_list = [i for i in range(len(stream_data))][::-1]
while len(quality_list) < 4:
quality_list.append(quality_list[-1])
video_quality_options = {
"原画": quality_list[0],
"蓝光": quality_list[0],
"超清": quality_list[1],
"高清": quality_list[2],
"标清": quality_list[3]
}
if video_quality not in video_quality_options:
raise ValueError(
f"Invalid video quality. Available options are: {', '.join(video_quality_options.keys())}")
flv_url = stream_data[video_quality_options[video_quality]]['url']
result['flv_url'] = flv_url
result['is_live'] = True
result['record_url'] = flv_url # 快手只有flv视频流
return result
@trace_error_decorator
def get_huya_stream_url(json_data):
# TODO: 获取虎牙直播源地址
game_live_info = json_data['data'][0]['gameLiveInfo']
stream_info_list = json_data['data'][0]['gameStreamInfoList']
anchor_name = game_live_info['nick']
# 如果stream_info_list 值为空,则未开直播
if len(stream_info_list) == 0:
data = [anchor_name, False, '', '']
else:
# stream_info_list 索引从小到大 分别是'al', 'tx', 'hw', 'hs'四种cdn线路
# 默认使用第一种 即host链接开头为al的cdn
game_live_info = json_data.get('data', [])[0].get('gameLiveInfo', {})
stream_info_list = json_data.get('data', [])[0].get('gameStreamInfoList', [])
anchor_name = game_live_info.get('nick', '')
result = {
"anchor_name": anchor_name,
"is_live": False,
}
if stream_info_list:
select_cdn = stream_info_list[0]
s_flv_url = select_cdn['sFlvUrl']
s_stream_name = select_cdn['sStreamName']
s_flv_url_suffix = select_cdn['sFlvUrlSuffix']
s_hls_url = select_cdn['sHlsUrl']
s_hls_url_suffix = select_cdn['sHlsUrlSuffix']
s_flv_anti_code = select_cdn['sFlvAntiCode']
quality_list = s_flv_anti_code.split('&exsphd=')
s_flv_url = select_cdn.get('sFlvUrl')
s_stream_name = select_cdn.get('sStreamName')
s_flv_url_suffix = select_cdn.get('sFlvUrlSuffix')
s_hls_url = select_cdn.get('sHlsUrl')
s_hls_url_suffix = select_cdn.get('sHlsUrlSuffix')
s_flv_anti_code = select_cdn.get('sFlvAntiCode')
flv_url = f'{s_flv_url}/{s_stream_name}.{s_flv_url_suffix}?{s_flv_anti_code}&ratio='
m3u8_url = f'{s_hls_url}/{s_stream_name}.{s_hls_url_suffix}?{s_flv_anti_code}&ratio='
if len(quality_list) != 1:
quality_list = s_flv_anti_code.split('&exsphd=')
if len(quality_list) > 1:
pattern = r"(?<=264_)\d+"
quality_list = [x for x in re.findall(pattern, quality_list[1])][::-1]
while len(quality_list) < 4:
quality_list.append(quality_list[-1])
if video_quality == "原画" or video_quality == "蓝光":
flv_url = flv_url + str(quality_list[0])
m3u8_url = m3u8_url + str(quality_list[0])
elif video_quality == "超清":
flv_url = flv_url + str(quality_list[1])
m3u8_url = m3u8_url + str(quality_list[1])
elif video_quality == "高清":
flv_url = flv_url + str(quality_list[2])
m3u8_url = m3u8_url + str(quality_list[2])
elif video_quality == "标清":
flv_url = flv_url + str(quality_list[3])
m3u8_url = m3u8_url + str(quality_list[3])
data = [anchor_name, True, flv_url, m3u8_url] # 虎牙目前只能使用flv视频流录制
return data
video_quality_options = {
"原画": quality_list[0],
"蓝光": quality_list[0],
"超清": quality_list[1],
"高清": quality_list[2],
"标清": quality_list[3]
}
if video_quality not in video_quality_options:
raise ValueError(
f"Invalid video quality. Available options are: {', '.join(video_quality_options.keys())}")
flv_url = flv_url + str(video_quality_options[video_quality])
m3u8_url = m3u8_url + str(video_quality_options[video_quality])
result['flv_url'] = flv_url
result['m3u8_url'] = m3u8_url
result['is_live'] = True
result['record_url'] = flv_url # 虎牙使用flv视频流录制
return result
@trace_error_decorator
def get_douyu_stream_url(json_data):
# TODO: 获取斗鱼直播源地址
def get_douyu_stream_url(json_data, cookies):
# 获取斗鱼直播源地址
room_info = json_data.get('pageContext',json_data)['pageProps']['room']['roomInfo']['roomInfo']
anchor_name = room_info['nickname']
status = room_info['isLive']
video_quality_options = {
"原画": '0',
"蓝光": '0',
"超清": '3',
"高清": '2',
"标清": '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:
data = [anchor_name, False, '', '']
else:
# rate: 1流畅2高清3超清4蓝光4M0蓝光8M或10M
if status == 1:
rid = str(room_info['rid'])
if video_quality == "原画" or video_quality == "蓝光":
flv_url = get_douyu_stream_data(rid, rate='0')['data']['url']
elif video_quality == "超清":
flv_url = get_douyu_stream_data(rid, rate='3')['data']['url']
elif video_quality == "高清":
flv_url = get_douyu_stream_data(rid, rate='2')['data']['url']
elif video_quality == "标清":
flv_url = get_douyu_stream_data(rid, rate='1')['data']['url']
data = [anchor_name, True, flv_url, flv_url] # 斗鱼目前只能使用flv视频流录制
return data
rate = video_quality_options.get(video_quality, '0') # 默认为原画
flv_data = get_douyu_stream_data(rid, rate, cookies)
flv_url = flv_data['data']['url']
result['flv_url'] = flv_url
result['is_live'] = True
result['record_url'] = flv_url # 斗鱼目前只能使用flv视频流录制
return result
@trace_error_decorator
def get_yy_stream_url(json_data):
# TODO: 获取YY直播源地址
anchor_name = json_data['anchor_name']
if 'avp_info_res' not in json_data:
data = [anchor_name, False, '', '']
else:
anchor_name = json_data.get('anchor_name', '')
result = {
"anchor_name": anchor_name,
"is_live": False,
}
if 'avp_info_res' in json_data:
stream_line_addr = json_data['avp_info_res']['stream_line_addr']
# 获取最后一个键的值
cdn_info = list(stream_line_addr.values())[0]
stream_url = cdn_info['cdn_info']['url'] # 清晰度暂时默认高清
data = [anchor_name, True, stream_url, stream_url] # 斗鱼目前只能使用flv视频流录制
return data
flv_url = cdn_info['cdn_info']['url'] # 清晰度暂时默认高清
result['flv_url'] = flv_url
result['is_live'] = True
result['record_url'] = flv_url
return result
@trace_error_decorator
def get_bilibili_stream_url(json_data):
# TODO: 获取B站直播源地址
anchor_name = json_data['roomInfoRes']['data']['anchor_info']['base_info']['uname']
anchor_name = json_data.get('roomInfoRes', {}).get('data', {}).get('anchor_info', {}).get('base_info', {}).get(
'uname', '')
playurl_info = json_data['roomInitRes']['data']['playurl_info']
if not playurl_info:
data = [anchor_name, False, '', '']
else:
result = {
"anchor_name": anchor_name,
"is_live": False,
}
if playurl_info:
def get_url(m, n):
format_list = ['.flv', '.m3u8']
# 字典中的键就是qn其中qn=30000为杜比 20000为4K 10000为原画 400蓝光 250超清 150高清qn=0是默认画质
@ -476,16 +518,24 @@ def get_bilibili_stream_url(json_data):
elif video_quality == "标清":
flv_url = get_url(0, 3)
m3u8_url = get_url(1, 3)
data = [anchor_name, True, m3u8_url, flv_url] # B站使用m3u8链接进行录制
return data
else:
flv_url = get_url(0, 0)
m3u8_url = get_url(1, 0)
result['flv_url'] = flv_url
result['m3u8_url'] = m3u8_url
result['is_live'] = True
result['record_url'] = m3u8_url # B站使用m3u8链接进行录制
return result
def start_record(line, count_variable=-1):
def start_record(url_tuple, count_variable=-1):
global warning_count
global video_save_path
global live_list
global not_record_list
global recording_time_list
while True:
try:
record_finished = False
@ -493,7 +543,6 @@ def start_record(line, count_variable=-1):
Runonce = False
is_long_url = False
count_time = time.time()
url_tuple = line
record_url = url_tuple[0]
anchor_name = url_tuple[1]
print("\r运行新线程,传入地址 " + record_url)
@ -524,7 +573,7 @@ def start_record(line, count_variable=-1):
with semaphore:
if use_proxy:
if global_proxy or proxy_addr != '':
json_data = get_tiktok_stream_data(record_url, proxy_addr,tiktok_cookie)
json_data = get_tiktok_stream_data(record_url, proxy_addr, tiktok_cookie)
port_info = get_tiktok_stream_url(json_data)
elif record_url.find("https://live.kuaishou.com/") > -1:
@ -534,31 +583,30 @@ def start_record(line, count_variable=-1):
elif record_url.find("https://www.huya.com/") > -1:
with semaphore:
json_data = get_huya_stream_data(record_url,hy_cookie)
json_data = get_huya_stream_data(record_url, hy_cookie)
port_info = get_huya_stream_url(json_data)
elif record_url.find("https://www.douyu.com/") > -1:
with semaphore:
json_data = get_douyu_info_data(record_url)
port_info = get_douyu_stream_url(json_data,douyu_cookie)
port_info = get_douyu_stream_url(json_data, douyu_cookie)
elif record_url.find("https://www.yy.com/") > -1:
with semaphore:
json_data = get_yy_stream_data(record_url,yy_cookie)
json_data = get_yy_stream_data(record_url, yy_cookie)
port_info = get_yy_stream_url(json_data)
elif record_url.find("https://live.bilibili.com/") > -1:
with semaphore:
json_data = get_bilibili_stream_data(record_url,bili_cookie)
json_data = get_bilibili_stream_data(record_url, bili_cookie)
port_info = get_bilibili_stream_url(json_data)
# print("端口信息:" + str(port_info))
# port_info=['主播名','状态码','m3u8地址','flv地址']
if len(port_info) != 4:
print(f'序号{count_variable} 网址内容获取失败,进行重试中...获取失败的地址是:{line}')
anchor_name = port_info.get("anchor_name", '')
if not anchor_name:
print(f'序号{count_variable} 网址内容获取失败,进行重试中...获取失败的地址是:{url_tuple}')
warning_count += 1
else:
anchor_name = port_info[0]
anchor_name = re.sub(rstr, "_", anchor_name) # 过滤不能作为文件名的字符,替换为下划线
record_name = f'序号{count_variable} {anchor_name}'
@ -574,7 +622,7 @@ def start_record(line, count_variable=-1):
name_list.append(f'{record_url}|{record_url},主播: {anchor_name.strip()}')
Runonce = True
if port_info[1] is False:
if port_info['is_live'] is False:
print(f"{record_name} 等待直播... ")
else:
content = f"{record_name} 正在直播中..."
@ -582,25 +630,13 @@ def start_record(line, count_variable=-1):
# 推送通知
if live_status_push != '':
if '微信' in live_status_push:
xizhi(xizhi_api_url,content)
xizhi(xizhi_api_url, content)
if '钉钉' in live_status_push:
dingtalk(dingtalk_api_url, content, dingtalk_phone_num)
# 是否显示直播地址
if video_m3u8:
if video_save_type == "FLV":
print(f"{port_info[0]} 直播地址为:{port_info[3]}")
else:
print(f"{port_info[0]} 直播地址为:{port_info[2]}")
real_url = port_info[2] # 默认使用第一种地址进行下载
real_url = port_info['record_url']
full_path = f'{default_path}/{anchor_name}'
if real_url == "":
print('解析错误,直播间视频流未找到...')
pass
else:
if real_url != "":
live_list.append(anchor_name)
now = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
try:
@ -620,8 +656,7 @@ def start_record(line, count_variable=-1):
if not os.path.exists(full_path):
print("保存路径不存在,不能生成录制.请避免把本程序放在c盘,桌面,下载文件夹,qq默认传输目录.请重新检查设置")
video_save_path = ""
print(f"因为配置文件的路径错误,本次录制在程序目录 {default_path}")
logger.warning("错误信息: 保存路径不存在,不能生成录制.请避免把本程序放在c盘,桌面,下载文件夹,qq默认传输目录.请重新检查设置")
ffmpeg_command = [
ffmpeg_path, "-y",
@ -678,14 +713,17 @@ def start_record(line, count_variable=-1):
try:
# “port_info[3]”对应的是flv地址使用老方法下载直接请求下载flv只能是下载flv流的。
real_url = port_info[3]
real_url = port_info['flv_url']
_filepath, _ = urllib.request.urlretrieve(real_url, full_path + '/' + filename)
record_finished = True
record_finished_2 = True
count_time = time.time()
except:
except Exception as e:
print('\r' + time.strftime('%Y-%m-%d %H:%M:%S ') + anchor_name + ' 未开播')
logger.warning(
"错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
elif video_save_type == "MKV":
@ -852,8 +890,8 @@ def start_record(line, count_variable=-1):
threading.Thread(target=converts_m4a, args=(file,)).start()
except subprocess.CalledProcessError as e:
# logging.warning(str(e.output))
# logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno))
logging.warning(str(e.output))
logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno))
break
@ -913,7 +951,7 @@ def start_record(line, count_variable=-1):
except Exception as e:
print(
"错误信息644:" + str(e) + "\r\n读取的地址为: " + str(record_url) + " 发生错误的行数: " + str(
"错误信息:" + str(e) + "\r\n读取的地址为: " + str(record_url) + " 发生错误的行数: " + str(
e.__traceback__.tb_lineno))
logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
warning_count += 1
@ -942,14 +980,13 @@ def start_record(line, count_variable=-1):
# 这里是正常循环
while x:
x = x - 1
# print('\r循环等待%d秒 '%x)
if loop_time:
print('\r' + anchor_name + ' 循环等待%d' % x, end="")
time.sleep(1)
if loop_time:
print('\r检测直播间中...', end="")
except Exception as e:
print("错误信息644:" + str(e) + "\r\n发生错误的行数: " + str(e.__traceback__.tb_lineno))
print("错误信息:" + str(e) + "\r\n发生错误的行数: " + str(e.__traceback__.tb_lineno))
logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
print("线程崩溃2秒后重试.错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
warning_count += 1
@ -1031,7 +1068,6 @@ if not os.path.exists('./config'):
# 备份配置
t3 = threading.Thread(target=backup_file_start, args=(), daemon=True)
t3.start()
Monitoring = 0
# 录制tiktok时如果开启了电脑全局/规则代理,可以不用再在配置文件中填写代理地址
# 但强烈建议还是配置一下代理地址,否则非常不稳定
@ -1094,7 +1130,6 @@ while True:
semaphore = threading.Semaphore(max_request)
delay_default = int(read_config_value(config, '录制设置', '循环时间(秒)', 60))
local_delay_default = int(read_config_value(config, '录制设置', '排队读取网址时间(秒)', 0))
video_m3u8 = read_config_value(config, '录制设置', '是否显示直播地址', "")
loop_time = read_config_value(config, '录制设置', '是否显示循环秒数', "")
Splitvideobysize = read_config_value(config, '录制设置', 'TS格式分段录制是否开启', "")
Splitsize = int(read_config_value(config, '录制设置', '视频分段大小(兆)', '1000'))
@ -1151,7 +1186,6 @@ while True:
"": False
}
use_proxy = options.get(use_proxy, False) # 是否使用代理ip
video_m3u8 = options.get(video_m3u8, False) # 是否显示直播地址
loop_time = options.get(loop_time, False) # 是否显示循环秒数
Splitvideobysize = options.get(Splitvideobysize, False) # 这里是控制TS是否分段
create_time_file = options.get(create_time_file, False) # 这里控制是否生成时间文件
@ -1198,7 +1232,6 @@ while True:
if replacewords[0] != replacewords[1]:
update_file(url_config_file, replacewords[0], replacewords[1])
# print('url_tuples_list',url_tuples_list)
if len(url_tuples_list) > 0:
textNoRepeatUrl = list(set(url_tuples_list))
if len(textNoRepeatUrl) > 0:
@ -1221,7 +1254,7 @@ while True:
first_start = False
except Exception as e:
print("错误信息644:" + str(e) + "\r\n发生错误的行数: " + str(e.__traceback__.tb_lineno))
print("错误信息:" + str(e) + "\r\n发生错误的行数: " + str(e.__traceback__.tb_lineno))
logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
if firstRunOtherLine:
@ -1233,4 +1266,7 @@ while True:
firstRunOtherLine = False
# 总体循环3s
time.sleep(3)
time.sleep(3)

View File

@ -4,7 +4,7 @@
Author: Hmily
Github:https://github.com/ihmily
Date: 2023-07-15 23:15:00
Update: 2023-09-14 00:27:55
Update: 2023-09-17 16:59:01
Copyright (c) 2023 by Hmily, All Rights Reserved.
Function: Get live stream data.
"""
@ -25,14 +25,14 @@ opener = urllib.request.build_opener(no_proxy_handler)
# 直接选择从网页HTML中获取直播间数据
def get_douyin_stream_data(url, cookies=''):
cookie = 'ttwid=1%7CB1qls3GdnZhUov9o2NxOMxxYS2ff6OSvEWbv0ytbES4%7C1680522049%7C280d802d6d478e3e78d0c807f7c487e7ffec0ae4e5fdd6a0fe74c3c6af149511; my_rd=1; passport_csrf_token=3ab34460fa656183fccfb904b16ff742; passport_csrf_token_default=3ab34460fa656183fccfb904b16ff742; d_ticket=9f562383ac0547d0b561904513229d76c9c21; n_mh=hvnJEQ4Q5eiH74-84kTFUyv4VK8xtSrpRZG1AhCeFNI; store-region=cn-fj; store-region-src=uid; LOGIN_STATUS=1; __security_server_data_status=1; FORCE_LOGIN=%7B%22videoConsumedRemainSeconds%22%3A180%7D; pwa2=%223%7C0%7C3%7C0%22; download_guide=%223%2F20230729%2F0%22; volume_info=%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Afalse%2C%22volume%22%3A0.6%7D; strategyABtestKey=%221690824679.923%22; stream_recommend_feed_params=%22%7B%5C%22cookie_enabled%5C%22%3Atrue%2C%5C%22screen_width%5C%22%3A1536%2C%5C%22screen_height%5C%22%3A864%2C%5C%22browser_online%5C%22%3Atrue%2C%5C%22cpu_core_num%5C%22%3A8%2C%5C%22device_memory%5C%22%3A8%2C%5C%22downlink%5C%22%3A10%2C%5C%22effective_type%5C%22%3A%5C%224g%5C%22%2C%5C%22round_trip_time%5C%22%3A150%7D%22; VIDEO_FILTER_MEMO_SELECT=%7B%22expireTime%22%3A1691443863751%2C%22type%22%3Anull%7D; home_can_add_dy_2_desktop=%221%22; __live_version__=%221.1.1.2169%22; device_web_cpu_core=8; device_web_memory_size=8; xgplayer_user_id=346045893336; csrf_session_id=2e00356b5cd8544d17a0e66484946f28; odin_tt=724eb4dd23bc6ffaed9a1571ac4c757ef597768a70c75fef695b95845b7ffcd8b1524278c2ac31c2587996d058e03414595f0a4e856c53bd0d5e5f56dc6d82e24004dc77773e6b83ced6f80f1bb70627; __ac_nonce=064caded4009deafd8b89; __ac_signature=_02B4Z6wo00f01HLUuwwAAIDBh6tRkVLvBQBy9L-AAHiHf7; ttcid=2e9619ebbb8449eaa3d5a42d8ce88ec835; webcast_leading_last_show_time=1691016922379; webcast_leading_total_show_times=1; webcast_local_quality=sd; live_can_add_dy_2_desktop=%221%22; msToken=1JDHnVPw_9yTvzIrwb7cQj8dCMNOoesXbA_IooV8cezcOdpe4pzusZE7NB7tZn9TBXPr0ylxmv-KMs5rqbNUBHP4P7VBFUu0ZAht_BEylqrLpzgt3y5ne_38hXDOX8o=; msToken=jV_yeN1IQKUd9PlNtpL7k5vthGKcHo0dEh_QPUQhr8G3cuYv-Jbb4NnIxGDmhVOkZOCSihNpA2kvYtHiTW25XNNX_yrsv5FN8O6zm3qmCIXcEe0LywLn7oBO2gITEeg=; tt_scid=mYfqpfbDjqXrIGJuQ7q-DlQJfUSG51qG.KUdzztuGP83OjuVLXnQHjsz-BRHRJu4e986'
if cookies != '':
cookie = cookies
if cookies == '':
cookies = 'ttwid=1%7CB1qls3GdnZhUov9o2NxOMxxYS2ff6OSvEWbv0ytbES4%7C1680522049%7C280d802d6d478e3e78d0c807f7c487e7ffec0ae4e5fdd6a0fe74c3c6af149511; my_rd=1; passport_csrf_token=3ab34460fa656183fccfb904b16ff742; passport_csrf_token_default=3ab34460fa656183fccfb904b16ff742; d_ticket=9f562383ac0547d0b561904513229d76c9c21; n_mh=hvnJEQ4Q5eiH74-84kTFUyv4VK8xtSrpRZG1AhCeFNI; store-region=cn-fj; store-region-src=uid; LOGIN_STATUS=1; __security_server_data_status=1; FORCE_LOGIN=%7B%22videoConsumedRemainSeconds%22%3A180%7D; pwa2=%223%7C0%7C3%7C0%22; download_guide=%223%2F20230729%2F0%22; volume_info=%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Afalse%2C%22volume%22%3A0.6%7D; strategyABtestKey=%221690824679.923%22; stream_recommend_feed_params=%22%7B%5C%22cookie_enabled%5C%22%3Atrue%2C%5C%22screen_width%5C%22%3A1536%2C%5C%22screen_height%5C%22%3A864%2C%5C%22browser_online%5C%22%3Atrue%2C%5C%22cpu_core_num%5C%22%3A8%2C%5C%22device_memory%5C%22%3A8%2C%5C%22downlink%5C%22%3A10%2C%5C%22effective_type%5C%22%3A%5C%224g%5C%22%2C%5C%22round_trip_time%5C%22%3A150%7D%22; VIDEO_FILTER_MEMO_SELECT=%7B%22expireTime%22%3A1691443863751%2C%22type%22%3Anull%7D; home_can_add_dy_2_desktop=%221%22; __live_version__=%221.1.1.2169%22; device_web_cpu_core=8; device_web_memory_size=8; xgplayer_user_id=346045893336; csrf_session_id=2e00356b5cd8544d17a0e66484946f28; odin_tt=724eb4dd23bc6ffaed9a1571ac4c757ef597768a70c75fef695b95845b7ffcd8b1524278c2ac31c2587996d058e03414595f0a4e856c53bd0d5e5f56dc6d82e24004dc77773e6b83ced6f80f1bb70627; __ac_nonce=064caded4009deafd8b89; __ac_signature=_02B4Z6wo00f01HLUuwwAAIDBh6tRkVLvBQBy9L-AAHiHf7; ttcid=2e9619ebbb8449eaa3d5a42d8ce88ec835; webcast_leading_last_show_time=1691016922379; webcast_leading_total_show_times=1; webcast_local_quality=sd; live_can_add_dy_2_desktop=%221%22; msToken=1JDHnVPw_9yTvzIrwb7cQj8dCMNOoesXbA_IooV8cezcOdpe4pzusZE7NB7tZn9TBXPr0ylxmv-KMs5rqbNUBHP4P7VBFUu0ZAht_BEylqrLpzgt3y5ne_38hXDOX8o=; msToken=jV_yeN1IQKUd9PlNtpL7k5vthGKcHo0dEh_QPUQhr8G3cuYv-Jbb4NnIxGDmhVOkZOCSihNpA2kvYtHiTW25XNNX_yrsv5FN8O6zm3qmCIXcEe0LywLn7oBO2gITEeg=; tt_scid=mYfqpfbDjqXrIGJuQ7q-DlQJfUSG51qG.KUdzztuGP83OjuVLXnQHjsz-BRHRJu4e986'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
'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',
'Referer': 'https://live.douyin.com/',
'Cookie': cookie
'Cookie': cookies
}
# response = requests.get(url, headers=headers)
@ -53,12 +53,11 @@ def get_douyin_stream_data(url, cookies=''):
def get_tiktok_stream_data(url, proxy_addr, cookies=''):
cookie = 'ttwid=1%7CM-rF193sJugKuNz2RGNt-rh6pAAR9IMceUSzlDnPCNI%7C1683274418%7Cf726d4947f2fc37fecc7aeb0cdaee52892244d04efde6f8a8edd2bb168263269; tiktok_webapp_theme=light; tt_chain_token=VWkygAWDlm1cFg/k8whmOg==; passport_csrf_token=6e422c5a7991f8cec7033a8082921510; passport_csrf_token_default=6e422c5a7991f8cec7033a8082921510; d_ticket=f8c267d4af4523c97be1ccb355e9991e2ae06; odin_tt=320b5f386cdc23f347be018e588873db7f7aea4ea5d1813681c3fbc018ea025dde957b94f74146dbc0e3612426b865ccb95ec8abe4ee36cca65f15dbffec0deff7b0e69e8ea536d46e0f82a4fc37d211; cmpl_token=AgQQAPNSF-RO0rT04baWtZ0T_jUjl4fVP4PZYM2QPw; uid_tt=319b558dbba684bb1557206c92089cd113a875526a89aee30595925d804b81c7; uid_tt_ss=319b558dbba684bb1557206c92089cd113a875526a89aee30595925d804b81c7; sid_tt=ad5e736f4bedb2f6d42ccd849e706b1d; sessionid=ad5e736f4bedb2f6d42ccd849e706b1d; sessionid_ss=ad5e736f4bedb2f6d42ccd849e706b1d; store-idc=useast5; store-country-code=us; store-country-code-src=uid; tt-target-idc=useast5; tt-target-idc-sign=qXNk0bb1pDQ0FbCNF120Pl9WWMLZg9Edv5PkfyCbS4lIk5ieW5tfLP7XWROnN0mEaSlc5hg6Oji1pF-yz_3ZXnUiNMrA9wNMPvI6D9IFKKVmq555aQzwPIGHv0aQC5dNRgKo5Z5LBkgxUMWEojTKclq2_L8lBciw0IGdhFm_XyVJtbqbBKKgybGDLzK8ZyxF4Jl_cYRXaDlshZjc38JdS6wruDueRSHe7YvNbjxCnApEFUv-OwJANSPU_4rvcqpVhq3JI2VCCfw-cs_4MFIPCDOKisk5EhAo2JlHh3VF7_CLuv80FXg_7ZqQ2pJeMOog294rqxwbbQhl3ATvjQV_JsWyUsMd9zwqecpylrPvtySI2u1qfoggx1owLrrUynee1R48QlanLQnTNW_z1WpmZBgVJqgEGLwFoVOmRzJuFFNj8vIqdjM2nDSdWqX8_wX3wplohkzkPSFPfZgjzGnQX28krhgTytLt7BXYty5dpfGtsdb11WOFHM6MZ9R9uLVB; sid_guard=ad5e736f4bedb2f6d42ccd849e706b1d%7C1690990657%7C15525213%7CMon%2C+29-Jan-2024+08%3A11%3A10+GMT; sid_ucp_v1=1.0.0-KGM3YzgwYjZhODgyYWI1NjIwNTA0NjBmOWUxMGRhMjIzYTI2YjMxNDUKGAiqiJ30keKD5WQQwfCppgYYsws4AkDsBxAEGgd1c2Vhc3Q1IiBhZDVlNzM2ZjRiZWRiMmY2ZDQyY2NkODQ5ZTcwNmIxZA; ssid_ucp_v1=1.0.0-KGM3YzgwYjZhODgyYWI1NjIwNTA0NjBmOWUxMGRhMjIzYTI2YjMxNDUKGAiqiJ30keKD5WQQwfCppgYYsws4AkDsBxAEGgd1c2Vhc3Q1IiBhZDVlNzM2ZjRiZWRiMmY2ZDQyY2NkODQ5ZTcwNmIxZA; tt_csrf_token=dD0EIH8q-pe3qDQsCyyD1jLN6KizJDRjOEyk; __tea_cache_tokens_1988={%22_type_%22:%22default%22%2C%22user_unique_id%22:%227229608516049831425%22%2C%22timestamp%22:1683274422659}; ttwid=1%7CM-rF193sJugKuNz2RGNt-rh6pAAR9IMceUSzlDnPCNI%7C1694002151%7Cd89b77afc809b1a610661a9d1c2784d80ebef9efdd166f06de0d28e27f7e4efe; msToken=KfJAVZ7r9D_QVeQlYAUZzDFbc1Yx-nZz6GF33eOxgd8KlqvTg1lF9bMXW7gFV-qW4MCgUwnBIhbiwU9kdaSpgHJCk-PABsHCtTO5J3qC4oCTsrXQ1_E0XtbqiE4OVLZ_jdF1EYWgKNPT2SnwGkQ=; msToken=KfJAVZ7r9D_QVeQlYAUZzDFbc1Yx-nZz6GF33eOxgd8KlqvTg1lF9bMXW7gFV-qW4MCgUwnBIhbiwU9kdaSpgHJCk-PABsHCtTO5J3qC4oCTsrXQ1_E0XtbqiE4OVLZ_jdF1EYWgKNPT2SnwGkQ='
if cookies != '':
cookie = cookies
if cookies == '':
cookies = 'ttwid=1%7CM-rF193sJugKuNz2RGNt-rh6pAAR9IMceUSzlDnPCNI%7C1683274418%7Cf726d4947f2fc37fecc7aeb0cdaee52892244d04efde6f8a8edd2bb168263269; tiktok_webapp_theme=light; tt_chain_token=VWkygAWDlm1cFg/k8whmOg==; passport_csrf_token=6e422c5a7991f8cec7033a8082921510; passport_csrf_token_default=6e422c5a7991f8cec7033a8082921510; d_ticket=f8c267d4af4523c97be1ccb355e9991e2ae06; odin_tt=320b5f386cdc23f347be018e588873db7f7aea4ea5d1813681c3fbc018ea025dde957b94f74146dbc0e3612426b865ccb95ec8abe4ee36cca65f15dbffec0deff7b0e69e8ea536d46e0f82a4fc37d211; cmpl_token=AgQQAPNSF-RO0rT04baWtZ0T_jUjl4fVP4PZYM2QPw; uid_tt=319b558dbba684bb1557206c92089cd113a875526a89aee30595925d804b81c7; uid_tt_ss=319b558dbba684bb1557206c92089cd113a875526a89aee30595925d804b81c7; sid_tt=ad5e736f4bedb2f6d42ccd849e706b1d; sessionid=ad5e736f4bedb2f6d42ccd849e706b1d; sessionid_ss=ad5e736f4bedb2f6d42ccd849e706b1d; store-idc=useast5; store-country-code=us; store-country-code-src=uid; tt-target-idc=useast5; tt-target-idc-sign=qXNk0bb1pDQ0FbCNF120Pl9WWMLZg9Edv5PkfyCbS4lIk5ieW5tfLP7XWROnN0mEaSlc5hg6Oji1pF-yz_3ZXnUiNMrA9wNMPvI6D9IFKKVmq555aQzwPIGHv0aQC5dNRgKo5Z5LBkgxUMWEojTKclq2_L8lBciw0IGdhFm_XyVJtbqbBKKgybGDLzK8ZyxF4Jl_cYRXaDlshZjc38JdS6wruDueRSHe7YvNbjxCnApEFUv-OwJANSPU_4rvcqpVhq3JI2VCCfw-cs_4MFIPCDOKisk5EhAo2JlHh3VF7_CLuv80FXg_7ZqQ2pJeMOog294rqxwbbQhl3ATvjQV_JsWyUsMd9zwqecpylrPvtySI2u1qfoggx1owLrrUynee1R48QlanLQnTNW_z1WpmZBgVJqgEGLwFoVOmRzJuFFNj8vIqdjM2nDSdWqX8_wX3wplohkzkPSFPfZgjzGnQX28krhgTytLt7BXYty5dpfGtsdb11WOFHM6MZ9R9uLVB; sid_guard=ad5e736f4bedb2f6d42ccd849e706b1d%7C1690990657%7C15525213%7CMon%2C+29-Jan-2024+08%3A11%3A10+GMT; sid_ucp_v1=1.0.0-KGM3YzgwYjZhODgyYWI1NjIwNTA0NjBmOWUxMGRhMjIzYTI2YjMxNDUKGAiqiJ30keKD5WQQwfCppgYYsws4AkDsBxAEGgd1c2Vhc3Q1IiBhZDVlNzM2ZjRiZWRiMmY2ZDQyY2NkODQ5ZTcwNmIxZA; ssid_ucp_v1=1.0.0-KGM3YzgwYjZhODgyYWI1NjIwNTA0NjBmOWUxMGRhMjIzYTI2YjMxNDUKGAiqiJ30keKD5WQQwfCppgYYsws4AkDsBxAEGgd1c2Vhc3Q1IiBhZDVlNzM2ZjRiZWRiMmY2ZDQyY2NkODQ5ZTcwNmIxZA; tt_csrf_token=dD0EIH8q-pe3qDQsCyyD1jLN6KizJDRjOEyk; __tea_cache_tokens_1988={%22_type_%22:%22default%22%2C%22user_unique_id%22:%227229608516049831425%22%2C%22timestamp%22:1683274422659}; ttwid=1%7CM-rF193sJugKuNz2RGNt-rh6pAAR9IMceUSzlDnPCNI%7C1694002151%7Cd89b77afc809b1a610661a9d1c2784d80ebef9efdd166f06de0d28e27f7e4efe; msToken=KfJAVZ7r9D_QVeQlYAUZzDFbc1Yx-nZz6GF33eOxgd8KlqvTg1lF9bMXW7gFV-qW4MCgUwnBIhbiwU9kdaSpgHJCk-PABsHCtTO5J3qC4oCTsrXQ1_E0XtbqiE4OVLZ_jdF1EYWgKNPT2SnwGkQ=; msToken=KfJAVZ7r9D_QVeQlYAUZzDFbc1Yx-nZz6GF33eOxgd8KlqvTg1lF9bMXW7gFV-qW4MCgUwnBIhbiwU9kdaSpgHJCk-PABsHCtTO5J3qC4oCTsrXQ1_E0XtbqiE4OVLZ_jdF1EYWgKNPT2SnwGkQ='
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.79',
'Cookie': cookie
'Cookie': cookies
}
if proxy_addr != '':
@ -79,37 +78,39 @@ def get_tiktok_stream_data(url, proxy_addr, cookies=''):
json_str = re.findall(
'<script id="SIGI_STATE" type="application/json">(.*?)<\/script><script id="SIGI_RETRY" type="application\/json">',
html_str)[0]
# print(json_str)
json_data = json.loads(json_str)
return json_data
def get_kuaishou_stream_data(url, cookies=''):
cookie = 'did=web_7a4e65ac197566d457bc452ee5ade7d0; clientid=3; did=web_7a4e65ac197566d457bc452ee5ade7d0; client_key=65890b29; kpn=GAME_ZONE; kuaishou.live.bfb1s=3e261140b0cf7444a0ba411c6f227d88'
if cookies != '':
cookie = cookies
if cookies == '':
url2 = 'https://live.kuaishou.com/'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0', }
req = urllib.request.Request(url2, headers=headers)
cookies = opener.open(req, timeout=15).getheader('Set-Cookie')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
'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',
'Cookie': cookie
'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.findall('__INITIAL_STATE__=(.*?);\(function', html_str)[0]
json_str = re.search('__INITIAL_STATE__=(.*?);\(function', html_str).group(1)
json_data = json.loads(json_str)
return json_data
def get_huya_stream_data(url, cookies=''):
cookie = 'huya_ua=webh5&0.0.1&websocket; SoundValue=0.50; alphaValue=0.80; isInLiveRoom=true; game_did=-GcWYDglXNu2ZzqqTr4X-L4PSTclU2iheFm; Hm_lvt_51700b6c722f5bb4cf39906a596ea41f=1691210433,1691477318; udb_deviceid=w_610934293339279360; __yamid_tt1=0.5879880896254449; __yamid_new=C9EFFE0C63A00001A7A94510B5E718A6; guid=0a70d5e7b1d2cd644301d168d268de7b; guid=0a70d5e7b1d2cd644301d168d268de7b; udb_cred=CnDN6T9nhzPKEPgJieRfkuh2PcVTTfwhGVayc7q49srtD2angI9ShGfVHENqGqcGVvyssMbG1spibOt1mjsa57ZsNwEJ1sYVRedE_rsSN30WBp783NmwViE2I-Zh1yPV1MD6NRQURwYmyAUA5YOaY8iT; udb_passdata=3'
if cookies != '':
cookie = cookies
if cookies == '':
cookies = 'huya_ua=webh5&0.0.1&websocket; SoundValue=0.50; alphaValue=0.80; isInLiveRoom=true; game_did=-GcWYDglXNu2ZzqqTr4X-L4PSTclU2iheFm; Hm_lvt_51700b6c722f5bb4cf39906a596ea41f=1691210433,1691477318; udb_deviceid=w_610934293339279360; __yamid_tt1=0.5879880896254449; __yamid_new=C9EFFE0C63A00001A7A94510B5E718A6; guid=0a70d5e7b1d2cd644301d168d268de7b; guid=0a70d5e7b1d2cd644301d168d268de7b; udb_cred=CnDN6T9nhzPKEPgJieRfkuh2PcVTTfwhGVayc7q49srtD2angI9ShGfVHENqGqcGVvyssMbG1spibOt1mjsa57ZsNwEJ1sYVRedE_rsSN30WBp783NmwViE2I-Zh1yPV1MD6NRQURwYmyAUA5YOaY8iT; udb_passdata=3'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
'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',
'Cookie': cookie
'Cookie': cookies
}
req = urllib.request.Request(url, headers=headers)
@ -148,7 +149,6 @@ def get_token_js(rid, did):
js = execjs.compile(func_sign)
params = js.call('sign', rid, did, t10)
# print(params)
params_list = re.findall('=(.*?)(?=&|$)', params)
return params_list
@ -168,7 +168,6 @@ def get_douyu_info_data(url):
html_str = response.read().decode('utf-8')
json_str = re.search('ssr_pageContext" type="application\/json">(.*?)<\/script>', html_str).group(1)
# print(json_str)
json_data = json.loads(json_str)
return json_data
@ -177,13 +176,12 @@ def get_douyu_stream_data(rid, rate='-1', cookies=''):
did = '10000000000000000000000000003306'
params_list = get_token_js(rid, did)
cookie = 'dy_did=413b835d2ae00270f0c69f6400031601; acf_did=413b835d2ae00270f0c69f6400031601; Hm_lvt_e99aee90ec1b2106afe7ec3b199020a7=1692068308,1694003758; m_did=96003918aa5365bc6dcb4933000316p1; dy_teen_mode=%7B%22uid%22%3A%22472647365%22%2C%22status%22%3A0%2C%22birthday%22%3A%22%22%2C%22password%22%3A%22%22%7D; PHPSESSID=td59qi2fu2gepngb8mlehbeme3; acf_auth=94fc9s%2FeNj%2BKlpU%2Br8tZC3Jo9sZ0wz9ClcHQ1akL2Nhb6ZyCmfjVWSlR3LFFPuePWHRAMo0dt9vPSCoezkFPOeNy4mYcdVOM1a8CbW0ZAee4ipyNB%2Bflr58; dy_auth=bec5yzM8bUFYe%2FnVAjmUAljyrsX%2FcwRW%2FyMHaoArYb5qi8FS9tWR%2B96iCzSnmAryLOjB3Qbeu%2BBD42clnI7CR9vNAo9mva5HyyL41HGsbksx1tEYFOEwxSI; wan_auth37wan=5fd69ed5b27fGM%2FGoswWwDo%2BL%2FRMtnEa4Ix9a%2FsH26qF0sR4iddKMqfnPIhgfHZUqkAk%2FA1d8TX%2B6F7SNp7l6buIxAVf3t9YxmSso8bvHY0%2Fa6RUiv8; acf_uid=472647365; acf_username=472647365; acf_nickname=%E7%94%A8%E6%88%B776576662; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F24_; acf_ct=0; acf_ltkid=25305099; acf_biz=1; acf_stk=90754f8ed18f0c24; Hm_lpvt_e99aee90ec1b2106afe7ec3b199020a7=1694003778'
if cookies != '':
cookie = cookies
if cookies == '':
cookies = 'dy_did=413b835d2ae00270f0c69f6400031601; acf_did=413b835d2ae00270f0c69f6400031601; Hm_lvt_e99aee90ec1b2106afe7ec3b199020a7=1692068308,1694003758; m_did=96003918aa5365bc6dcb4933000316p1; dy_teen_mode=%7B%22uid%22%3A%22472647365%22%2C%22status%22%3A0%2C%22birthday%22%3A%22%22%2C%22password%22%3A%22%22%7D; PHPSESSID=td59qi2fu2gepngb8mlehbeme3; acf_auth=94fc9s%2FeNj%2BKlpU%2Br8tZC3Jo9sZ0wz9ClcHQ1akL2Nhb6ZyCmfjVWSlR3LFFPuePWHRAMo0dt9vPSCoezkFPOeNy4mYcdVOM1a8CbW0ZAee4ipyNB%2Bflr58; dy_auth=bec5yzM8bUFYe%2FnVAjmUAljyrsX%2FcwRW%2FyMHaoArYb5qi8FS9tWR%2B96iCzSnmAryLOjB3Qbeu%2BBD42clnI7CR9vNAo9mva5HyyL41HGsbksx1tEYFOEwxSI; wan_auth37wan=5fd69ed5b27fGM%2FGoswWwDo%2BL%2FRMtnEa4Ix9a%2FsH26qF0sR4iddKMqfnPIhgfHZUqkAk%2FA1d8TX%2B6F7SNp7l6buIxAVf3t9YxmSso8bvHY0%2Fa6RUiv8; acf_uid=472647365; acf_username=472647365; acf_nickname=%E7%94%A8%E6%88%B776576662; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F24_; acf_ct=0; acf_ltkid=25305099; acf_biz=1; acf_stk=90754f8ed18f0c24; Hm_lpvt_e99aee90ec1b2106afe7ec3b199020a7=1694003778'
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',
'Referer': 'https://m.douyu.com/3125893?rid=3125893&dyshid=0-96003918aa5365bc6dcb4933000316p1&dyshci=181',
'Cookie': cookie
'Cookie': cookies
}
data = {
'v': params_list[0],
@ -206,15 +204,13 @@ def get_douyu_stream_data(rid, rate='-1', cookies=''):
def get_yy_stream_data(url, cookies=''):
cid = re.search('yy.com/(.*?)/', url).group(1)
cookie = 'hd_newui=0.2103068903976506; hdjs_session_id=0.4929014850884579; hdjs_session_time=1694004002636; hiido_ui=0.923076230899782'
if cookies != '':
cookie = cookies
if cookies == '':
cookies = 'hd_newui=0.2103068903976506; hdjs_session_id=0.4929014850884579; hdjs_session_time=1694004002636; hiido_ui=0.923076230899782'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
'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',
'Referer': 'https://www.yy.com/',
'Cookie': cookie
'Cookie': cookies
}
req = urllib.request.Request(url, headers=headers)
response = opener.open(req, timeout=15)
@ -234,14 +230,13 @@ def get_yy_stream_data(url, cookies=''):
def get_bilibili_stream_data(url, cookies=''):
cookie = "buvid3=13436C33-39B8-C4D5-C5C6-3F31B85716A131745infoc; b_nut=1680525931; CURRENT_FNVAL=4048; _uuid=B10E775DC-168D-CA47-E1B8-CEF7C52FA84234052infoc; buvid_fp=a2f7f8f3977824b52ec75cf23e5b6754; CURRENT_PID=70fa2680-d21d-11ed-ba58-9979ebfa5794; rpdid=|(JYYJ|uuYm)0J'uY)|lklmRJ; buvid4=C29E3582-5740-8FF3-AFD1-98B345DDAF5393968-022082019-Vk7oLekZ8O%2FtgWtFEu98GQ%3D%3D; DedeUserID=623475372; DedeUserID__ckMd5=db79fcea5a8315aa; i-wanna-go-back=-1; b_ut=5; FEED_LIVE_VERSION=V8; header_theme_version=CLOSE; home_feed_column=5; browser_resolution=1483-722; SESSDATA=122468fe%2C1707184844%2C2c98c%2A827Ts7uT3NZIxeOzop88h3EdmSUIG9NhWF9VkiidKIkTgJkTbh5WcONjTKuaOwfeR9t6uUZAAASAA; bili_jct=b8479df41520c402eb0a1a7f37a26de8; bp_video_offset_623475372=827303476826472609; PVID=1; LIVE_BUVID=AUTO5816940041629512; GIFT_BLOCK_COOKIE=GIFT_BLOCK_COOKIE"
if cookies != '':
cookie = cookies
if cookies == '':
cookies = "buvid3=13436C33-39B8-C4D5-C5C6-3F31B85716A131745infoc; b_nut=1680525931; CURRENT_FNVAL=4048; _uuid=B10E775DC-168D-CA47-E1B8-CEF7C52FA84234052infoc; buvid_fp=a2f7f8f3977824b52ec75cf23e5b6754; CURRENT_PID=70fa2680-d21d-11ed-ba58-9979ebfa5794; rpdid=|(JYYJ|uuYm)0J'uY)|lklmRJ; buvid4=C29E3582-5740-8FF3-AFD1-98B345DDAF5393968-022082019-Vk7oLekZ8O%2FtgWtFEu98GQ%3D%3D; DedeUserID=623475372; DedeUserID__ckMd5=db79fcea5a8315aa; i-wanna-go-back=-1; b_ut=5; FEED_LIVE_VERSION=V8; header_theme_version=CLOSE; home_feed_column=5; browser_resolution=1483-722; SESSDATA=122468fe%2C1707184844%2C2c98c%2A827Ts7uT3NZIxeOzop88h3EdmSUIG9NhWF9VkiidKIkTgJkTbh5WcONjTKuaOwfeR9t6uUZAAASAA; bili_jct=b8479df41520c402eb0a1a7f37a26de8; bp_video_offset_623475372=827303476826472609; PVID=1; LIVE_BUVID=AUTO5816940041629512; GIFT_BLOCK_COOKIE=GIFT_BLOCK_COOKIE"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Referer': 'https://live.bilibili.com/?spm_id_from=333.1296.0.0',
'Cookie': cookie
'Cookie': cookies
}
req = urllib.request.Request(url, headers=headers)
response = opener.open(req, timeout=15)
@ -254,7 +249,6 @@ def get_bilibili_stream_data(url, cookies=''):
if __name__ == '__main__':
# 尽量用自己的cookie以避免默认的不可用导致无法获取数据
dy_cookie = ''
ks_cookie = ''
url = "https://live.douyin.com/745964462470" # 抖音直播
# url = "https://www.tiktok.com/@pearlgaga88/live" # Tiktok直播
# url = "https://live.kuaishou.com/u/yall1102" # 快手直播
@ -267,7 +261,7 @@ if __name__ == '__main__':
print(get_douyin_stream_data(url, dy_cookie))
# print(get_tiktok_stream_data(url,'http://127.0.0.1:7890'))
# print(get_kuaishou_stream_data(url,ks_cookie))
# print(get_kuaishou_stream_data(url))
# print(get_huya_stream_data(url))
# print(get_douyu_info_data(url))
# print(get_douyu_stream_data("4921614",rate='-1'))
@ -275,4 +269,3 @@ if __name__ == '__main__':
# print(get_bilibili_stream_data(url))