diff --git a/list b/list new file mode 100644 index 0000000..15f9ced --- /dev/null +++ b/list @@ -0,0 +1 @@ +https://live.douyin.com/525514386431 \ No newline at end of file diff --git a/live.py b/live.py index df4e287..04d342b 100644 --- a/live.py +++ b/live.py @@ -33,36 +33,60 @@ class AdaptiveHLS2YouTube: threading.Thread(target=self._abr_thread, daemon=True).start() def _pull_thread(self, real_url): - cmd = [ - "ffmpeg", - "-rw_timeout", "50000000", - "-user_agent", "Mozilla/5.0", - "-headers", "Referer: https://www.douyin.com/", - "-fflags", "+nobuffer+genpts", - "-flags", "low_delay", - "-reconnect", "1", - "-reconnect_at_eof", "1", - "-reconnect_streamed", "1", - "-reconnect_delay_max", "10", - "-i", real_url, - "-c", "copy", - "-f", "mpegts", - "-" - ] - try: - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) - print("[拉流] FFmpeg 启动成功") - while not self.stop_flag.is_set(): - data = proc.stdout.read(188*2000) # 大块读取,减少空跑 - if not data: - time.sleep(0.05) - continue + max_retry = 3 # 最大重试次数 + retry_count = 0 + while not self.stop_flag.is_set(): + cmd = [ + "ffmpeg", + "-rw_timeout", "50000000", + "-user_agent", "Mozilla/5.0", + "-headers", "Referer: https://www.douyin.com/", + "-fflags", "+nobuffer+genpts", + "-flags", "low_delay", + "-reconnect", "1", + "-reconnect_at_eof", "1", + "-reconnect_streamed", "1", + "-reconnect_delay_max", "10", + "-i", real_url, + "-c", "copy", + "-f", "mpegts", + "-" + ] + proc = None + try: + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + print("[拉流] FFmpeg 启动成功") + retry_count = 0 # 成功拉流重置重试计数 + while not self.stop_flag.is_set(): + data = proc.stdout.read(188*2000) # 大块读取 + if not data: + time.sleep(0.05) + continue + try: + self.buffer_queue.put_nowait(data) + except queue.Full: + continue + except Exception as e: + print(f"拉流异常: {e}") + finally: try: - self.buffer_queue.put_nowait(data) # 非阻塞写入队列 - except queue.Full: - continue # 队列满直接丢掉,保证不卡 - except Exception as e: - print(f"拉流异常: {e}") + if proc and proc.poll() is None: + proc.kill() + except: + pass + + # 轮训逻辑:直播未开播时重试 + retry_count += 1 + if retry_count >= max_retry: + print(f"[拉流] 连续 {max_retry} 次没有拉到流,停止推流") + self.stop_all() + break + else: + print(f"[拉流] 等待 10 秒后重试 ({retry_count}/{max_retry})...") + for _ in range(6): # 每分钟 6 次,10 秒/次 + if self.stop_flag.is_set(): + break + time.sleep(10) def _push_thread(self, youtube_key, room_name): min_buffer = max(1, self.delay_sec // self.segment_time) @@ -111,7 +135,7 @@ class AdaptiveHLS2YouTube: proc.stdin.write(data) proc.stdin.flush() except (BrokenPipeError, OSError): - break # 管道断开,退出推流循环 + break except queue.Empty: continue except Exception as e: @@ -129,7 +153,7 @@ class AdaptiveHLS2YouTube: if new_bitrate != self.current_bitrate: print(f"[ABR] 调整码率: {self.current_bitrate}k -> {new_bitrate}k") self.current_bitrate = new_bitrate - time.sleep(max(self.check_interval, 10)) # 降低 ABR 更新频率,避免频繁干扰 + time.sleep(max(self.check_interval, 10)) def stop_all(self): print("停止所有推流...") @@ -185,7 +209,7 @@ def monitor_panel_thread(hls_instance, interval=2, max_width=50): # ----------------- 示例 ----------------- if __name__ == "__main__": - DOUYIN_URL = "https://live.douyin.com/525514386431" + DOUYIN_URL = "https://live.douyin.com/722725928418" YOUTUBE_KEY = "qxvb-r47b-r5ju-6ud3-6k7z" hls_instance = start_douyin_to_youtube(DOUYIN_URL, YOUTUBE_KEY, delay_sec=40)