This commit is contained in:
eric
2025-09-16 21:29:47 +08:00
parent ea12e8bfaa
commit 8395418a12
2 changed files with 206 additions and 132 deletions

View File

@@ -8,9 +8,10 @@ import signal
import sys
import logging
import platform
import spider
import stream
import aiohttp
import select
import spider # 你原来的 spider 模块
import stream # 你原来的 stream 模块
# ----------------- Logging Setup -----------------
logging.basicConfig(
@@ -38,7 +39,7 @@ class DouyinToYouTubeStreamer:
self.pushing = False
self.out_file = None
self.max_minutes = max_minutes
self.enable_recording = enable_recording # 控制录制开关
self.enable_recording = enable_recording
os.makedirs("records", exist_ok=True)
@@ -73,6 +74,7 @@ class DouyinToYouTubeStreamer:
'-bufsize', f'{self.bitrate * 2}k',
'-r', str(self.fps),
'-g', str(self.gop),
'-s', self.resolution,
'-c:a', 'aac', '-b:a', f'{self.audio_bitrate}k', '-ar', '44100', '-ac', '2',
'-f', 'flv',
f'rtmp://a.rtmp.youtube.com/live2/{youtube_key}'
@@ -98,7 +100,7 @@ class DouyinToYouTubeStreamer:
]
return cmd
# ----------------- 推流循环 -----------------
# ----------------- 推流循环(优化版) -----------------
def _run_stream_loop(self, real_url, youtube_key):
while not self.stop_flag.is_set():
try:
@@ -107,26 +109,31 @@ class DouyinToYouTubeStreamer:
# ---- 启动推流 ----
push_cmd = self._build_push_cmd(real_url, youtube_key)
self.push_proc = subprocess.Popen(push_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
self.push_proc = subprocess.Popen(
push_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE,
bufsize=1, universal_newlines=True
)
logging.info(f"[{self.room_name}] 推流进程 PID: {self.push_proc.pid}")
# ---- 启动本地录制(根据开关决定) ----
# ---- 启动本地录制 ----
if self.enable_recording:
record_cmd = self._build_record_cmd(real_url)
self.record_proc = subprocess.Popen(record_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
self.record_proc = subprocess.Popen(
record_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE,
bufsize=1, universal_newlines=True
)
logging.info(f"[{self.room_name}] 本地录制开始: {self.out_file}")
# ---- monitor stderr ----
# ---- monitor stderr 非阻塞 ----
def monitor_proc(proc, name):
try:
while not self.stop_flag.is_set() and proc and proc.poll() is None:
data = proc.stderr.read(1024)
if data and name == "push" and not self.pushing:
decoded = data.decode('utf-8', errors='ignore')
if "frame=" in decoded and "fps=" in decoded:
self.pushing = True
logging.info(f"[{self.room_name}] 推流到 YouTube 成功")
time.sleep(0.05)
line = proc.stderr.readline()
if not line:
continue
if name == "push" and not self.pushing and "frame=" in line and "fps=" in line:
self.pushing = True
logging.info(f"[{self.room_name}] 推流到 YouTube 成功")
except Exception as e:
logging.error(f"[{self.room_name} {name} stderr异常: {e}")
@@ -134,13 +141,14 @@ class DouyinToYouTubeStreamer:
if self.enable_recording:
threading.Thread(target=monitor_proc, args=(self.record_proc, "record"), daemon=True).start()
# ---- 循环检查 ----
# ---- 循环检查进程状态 ----
while not self.stop_flag.is_set():
elapsed = time.time() - self.start_time
if elapsed >= self.max_minutes * 60:
logging.warning(f"[{self.room_name}] 超过 {self.max_minutes} 分钟,自动停止推流和录制")
self.stop()
break
if self.push_proc.poll() is not None:
logging.warning(f"[{self.room_name}] 推流进程退出,5秒后重启...")
time.sleep(5)
@@ -149,7 +157,6 @@ class DouyinToYouTubeStreamer:
logging.warning(f"[{self.room_name}] 录制进程退出,5秒后重启...")
time.sleep(5)
break
time.sleep(2)
except Exception as e:
logging.error(f"[{self.room_name}] FFmpeg异常: {e}")
@@ -231,6 +238,7 @@ async def get_douyin_real_url(record_url, record_quality="原画", proxy_addr=No
logging.error(f"初始化 aiohttp session 失败: {e}")
return None
def start_douyin_to_youtube(record_url, youtube_key, room_name=None,
proxy_addr=None, cookies='', bitrate=5000, enable_recording=False):
real_url = asyncio.run(get_douyin_real_url(record_url, proxy_addr=proxy_addr, cookies=cookies))
@@ -245,7 +253,7 @@ def start_douyin_to_youtube(record_url, youtube_key, room_name=None,
# ----------------- Main Program -----------------
if __name__ == "__main__":
DOUYIN_URL = "https://live.douyin.com/851742623054"
DOUYIN_URL = "https://live.douyin.com/722725928418"
YOUTUBE_KEY = "qxvb-r47b-r5ju-6ud3-6k7z"
# enable_recording=True 可开启本地录制