106 lines
2.7 KiB
Python
106 lines
2.7 KiB
Python
import subprocess
|
|
from pathlib import Path
|
|
|
|
from database.database import SessionLocal
|
|
|
|
VIDEOS_DIR = "/videos"
|
|
CHUNK_SIZE = 64 * 1024
|
|
|
|
|
|
def streamAndSave(videoId: int, youtubeUrl: str):
|
|
from model.video import Video # Lazy-Import gegen Zirkular
|
|
outputPath = f"{VIDEOS_DIR}/{videoId}.mp4"
|
|
yield from streamVideoLive(videoId, youtubeUrl)
|
|
if Path(outputPath).exists():
|
|
db = SessionLocal()
|
|
try:
|
|
Video.updateFilePath(db, videoId, outputPath)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
def _getStreamUrls(youtubeUrl: str):
|
|
result = subprocess.run(
|
|
[
|
|
"yt-dlp",
|
|
"-f", "bestvideo[ext=mp4][vcodec^=avc]+bestaudio[ext=m4a]/best[ext=mp4]",
|
|
"--print", "urls",
|
|
youtubeUrl,
|
|
],
|
|
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 streamVideoLive(videoId: int, youtubeUrl: str):
|
|
outputPath = f"{VIDEOS_DIR}/{videoId}.mp4"
|
|
|
|
videoUrl, audioUrl = _getStreamUrls(youtubeUrl)
|
|
if not videoUrl:
|
|
return
|
|
|
|
if audioUrl:
|
|
cmd = [
|
|
"ffmpeg",
|
|
"-reconnect", "1",
|
|
"-reconnect_streamed", "1",
|
|
"-reconnect_delay_max", "5",
|
|
"-i", videoUrl,
|
|
"-reconnect", "1",
|
|
"-reconnect_streamed", "1",
|
|
"-reconnect_delay_max", "5",
|
|
"-i", audioUrl,
|
|
"-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", videoUrl,
|
|
"-c", "copy",
|
|
"-movflags", "frag_keyframe+empty_moov+default_base_moof",
|
|
"-f", "mp4",
|
|
"pipe:1",
|
|
]
|
|
|
|
process = subprocess.Popen(
|
|
cmd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
|
|
try:
|
|
with open(outputPath, "wb") as f:
|
|
while True:
|
|
chunk = process.stdout.read(CHUNK_SIZE)
|
|
if not chunk:
|
|
break
|
|
f.write(chunk)
|
|
yield chunk
|
|
except GeneratorExit:
|
|
pass
|
|
finally:
|
|
if process.poll() is None:
|
|
process.kill()
|
|
process.wait()
|
|
if process.stdout:
|
|
process.stdout.close()
|
|
|
|
path = Path(outputPath)
|
|
if process.returncode != 0 and path.exists():
|
|
path.unlink()
|