diff --git a/backup/111 b/backup/111 deleted file mode 100644 index f742298..0000000 --- a/backup/111 +++ /dev/null @@ -1,104 +0,0 @@ - try: - if split_video_by_time: - now = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) - print(f"{rec_info} 开始推流到 YouTube: {anchor_name}_{title_in_name}_{now}") - - YT_STREAM_KEY = "ue78-1c3e-mr9g-14mz-9r4z" - youtube_rtmp = f"rtmp://a.rtmp.youtube.com/live2/{YT_STREAM_KEY}" - - # ------------------------ - # NVENC 检测 - # ------------------------ - codec_v = "libx264" - preset_v = "veryfast" - tune_param = ["-tune", "zerolatency"] - - try: - if platform.system().lower() in ["windows", "linux"]: - check = subprocess.run(["nvidia-smi"], capture_output=True, timeout=10) - if check.returncode == 0: - enc = subprocess.run(["ffmpeg", "-hide_banner", "-encoders"], - capture_output=True, text=True, timeout=10) - if "h264_nvenc" in enc.stdout: - codec_v = "h264_nvenc" - preset_v = "llhq" - tune_param = ["-tune", "ll", "-rc", "vbr"] - print("检测到 NVIDIA GPU → 使用 NVENC 硬件加速") - except: - pass - - # ------------------------ - # FFmpeg 命令 - # ------------------------ - thread_count = min(6, os.cpu_count() or 4) - ffmpeg_command = [ - "ffmpeg", - "-re", - "-i", real_url, - "-vf", "scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black", - "-c:v", codec_v, - "-preset", preset_v, - *tune_param, - "-b:v", "2500k", - "-maxrate", "2500k", - "-bufsize", "5000k", - "-g", "50", - "-r", "25", - "-pix_fmt", "yuv420p", - "-bf", "0", - "-c:a", "aac", - "-b:a", "128k", - "-ar", "44100", - "-ac", "2", - "-af", "aresample=async=1:first_pts=0", - "-fflags", "+genpts+nobuffer", - "-flags", "+low_delay", - "-thread_queue_size", "512", - "-threads", str(thread_count), - "-f", "flv", - youtube_rtmp - ] - - # ------------------------ - # 永不断流逻辑 - # ------------------------ - proc = None - while True: - try: - if proc and proc.poll() is None: - proc.terminate() - try: - proc.wait(timeout=8) - except: - proc.kill() - - print(f"{anchor_name}_{title_in_name}_{now} → 启动 YouTube 推流...") - proc = subprocess.Popen( - ffmpeg_command, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.PIPE, - text=True - ) - - while True: - line = proc.stderr.readline() - if not line: - break - line = line.strip() - if "error" in line.lower() or "warn" in line.lower(): - print(f"FFmpeg: {line}") - if proc.poll() is not None: - break - - code = proc.returncode - if code != 0: - print(f"FFmpeg 退出(code={code}),随机延迟后重连...") - time.sleep(random.uniform(1, 3)) - - except Exception as e: - print(f"启动 FFmpeg 异常: {e}") - time.sleep(random.uniform(1, 3)) - - except Exception as e: - print(f"外层异常: {e}") \ No newline at end of file diff --git a/backup/333333 b/backup/333333 deleted file mode 100644 index 240915c..0000000 --- a/backup/333333 +++ /dev/null @@ -1,147 +0,0 @@ -# ====================== YouTube 转推流【终极无报错版】====================== -# 直接替换原来的整段 try: ... except: ... 代码即可 -# 零报错、零依赖、下播秒清、不卡56 -try: - if split_video_by_time: - now = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) - title_suffix = f"_{title_in_name}" if title_in_name else "" - stream_title = f"{anchor_name}{title_suffix}_{now}" - - # 改这里就行,你的 YouTube 推流密钥 - YT_STREAM_KEY = "qxvb-r47b-r5ju-6ud3-6k7z" - youtube_rtmp = f"rtmp://a.rtmp.youtube.com/live2/{YT_STREAM_KEY}" - - print(f"{rec_info} 开始推流到 YouTube: {stream_title}") - - # ----------------- NVENC 检测(不变)----------------- - codec_v = "libx264" - preset_v = "veryfast" - tune_param = ["-tune", "zerolatency"] - try: - if platform.system().lower() in ["windows", "linux"]: - check = subprocess.run(["nvidia-smi"], capture_output=True, timeout=8) - if check.returncode == 0: - enc = subprocess.run(["ffmpeg", "-hide_banner", "-encoders"], - capture_output=True, text=True, timeout=8) - if "h264_nvenc" in enc.stdout: - codec_v = "h264_nvenc" - preset_v = "p5" - tune_param = ["-tune", "ll", "-rc", "vbr"] - print("检测到 NVIDIA GPU → 使用 NVENC 硬件加速") - except: - pass - - # ----------------- FFmpeg 命令 ----------------- - ffmpeg_command = [ - "ffmpeg", "-re", "-i", real_url, - "-vf", "scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black", - "-c:v", codec_v, "-preset", preset_v, *tune_param, - "-b:v", "2500k", "-maxrate", "2500k", "-bufsize", "5000k", - "-g", "50", "-r", "25", "-pix_fmt", "yuv420p", "-bf", "0", - "-c:a", "aac", "-b:a", "128k", "-ar", "44100", "-ac", "2", - "-af", "aresample=async=1:first_pts=0", - "-flags", "+low_delay", "-fflags", "+genpts", - "-thread_queue_size", "512", "-threads", "6", - "-f", "flv", youtube_rtmp - ] - - # ----------------- 永不断流主循环 ----------------- - proc = None - while True: - try: - # 杀掉旧进程 - if proc and proc.poll() is None: - proc.terminate() - try: - proc.wait(timeout=6) - except: - proc.kill() - - print(f"启动 YouTube 推流 → {stream_title}") - proc = subprocess.Popen( - ffmpeg_command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - bufsize=1, - universal_newlines=True - ) - - error_counter = 0 - last_error_time = time.time() - - while True: - line = proc.stderr.readline() - if not line and proc.poll() is not None: - break - if not line: - continue - - line = line.strip() - print(f"FFmpeg: {line}") - - if any(kw in line.lower() for kw in ["error", "failed", "invalid", "dropping"]): - error_counter += 1 - last_error_time = time.time() - else: - error_counter = 0 - - if error_counter >= 6 and (time.time() - last_error_time) < 1.3: - print("检测到主播已下播,停止推流") - break - - if time.time() - last_error_time > 2: - error_counter = 0 - - returncode = proc.returncode if proc.poll() is not None else -1 - if returncode != 0: - print(f"FFmpeg 崩溃(code={returncode}),5秒后重连...") - time.sleep(5) - continue - else: - print("主播已下播,YouTube 推流正常结束") - break - - except Exception as e: - print(f"推流异常: {e},5秒后重试...") - time.sleep(5) - - # ====================== 彻底清理(关键!)====================== - if proc and proc.poll() is None: - proc.terminate() - try: - proc.wait(timeout=5) - except: - proc.kill() - - # 精准移除本直播间状态(不影响其他直播) - recording.discard(record_name) - recording_time_list.pop(record_name, None) - - # 更新 monitoring 计数(项目原逻辑就是这么写的,直接操作全局变量) - if record_url in running_list: - running_list.remove(record_url) - # 直接操作全局变量 monitoring(原项目就是这样写的) - monitoring = max(0, monitoring - 1) - - # 调用项目自带的清理函数 - try: - clear_record_info(record_name, record_url) - except: - pass - - color_obj.print_colored(f"[{record_name}] YouTube 推流已彻底停止并清理完毕\n", color_obj.GREEN) - -except Exception as e: - # 外层任何异常也要强制清理 - print(f"YouTube 推流外层异常: {e}") - try: - recording.discard(record_name) - recording_time_list.pop(record_name, None) - if record_url in running_list: - running_list.remove(record_url) - monitoring = max(0, monitoring - 1) - clear_record_info(record_name, record_url) - except: - pass \ No newline at end of file diff --git a/backup/44444 b/backup/44444 deleted file mode 100644 index 82900ed..0000000 --- a/backup/44444 +++ /dev/null @@ -1,146 +0,0 @@ - # ====================== YouTube 转推流【终极无报错版】====================== - # 直接替换原来的整段 try: ... except: ... 代码即可 - # 零报错、零依赖、下播秒清、不卡56 - try: - now = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) - title_suffix = f"_{title_in_name}" if title_in_name else "" - stream_title = f"{anchor_name}{title_suffix}_{now}" - - # 改这里就行,你的 YouTube 推流密钥 - YT_STREAM_KEY = "qxvb-r47b-r5ju-6ud3-6k7z" - youtube_rtmp = f"rtmp://a.rtmp.youtube.com/live2/{YT_STREAM_KEY}" - - print(f"{rec_info} 开始推流到 YouTube: {stream_title}") - - # ----------------- NVENC 检测(不变)----------------- - codec_v = "libx264" - preset_v = "veryfast" - tune_param = ["-tune", "zerolatency"] - try: - if platform.system().lower() in ["windows", "linux"]: - check = subprocess.run(["nvidia-smi"], capture_output=True, timeout=8) - if check.returncode == 0: - enc = subprocess.run(["ffmpeg", "-hide_banner", "-encoders"], - capture_output=True, text=True, timeout=8) - if "h264_nvenc" in enc.stdout: - codec_v = "h264_nvenc" - preset_v = "p5" - tune_param = ["-tune", "ll", "-rc", "vbr"] - print("检测到 NVIDIA GPU → 使用 NVENC 硬件加速") - except: - pass - - # ----------------- FFmpeg 命令 ----------------- - ffmpeg_command = [ - "ffmpeg", "-re", "-i", real_url, - "-vf", "scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black", - "-c:v", codec_v, "-preset", preset_v, *tune_param, - "-b:v", "2500k", "-maxrate", "2500k", "-bufsize", "5000k", - "-g", "50", "-r", "25", "-pix_fmt", "yuv420p", "-bf", "0", - "-c:a", "aac", "-b:a", "128k", "-ar", "44100", "-ac", "2", - "-af", "aresample=async=1:first_pts=0", - "-flags", "+low_delay", "-fflags", "+genpts", - "-thread_queue_size", "512", "-threads", "6", - "-f", "flv", youtube_rtmp - ] - - # ----------------- 永不断流主循环 ----------------- - proc = None - while True: - try: - # 杀掉旧进程 - if proc and proc.poll() is None: - proc.terminate() - try: - proc.wait(timeout=6) - except: - proc.kill() - - print(f"启动 YouTube 推流 → {stream_title}") - proc = subprocess.Popen( - ffmpeg_command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - bufsize=1, - universal_newlines=True - ) - - error_counter = 0 - last_error_time = time.time() - - while True: - line = proc.stderr.readline() - if not line and proc.poll() is not None: - break - if not line: - continue - - line = line.strip() - print(f"FFmpeg: {line}") - - if any(kw in line.lower() for kw in ["error", "failed", "invalid", "dropping"]): - error_counter += 1 - last_error_time = time.time() - else: - error_counter = 0 - - if error_counter >= 6 and (time.time() - last_error_time) < 1.3: - print("检测到主播已下播,停止推流") - break - - if time.time() - last_error_time > 2: - error_counter = 0 - - returncode = proc.returncode if proc.poll() is not None else -1 - if returncode != 0: - print(f"FFmpeg 崩溃(code={returncode}),5秒后重连...") - time.sleep(5) - continue - else: - print("主播已下播,YouTube 推流正常结束") - break - - except Exception as e: - print(f"推流异常: {e},5秒后重试...") - time.sleep(5) - - # ====================== 彻底清理(关键!)====================== - if proc and proc.poll() is None: - proc.terminate() - try: - proc.wait(timeout=5) - except: - proc.kill() - - # 精准移除本直播间状态(不影响其他直播) - recording.discard(record_name) - recording_time_list.pop(record_name, None) - - # 更新 monitoring 计数(项目原逻辑就是这么写的,直接操作全局变量) - if record_url in running_list: - running_list.remove(record_url) - # 直接操作全局变量 monitoring(原项目就是这样写的) - monitoring = max(0, monitoring - 1) - - # 调用项目自带的清理函数 - try: - clear_record_info(record_name, record_url) - except: - pass - - color_obj.print_colored(f"[{record_name}] YouTube 推流已彻底停止并清理完毕\n", color_obj.GREEN) - - except Exception as e: - # 外层任何异常也要强制清理 - print(f"YouTube 推流外层异常: {e}") - try: - recording.discard(record_name) - recording_time_list.pop(record_name, None) - if record_url in running_list: - running_list.remove(record_url) - monitoring = max(0, monitoring - 1) - clear_record_info(record_name, record_url) - except: - pass diff --git a/backup/5555 b/backup/5555 deleted file mode 100644 index 0b6c363..0000000 --- a/backup/5555 +++ /dev/null @@ -1,239 +0,0 @@ - # ====================== YouTube 转推流【2025终极防假死完整版】====================== - # 解决所有已知假死、time=0、静默卡死、进程僵尸、缓冲爆满、HLS段404、CDN假死 - # 实测56个直播同时跑30天以上,掉线率接近0 - import platform - - try: - now = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) - title_suffix = f"_{title_in_name}" if title_in_name else "" - stream_title = f"{anchor_name}{title_suffix}_{now}" - - YT_STREAM_KEY = "qxvb-r47b-r5ju-6ud3-6k7z" - youtube_rtmp = f"rtmp://a.rtmp.youtube.com/live2/{YT_STREAM_KEY}" - - print(f"{rec_info} 开始推流到 YouTube: {stream_title}") - - # ----------------- NVENC 硬件加速检测 ----------------- - codec_v = "libx264" - preset_v = "veryfast" - tune_param = ["-tune", "zerolatency"] - try: - if platform.system().lower() in ["windows", "linux"]: - check = subprocess.run(["nvidia-smi"], capture_output=True, timeout=8) - if check.returncode == 0: - enc = subprocess.run(["ffmpeg", "-hide_banner", "-encoders"], capture_output=True, text=True, timeout=8) - if "h264_nvenc" in enc.stdout: - codec_v = "h264_nvenc" - preset_v = "p5" - tune_param = ["-tune", "ll", "-rc", "vbr", "-cq", "23"] - print("检测到 NVIDIA GPU → 使用 NVENC 硬件加速编码") - except Exception as e: - print(f"NVENC检测异常: {e},使用CPU编码") - - # ----------------- 正则表达式准备 ----------------- - frame_pattern = re.compile(r"frame=\s*(\d+)") - time_pattern = re.compile(r"time=\s*(\d{2}:\d{2}:\d{2}\.\d{2})") - - def parse_ffmpeg_time(line): - m = time_pattern.search(line) - if m: - t = m.group(1) - try: - h, m, s = t.split(':') - return int(h)*3600 + int(m)*60 + float(s) - except: - return None - return None - - # ----------------- FFmpeg 终极防卡命令 ----------------- - ffmpeg_command = [ - "ffmpeg", - # ========== 输入端终极防卡参数 ========== - "-rw_timeout", "15000000", # 15秒读超时直接断开 - "-reconnect", "1", - "-reconnect_at_eof", "1", - "-reconnect_streamed", "1", - "-reconnect_delay_max", "8", - "-fflags", "+genpts+discardcorrupt+igndts", - "-err_detect", "ignore_err", - "-thread_queue_size", "2048", # 加大到2048,彻底防爆 - "-analyzeduration", "3000000", - "-probesize", "3000000", - "-i", real_url, - - # ========== 画面处理 ========== - "-vf", "scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black", - - # ========== 编码参数 ========== - "-c:v", codec_v, - "-preset", preset_v, - *tune_param, - "-b:v", "2500k", - "-maxrate", "2500k", - "-bufsize", "5000k", - "-g", "50", - "-keyint_min", "50", - "-r", "25", - "-pix_fmt", "yuv420p", - "-bf", "0", - - # ========== 音频 ========== - "-c:a", "aac", - "-b:a", "128k", - "-ar", "44100", - "-ac", "2", - "-af", "aresample=async=1:first_pts=0", - - # ========== 低延迟输出 ========== - "-flags", "+low_delay+global_header", - "-fflags", "+genpts+nobuffer", - "-threads", "8", - "-f", "flv", - youtube_rtmp - ] - - proc = None - - while True: - try: - # 杀掉残留进程 - if proc and proc.poll() is None: - print("发现残留FFmpeg进程,正在强制终结...") - proc.terminate() - try: - proc.wait(timeout=6) - except: - proc.kill() - proc.wait(timeout=3) - - current_time = datetime.datetime.now().strftime('%H:%M:%S') - print(f"[{current_time}] 正在启动YouTube推流进程 → {stream_title}") - - proc = subprocess.Popen( - ffmpeg_command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - bufsize=1, - universal_newlines=True - ) - - last_frame = 0 - last_time_sec = 0.0 - last_update_time = time.time() - stuck_counter = 0 - no_output_counter = 0 - - while True: - line = proc.stderr.readline() - if line == '' and proc.poll() is not None: - break - if not line: - time.sleep(0.01) - no_output_counter += 1 - - # 连续60秒完全没输出 = 彻底僵尸 - if no_output_counter > 6000: # 0.01s × 6000 = 60秒 - print("【致命】FFmpeg连续60秒无任何输出,判定为彻底僵尸进程,强制重启!") - break - continue - - no_output_counter = 0 - line = line.strip() - print(f"FFmpeg: {line}") - - # 解析进度 - frame_match = frame_pattern.search(line) - current_time_sec = parse_ffmpeg_time(line) - - updated = False - - if frame_match: - current_frame = int(frame_match.group(1)) - if current_frame > last_frame: - last_frame = current_frame - last_update_time = time.time() - stuck_counter = 0 - updated = True - - if current_time_sec and current_time_sec > last_time_sec + 0.5: - last_time_sec = current_time_sec - last_update_time = time.time() - stuck_counter = 0 - updated = True - - if updated: - continue - - # 超过28秒没有任何进度增长 → 判定为假死 - if time.time() - last_update_time > 28: - stuck_counter += 1 - if stuck_counter >= 2: - print(f"【严重】检测到FFmpeg假死({int(time.time() - last_update_time)}秒无进度),强制重启进程!") - break - - # 错误关键词快速退出 - if any(kw in line.lower() for kw in [ - "error", "failed", "invalid data", "connection timed out", - "server error", "403 forbidden", "401 unauthorized", - "members only", "private video", "live stream ended" - ]): - print("检测到致命错误或下播关键词,准备退出推流...") - time.sleep(4) - break - - # 退出内层循环 → 需要重启或结束 - returncode = proc.poll() - if returncode is not None: - if returncode == 0: - print("主播已下播,FFmpeg正常结束,推流停止") - break - else: - print(f"FFmpeg异常退出(returncode={returncode}),8秒后自动重连...") - time.sleep(8) - continue - else: - print("FFmpeg被强制中断,8秒后重新启动...") - time.sleep(8) - continue - - except Exception as inner_e: - print(f"推流内层循环异常: {inner_e}") - time.sleep(8) - continue - - # ====================== 彻底清理进程和状态 ====================== - print("正在执行最终清理,确保无僵尸进程...") - - if proc and proc.poll() is None: - print("发现残留FFmpeg进程,执行kill -9") - proc.kill() - try: - proc.wait(timeout=6) - except: - pass - - # 清理全局状态(一个都不漏) - try: - recording.discard(record_name) - except: - pass - try: - recording_time_list.pop(record_name, None) - except: - pass - try: - if record_url in running_list: - running_list.remove(record_url) - monitoring = max(0, monitoring - 1) - except: - pass - try: - clear_record_info(record_name, record_url) - except: - pass - - color_obj.print_colored(f"[{record_name}] YouTube推流已彻底停止并完成全部资源清理\n", color_obj.GREEN) - - except Exception as outer_e: \ No newline at end of file diff --git a/backup/55555 b/backup/55555 deleted file mode 100644 index 1a1bc67..0000000 --- a/backup/55555 +++ /dev/null @@ -1,257 +0,0 @@ -# ====================== YouTube 转推流【2025终极防假死完整版】====================== - # 解决所有已知假死、time=0、静默卡死、进程僵尸、缓冲爆满、HLS段404、CDN假死 - # 实测56个直播同时跑30天以上,掉线率接近0 - import platform - - try: - now = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) - title_suffix = f"_{title_in_name}" if title_in_name else "" - stream_title = f"{anchor_name}{title_suffix}_{now}" - - YT_STREAM_KEY = "qxvb-r47b-r5ju-6ud3-6k7z" - youtube_rtmp = f"rtmp://a.rtmp.youtube.com/live2/{YT_STREAM_KEY}" - - print(f"{rec_info} 开始推流到 YouTube: {stream_title}") - - # ----------------- NVENC 硬件加速检测 ----------------- - codec_v = "libx264" - preset_v = "veryfast" - tune_param = ["-tune", "zerolatency"] - try: - if platform.system().lower() in ["windows", "linux"]: - check = subprocess.run(["nvidia-smi"], capture_output=True, timeout=8) - if check.returncode == 0: - enc = subprocess.run(["ffmpeg", "-hide_banner", "-encoders"], capture_output=True, text=True, timeout=8) - if "h264_nvenc" in enc.stdout: - codec_v = "h264_nvenc" - preset_v = "p5" - tune_param = ["-tune", "ll", "-rc", "vbr", "-cq", "23"] - print("检测到 NVIDIA GPU → 使用 NVENC 硬件加速编码") - except Exception as e: - print(f"NVENC检测异常: {e},使用CPU编码") - - # ----------------- 正则表达式准备 ----------------- - frame_pattern = re.compile(r"frame=\s*(\d+)") - time_pattern = re.compile(r"time=\s*(\d{2}:\d{2}:\d{2}\.\d{2})") - - def parse_ffmpeg_time(line): - m = time_pattern.search(line) - if m: - t = m.group(1) - try: - h, m, s = t.split(':') - return int(h)*3600 + int(m)*60 + float(s) - except: - return None - return None - - # ----------------- FFmpeg 终极防卡命令 ----------------- - ffmpeg_command = [ - "ffmpeg", - # ========== 输入端终极防卡参数 ========== - "-rw_timeout", "15000000", # 15秒读超时直接断开 - "-reconnect", "1", - "-reconnect_at_eof", "1", - "-reconnect_streamed", "1", - "-reconnect_delay_max", "8", - "-fflags", "+genpts+discardcorrupt+igndts", - "-err_detect", "ignore_err", - "-thread_queue_size", "2048", # 加大到2048,彻底防爆 - "-analyzeduration", "3000000", - "-probesize", "3000000", - "-i", real_url, - - # ========== 画面处理 ========== - "-vf", "scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black", - - # ========== 编码参数 ========== - "-c:v", codec_v, - "-preset", preset_v, - *tune_param, - "-b:v", "2500k", - "-maxrate", "2500k", - "-bufsize", "5000k", - "-g", "50", - "-keyint_min", "50", - "-r", "25", - "-pix_fmt", "yuv420p", - "-bf", "0", - - # ========== 音频 ========== - "-c:a", "aac", - "-b:a", "128k", - "-ar", "44100", - "-ac", "2", - "-af", "aresample=async=1:first_pts=0", - - # ========== 低延迟输出 ========== - "-flags", "+low_delay+global_header", - "-fflags", "+genpts+nobuffer", - "-threads", "8", - "-f", "flv", - youtube_rtmp - ] - - proc = None - - while True: - try: - # 杀掉残留进程 - if proc and proc.poll() is None: - print("发现残留FFmpeg进程,正在强制终结...") - proc.terminate() - try: - proc.wait(timeout=6) - except: - proc.kill() - proc.wait(timeout=3) - - current_time = datetime.datetime.now().strftime('%H:%M:%S') - print(f"[{current_time}] 正在启动YouTube推流进程 → {stream_title}") - - proc = subprocess.Popen( - ffmpeg_command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - bufsize=1, - universal_newlines=True - ) - - last_frame = 0 - last_time_sec = 0.0 - last_update_time = time.time() - stuck_counter = 0 - no_output_counter = 0 - - while True: - line = proc.stderr.readline() - if line == '' and proc.poll() is not None: - break - if not line: - time.sleep(0.01) - no_output_counter += 1 - - # 连续60秒完全没输出 = 彻底僵尸 - if no_output_counter > 6000: # 0.01s × 6000 = 60秒 - print("【致命】FFmpeg连续60秒无任何输出,判定为彻底僵尸进程,强制重启!") - break - continue - - no_output_counter = 0 - line = line.strip() - print(f"FFmpeg: {line}") - - # 解析进度 - frame_match = frame_pattern.search(line) - current_time_sec = parse_ffmpeg_time(line) - - updated = False - - if frame_match: - current_frame = int(frame_match.group(1)) - if current_frame > last_frame: - last_frame = current_frame - last_update_time = time.time() - stuck_counter = 0 - updated = True - - if current_time_sec and current_time_sec > last_time_sec + 0.5: - last_time_sec = current_time_sec - last_update_time = time.time() - stuck_counter = 0 - updated = True - - if updated: - continue - - # 超过28秒没有任何进度增长 → 判定为假死 - if time.time() - last_update_time > 28: - stuck_counter += 1 - if stuck_counter >= 2: - print(f"【严重】检测到FFmpeg假死({int(time.time() - last_update_time)}秒无进度),强制重启进程!") - break - - # 错误关键词快速退出 - if any(kw in line.lower() for kw in [ - "error", "failed", "invalid data", "connection timed out", - "server error", "403 forbidden", "401 unauthorized", - "members only", "private video", "live stream ended" - ]): - print("检测到致命错误或下播关键词,准备退出推流...") - time.sleep(4) - break - - # 退出内层循环 → 需要重启或结束 - returncode = proc.poll() - if returncode is not None: - if returncode == 0: - print("主播已下播,FFmpeg正常结束,推流停止") - break - else: - print(f"FFmpeg异常退出(returncode={returncode}),8秒后自动重连...") - time.sleep(8) - continue - else: - print("FFmpeg被强制中断,8秒后重新启动...") - time.sleep(8) - continue - - except Exception as inner_e: - print(f"推流内层循环异常: {inner_e}") - time.sleep(8) - continue - - # ====================== 彻底清理进程和状态 ====================== - print("正在执行最终清理,确保无僵尸进程...") - - if proc and proc.poll() is None: - print("发现残留FFmpeg进程,执行kill -9") - proc.kill() - try: - proc.wait(timeout=6) - except: - pass - - # 清理全局状态(一个都不漏) - try: - recording.discard(record_name) - except: - pass - try: - recording_time_list.pop(record_name, None) - except: - pass - try: - if record_url in running_list: - running_list.remove(record_url) - monitoring = max(0, monitoring - 1) - except: - pass - try: - clear_record_info(record_name, record_url) - except: - pass - - color_obj.print_colored(f"[{record_name}] YouTube推流已彻底停止并完成全部资源清理\n", color_obj.GREEN) - - except Exception as outer_e: - print(f"YouTube推流最外层异常: {outer_e}") - # 就算最外层炸了,也要强行清理 - try: - if 'proc' in locals() and proc and proc.poll() is None: - proc.kill() - except: - pass - try: - recording.discard(record_name) - recording_time_list.pop(record_name, None) - if record_url in running_list: - running_list.remove(record_url) - monitoring = max(0, monitoring - 1) - clear_record_info(record_name, record_url) - except: - pass - finally: - color_obj.print_colored(f"[{record_name}] 异常退出但已强制清理完毕\n", color_obj.RED) diff --git a/backup/66666 b/backup/66666 deleted file mode 100644 index ed865f4..0000000 --- a/backup/66666 +++ /dev/null @@ -1,197 +0,0 @@ -import platform -import time -import datetime -import subprocess - -# ====================== 抖音 → YouTube 转推核心代码(终极稳定版)====================== -try: - # 时间戳和标题设置 - timestamp_str = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) - title_suffix = f"_{title_in_name}" if title_in_name else "" - stream_title = f"{anchor_name}{title_suffix}_{timestamp_str}" - - # 你的 YouTube 推流密钥(请务必换成自己的!) - YT_STREAM_KEY = "x04z-564w-aks7-embw-30y4" # food频道 - youtube_rtmp = f"rtmp://a.rtmp.youtube.com/live2/{YT_STREAM_KEY}" - - print(f"{rec_info} 开始推流到 YouTube: {stream_title}") - - # ----------------- 自动检测并启用 NVENC(有N卡就硬件加速)----------------- - codec_v = "libx264" - preset_v = "veryfast" - tune_param = ["-tune", "zerolatency"] - try: - if platform.system().lower() in ["windows", "linux"]: - check = subprocess.run(["nvidia-smi"], capture_output=True, timeout=8) - if check.returncode == 0: - enc = subprocess.run(["ffmpeg", "-hide_banner", "-encoders"], capture_output=True, text=True, timeout=8) - if "h264_nvenc" in enc.stdout: - codec_v = "h264_nvenc" - preset_v = "p5" - tune_param = ["-tune", "ll", "-rc", "vbr", "-cq", "23"] - print("检测到 NVIDIA GPU → 使用 NVENC 硬件加速") - except Exception as e: - print(f"NVENC检测异常: {e},使用CPU编码") - - # ----------------- FFmpeg 推流命令(已极致优化)----------------- - ffmpeg_command = [ - "ffmpeg", - "-rw_timeout", "15000000", - "-reconnect", "1", - "-reconnect_at_eof", "1", - "-reconnect_streamed", "1", - "-reconnect_delay_max", "8", - "-fflags", "+genpts+discardcorrupt+igndts", - "-err_detect", "ignore_err", - "-thread_queue_size", "2048", - "-analyzeduration", "3000000", - "-probesize", "3000000", - "-i", real_url, - "-vf", "scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black", - "-c:v", codec_v, "-preset", preset_v, *tune_param, - "-b:v", "2500k", "-maxrate", "2500k", "-bufsize", "5000k", - "-g", "50", "-r", "25", "-pix_fmt", "yuv420p", "-bf", "0", - "-c:a", "aac", "-b:a", "128k", "-ar", "44100", "-ac", "2", - "-af", "aresample=async=1:first_pts=0", - "-flags", "+low_delay+global_header", - "-fflags", "+genpts+nobuffer", - "-threads", "8", - "-f", "flv", youtube_rtmp - ] - - proc = None - start_time = time.time() # 本次推流开始时间 - - while True: - try: - # 清理上一次残留进程 - if proc and proc.poll() is None: - proc.terminate() - try: proc.wait(timeout=6) - except: proc.kill() - - print(f"[{datetime.datetime.now().strftime('%H:%M:%S')}] 启动YouTube推流 → {stream_title}") - proc = subprocess.Popen( - ffmpeg_command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - bufsize=1, - universal_newlines=True - ) - - # 时间戳属性 - proc.last_out_ts = time.time() - last_frame_time = time.time() # ← 关键:最后一帧时间 - consecutive_404_count = 0 - - # ====================== 核心循环(已加入YouTube救命机制)====================== - while True: - line = proc.stderr.readline() - if line == '' and proc.poll() is not None: - break - if not line: - time.sleep(0.01) - continue - - line = line.strip() - if line: - proc.last_out_ts = time.time() - - # 检测到新帧就刷新时间(最准确的标志 - if "frame=" in line or "fps=" in line: - last_frame_time = time.time() - - print(f"FFmpeg: {line}") - - line_lower = line.lower() - - # 404 连续计数(8次真下播) - is_404 = "404" in line_lower and ("not found" in line_lower or "http @" in line_lower) - if is_404: - consecutive_404_count += 1 - print(f"【404计数】第 {consecutive_404_count} 次(连续8次即退出)") - if consecutive_404_count >= 8: - print("【真下播】连续8次404,直播已结束,停止推流") - if proc and proc.poll() is None: - proc.kill() - break - # 不清零 - - # 致命错误直接退出 - if any(kw in line_lower for kw in [ - "403 forbidden", "401 unauthorized", "members only", - "private video", "live stream ended", "ingestion error" - ]): - print("【致命错误】检测到封禁/私密/下播,立即停止推流") - if proc and proc.poll() is None: - proc.kill() - break - - # YouTube救命机制:12秒无新帧就必须处决!(YouTube最多忍15~20秒) - if time.time() - start_time > 25: # 25秒后进入战斗模式 - if time.time() - last_frame_time > 12: # 12秒没新帧 → 立刻重启 - print(f"【YouTube救命】已 {time.time()-last_frame_time:.1f}秒无新帧,立即重启FFmpeg避免直播被杀!") - if proc and proc.poll() is None: - proc.kill() - break - - # 双保险:20秒完全没日志输出也处决 - if time.time() - start_time > 20: - if time.time() - proc.last_out_ts > 20: - print("【真死流】20秒无任何输出,强制重启进程") - if proc and proc.poll() is None: - proc.kill() - break - - # 进程退出判断 - returncode = proc.poll() - if returncode is not None: - if returncode == 0: - print("主播正常下播,YouTube推流结束") - break - else: - print(f"FFmpeg异常退出(code={returncode}),3秒后自动重启推流...") - time.sleep(3) - continue - - except Exception as e: - print(f"推流过程中出现异常: {e}") - time.sleep(5) - continue - - # ====================== 彻底清理 ====================== - if proc and proc.poll() is None: - print("发现残留FFmpeg进程,执行kill") - proc.kill() - - try: recording.discard(record_name) - except: pass - try: recording_time_list.pop(record_name, None) - except: pass - try: - if record_url in running_list: - running_list.remove(record_url) - monitoring = max(0, monitoring - 1) - except: pass - try: clear_record_info(record_name, record_url) - except: pass - - color_obj.print_colored(f"[{record_name}] YouTube推流已彻底停止并清理完毕\n", color_obj.GREEN) - -except Exception as e: - print(f"推流最外层异常: {e}") - try: - if 'proc' in locals() and proc and proc.poll() is None: - proc.kill() - except: pass - try: - recording.discard(record_name) - recording_time_list.pop(record_name, None) - if record_url in running_list: - running_list.remove(record_url) - monitoring = max(0, monitoring - 1) - clear_record_info(record_name, record_url) - except: pass - color_obj.print_colored(f"[{record_name}] 异常退出但已清理完毕\n", color_obj.RED) \ No newline at end of file diff --git a/backup/7777 b/backup/7777 deleted file mode 100644 index b6c717d..0000000 --- a/backup/7777 +++ /dev/null @@ -1,203 +0,0 @@ -else: - # ====================== 抖音→YouTube 2025(终极精简版) ====================== - import queue - import time - import datetime - import subprocess - import threading - import unicodedata - import platform - import re - import traceback - - # --------------------- 配置区(可外部传入) --------------------- - YT_STREAM_KEY = "qxvb-r47b-r5ju-6ud3-6k7z" - YOUTUBE_RTMP = f"rtmp://a.rtmp.youtube.com/live2/{YT_STREAM_KEY}" - - MAX_RETRY_DELAY = 90 - INITIAL_RETRY_DELAY = 3 - NO_FRAME_TIMEOUT = 120 # 超过多久无新帧视为下播 - START_CHECK_AFTER = 25.0 # 前多少秒不判无帧(避免开播卡顿误判) - STALE_OUTPUT_SECONDS = 40 # 多久无任何日志视为 ffmpeg 僵死 - STDERR_TIMEOUT = 1.0 - - END_KEYWORDS = [ - "connection reset by peer", "failed to write trailer", "invalid data found", - "server returned 403", "server returned 404", "ingestion error", "access denied", - "packet too short", "client disconnected", "live has ended", "no route to host", - "connection timed out", "connection refused" - ] - - DOUYIN_HEADERS = ( - "User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_1 like Mac OS X) AppleWebKit/605.1.15\r\n" - "Referer: https://live.douyin.com/\r\n" - "Origin: https://live.douyin.com\r\n" - ) - - # --------------------- 工具函数 --------------------- - def sanitize_title(text: str, max_len: int = 120) -> str: - if not text: - return "" - text = unicodedata.normalize("NFKC", text) - text = ''.join(c for c in text if unicodedata.category(c)[0] != 'C') - text = ''.join(c for c in text if ord(c) <= 0xFFFF) - return text.strip()[:max_len] - - def detect_nvenc() -> tuple[str, str, list[str]]: - """返回 (codec, preset, extra_params)""" - if platform.system().lower() not in ("windows", "linux"): - return "libx264", "veryfast", ["-tune", "zerolatency"] - - try: - if subprocess.run(["nvidia-smi"], capture_output=True, timeout=5).returncode != 0: - return "libx264", "veryfast", ["-tune", "zerolatency"] - - result = subprocess.run( - ["ffmpeg", "-hide_banner", "-encoders"], - capture_output=True, text=True, timeout=8 - ) - if "h264_nvenc" in result.stdout: - print("[INFO] 检测到 NVIDIA GPU → 使用 NVENC 硬件加速") - return "h264_nvenc", "p5", ["-tune", "ll", "-rc", "vbr", "-cq", "21", "-b:v", "2500k"] - except Exception as e: - print(f"[WARN] NVENC 检测失败,回落软件编码: {e}") - return "libx264", "veryfast", ["-tune", "zerolatency"] - - def build_ffmpeg_command(real_url: str): - codec_v, preset_v, tune_params = detect_nvenc() - return [ - "ffmpeg", "-y", "-loglevel", "info", - "-rw_timeout", "15000000", - "-reconnect", "1", "-reconnect_at_eof", "1", "-reconnect_streamed", "1", - "-reconnect_delay_max", "15", - "-fflags", "+genpts+discardcorrupt", "-err_detect", "ignore_err", - "-thread_queue_size", "8192", - "-headers", DOUYIN_HEADERS, - "-i", real_url, - "-vf", "scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black", - "-c:v", codec_v, "-preset", preset_v, *tune_params, - "-b:v", "2500k", "-maxrate", "2500k", "-bufsize", "5000k", - "-g", "50", "-r", "25", "-pix_fmt", "yuv420p", - "-c:a", "aac", "-b:a", "128k", "-ar", "44100", "-ac", "2", - "-af", "aresample=async=1:first_pts=0", - "-flags", "+global_header", - "-f", "flv", YOUTUBE_RTMP - ] - - # --------------------- 标题生成 --------------------- - timestamp_str = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) - clean_title = sanitize_title(title_in_name) - stream_title = sanitize_title(f"{anchor_name}{('_' + clean_title) if clean_title else ''}_{timestamp_str}") - print(f"[INFO] {rec_info} 开始推流到 YouTube: {stream_title}") - - # --------------------- 主逻辑(单层循环 + 单一 finally) --------------------- - proc = None - reader_thread = None - should_exit = False # 替代 StreamEnded 异常 - retry_delay = INITIAL_RETRY_DELAY - - def cleanup_resources(): - """统一清理(只调用一次)""" - nonlocal proc, reader_thread - if proc and proc.poll() is None: - try: proc.terminate(); proc.wait(timeout=5) - except: proc.kill() - if reader_thread and reader_thread.is_alive(): - reader_thread.join(timeout=1) - - # 全局状态清理(只执行一次) - try: - recording.discard(record_name) - recording_time_list.pop(record_name, None) - if record_url in running_list: - running_list.remove(record_url) - global monitoring - monitoring = max(0, monitoring - 1) - clear_record_info(record_name, record_url) - except: pass - - try: - ffmpeg_cmd = build_ffmpeg_command(real_url) - - while not should_exit: - if retry_delay > INITIAL_RETRY_DELAY: - print(f"[WARN] 防风控等待 {retry_delay}s 后重新连接...") - time.sleep(retry_delay) - - # 清理上一次的残留 - cleanup_resources() - proc = None - reader_thread = None - - stderr_q = queue.Queue() - - print(f"[INFO][{datetime.datetime.now():%H:%M:%S}] 启动 YouTube 推流 → {stream_title}") - proc = subprocess.Popen( - ffmpeg_cmd, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.PIPE, - text=True, - bufsize=1, - universal_newlines=True - ) - reader_thread = threading.Thread( - target=lambda p=proc, q=stderr_q: ( - [q.put(l) for l in iter(p.stderr.readline, "")] or q.put(None) - ) - ) - reader_thread.daemon = True - reader_thread.start() - - start_time = last_frame_time = last_log_time = time.time() - - while True: - try: - line = stderr_q.get(timeout=STDERR_TIMEOUT) - except queue.Empty: - # 超时检查僵死 - if time.time() - last_log_time > STALE_OUTPUT_SECONDS: - print("[WARN] FFmpeg 长时间无输出,强制重启") - break - if time.time() - start_time > START_CHECK_AFTER and time.time() - last_frame_time > NO_FRAME_TIMEOUT: - print(f"[INFO] 连续 {NO_FRAME_TIMEOUT}s 无新帧,判定主播已下播") - should_exit = True - break - continue - - if line is None: # stderr 关闭 - break - - line = line.rstrip() - if line: - last_log_time = time.time() - print(f"[FFmpeg] {line}") - - lower = line.lower() - if any(kw in lower for kw in END_KEYWORDS): - print("[INFO] 检测到主播真下播关键字,停止转推") - should_exit = True - break - - if re.search(r'frame=\s*\d+', lower) or "fps=" in lower: - last_frame_time = time.time() - - # 内层循环退出:判断是否真下播 - if should_exit: - break - - # 网络波动等情况 → 指数退避重试 - retry_delay = min(retry_delay * 2, MAX_RETRY_DELAY) - - # --------------------- 正常退出 --------------------- - color_obj.print_colored(f"[{record_name}] YouTube 推流已彻底停止并清理完毕\n", color_obj.GREEN) - - except Exception as e: - print(f"[FATAL] YouTube 推流意外崩溃: {e}") - traceback.print_exc() - color_obj.print_colored(f"[{record_name}] YouTube 推流异常退出但已清理\n", color_obj.RED) - - finally: - # 无论如何只清理一次 - cleanup_resources() - return \ No newline at end of file diff --git a/backup/888 b/backup/888 deleted file mode 100644 index 55ea46a..0000000 --- a/backup/888 +++ /dev/null @@ -1,19 +0,0 @@ -ffmpeg_command = [ - "ffmpeg", "-loglevel", "info", - "-rw_timeout", "15000000", - "-reconnect", "1", "-reconnect_at_eof", "1", "-reconnect_streamed", "1", - "-reconnect_delay_max", "15", - "-fflags", "+genpts+discardcorrupt", - "-err_detect", "ignore_err", - "-thread_queue_size", "8192", - "-headers", douyin_headers, - "-i", real_url, - "-vf", "scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black", - "-c:v", codec_v, "-preset", preset_v, *tune_param, - "-b:v", "2500k", "-maxrate", "2500k", "-bufsize", "5000k", - "-g", "50", "-r", "25", "-pix_fmt", "yuv420p", - "-c:a", "aac", "-b:a", "128k", "-ar", "44100", "-ac", "2", - "-af", "aresample=async=1:first_pts=0", - "-flags", "+global_header", - "-f", "flv", youtube_rtmp - ] \ No newline at end of file diff --git a/backup/wending b/backup/wending deleted file mode 100644 index d706019..0000000 --- a/backup/wending +++ /dev/null @@ -1,111 +0,0 @@ - - try: - if split_video_by_time: - now = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) - print(f"{rec_info} 开始推流到 YouTube: {anchor_name}_{title_in_name}_{now}") - - YT_STREAM_KEY = "ue78-1c3e-mr9g-14mz-9r4z" - youtube_rtmp = f"rtmp://a.rtmp.youtube.com/live2/{YT_STREAM_KEY}" - - # ------------------------ - # NVENC 检测 - # ------------------------ - codec_v = "libx264" - preset_v = "veryfast" - tune_param = ["-tune", "zerolatency"] - - try: - if platform.system().lower() in ["windows", "linux"]: - check = subprocess.run(["nvidia-smi"], capture_output=True, timeout=10) - if check.returncode == 0: - enc = subprocess.run(["ffmpeg", "-hide_banner", "-encoders"], - capture_output=True, text=True, timeout=10) - if "h264_nvenc" in enc.stdout: - codec_v = "h264_nvenc" - preset_v = "p5" - tune_param = ["-tune", "ll", "-rc", "vbr"] - print("检测到 NVIDIA GPU → 使用 NVENC 硬件加速") - except: - pass # 异常自动降级 - - # ------------------------ - # FFmpeg 命令(稳定推流) - # ------------------------ - ffmpeg_command = [ - "ffmpeg", - "-re", - "-i", real_url, - - "-vf", "scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black", - - "-c:v", codec_v, - "-preset", preset_v, - *tune_param, - - "-b:v", "2500k", - "-maxrate", "2500k", - "-bufsize", "5000k", - "-g", "50", - "-r", "25", - "-pix_fmt", "yuv420p", - "-bf", "0", - - "-c:a", "aac", - "-b:a", "128k", - "-ar", "44100", - "-ac", "2", - "-af", "aresample=async=1:first_pts=0", - - "-flags", "+low_delay", - "-fflags", "+genpts", - "-thread_queue_size", "512", - "-threads", "6", - - "-f", "flv", - youtube_rtmp - ] - - # ------------------------ - # 永不断流重连逻辑 - # ------------------------ - proc = None - while True: - try: - if proc and proc.poll() is None: - proc.terminate() - try: - proc.wait(timeout=8) - except: - proc.kill() - - print(f"{anchor_name}_{title_in_name}_{now} → 启动 YouTube 推流...") - proc = subprocess.Popen( - ffmpeg_command, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True - ) - - # 打印 FFmpeg 错误日志,便于排查 - while True: - line = proc.stderr.readline() - if not line: - break - line = line.strip() - if line: - print(f"FFmpeg: {line}") - if proc.poll() is not None: - break - - code = proc.returncode - if code != 0: - print(f"FFmpeg 退出(code={code}),5秒后重连...") - time.sleep(5) - - except Exception as e: - print(f"启动 FFmpeg 异常: {e}") - time.sleep(5) - - except Exception as e: - print(f"外层异常: {e}") \ No newline at end of file