This commit is contained in:
eric
2025-09-16 18:31:01 +08:00
parent 687294ce5d
commit 0504c104d3
3 changed files with 24 additions and 10 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
cache/ cache/
__pycache__/ __pycache__/
records/

View File

@@ -7,6 +7,7 @@ import time
import signal import signal
import sys import sys
import logging import logging
import platform
import spider import spider
import stream import stream
@@ -17,6 +18,8 @@ logging.basicConfig(
datefmt="%H:%M:%S" datefmt="%H:%M:%S"
) )
IS_WINDOWS = platform.system() == "Windows"
# ----------------- Douyin to YouTube Streamer ----------------- # ----------------- Douyin to YouTube Streamer -----------------
class DouyinToYouTubeStreamer: class DouyinToYouTubeStreamer:
def __init__(self, bitrate=5000, audio_bitrate=128, resolution='720x1280', def __init__(self, bitrate=5000, audio_bitrate=128, resolution='720x1280',
@@ -87,7 +90,7 @@ class DouyinToYouTubeStreamer:
'-b:a', f'{self.audio_bitrate}k', '-b:a', f'{self.audio_bitrate}k',
'-ar', '44100', '-ar', '44100',
'-ac', '2', '-ac', '2',
'-movflags', '+faststart+frag_keyframe', # <--- 关键修改 '-movflags', '+faststart+frag_keyframe',
self.out_file self.out_file
] ]
return cmd return cmd
@@ -181,10 +184,14 @@ class DouyinToYouTubeStreamer:
def _finalize_recording(self): def _finalize_recording(self):
if self.out_file and os.path.exists(self.out_file): if self.out_file and os.path.exists(self.out_file):
try: try:
# 如果进程未退出,先发送中断信号完成封装 # 根据系统发送信号完成封装
if self.record_proc and self.record_proc.poll() is None: if self.record_proc and self.record_proc.poll() is None:
self.record_proc.send_signal(signal.CTRL_BREAK_EVENT if os.name == 'nt' else signal.SIGINT) sig = signal.CTRL_BREAK_EVENT if IS_WINDOWS else signal.SIGINT
self.record_proc.send_signal(sig)
try:
self.record_proc.wait(timeout=5) self.record_proc.wait(timeout=5)
except subprocess.TimeoutExpired:
self.record_proc.kill()
except Exception: except Exception:
pass pass
@@ -197,7 +204,6 @@ class DouyinToYouTubeStreamer:
except FileExistsError: except FileExistsError:
new_name = self.out_file.replace(".mp4", f"_{uuid.uuid4().hex[:4]}.mp4") new_name = self.out_file.replace(".mp4", f"_{uuid.uuid4().hex[:4]}.mp4")
os.rename(self.out_file, new_name) os.rename(self.out_file, new_name)
size_mb = os.path.getsize(new_name) / (1024 * 1024) size_mb = os.path.getsize(new_name) / (1024 * 1024)
logging.info(f"[{self.room_name}] 本地录制已结束: {new_name} | 大小: {size_mb:.2f} MB") logging.info(f"[{self.room_name}] 本地录制已结束: {new_name} | 大小: {size_mb:.2f} MB")
@@ -246,5 +252,6 @@ if __name__ == "__main__":
signal.signal(signal.SIGTERM, signal_handler) signal.signal(signal.SIGTERM, signal_handler)
logging.info("程序已启动,低延迟推流中,按 Ctrl+C 停止...") logging.info("程序已启动,低延迟推流中,按 Ctrl+C 停止...")
while True: while True:
time.sleep(60) time.sleep(60)

14
live.py
View File

@@ -7,6 +7,7 @@ import time
import signal import signal
import sys import sys
import logging import logging
import platform
import spider import spider
import stream import stream
@@ -17,6 +18,8 @@ logging.basicConfig(
datefmt="%H:%M:%S" datefmt="%H:%M:%S"
) )
IS_WINDOWS = platform.system() == "Windows"
# ----------------- Douyin to YouTube Streamer ----------------- # ----------------- Douyin to YouTube Streamer -----------------
class DouyinToYouTubeStreamer: class DouyinToYouTubeStreamer:
def __init__(self, bitrate=5000, audio_bitrate=128, resolution='720x1280', def __init__(self, bitrate=5000, audio_bitrate=128, resolution='720x1280',
@@ -87,7 +90,7 @@ class DouyinToYouTubeStreamer:
'-b:a', f'{self.audio_bitrate}k', '-b:a', f'{self.audio_bitrate}k',
'-ar', '44100', '-ar', '44100',
'-ac', '2', '-ac', '2',
'-movflags', '+faststart+frag_keyframe', # <--- 关键修改 '-movflags', '+faststart+frag_keyframe',
self.out_file self.out_file
] ]
return cmd return cmd
@@ -181,10 +184,14 @@ class DouyinToYouTubeStreamer:
def _finalize_recording(self): def _finalize_recording(self):
if self.out_file and os.path.exists(self.out_file): if self.out_file and os.path.exists(self.out_file):
try: try:
# 如果进程未退出,先发送中断信号完成封装 # 根据系统发送信号完成封装
if self.record_proc and self.record_proc.poll() is None: if self.record_proc and self.record_proc.poll() is None:
self.record_proc.send_signal(signal.CTRL_BREAK_EVENT if os.name == 'nt' else signal.SIGINT) sig = signal.CTRL_BREAK_EVENT if IS_WINDOWS else signal.SIGINT
self.record_proc.send_signal(sig)
try:
self.record_proc.wait(timeout=5) self.record_proc.wait(timeout=5)
except subprocess.TimeoutExpired:
self.record_proc.kill()
except Exception: except Exception:
pass pass
@@ -197,7 +204,6 @@ class DouyinToYouTubeStreamer:
except FileExistsError: except FileExistsError:
new_name = self.out_file.replace(".mp4", f"_{uuid.uuid4().hex[:4]}.mp4") new_name = self.out_file.replace(".mp4", f"_{uuid.uuid4().hex[:4]}.mp4")
os.rename(self.out_file, new_name) os.rename(self.out_file, new_name)
size_mb = os.path.getsize(new_name) / (1024 * 1024) size_mb = os.path.getsize(new_name) / (1024 * 1024)
logging.info(f"[{self.room_name}] 本地录制已结束: {new_name} | 大小: {size_mb:.2f} MB") logging.info(f"[{self.room_name}] 本地录制已结束: {new_name} | 大小: {size_mb:.2f} MB")