Files
OpenMontage/lib/media_profiles.py
calesthio a3e735cc7a Initial release — OpenMontage: the first open-source agentic video production system
11 production pipelines, 47 tools, 124 agent skills.
Supports cloud APIs (fal.ai, OpenAI, ElevenLabs, Suno, HeyGen, Runway) and
free local providers (diffusers, Piper TTS, WAN 2.1, Hunyuan, CogVideo).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 08:25:17 -07:00

166 lines
4.5 KiB
Python

"""Internal media profile constants and render-profile helpers.
Defines platform-specific media profiles (resolution, aspect ratio, codec, etc.)
so the composer and publisher agents can format output correctly.
"""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import Optional
class AspectRatio(str, Enum):
LANDSCAPE_16_9 = "16:9"
PORTRAIT_9_16 = "9:16"
SQUARE_1_1 = "1:1"
CINEMATIC_21_9 = "21:9"
STANDARD_4_3 = "4:3"
@dataclass(frozen=True)
class MediaProfile:
"""A named render profile for a target platform/format."""
name: str
width: int
height: int
aspect_ratio: AspectRatio
fps: int
codec: str
audio_codec: str
crf: int
pixel_format: str = "yuv420p"
max_file_size_mb: Optional[float] = None
max_duration_seconds: Optional[float] = None
caption_format: str = "srt"
notes: str = ""
# ---- Platform profiles ----
YOUTUBE_LANDSCAPE = MediaProfile(
name="youtube_landscape",
width=1920, height=1080,
aspect_ratio=AspectRatio.LANDSCAPE_16_9,
fps=30, codec="libx264", audio_codec="aac", crf=18,
caption_format="srt",
notes="YouTube standard HD upload",
)
YOUTUBE_4K = MediaProfile(
name="youtube_4k",
width=3840, height=2160,
aspect_ratio=AspectRatio.LANDSCAPE_16_9,
fps=30, codec="libx264", audio_codec="aac", crf=18,
caption_format="srt",
notes="YouTube 4K upload",
)
YOUTUBE_SHORTS = MediaProfile(
name="youtube_shorts",
width=1080, height=1920,
aspect_ratio=AspectRatio.PORTRAIT_9_16,
fps=30, codec="libx264", audio_codec="aac", crf=20,
max_duration_seconds=60,
caption_format="srt",
notes="YouTube Shorts (max 60s, vertical)",
)
INSTAGRAM_REELS = MediaProfile(
name="instagram_reels",
width=1080, height=1920,
aspect_ratio=AspectRatio.PORTRAIT_9_16,
fps=30, codec="libx264", audio_codec="aac", crf=20,
max_file_size_mb=250,
max_duration_seconds=90,
caption_format="srt",
notes="Instagram Reels (max 90s, vertical)",
)
INSTAGRAM_FEED = MediaProfile(
name="instagram_feed",
width=1080, height=1080,
aspect_ratio=AspectRatio.SQUARE_1_1,
fps=30, codec="libx264", audio_codec="aac", crf=20,
max_file_size_mb=250,
max_duration_seconds=60,
notes="Instagram feed video (square)",
)
TIKTOK = MediaProfile(
name="tiktok",
width=1080, height=1920,
aspect_ratio=AspectRatio.PORTRAIT_9_16,
fps=30, codec="libx264", audio_codec="aac", crf=20,
max_file_size_mb=287,
max_duration_seconds=600,
caption_format="srt",
notes="TikTok (max 10min, vertical preferred)",
)
LINKEDIN = MediaProfile(
name="linkedin",
width=1920, height=1080,
aspect_ratio=AspectRatio.LANDSCAPE_16_9,
fps=30, codec="libx264", audio_codec="aac", crf=20,
max_file_size_mb=5120,
max_duration_seconds=600,
caption_format="srt",
notes="LinkedIn video (landscape preferred, max 10min)",
)
CINEMATIC = MediaProfile(
name="cinematic",
width=2560, height=1080,
aspect_ratio=AspectRatio.CINEMATIC_21_9,
fps=24, codec="libx264", audio_codec="aac", crf=16,
notes="Cinematic ultra-wide format",
)
GENERIC_HD = MediaProfile(
name="generic_hd",
width=1920, height=1080,
aspect_ratio=AspectRatio.LANDSCAPE_16_9,
fps=30, codec="libx264", audio_codec="aac", crf=23,
caption_format="srt",
notes="Generic HD output (no platform-specific constraints)",
)
# ---- Profile registry ----
ALL_PROFILES: dict[str, MediaProfile] = {
p.name: p for p in [
YOUTUBE_LANDSCAPE, YOUTUBE_4K, YOUTUBE_SHORTS,
INSTAGRAM_REELS, INSTAGRAM_FEED,
TIKTOK, LINKEDIN, CINEMATIC, GENERIC_HD,
]
}
def get_profile(name: str) -> MediaProfile:
"""Get a media profile by name."""
if name not in ALL_PROFILES:
available = ", ".join(ALL_PROFILES.keys())
raise ValueError(f"Unknown profile {name!r}. Available: {available}")
return ALL_PROFILES[name]
def get_profiles_for_platform(platform: str) -> list[MediaProfile]:
"""Get all profiles matching a platform prefix."""
return [p for name, p in ALL_PROFILES.items() if name.startswith(platform)]
def ffmpeg_output_args(profile: MediaProfile) -> list[str]:
"""Generate FFmpeg output arguments for a media profile."""
args = [
"-c:v", profile.codec,
"-c:a", profile.audio_codec,
"-crf", str(profile.crf),
"-pix_fmt", profile.pixel_format,
"-r", str(profile.fps),
"-vf", f"scale={profile.width}:{profile.height}",
]
return args