File size: 2,245 Bytes
dbc3c35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import asyncio
import shutil
import subprocess
from pathlib import Path

from fastapi import UploadFile

from app.core.config import Settings


async def save_upload(upload: UploadFile, job_dir: Path) -> Path:
    suffix = Path(upload.filename or "upload.mp4").suffix or ".mp4"
    destination = job_dir / f"source{suffix.lower()}"
    with destination.open("wb") as handle:
        while chunk := await upload.read(1024 * 1024):
            handle.write(chunk)
    return destination


async def resolve_youtube_url(url: str, job_dir: Path, settings: Settings) -> Path:
    if settings.demo_mode:
        return await asyncio.to_thread(create_demo_video, job_dir, settings)

    try:
        import yt_dlp
    except Exception as exc:
        raise RuntimeError("yt-dlp is required for YouTube ingestion") from exc

    output_template = str(job_dir / "source.%(ext)s")
    ydl_opts = {
        "outtmpl": output_template,
        "format": "bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]/best",
        "merge_output_format": "mp4",
        "quiet": True,
        "noprogress": True,
    }

    def download() -> Path:
        with yt_dlp.YoutubeDL(ydl_opts) as ydl:
            ydl.download([url])
        matches = sorted(job_dir.glob("source.*"))
        if not matches:
            raise RuntimeError("yt-dlp finished without producing a video")
        return matches[0]

    return await asyncio.to_thread(download)


def create_demo_video(job_dir: Path, settings: Settings) -> Path:
    destination = job_dir / "source.mp4"
    ffmpeg = shutil.which(settings.ffmpeg_binary)
    if not ffmpeg:
        destination.write_bytes(b"")
        return destination

    command = [
        ffmpeg,
        "-y",
        "-f",
        "lavfi",
        "-i",
        "testsrc2=size=1280x720:rate=30:duration=120",
        "-f",
        "lavfi",
        "-i",
        "sine=frequency=660:sample_rate=48000:duration=120",
        "-shortest",
        "-c:v",
        "libx264",
        "-pix_fmt",
        "yuv420p",
        "-c:a",
        "aac",
        str(destination),
    ]
    try:
        subprocess.run(command, check=True, capture_output=True, text=True, timeout=45)
    except Exception:
        destination.write_bytes(b"")
    return destination