update
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -104,7 +104,8 @@ def download_file(video_id: int, db: Session = Depends(get_db)):
|
||||
|
||||
path = Path(video.file_path)
|
||||
if not path.exists():
|
||||
raise HTTPException(status_code=404, detail="Videodatei nicht gefunden")
|
||||
video_service.update_file_path(db, video_id, None)
|
||||
raise HTTPException(status_code=404, detail="Video noch nicht heruntergeladen")
|
||||
|
||||
return FileResponse(path, media_type="video/mp4", filename=f"{video.title}.mp4")
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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()
|
||||
|
||||
@@ -8,10 +8,11 @@ def create_video(db: Session, video_data: VideoCreate) -> Video:
|
||||
profile_id = video_data.profile_id
|
||||
data = video_data.model_dump(exclude={"profile_id"})
|
||||
video = Video(**data)
|
||||
if profile_id:
|
||||
profile = db.query(Profile).filter(Profile.id == profile_id).first()
|
||||
if profile:
|
||||
video.profiles.append(profile)
|
||||
if not profile_id:
|
||||
profile_id = 1
|
||||
profile = db.query(Profile).filter(Profile.id == profile_id).first()
|
||||
if profile:
|
||||
video.profiles.append(profile)
|
||||
db.add(video)
|
||||
db.commit()
|
||||
db.refresh(video)
|
||||
|
||||
Reference in New Issue
Block a user