124 lines
4.3 KiB
Python
124 lines
4.3 KiB
Python
"""End-to-end orchestrator.
|
|
|
|
Two modes:
|
|
* mode="api" (default) — MuAPI does download / transcribe / LLM / autocrop.
|
|
Fast, no local deps, pay-per-call.
|
|
* mode="local" — yt-dlp + faster-whisper + OpenAI or Gemini + ffmpeg/opencv.
|
|
Self-hosted, LLM_PROVIDER selects OpenAI or Gemini.
|
|
"""
|
|
from typing import Dict, List, Optional
|
|
|
|
from .clipper import crop_highlights
|
|
from .downloader import download_youtube
|
|
from .highlights import call_muapi_llm, get_highlights
|
|
from .transcriber import transcribe
|
|
|
|
|
|
def _run_local(
|
|
youtube_url: str,
|
|
num_clips: int,
|
|
aspect_ratio: str,
|
|
download_format: str,
|
|
language: Optional[str],
|
|
) -> Dict:
|
|
from .local.clipper import crop_highlights_local
|
|
from .local.downloader import download_youtube_local
|
|
from .local.llm import call_local_llm
|
|
from .local.transcriber import transcribe_local
|
|
|
|
source_path = download_youtube_local(youtube_url, fmt=download_format)
|
|
|
|
transcript = transcribe_local(source_path, language=language)
|
|
if not transcript["segments"]:
|
|
raise RuntimeError(
|
|
"Whisper produced no segments. The video may have no detectable speech."
|
|
)
|
|
|
|
highlights_result = get_highlights(transcript, num_clips=num_clips, llm_fn=call_local_llm)
|
|
all_highlights: List[Dict] = highlights_result.get("highlights", [])
|
|
if not all_highlights:
|
|
raise RuntimeError("Highlight generator returned zero clips.")
|
|
|
|
top = sorted(all_highlights, key=lambda h: int(h.get("score", 0)), reverse=True)[:num_clips]
|
|
print(f"[pipeline/local] cropping {len(top)} of {len(all_highlights)} candidates", flush=True)
|
|
|
|
shorts = crop_highlights_local(source_path, top, aspect_ratio=aspect_ratio)
|
|
|
|
return {
|
|
"mode": "local",
|
|
"source_video_url": source_path,
|
|
"transcript": transcript,
|
|
"highlights": all_highlights,
|
|
"shorts": shorts,
|
|
}
|
|
|
|
|
|
def _run_api(
|
|
youtube_url: str,
|
|
num_clips: int,
|
|
aspect_ratio: str,
|
|
download_format: str,
|
|
language: Optional[str],
|
|
) -> Dict:
|
|
source_url = download_youtube(youtube_url, fmt=download_format)
|
|
|
|
transcript = transcribe(source_url, language=language)
|
|
if not transcript["segments"]:
|
|
raise RuntimeError(
|
|
"Whisper produced no segments. The video may have no detectable speech."
|
|
)
|
|
|
|
highlights_result = get_highlights(transcript, num_clips=num_clips, llm_fn=call_muapi_llm)
|
|
all_highlights: List[Dict] = highlights_result.get("highlights", [])
|
|
if not all_highlights:
|
|
raise RuntimeError("Highlight generator returned zero clips.")
|
|
|
|
top = sorted(all_highlights, key=lambda h: int(h.get("score", 0)), reverse=True)[:num_clips]
|
|
print(f"[pipeline] cropping {len(top)} of {len(all_highlights)} candidates", flush=True)
|
|
|
|
shorts = crop_highlights(source_url, top, aspect_ratio=aspect_ratio)
|
|
|
|
return {
|
|
"mode": "api",
|
|
"source_video_url": source_url,
|
|
"transcript": transcript,
|
|
"highlights": all_highlights,
|
|
"shorts": shorts,
|
|
}
|
|
|
|
|
|
def generate_shorts(
|
|
youtube_url: str,
|
|
num_clips: int = 3,
|
|
aspect_ratio: str = "9:16",
|
|
download_format: str = "720",
|
|
language: Optional[str] = None,
|
|
mode: str = "api",
|
|
) -> Dict:
|
|
"""Run the full pipeline and return a structured result.
|
|
|
|
Args:
|
|
youtube_url: source URL.
|
|
num_clips: how many shorts to render.
|
|
aspect_ratio: e.g. "9:16", "1:1".
|
|
download_format: source resolution ("360" / "480" / "720" / "1080").
|
|
language: ISO-639-1 to force Whisper language detection.
|
|
mode: "api" (default, MuAPI) or "local" (yt-dlp + faster-whisper +
|
|
OpenAI or Gemini + ffmpeg).
|
|
|
|
Returns:
|
|
{
|
|
"mode": "api" | "local",
|
|
"source_video_url": str, # hosted URL (api) or local path (local)
|
|
"transcript": {...},
|
|
"highlights": [...], # all candidates ranked
|
|
"shorts": [...], # top `num_clips` with clip_url / local path
|
|
}
|
|
"""
|
|
mode = (mode or "api").lower()
|
|
if mode == "local":
|
|
return _run_local(youtube_url, num_clips, aspect_ratio, download_format, language)
|
|
if mode == "api":
|
|
return _run_api(youtube_url, num_clips, aspect_ratio, download_format, language)
|
|
raise ValueError(f"Unknown mode: {mode!r}. Use 'api' or 'local'.")
|