's'
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
https://live.douyin.com/956616126418
|
||||
https://live.douyin.com/754546227599
|
||||
https://live.douyin.com/382919014818
|
||||
https://live.douyin.com/591442402624
|
||||
https://live.douyin.com/19153084007
|
||||
|
||||
@@ -30,102 +30,131 @@ mp4格式重新编码为h264 = 否
|
||||
额外使用代理录制的平台(逗号分隔) =
|
||||
|
||||
[推送配置]
|
||||
# 可选微信|钉钉|tg|邮箱|bark|ntfy|pushplus 可填多个
|
||||
直播状态推送渠道 =
|
||||
钉钉推送接口链接 =
|
||||
微信推送接口链接 =
|
||||
bark推送接口链接 =
|
||||
直播状态推送渠道 =
|
||||
钉钉推送接口链接 =
|
||||
微信推送接口链接 =
|
||||
bark推送接口链接 =
|
||||
bark推送中断级别 = active
|
||||
bark推送铃声 =
|
||||
钉钉通知@对象(填手机号) =
|
||||
bark推送铃声 =
|
||||
钉钉通知@对象(填手机号) =
|
||||
钉钉通知@全体(是/否) = 否
|
||||
tgapi令牌 =
|
||||
tg聊天id(个人或者群组id) =
|
||||
smtp邮件服务器 =
|
||||
是否使用SMTP服务SSL加密(是/否) =
|
||||
SMTP邮件服务器端口 =
|
||||
邮箱登录账号 =
|
||||
发件人密码(授权码) =
|
||||
发件人邮箱 =
|
||||
发件人显示昵称 =
|
||||
收件人邮箱 =
|
||||
tgapi令牌 =
|
||||
tg聊天id(个人或者群组id) =
|
||||
smtp邮件服务器 =
|
||||
是否使用smtp服务ssl加密(是/否) =
|
||||
smtp邮件服务器端口 =
|
||||
邮箱登录账号 =
|
||||
发件人密码(授权码) =
|
||||
发件人邮箱 =
|
||||
发件人显示昵称 =
|
||||
收件人邮箱 =
|
||||
ntfy推送地址 = https://ntfy.sh/xxxx
|
||||
ntfy推送标签 = tada
|
||||
ntfy推送邮箱 =
|
||||
pushplus推送token =
|
||||
自定义推送标题 =
|
||||
自定义开播推送内容 =
|
||||
自定义关播推送内容 =
|
||||
ntfy推送邮箱 =
|
||||
pushplus推送token =
|
||||
自定义推送标题 =
|
||||
自定义开播推送内容 =
|
||||
自定义关播推送内容 =
|
||||
只推送通知不录制(是/否) = 否
|
||||
直播推送检测频率(秒) = 1800
|
||||
开播推送开启(是/否) = 是
|
||||
关播推送开启(是/否)= 否
|
||||
关播推送开启(是/否) = 否
|
||||
只推送通知不直播(是/否) = 否
|
||||
|
||||
[Cookie]
|
||||
# 录制抖音必填
|
||||
抖音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
|
||||
快手cookie =
|
||||
tiktok_cookie =
|
||||
虎牙cookie =
|
||||
斗鱼cookie =
|
||||
yy_cookie =
|
||||
b站cookie =
|
||||
小红书cookie =
|
||||
bigo_cookie =
|
||||
blued_cookie =
|
||||
sooplive_cookie =
|
||||
netease_cookie =
|
||||
千度热播_cookie =
|
||||
pandatv_cookie =
|
||||
猫耳fm_cookie =
|
||||
winktv_cookie =
|
||||
flextv_cookie =
|
||||
look_cookie =
|
||||
twitcasting_cookie =
|
||||
baidu_cookie =
|
||||
weibo_cookie =
|
||||
kugou_cookie =
|
||||
twitch_cookie =
|
||||
liveme_cookie =
|
||||
huajiao_cookie =
|
||||
liuxing_cookie =
|
||||
showroom_cookie =
|
||||
acfun_cookie =
|
||||
changliao_cookie =
|
||||
yinbo_cookie =
|
||||
yingke_cookie =
|
||||
zhihu_cookie =
|
||||
chzzk_cookie =
|
||||
haixiu_cookie =
|
||||
vvxqiu_cookie =
|
||||
17live_cookie =
|
||||
langlive_cookie =
|
||||
pplive_cookie =
|
||||
6room_cookie =
|
||||
lehaitv_cookie =
|
||||
huamao_cookie =
|
||||
shopee_cookie =
|
||||
youtube_cookie =
|
||||
taobao_cookie =
|
||||
jd_cookie =
|
||||
faceit_cookie =
|
||||
migu_cookie =
|
||||
lianjie_cookie =
|
||||
laixiu_cookie =
|
||||
picarto_cookie =
|
||||
|
||||
快手cookie =
|
||||
tiktok_cookie =
|
||||
虎牙cookie =
|
||||
斗鱼cookie =
|
||||
yy_cookie =
|
||||
b站cookie =
|
||||
小红书cookie =
|
||||
bigo_cookie =
|
||||
blued_cookie =
|
||||
sooplive_cookie =
|
||||
netease_cookie =
|
||||
千度热播_cookie =
|
||||
pandatv_cookie =
|
||||
猫耳fm_cookie =
|
||||
winktv_cookie =
|
||||
flextv_cookie =
|
||||
look_cookie =
|
||||
twitcasting_cookie =
|
||||
baidu_cookie =
|
||||
weibo_cookie =
|
||||
kugou_cookie =
|
||||
twitch_cookie =
|
||||
liveme_cookie =
|
||||
huajiao_cookie =
|
||||
liuxing_cookie =
|
||||
showroom_cookie =
|
||||
acfun_cookie =
|
||||
changliao_cookie =
|
||||
yinbo_cookie =
|
||||
yingke_cookie =
|
||||
zhihu_cookie =
|
||||
chzzk_cookie =
|
||||
haixiu_cookie =
|
||||
vvxqiu_cookie =
|
||||
17live_cookie =
|
||||
langlive_cookie =
|
||||
pplive_cookie =
|
||||
6room_cookie =
|
||||
lehaitv_cookie =
|
||||
huamao_cookie =
|
||||
shopee_cookie =
|
||||
youtube_cookie =
|
||||
taobao_cookie =
|
||||
jd_cookie =
|
||||
faceit_cookie =
|
||||
migu_cookie =
|
||||
lianjie_cookie =
|
||||
laixiu_cookie =
|
||||
picarto_cookie =
|
||||
|
||||
[Authorization]
|
||||
popkontv_token =
|
||||
popkontv_token =
|
||||
|
||||
[账号密码]
|
||||
sooplive账号 =
|
||||
sooplive密码 =
|
||||
flextv账号 =
|
||||
flextv密码 =
|
||||
popkontv账号 =
|
||||
sooplive账号 =
|
||||
sooplive密码 =
|
||||
flextv账号 =
|
||||
flextv密码 =
|
||||
popkontv账号 =
|
||||
partner_code = P-00001
|
||||
popkontv密码 =
|
||||
popkontv密码 =
|
||||
twitcasting账号类型 = normal
|
||||
twitcasting账号 =
|
||||
twitcasting密码 =
|
||||
twitcasting账号 =
|
||||
twitcasting密码 =
|
||||
|
||||
[直播设置]
|
||||
language(zh_cn/en) = zh_cn
|
||||
是否跳过代理检测(是/否) = 否
|
||||
直播保存路径(不填则默认) =
|
||||
保存文件夹是否以作者区分 = 是
|
||||
保存文件夹是否以时间区分 = 否
|
||||
保存文件夹是否以标题区分 = 否
|
||||
保存文件名是否包含标题 = 否
|
||||
是否去除名称中的表情符号 = 是
|
||||
视频保存格式ts|mkv|flv|mp4|mp3音频|m4a音频 = mp4
|
||||
原画|超清|高清|标清|流畅 = 原画
|
||||
是否使用代理ip(是/否) = 是
|
||||
代理地址 =
|
||||
同一时间访问网络的线程数 = 3
|
||||
循环时间(秒) = 120
|
||||
排队读取网址时间(秒) = 0
|
||||
是否显示循环秒数 = 否
|
||||
是否显示直播源地址 = 否
|
||||
分段直播是否开启 = 否
|
||||
是否强制启用https直播 = 否
|
||||
直播空间剩余阈值(gb) = 1.0
|
||||
视频分段时间(秒) = 1800
|
||||
直播完成后自动转为mp4格式 = 否
|
||||
mp4格式重新编码为h264 = 否
|
||||
追加格式后删除原文件 = 否
|
||||
生成时间字幕文件 = 否
|
||||
是否直播完成后执行自定义脚本 = 否
|
||||
使用代理直播的平台(逗号分隔) = tiktok, soop, pandalive, winktv, flextv, popkontv, twitch, liveme, showroom, chzzk, shopee, shp, youtu, faceit
|
||||
额外使用代理直播的平台(逗号分隔) =
|
||||
|
||||
|
||||
72
main.py
72
main.py
@@ -781,6 +781,58 @@ def direct_download_stream(source_url: str, save_path: str, record_name: str, li
|
||||
logger.error(f"FLV下载错误: {e} 发生错误的行数: {e.__traceback__.tb_lineno}")
|
||||
return False
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
import subprocess
|
||||
|
||||
async def check_subprocess_with_heartbeat(record_name: str,
|
||||
record_url: str,
|
||||
ffmpeg_command: list,
|
||||
save_type: str,
|
||||
script_command: str | None = None,
|
||||
heartbeat_timeout: int = 10) -> bool:
|
||||
"""
|
||||
启动 ffmpeg 子进程并进行心跳检测:
|
||||
- 每 heartbeat_timeout 秒内必须有输出,否则认为卡死,直接 kill。
|
||||
- 进程退出 returncode=0 正常,否则异常。
|
||||
"""
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*ffmpeg_command,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
|
||||
last_heartbeat = datetime.datetime.now()
|
||||
|
||||
async def read_stream(stream, name):
|
||||
nonlocal last_heartbeat
|
||||
async for line in stream:
|
||||
decoded = line.decode(errors="ignore").strip()
|
||||
if decoded:
|
||||
last_heartbeat = datetime.datetime.now()
|
||||
# 可根据需要过滤/解析码率、fps
|
||||
print(f"[{record_name}][{name}] {decoded}")
|
||||
|
||||
# 并行读取 stdout 和 stderr
|
||||
asyncio.create_task(read_stream(process.stdout, "stdout"))
|
||||
asyncio.create_task(read_stream(process.stderr, "stderr"))
|
||||
|
||||
while True:
|
||||
# 检查进程是否退出
|
||||
if process.returncode is not None:
|
||||
break
|
||||
|
||||
# 检查心跳超时
|
||||
now = datetime.datetime.now()
|
||||
if (now - last_heartbeat).total_seconds() > heartbeat_timeout:
|
||||
print(f"[心跳超时] {record_name}: {heartbeat_timeout}s 无数据,强制重启 ffmpeg")
|
||||
process.kill()
|
||||
await process.wait()
|
||||
return False
|
||||
|
||||
await asyncio.sleep(2)
|
||||
|
||||
return process.returncode == 0
|
||||
|
||||
def check_subprocess(record_name: str, record_url: str, ffmpeg_command: list, save_type: str,
|
||||
script_command: str | None = None) -> bool:
|
||||
@@ -1237,13 +1289,19 @@ def start_record(url_data: tuple, count_variable: int = -1) -> None:
|
||||
]
|
||||
|
||||
ffmpeg_command.extend(command)
|
||||
comment_end = check_subprocess(
|
||||
record_name,
|
||||
record_url,
|
||||
ffmpeg_command,
|
||||
record_save_type,
|
||||
custom_script
|
||||
)
|
||||
# comment_end = check_subprocess(
|
||||
# record_name,
|
||||
# record_url,
|
||||
# ffmpeg_command,
|
||||
# record_save_type,
|
||||
# custom_script
|
||||
# )
|
||||
comment_end = asyncio.run(
|
||||
check_subprocess_with_heartbeat(
|
||||
record_name, record_url, ffmpeg_command, record_save_type, custom_script, heartbeat_timeout=15
|
||||
)
|
||||
)
|
||||
|
||||
if comment_end:
|
||||
return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user