's
This commit is contained in:
44
backup/back9
44
backup/back9
@@ -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 可开启本地录制
|
||||
|
||||
Reference in New Issue
Block a user