| from __future__ import annotations |
|
|
| from copy import deepcopy |
| from dataclasses import dataclass, field |
| from typing import Any |
|
|
|
|
| @dataclass(frozen=True) |
| class CreativeStyle: |
| key: str |
| label: str |
| description: str |
| platform: str |
| template: str |
| scene_duration: float |
| transition_sequence: tuple[str, ...] |
| scene_effect_sequence: tuple[str, ...] |
| render_defaults: dict[str, Any] = field(default_factory=dict) |
| metadata: dict[str, Any] = field(default_factory=dict) |
|
|
| def metadata_payload(self) -> dict[str, Any]: |
| return { |
| "key": self.key, |
| "label": self.label, |
| "description": self.description, |
| "platform": self.platform, |
| "template": self.template, |
| "scene_duration": self.scene_duration, |
| "transition_sequence": list(self.transition_sequence), |
| "scene_effect_sequence": list(self.scene_effect_sequence), |
| "render_defaults": deepcopy(self.render_defaults), |
| "metadata": deepcopy(self.metadata), |
| } |
|
|
|
|
| SCENE_EFFECTS: dict[str, dict[str, str]] = { |
| "none": {"label": "None", "filter": ""}, |
| "sharp_pop": { |
| "label": "Sharp Pop", |
| "filter": "eq=contrast=1.08:saturation=1.20:brightness=0.01,unsharp=5:5:0.8:3:3:0.4", |
| }, |
| "clean_beauty": { |
| "label": "Clean Beauty", |
| "filter": "hqdn3d=1.5:1.5:6:6,eq=saturation=1.08:contrast=1.03", |
| }, |
| "warm_glow": { |
| "label": "Warm Glow", |
| "filter": "eq=contrast=1.04:saturation=1.18:gamma_r=1.04:gamma_b=0.96,gblur=sigma=0.25", |
| }, |
| "cinematic": { |
| "label": "Cinematic", |
| "filter": "eq=contrast=1.14:saturation=0.95:brightness=-0.015,vignette=PI/6", |
| }, |
| "dreamy": { |
| "label": "Dreamy", |
| "filter": "gblur=sigma=0.6,eq=contrast=1.04:saturation=1.18:brightness=0.02", |
| }, |
| "flash_pop": { |
| "label": "Flash Pop", |
| "filter": "eq=contrast=1.16:saturation=1.25:brightness=0.035", |
| }, |
| "grain": { |
| "label": "Fine Grain", |
| "filter": "noise=alls=8:allf=t+u,eq=contrast=1.07:saturation=1.02", |
| }, |
| "motion_blur": { |
| "label": "Motion Blur", |
| "filter": "tmix=frames=3:weights='1 2 1',eq=contrast=1.05:saturation=1.08", |
| }, |
| "noir": { |
| "label": "Noir", |
| "filter": "hue=s=0,eq=contrast=1.18:brightness=-0.02", |
| }, |
| "glitch": { |
| "label": "Glitch", |
| "filter": "rgbashift=rh=4:bh=-4,eq=contrast=1.12:saturation=1.18", |
| }, |
| "rgb_split": { |
| "label": "RGB Split", |
| "filter": "rgbashift=rh=3:gv=1:bh=-3", |
| }, |
| "vhs": { |
| "label": "VHS", |
| "filter": "noise=alls=18:allf=t+u,eq=saturation=0.82:contrast=1.08", |
| }, |
| "crt": { |
| "label": "CRT", |
| "filter": "vignette=PI/4,noise=alls=10:allf=t+u,eq=contrast=1.15:saturation=0.9", |
| }, |
| "bloom": { |
| "label": "Bloom", |
| "filter": "gblur=sigma=0.35,eq=brightness=0.025:saturation=1.14", |
| }, |
| "glow": { |
| "label": "Glow", |
| "filter": "gblur=sigma=0.45,eq=contrast=1.05:brightness=0.03", |
| }, |
| "chromatic_aberration": { |
| "label": "Chromatic Aberration", |
| "filter": "rgbashift=rh=2:rv=1:bh=-2:bv=-1", |
| }, |
| "neon": { |
| "label": "Neon", |
| "filter": "eq=contrast=1.2:saturation=1.55:brightness=0.02", |
| }, |
| "cyberpunk": { |
| "label": "Cyberpunk", |
| "filter": "eq=contrast=1.18:saturation=1.45:gamma_r=1.08:gamma_b=1.18", |
| }, |
| "comic": { |
| "label": "Comic", |
| "filter": "edgedetect=low=0.08:high=0.25,eq=contrast=1.2:saturation=1.35", |
| }, |
| "cartoon": { |
| "label": "Cartoon", |
| "filter": "edgedetect=low=0.05:high=0.2,eq=saturation=1.45:contrast=1.15", |
| }, |
| "anime": { |
| "label": "Anime", |
| "filter": "eq=saturation=1.35:contrast=1.12:brightness=0.02,unsharp=5:5:0.6", |
| }, |
| "sketch": { |
| "label": "Sketch", |
| "filter": "edgedetect=low=0.03:high=0.18,hue=s=0", |
| }, |
| "oil_painting": { |
| "label": "Oil Painting", |
| "filter": "gblur=sigma=0.7,eq=saturation=1.25:contrast=1.1", |
| }, |
| "pixel_art": { |
| "label": "Pixel Art", |
| "filter": "scale=iw/8:ih/8,scale=iw*8:ih*8:flags=neighbor", |
| }, |
| } |
|
|
|
|
| CREATIVE_STYLES: dict[str, CreativeStyle] = { |
| "viral_shorts": CreativeStyle( |
| key="viral_shorts", |
| label="Viral Shorts", |
| description="Fast vertical pacing, punchy captions, bright contrast, and whip-style movement.", |
| platform="tiktok", |
| template="tiktok_zoom", |
| scene_duration=2.4, |
| transition_sequence=("whip", "flash", "zoom", "glitch"), |
| scene_effect_sequence=("sharp_pop", "flash_pop", "clean_beauty"), |
| render_defaults={ |
| "subtitle_format": "ass", |
| "auto_subtitles": True, |
| "audio_normalize": True, |
| "music_volume": 0.24, |
| "music_fade_in": 0.25, |
| "music_fade_out": 0.8, |
| "music_ducking": True, |
| "normalize": True, |
| }, |
| metadata={"energy": "high", "best_for": "hooks, offers, memes, short promos"}, |
| ), |
| "product_demo": CreativeStyle( |
| key="product_demo", |
| label="Product Demo", |
| description="Clean cuts, readable captions, and polished color for launches and tutorials.", |
| platform="instagram_reels", |
| template="modern_minimal", |
| scene_duration=3.2, |
| transition_sequence=("slide", "wipe_left", "push", "dissolve"), |
| scene_effect_sequence=("clean_beauty", "sharp_pop"), |
| render_defaults={ |
| "subtitle_format": "ass", |
| "auto_subtitles": True, |
| "audio_normalize": True, |
| "music_volume": 0.18, |
| "music_fade_in": 0.3, |
| "music_fade_out": 0.7, |
| "music_ducking": True, |
| "normalize": True, |
| }, |
| metadata={"energy": "medium", "best_for": "software demos, ecommerce, tutorials"}, |
| ), |
| "story_vlog": CreativeStyle( |
| key="story_vlog", |
| label="Story Vlog", |
| description="Warm color, softer movement, and natural pacing for personality-led edits.", |
| platform="instagram_reels", |
| template="tiktok_classic", |
| scene_duration=3.8, |
| transition_sequence=("fade", "smooth_right", "dissolve"), |
| scene_effect_sequence=("warm_glow", "dreamy", "clean_beauty"), |
| render_defaults={ |
| "subtitle_format": "ass", |
| "auto_subtitles": True, |
| "audio_normalize": True, |
| "music_volume": 0.22, |
| "music_fade_in": 0.5, |
| "music_fade_out": 1.0, |
| "music_ducking": True, |
| "normalize": True, |
| }, |
| metadata={"energy": "medium", "best_for": "founder updates, day-in-life edits, testimonials"}, |
| ), |
| "podcast_clip": CreativeStyle( |
| key="podcast_clip", |
| label="Podcast Clip", |
| description="Square-safe framing, calmer captions, and narration-first audio treatment.", |
| platform="instagram_feed_square", |
| template="podcast_style", |
| scene_duration=5.0, |
| transition_sequence=("fade", "dissolve"), |
| scene_effect_sequence=("clean_beauty", "sharp_pop"), |
| render_defaults={ |
| "subtitle_format": "ass", |
| "auto_subtitles": True, |
| "audio_normalize": True, |
| "music_volume": 0.12, |
| "music_fade_in": 0.6, |
| "music_fade_out": 1.2, |
| "music_ducking": True, |
| "normalize": True, |
| }, |
| metadata={"energy": "low", "best_for": "interviews, audiograms, education"}, |
| ), |
| "cinematic_story": CreativeStyle( |
| key="cinematic_story", |
| label="Cinematic Story", |
| description="Deeper contrast, film grain, and slower transitions for mini-documentary edits.", |
| platform="youtube_shorts", |
| template="modern_minimal", |
| scene_duration=4.2, |
| transition_sequence=("dissolve", "fadeblack", "smooth_left"), |
| scene_effect_sequence=("cinematic", "grain"), |
| render_defaults={ |
| "subtitle_format": "ass", |
| "auto_subtitles": True, |
| "audio_normalize": True, |
| "music_volume": 0.26, |
| "music_fade_in": 0.8, |
| "music_fade_out": 1.4, |
| "music_ducking": True, |
| "normalize": True, |
| }, |
| metadata={"energy": "low", "best_for": "brand films, travel, documentary shorts"}, |
| ), |
| "news_explainer": CreativeStyle( |
| key="news_explainer", |
| label="News Explainer", |
| description="Readable lower-third style captions with stable landscape or vertical exports.", |
| platform="youtube_1080p", |
| template="news_style", |
| scene_duration=4.0, |
| transition_sequence=("wipe_left", "slide", "fade"), |
| scene_effect_sequence=("sharp_pop", "clean_beauty"), |
| render_defaults={ |
| "subtitle_format": "ass", |
| "auto_subtitles": False, |
| "audio_normalize": True, |
| "music_volume": 0.1, |
| "music_fade_in": 0.5, |
| "music_fade_out": 1.0, |
| "music_ducking": True, |
| "normalize": True, |
| }, |
| metadata={"energy": "medium", "best_for": "explainers, news, thought leadership"}, |
| ), |
| } |
|
|
|
|
| def apply_creative_style(payload: dict[str, Any]) -> dict[str, Any]: |
| style_key = payload.get("creative_style") or payload.get("metadata", {}).get("creative_style") |
| if not style_key: |
| return payload |
|
|
| style = get_creative_style(str(style_key)) |
| output = deepcopy(payload) |
| output["creative_style"] = style.key |
| _set_default(output, "platform", style.platform) |
| _set_default(output, "template", style.template) |
| for key, value in style.render_defaults.items(): |
| _set_default(output, key, deepcopy(value)) |
|
|
| metadata = deepcopy(style.metadata) |
| metadata.update(output.get("metadata", {})) |
| metadata["creative_style"] = style.key |
| metadata["creative_style_label"] = style.label |
| output["metadata"] = metadata |
|
|
| scenes = output.get("scenes") |
| if isinstance(scenes, list): |
| output["scenes"] = [_style_scene(scene, style, index) for index, scene in enumerate(scenes)] |
| return output |
|
|
|
|
| def get_creative_style(key: str | None) -> CreativeStyle: |
| if not key: |
| return CREATIVE_STYLES["viral_shorts"] |
| return CREATIVE_STYLES.get(key, CREATIVE_STYLES["viral_shorts"]) |
|
|
|
|
| def list_creative_styles() -> list[str]: |
| return list(CREATIVE_STYLES.keys()) |
|
|
|
|
| def creative_style_metadata() -> dict[str, dict[str, Any]]: |
| return {key: style.metadata_payload() for key, style in CREATIVE_STYLES.items()} |
|
|
|
|
| def list_scene_effects() -> list[str]: |
| return list(SCENE_EFFECTS.keys()) |
|
|
|
|
| def scene_effect_metadata() -> dict[str, dict[str, str]]: |
| return {key: {"label": value["label"]} for key, value in SCENE_EFFECTS.items()} |
|
|
|
|
| def scene_effect_filter(key: str | None) -> str: |
| if not key: |
| return "" |
| return SCENE_EFFECTS.get(key, SCENE_EFFECTS["none"])["filter"] |
|
|
|
|
| def _set_default(payload: dict[str, Any], key: str, value: Any) -> None: |
| if key not in payload or payload[key] in (None, ""): |
| payload[key] = value |
|
|
|
|
| def _style_scene(scene: Any, style: CreativeStyle, index: int) -> Any: |
| if not isinstance(scene, dict): |
| return scene |
| styled = deepcopy(scene) |
| transition = styled.get("transition") |
| if transition in (None, "", "fade"): |
| styled["transition"] = style.transition_sequence[index % len(style.transition_sequence)] |
| if styled.get("effect") in (None, ""): |
| styled["effect"] = style.scene_effect_sequence[index % len(style.scene_effect_sequence)] |
| _set_default(styled, "background", "blur") |
| _set_default(styled, "layout", "fill") |
| return styled |
|
|