This commit is contained in:
Marek Lenczewski
2026-04-06 10:42:29 +02:00
parent b6635a107d
commit 8ecef00d0a
14 changed files with 128 additions and 61 deletions

View File

@@ -1,53 +1,91 @@
import subprocess
import time
from pathlib import Path
VIDEOS_DIR = "/videos"
CHUNK_SIZE = 64 * 1024
def _get_stream_urls(youtube_url: str):
result = subprocess.run(
[
"yt-dlp",
"-f", "bestvideo[ext=mp4][vcodec^=avc]+bestaudio[ext=m4a]/best[ext=mp4]",
"--print", "urls",
youtube_url,
],
capture_output=True, text=True, timeout=30,
)
if result.returncode != 0:
return None, None
lines = result.stdout.strip().splitlines()
if len(lines) >= 2:
return lines[0], lines[1]
elif len(lines) == 1:
return lines[0], None
return None, None
def stream_video_live(video_id: int, youtube_url: str):
output_path = f"{VIDEOS_DIR}/{video_id}.mp4"
path = Path(output_path)
video_url, audio_url = _get_stream_urls(youtube_url)
if not video_url:
return
if audio_url:
cmd = [
"ffmpeg",
"-reconnect", "1",
"-reconnect_streamed", "1",
"-reconnect_delay_max", "5",
"-i", video_url,
"-reconnect", "1",
"-reconnect_streamed", "1",
"-reconnect_delay_max", "5",
"-i", audio_url,
"-c:v", "copy",
"-c:a", "aac",
"-movflags", "frag_keyframe+empty_moov+default_base_moof",
"-f", "mp4",
"pipe:1",
]
else:
cmd = [
"ffmpeg",
"-reconnect", "1",
"-reconnect_streamed", "1",
"-reconnect_delay_max", "5",
"-i", video_url,
"-c", "copy",
"-movflags", "frag_keyframe+empty_moov+default_base_moof",
"-f", "mp4",
"pipe:1",
]
process = subprocess.Popen(
[
"yt-dlp",
"-f", "best[ext=mp4][vcodec^=avc]/best[ext=mp4]",
"-o", output_path,
youtube_url,
],
stdout=subprocess.DEVNULL,
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
)
# Warte bis Datei existiert und mindestens 1MB hat
while process.poll() is None:
if path.exists() and path.stat().st_size >= 1024 * 1024:
break
time.sleep(0.5)
if not path.exists():
process.wait()
return
# Streame aus der wachsenden Datei
pos = 0
stall_count = 0
with open(output_path, "rb") as f:
while True:
chunk = f.read(1024 * 1024)
if chunk:
pos += len(chunk)
stall_count = 0
try:
with open(output_path, "wb") as f:
while True:
chunk = process.stdout.read(CHUNK_SIZE)
if not chunk:
break
f.write(chunk)
yield chunk
else:
if process.poll() is not None:
# Download fertig — restliche Bytes lesen
remaining = f.read()
if remaining:
yield remaining
break
stall_count += 1
if stall_count > 60: # 30 Sekunden ohne neue Daten
break
time.sleep(0.5)
except GeneratorExit:
pass
finally:
if process.poll() is None:
process.kill()
process.wait()
if process.stdout:
process.stdout.close()
path = Path(output_path)
if process.returncode != 0 and path.exists():
path.unlink()