refactor: Adjust import paths to use `src.config` and enhance text clip rendering with dynamic font sizing and vertical positioning.
Browse files- src/a2e_avatar.py +1 -1
- src/asset_manager/asset_downloader.py +1 -1
- src/asset_manager/asset_processor.py +1 -1
- src/asset_manager/audio_lib.py +1 -1
- src/asset_manager/content_strategy_lib.py +1 -1
- src/asset_manager/text_overlay_lib.py +2 -2
- src/asset_manager/video_lib.py +1 -1
- src/cleanup_manager.py +1 -1
- src/config.py +2 -1
- src/delete_old_videos.py +1 -1
- src/execution_tracker.py +1 -1
- src/main.py +1 -1
- src/pipeline_processor.py +1 -1
- src/pipelines/ai_pipeline.py +1 -1
- src/pipelines/avatar_ai_pipeline.py +1 -1
- src/pipelines/base.py +1 -1
- src/pipelines/beats_cut_pipeline.py +1 -1
- src/pipelines/factory.py +1 -1
- src/pipelines/hard_cut_pipeline.py +1 -1
- src/pipelines/non_ai_pipeline.py +1 -1
- src/pipelines/standard_ai_pipeline.py +1 -1
- src/runwayml/generate_video.py +1 -1
- src/video_editor/safe_zone.py +8 -7
- src/video_editor/text_clip.py +32 -7
- src/video_generation_process.py +1 -1
- src/video_renderer.py +6 -5
- src/workflows/content_strategy_workflow.py +1 -1
- src/workflows/plain_video_workflow.py +1 -1
src/a2e_avatar.py
CHANGED
|
@@ -11,7 +11,7 @@ import os
|
|
| 11 |
from utils import logger
|
| 12 |
from google_src import ai_studio_sdk
|
| 13 |
import json_repair
|
| 14 |
-
from config import get_config_value, set_config_value
|
| 15 |
from moviepy.editor import AudioFileClip
|
| 16 |
|
| 17 |
import uuid
|
|
|
|
| 11 |
from utils import logger
|
| 12 |
from google_src import ai_studio_sdk
|
| 13 |
import json_repair
|
| 14 |
+
from src.config import get_config_value, set_config_value
|
| 15 |
from moviepy.editor import AudioFileClip
|
| 16 |
|
| 17 |
import uuid
|
src/asset_manager/asset_downloader.py
CHANGED
|
@@ -11,7 +11,7 @@ from urllib.parse import urlparse
|
|
| 11 |
|
| 12 |
from utils import logger, is_valid_video, resize_video, remove_black_padding
|
| 13 |
from file_downloader import get_file_downloader
|
| 14 |
-
from config import get_config_value
|
| 15 |
from .video_lib import get_video_lib, VideoLib
|
| 16 |
|
| 17 |
|
|
|
|
| 11 |
|
| 12 |
from utils import logger, is_valid_video, resize_video, remove_black_padding
|
| 13 |
from file_downloader import get_file_downloader
|
| 14 |
+
from src.config import get_config_value
|
| 15 |
from .video_lib import get_video_lib, VideoLib
|
| 16 |
|
| 17 |
|
src/asset_manager/asset_processor.py
CHANGED
|
@@ -12,7 +12,7 @@ import json_repair
|
|
| 12 |
from moviepy.editor import VideoFileClip
|
| 13 |
from google_src import ai_studio_sdk
|
| 14 |
from utils import logger
|
| 15 |
-
from config import get_config_value
|
| 16 |
from .video_lib import get_video_lib
|
| 17 |
|
| 18 |
|
|
|
|
| 12 |
from moviepy.editor import VideoFileClip
|
| 13 |
from google_src import ai_studio_sdk
|
| 14 |
from utils import logger
|
| 15 |
+
from src.config import get_config_value
|
| 16 |
from .video_lib import get_video_lib
|
| 17 |
|
| 18 |
|
src/asset_manager/audio_lib.py
CHANGED
|
@@ -10,7 +10,7 @@ from typing import Optional, List
|
|
| 10 |
from utils import logger, clean_and_drop_empty
|
| 11 |
from google_src.google_sheet import GoogleSheetReader
|
| 12 |
from google_src import get_default_wrapper, GCloudWrapper
|
| 13 |
-
from config import get_config_value
|
| 14 |
class AudioLib:
|
| 15 |
"""
|
| 16 |
Singleton class that loads and manages audio library from Google Sheets.
|
|
|
|
| 10 |
from utils import logger, clean_and_drop_empty
|
| 11 |
from google_src.google_sheet import GoogleSheetReader
|
| 12 |
from google_src import get_default_wrapper, GCloudWrapper
|
| 13 |
+
from src.config import get_config_value
|
| 14 |
class AudioLib:
|
| 15 |
"""
|
| 16 |
Singleton class that loads and manages audio library from Google Sheets.
|
src/asset_manager/content_strategy_lib.py
CHANGED
|
@@ -9,7 +9,7 @@ from typing import Optional, List
|
|
| 9 |
from utils import logger, clean_and_drop_empty
|
| 10 |
from google_src.google_sheet import GoogleSheetReader
|
| 11 |
from google_src import get_default_wrapper, GCloudWrapper
|
| 12 |
-
from config import get_config_value
|
| 13 |
|
| 14 |
|
| 15 |
|
|
|
|
| 9 |
from utils import logger, clean_and_drop_empty
|
| 10 |
from google_src.google_sheet import GoogleSheetReader
|
| 11 |
from google_src import get_default_wrapper, GCloudWrapper
|
| 12 |
+
from src.config import get_config_value
|
| 13 |
|
| 14 |
|
| 15 |
|
src/asset_manager/text_overlay_lib.py
CHANGED
|
@@ -9,7 +9,7 @@ from typing import Optional
|
|
| 9 |
from utils import logger, clean_and_drop_empty
|
| 10 |
from google_src.google_sheet import GoogleSheetReader
|
| 11 |
from google_src import get_default_wrapper, GCloudWrapper
|
| 12 |
-
from config import get_config_value
|
| 13 |
|
| 14 |
|
| 15 |
|
|
@@ -107,7 +107,7 @@ class TextOverlayLib:
|
|
| 107 |
# Increment index for next call (loop back to start if needed)
|
| 108 |
self._current_index = (self._current_index + 1) % len(self._text_overlay_library)
|
| 109 |
|
| 110 |
-
return
|
| 111 |
|
| 112 |
def reset_index(self) -> None:
|
| 113 |
"""Reset text overlay index to start from beginning (useful for batch processing)"""
|
|
|
|
| 9 |
from utils import logger, clean_and_drop_empty
|
| 10 |
from google_src.google_sheet import GoogleSheetReader
|
| 11 |
from google_src import get_default_wrapper, GCloudWrapper
|
| 12 |
+
from src.config import get_config_value
|
| 13 |
|
| 14 |
|
| 15 |
|
|
|
|
| 107 |
# Increment index for next call (loop back to start if needed)
|
| 108 |
self._current_index = (self._current_index + 1) % len(self._text_overlay_library)
|
| 109 |
|
| 110 |
+
return "Wanting to start a theme page? Here's everything you need. 1000 clips. $50."
|
| 111 |
|
| 112 |
def reset_index(self) -> None:
|
| 113 |
"""Reset text overlay index to start from beginning (useful for batch processing)"""
|
src/asset_manager/video_lib.py
CHANGED
|
@@ -9,7 +9,7 @@ from typing import Optional, List, Dict
|
|
| 9 |
from utils import logger, clean_and_drop_empty
|
| 10 |
from google_src.google_sheet import GoogleSheetReader
|
| 11 |
from google_src import get_default_wrapper, GCloudWrapper
|
| 12 |
-
from config import get_config_value
|
| 13 |
|
| 14 |
|
| 15 |
|
|
|
|
| 9 |
from utils import logger, clean_and_drop_empty
|
| 10 |
from google_src.google_sheet import GoogleSheetReader
|
| 11 |
from google_src import get_default_wrapper, GCloudWrapper
|
| 12 |
+
from src.config import get_config_value
|
| 13 |
|
| 14 |
|
| 15 |
|
src/cleanup_manager.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import os
|
| 2 |
import shutil
|
| 3 |
from datetime import datetime
|
| 4 |
-
from config import get_config_value
|
| 5 |
from utils import logger
|
| 6 |
from google_src.google_sheet import GoogleSheetReader
|
| 7 |
from google_src.gcs_utils import delete_gcs_file
|
|
|
|
| 1 |
import os
|
| 2 |
import shutil
|
| 3 |
from datetime import datetime
|
| 4 |
+
from src.config import get_config_value
|
| 5 |
from utils import logger
|
| 6 |
from google_src.google_sheet import GoogleSheetReader
|
| 7 |
from google_src.gcs_utils import delete_gcs_file
|
src/config.py
CHANGED
|
@@ -256,7 +256,8 @@ def configure_job_environment(job_index: int):
|
|
| 256 |
"GOOGLE_AISTUDIO_3RD_API_KEY",
|
| 257 |
"GOOGLE_AISTUDIO_IMAGEN_API_KEY",
|
| 258 |
"GOOGLE_AISTUDIO_IMAGEN_API_KEY",
|
| 259 |
-
"GOOGLE_AI_API_KEY"
|
|
|
|
| 260 |
]
|
| 261 |
|
| 262 |
os.environ["RUNWAYML_API_KEY"] = os.getenv("RUNWAYML_API_KEY", "")
|
|
|
|
| 256 |
"GOOGLE_AISTUDIO_3RD_API_KEY",
|
| 257 |
"GOOGLE_AISTUDIO_IMAGEN_API_KEY",
|
| 258 |
"GOOGLE_AISTUDIO_IMAGEN_API_KEY",
|
| 259 |
+
"GOOGLE_AI_API_KEY",
|
| 260 |
+
"GEMINI_API_KEY"
|
| 261 |
]
|
| 262 |
|
| 263 |
os.environ["RUNWAYML_API_KEY"] = os.getenv("RUNWAYML_API_KEY", "")
|
src/delete_old_videos.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import os
|
| 2 |
from google.cloud import storage
|
| 3 |
from google_src.gcs_utils import get_gcs_client
|
| 4 |
-
from config import get_config_value
|
| 5 |
|
| 6 |
def delete_old_videos(bucket_name, prefix, count=9):
|
| 7 |
"""Deletes the first `count` blobs in the bucket with the given prefix."""
|
|
|
|
| 1 |
import os
|
| 2 |
from google.cloud import storage
|
| 3 |
from google_src.gcs_utils import get_gcs_client
|
| 4 |
+
from src.config import get_config_value
|
| 5 |
|
| 6 |
def delete_old_videos(bucket_name, prefix, count=9):
|
| 7 |
"""Deletes the first `count` blobs in the bucket with the given prefix."""
|
src/execution_tracker.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
from datetime import datetime
|
| 2 |
-
from config import get_config_value
|
| 3 |
from google_src.google_sheet import GoogleSheetReader
|
| 4 |
from utils import logger
|
| 5 |
|
|
|
|
| 1 |
from datetime import datetime
|
| 2 |
+
from src.config import get_config_value
|
| 3 |
from google_src.google_sheet import GoogleSheetReader
|
| 4 |
from utils import logger
|
| 5 |
|
src/main.py
CHANGED
|
@@ -5,7 +5,7 @@ import os
|
|
| 5 |
|
| 6 |
# Add project root to sys.path to allow 'from src.config import ...'
|
| 7 |
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
| 8 |
-
from config import get_config_value, configure_job_environment
|
| 9 |
from pipeline_processor import download_all_library_videos
|
| 10 |
from google_src.gcs_utils import list_gcs_files
|
| 11 |
from workflows import run_content_strategy_workflow, run_plain_video_workflow
|
|
|
|
| 5 |
|
| 6 |
# Add project root to sys.path to allow 'from src.config import ...'
|
| 7 |
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
| 8 |
+
from src.config import get_config_value, configure_job_environment
|
| 9 |
from pipeline_processor import download_all_library_videos
|
| 10 |
from google_src.gcs_utils import list_gcs_files
|
| 11 |
from workflows import run_content_strategy_workflow, run_plain_video_workflow
|
src/pipeline_processor.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import os
|
| 2 |
import asyncio
|
| 3 |
from utils import logger, clean_tts_script
|
| 4 |
-
from config import get_config_value, set_config_value
|
| 5 |
from pipelines.factory import get_automation_pipeline
|
| 6 |
from asset_manager import get_asset_downloader
|
| 7 |
from google_src.gcs_utils import list_gcs_files
|
|
|
|
| 1 |
import os
|
| 2 |
import asyncio
|
| 3 |
from utils import logger, clean_tts_script
|
| 4 |
+
from src.config import get_config_value, set_config_value
|
| 5 |
from pipelines.factory import get_automation_pipeline
|
| 6 |
from asset_manager import get_asset_downloader
|
| 7 |
from google_src.gcs_utils import list_gcs_files
|
src/pipelines/ai_pipeline.py
CHANGED
|
@@ -5,7 +5,7 @@ from moviepy.editor import AudioFileClip
|
|
| 5 |
from abc import abstractmethod
|
| 6 |
|
| 7 |
from utils import logger
|
| 8 |
-
from config import get_config_value, set_config_value
|
| 9 |
from google_src.gcs_utils import upload_file_to_gcs
|
| 10 |
from video_generation_process import generate_video_process
|
| 11 |
from google_src import ai_studio_sdk
|
|
|
|
| 5 |
from abc import abstractmethod
|
| 6 |
|
| 7 |
from utils import logger
|
| 8 |
+
from src.config import get_config_value, set_config_value
|
| 9 |
from google_src.gcs_utils import upload_file_to_gcs
|
| 10 |
from video_generation_process import generate_video_process
|
| 11 |
from google_src import ai_studio_sdk
|
src/pipelines/avatar_ai_pipeline.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
from moviepy.editor import AudioFileClip
|
| 2 |
from utils import logger
|
| 3 |
-
from config import get_config_value, set_config_value
|
| 4 |
from pipelines.ai_pipeline import AIContentAutomationBase
|
| 5 |
from a2e_avatar import create_greenscreen_video_workflow
|
| 6 |
|
|
|
|
| 1 |
from moviepy.editor import AudioFileClip
|
| 2 |
from utils import logger
|
| 3 |
+
from src.config import get_config_value, set_config_value
|
| 4 |
from pipelines.ai_pipeline import AIContentAutomationBase
|
| 5 |
from a2e_avatar import create_greenscreen_video_workflow
|
| 6 |
|
src/pipelines/base.py
CHANGED
|
@@ -5,7 +5,7 @@ from typing import Dict, Any, Optional
|
|
| 5 |
|
| 6 |
from utils import logger
|
| 7 |
import utils
|
| 8 |
-
from config import get_config_value, set_config_value
|
| 9 |
from google_src.gcs_utils import upload_file_to_gcs
|
| 10 |
from video_renderer import VideoRenderer
|
| 11 |
from google_src.tts import GoogleTTS
|
|
|
|
| 5 |
|
| 6 |
from utils import logger
|
| 7 |
import utils
|
| 8 |
+
from src.config import get_config_value, set_config_value
|
| 9 |
from google_src.gcs_utils import upload_file_to_gcs
|
| 10 |
from video_renderer import VideoRenderer
|
| 11 |
from google_src.tts import GoogleTTS
|
src/pipelines/beats_cut_pipeline.py
CHANGED
|
@@ -2,7 +2,7 @@ from typing import Dict, Any
|
|
| 2 |
from moviepy.editor import AudioFileClip
|
| 3 |
from utils import logger
|
| 4 |
import utils
|
| 5 |
-
from config import get_config_value
|
| 6 |
from pipelines.non_ai_pipeline import NonAIContentAutomationBase
|
| 7 |
|
| 8 |
class BeatsCutPipeline(NonAIContentAutomationBase):
|
|
|
|
| 2 |
from moviepy.editor import AudioFileClip
|
| 3 |
from utils import logger
|
| 4 |
import utils
|
| 5 |
+
from src.config import get_config_value
|
| 6 |
from pipelines.non_ai_pipeline import NonAIContentAutomationBase
|
| 7 |
|
| 8 |
class BeatsCutPipeline(NonAIContentAutomationBase):
|
src/pipelines/factory.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from config import get_config_value
|
| 2 |
from pipelines.base import ContentAutomationBase
|
| 3 |
from pipelines.standard_ai_pipeline import StandardAIPipeline
|
| 4 |
from pipelines.avatar_ai_pipeline import AvatarAIPipeline
|
|
|
|
| 1 |
+
from src.config import get_config_value
|
| 2 |
from pipelines.base import ContentAutomationBase
|
| 3 |
from pipelines.standard_ai_pipeline import StandardAIPipeline
|
| 4 |
from pipelines.avatar_ai_pipeline import AvatarAIPipeline
|
src/pipelines/hard_cut_pipeline.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
from typing import Dict, Any
|
| 2 |
from moviepy.editor import AudioFileClip
|
| 3 |
from utils import logger
|
| 4 |
-
from config import get_config_value
|
| 5 |
from pipelines.non_ai_pipeline import NonAIContentAutomationBase
|
| 6 |
|
| 7 |
class HardCutPipeline(NonAIContentAutomationBase):
|
|
|
|
| 1 |
from typing import Dict, Any
|
| 2 |
from moviepy.editor import AudioFileClip
|
| 3 |
from utils import logger
|
| 4 |
+
from src.config import get_config_value
|
| 5 |
from pipelines.non_ai_pipeline import NonAIContentAutomationBase
|
| 6 |
|
| 7 |
class HardCutPipeline(NonAIContentAutomationBase):
|
src/pipelines/non_ai_pipeline.py
CHANGED
|
@@ -7,7 +7,7 @@ from abc import abstractmethod
|
|
| 7 |
|
| 8 |
from utils import logger
|
| 9 |
import utils
|
| 10 |
-
from config import get_config_value, set_config_value
|
| 11 |
from pipelines.base import ContentAutomationBase
|
| 12 |
|
| 13 |
class NonAIContentAutomationBase(ContentAutomationBase):
|
|
|
|
| 7 |
|
| 8 |
from utils import logger
|
| 9 |
import utils
|
| 10 |
+
from src.config import get_config_value, set_config_value
|
| 11 |
from pipelines.base import ContentAutomationBase
|
| 12 |
|
| 13 |
class NonAIContentAutomationBase(ContentAutomationBase):
|
src/pipelines/standard_ai_pipeline.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
from utils import logger
|
| 2 |
-
from config import get_config_value, set_config_value
|
| 3 |
from pipelines.ai_pipeline import AIContentAutomationBase
|
| 4 |
|
| 5 |
class StandardAIPipeline(AIContentAutomationBase):
|
|
|
|
| 1 |
from utils import logger
|
| 2 |
+
from src.config import get_config_value, set_config_value
|
| 3 |
from pipelines.ai_pipeline import AIContentAutomationBase
|
| 4 |
|
| 5 |
class StandardAIPipeline(AIContentAutomationBase):
|
src/runwayml/generate_video.py
CHANGED
|
@@ -4,7 +4,7 @@ import asyncio
|
|
| 4 |
import base64
|
| 5 |
import logging
|
| 6 |
from typing import Dict, Tuple, Optional
|
| 7 |
-
from config import get_config_value
|
| 8 |
|
| 9 |
logger = logging.getLogger(__name__)
|
| 10 |
|
|
|
|
| 4 |
import base64
|
| 5 |
import logging
|
| 6 |
from typing import Dict, Tuple, Optional
|
| 7 |
+
from src.config import get_config_value
|
| 8 |
|
| 9 |
logger = logging.getLogger(__name__)
|
| 10 |
|
src/video_editor/safe_zone.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
from dataclasses import dataclass
|
| 2 |
from typing import Tuple, Literal
|
| 3 |
import os
|
| 4 |
-
from config import get_config_value
|
| 5 |
|
| 6 |
@dataclass
|
| 7 |
class SafeZone:
|
|
@@ -17,12 +17,13 @@ class SafeZone:
|
|
| 17 |
|
| 18 |
@staticmethod
|
| 19 |
def set_safe_zone_from_env():
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
| 26 |
|
| 27 |
@staticmethod
|
| 28 |
def get_caption_position(
|
|
|
|
| 1 |
from dataclasses import dataclass
|
| 2 |
from typing import Tuple, Literal
|
| 3 |
import os
|
| 4 |
+
from src.config import get_config_value
|
| 5 |
|
| 6 |
@dataclass
|
| 7 |
class SafeZone:
|
|
|
|
| 17 |
|
| 18 |
@staticmethod
|
| 19 |
def set_safe_zone_from_env():
|
| 20 |
+
pass
|
| 21 |
+
# if get_config_value("on_screen_text", False):
|
| 22 |
+
# print("⚠️ ON_SCREEN_TEXT enabled: Using full height safe zones")
|
| 23 |
+
# SafeZone.TOP_SAFE_ZONE = 0
|
| 24 |
+
# SafeZone.BOTTOM_SAFE_ZONE = 1600
|
| 25 |
+
# SafeZone.LEFT_SAFE_ZONE = 0
|
| 26 |
+
# SafeZone.RIGHT_SAFE_ZONE = 0
|
| 27 |
|
| 28 |
@staticmethod
|
| 29 |
def get_caption_position(
|
src/video_editor/text_clip.py
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from moviepy.editor import ImageClip, CompositeVideoClip, VideoClip, ColorClip
|
| 2 |
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
| 3 |
import numpy as np
|
|
@@ -30,18 +38,18 @@ PROGRESSIVE_FADE_DURATION = 0.2 # Duration for each word to softly fade in
|
|
| 30 |
KINETIC_TRANSITION_DURATION = 0.3 # Duration for slide and fade for the kinetic effect
|
| 31 |
|
| 32 |
|
| 33 |
-
def create(text, duration, target_width, target_height, start_time, fontsize=
|
| 34 |
word_timings=None, bg_color=None, word_color=None, enable_zoom=True,
|
| 35 |
highlight_color=None, highlight_word_bg_color=None,
|
| 36 |
bg_corner_radius=None, wrap_bg_tightly=False, font="Fonts/Bungee-Regular.ttf",
|
| 37 |
enable_glow=False, glow_radius=15, glow_intensity=3, entry_direction=None, enable_pulse=False,
|
| 38 |
-
enable_kinetic_pop=False, enable_progressive_view=False, enable_kinetic_typography=False, stroke_blur=False, shadow_offset=SHADOW_OFFSET, stroke_width=STROKE_WIDTH, height_stretch_factor=1.0, enable_blink_to_appear=False, safe_zone_padding=SAFE_ZONE_PADDING):
|
| 39 |
"""
|
| 40 |
Creates a text clip with optional word-by-word highlighting and global animations.
|
| 41 |
"""
|
| 42 |
try:
|
| 43 |
scale_factor = target_width / STANDARD_VIDEO_WIDTH
|
| 44 |
-
font = ImageFont.truetype(font, int(
|
| 45 |
|
| 46 |
ascent, descent = font.getmetrics()
|
| 47 |
line_height_from_font = ascent + descent
|
|
@@ -226,11 +234,11 @@ def create(text, duration, target_width, target_height, start_time, fontsize=10,
|
|
| 226 |
if enable_blink_to_appear: animated_clip = apply_blink_in_animation(animated_clip, duration=0.1)
|
| 227 |
|
| 228 |
if entry_direction:
|
| 229 |
-
final_pos = ("center", SafeZone.get_caption_position(video_width=target_width, video_height=target_height, caption_height=animated_clip.h, position=
|
| 230 |
if enable_kinetic_typography: final_pos = (0, 0)
|
| 231 |
animated_clip = apply_directional_entry_animation(animated_clip, direction=entry_direction, target_size=(target_width, target_height), final_pos=final_pos, duration=min(ENTRY_DURATION, duration))
|
| 232 |
elif not enable_kinetic_typography:
|
| 233 |
-
final_pos = ("center", SafeZone.get_caption_position(video_width=target_width, video_height=target_height, caption_height=animated_clip.h, position=
|
| 234 |
animated_clip = animated_clip.set_position(final_pos)
|
| 235 |
|
| 236 |
return CompositeVideoClip([animated_clip.set_start(start_time)], size=(target_width, target_height))
|
|
@@ -630,10 +638,22 @@ def caption_style_on_screen_text():
|
|
| 630 |
"font": "Fonts/Ubuntu-Bold.ttf"
|
| 631 |
}
|
| 632 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 633 |
if __name__ == "__main__":
|
| 634 |
print("🎬 Testing text_clip animation styles...\n")
|
| 635 |
VIDEO_WIDTH, VIDEO_HEIGHT = 1080, 1920
|
| 636 |
-
test_text = "
|
| 637 |
|
| 638 |
test_configs = [
|
| 639 |
{
|
|
@@ -717,6 +737,11 @@ if __name__ == "__main__":
|
|
| 717 |
},
|
| 718 |
]
|
| 719 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 720 |
try:
|
| 721 |
words_list = test_text.split()
|
| 722 |
time_per_word, gap_time = 0.2, 0.05
|
|
@@ -733,7 +758,7 @@ if __name__ == "__main__":
|
|
| 733 |
create_args = {k: v for k, v in config.items() if k not in ['name', 'output']}
|
| 734 |
output_filename = config.get('output', 'default_output.mp4')
|
| 735 |
|
| 736 |
-
phrases = group_words_by_time_and_width(test_word_timings, 1080, 1920, max_words_per_group = 2)
|
| 737 |
duration_per_phrase = total_duration / len(phrases)
|
| 738 |
text_clips = []
|
| 739 |
for i, phrase in enumerate(phrases):
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
# Add src to sys.path to allow running as a script
|
| 5 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
| 6 |
+
# Add project root to sys.path to allow imports from src.config
|
| 7 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
|
| 8 |
+
|
| 9 |
from moviepy.editor import ImageClip, CompositeVideoClip, VideoClip, ColorClip
|
| 10 |
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
| 11 |
import numpy as np
|
|
|
|
| 38 |
KINETIC_TRANSITION_DURATION = 0.3 # Duration for slide and fade for the kinetic effect
|
| 39 |
|
| 40 |
|
| 41 |
+
def create(text, duration, target_width, target_height, start_time, fontsize=60,
|
| 42 |
word_timings=None, bg_color=None, word_color=None, enable_zoom=True,
|
| 43 |
highlight_color=None, highlight_word_bg_color=None,
|
| 44 |
bg_corner_radius=None, wrap_bg_tightly=False, font="Fonts/Bungee-Regular.ttf",
|
| 45 |
enable_glow=False, glow_radius=15, glow_intensity=3, entry_direction=None, enable_pulse=False,
|
| 46 |
+
enable_kinetic_pop=False, enable_progressive_view=False, enable_kinetic_typography=False, stroke_blur=False, shadow_offset=SHADOW_OFFSET, stroke_width=STROKE_WIDTH, height_stretch_factor=1.0, enable_blink_to_appear=False, safe_zone_padding=SAFE_ZONE_PADDING, vertical_position=VERTICAL_POSITION):
|
| 47 |
"""
|
| 48 |
Creates a text clip with optional word-by-word highlighting and global animations.
|
| 49 |
"""
|
| 50 |
try:
|
| 51 |
scale_factor = target_width / STANDARD_VIDEO_WIDTH
|
| 52 |
+
font = ImageFont.truetype(font, int(fontsize * scale_factor))
|
| 53 |
|
| 54 |
ascent, descent = font.getmetrics()
|
| 55 |
line_height_from_font = ascent + descent
|
|
|
|
| 234 |
if enable_blink_to_appear: animated_clip = apply_blink_in_animation(animated_clip, duration=0.1)
|
| 235 |
|
| 236 |
if entry_direction:
|
| 237 |
+
final_pos = ("center", SafeZone.get_caption_position(video_width=target_width, video_height=target_height, caption_height=animated_clip.h, position=vertical_position, padding=safe_zone_padding)[1])
|
| 238 |
if enable_kinetic_typography: final_pos = (0, 0)
|
| 239 |
animated_clip = apply_directional_entry_animation(animated_clip, direction=entry_direction, target_size=(target_width, target_height), final_pos=final_pos, duration=min(ENTRY_DURATION, duration))
|
| 240 |
elif not enable_kinetic_typography:
|
| 241 |
+
final_pos = ("center", SafeZone.get_caption_position(video_width=target_width, video_height=target_height, caption_height=animated_clip.h, position=vertical_position, padding=safe_zone_padding)[1])
|
| 242 |
animated_clip = animated_clip.set_position(final_pos)
|
| 243 |
|
| 244 |
return CompositeVideoClip([animated_clip.set_start(start_time)], size=(target_width, target_height))
|
|
|
|
| 638 |
"font": "Fonts/Ubuntu-Bold.ttf"
|
| 639 |
}
|
| 640 |
|
| 641 |
+
def caption_style_on_screen_text_top():
|
| 642 |
+
return {
|
| 643 |
+
"enable_zoom": False,
|
| 644 |
+
"stroke_blur": True,
|
| 645 |
+
"shadow_offset": 4,
|
| 646 |
+
"stroke_width": 4,
|
| 647 |
+
"height_stretch_factor": 1.2,
|
| 648 |
+
"safe_zone_padding": 0,
|
| 649 |
+
"vertical_position": "top",
|
| 650 |
+
"font": "Fonts/Ubuntu-Bold.ttf"
|
| 651 |
+
}
|
| 652 |
+
|
| 653 |
if __name__ == "__main__":
|
| 654 |
print("🎬 Testing text_clip animation styles...\n")
|
| 655 |
VIDEO_WIDTH, VIDEO_HEIGHT = 1080, 1920
|
| 656 |
+
test_text = "Wanting to start a theme page? Here's everything you need. 1000 clips. $50."
|
| 657 |
|
| 658 |
test_configs = [
|
| 659 |
{
|
|
|
|
| 737 |
},
|
| 738 |
]
|
| 739 |
|
| 740 |
+
test_configs = [{
|
| 741 |
+
"name": "On Screen Text",
|
| 742 |
+
**caption_style_on_screen_text()
|
| 743 |
+
}]
|
| 744 |
+
|
| 745 |
try:
|
| 746 |
words_list = test_text.split()
|
| 747 |
time_per_word, gap_time = 0.2, 0.05
|
|
|
|
| 758 |
create_args = {k: v for k, v in config.items() if k not in ['name', 'output']}
|
| 759 |
output_filename = config.get('output', 'default_output.mp4')
|
| 760 |
|
| 761 |
+
phrases = group_words_by_time_and_width(test_word_timings, 1080, 1920, max_words_per_group = 2, group_all=True)
|
| 762 |
duration_per_phrase = total_duration / len(phrases)
|
| 763 |
text_clips = []
|
| 764 |
for i, phrase in enumerate(phrases):
|
src/video_generation_process.py
CHANGED
|
@@ -6,7 +6,7 @@ import time
|
|
| 6 |
import asyncio
|
| 7 |
from typing import Dict, Optional
|
| 8 |
|
| 9 |
-
from config import get_config_value
|
| 10 |
from google_src import ai_studio_sdk
|
| 11 |
from runwayml.generate_video import generate_video_runway
|
| 12 |
from utils import logger
|
|
|
|
| 6 |
import asyncio
|
| 7 |
from typing import Dict, Optional
|
| 8 |
|
| 9 |
+
from src.config import get_config_value
|
| 10 |
from google_src import ai_studio_sdk
|
| 11 |
from runwayml.generate_video import generate_video_runway
|
| 12 |
from utils import logger
|
src/video_renderer.py
CHANGED
|
@@ -30,7 +30,7 @@ import subprocess
|
|
| 30 |
import asyncio
|
| 31 |
import random
|
| 32 |
from moviepy.video.fx import crop
|
| 33 |
-
from config import get_config_value, set_config_value
|
| 34 |
import numpy as np
|
| 35 |
|
| 36 |
ALLOWED_BG_MUSIC_VOLUME = 0.08
|
|
@@ -634,13 +634,13 @@ class VideoRenderer:
|
|
| 634 |
wrapped_text = "\n".join(textwrap.wrap(text, width=max_chars_per_line))
|
| 635 |
line_count = len(wrapped_text.split("\n"))
|
| 636 |
|
| 637 |
-
# Adjusted font sizes (
|
| 638 |
if line_count > 2:
|
| 639 |
-
fontsize =
|
| 640 |
elif line_count > 1:
|
| 641 |
-
fontsize =
|
| 642 |
else:
|
| 643 |
-
fontsize =
|
| 644 |
|
| 645 |
text_clip = create_text_clip(
|
| 646 |
text,
|
|
@@ -1061,6 +1061,7 @@ class VideoRenderer:
|
|
| 1061 |
"caption_3": (caption_style_3, 3),
|
| 1062 |
"caption_4": (caption_style_4, 3),
|
| 1063 |
"caption_style_on_screen_text": (caption_style_on_screen_text, 3),
|
|
|
|
| 1064 |
}
|
| 1065 |
|
| 1066 |
if caption_style == "random":
|
|
|
|
| 30 |
import asyncio
|
| 31 |
import random
|
| 32 |
from moviepy.video.fx import crop
|
| 33 |
+
from src.config import get_config_value, set_config_value
|
| 34 |
import numpy as np
|
| 35 |
|
| 36 |
ALLOWED_BG_MUSIC_VOLUME = 0.08
|
|
|
|
| 634 |
wrapped_text = "\n".join(textwrap.wrap(text, width=max_chars_per_line))
|
| 635 |
line_count = len(wrapped_text.split("\n"))
|
| 636 |
|
| 637 |
+
# Adjusted font sizes (smaller to avoid hitting top safe zone)
|
| 638 |
if line_count > 2:
|
| 639 |
+
fontsize = 55 # Reduced from 75 to keep 3 lines lower
|
| 640 |
elif line_count > 1:
|
| 641 |
+
fontsize = 75 # Moderate size for 2 lines
|
| 642 |
else:
|
| 643 |
+
fontsize = 85 # Large size for single line
|
| 644 |
|
| 645 |
text_clip = create_text_clip(
|
| 646 |
text,
|
|
|
|
| 1061 |
"caption_3": (caption_style_3, 3),
|
| 1062 |
"caption_4": (caption_style_4, 3),
|
| 1063 |
"caption_style_on_screen_text": (caption_style_on_screen_text, 3),
|
| 1064 |
+
"caption_style_on_screen_text_top": (caption_style_on_screen_text_top, 3),
|
| 1065 |
}
|
| 1066 |
|
| 1067 |
if caption_style == "random":
|
src/workflows/content_strategy_workflow.py
CHANGED
|
@@ -3,7 +3,7 @@ from execution_tracker import load_executed_from_gsheet, log_progress_to_gsheet
|
|
| 3 |
from pipeline_processor import process_single_row, configure_row_settings
|
| 4 |
from cleanup_manager import process_delete_entries
|
| 5 |
from asset_manager import get_content_strategy_lib
|
| 6 |
-
from config import get_config_value, set_config_value
|
| 7 |
from utils import logger
|
| 8 |
|
| 9 |
async def run_content_strategy_workflow(commit=False, job_index=None, total_jobs=None):
|
|
|
|
| 3 |
from pipeline_processor import process_single_row, configure_row_settings
|
| 4 |
from cleanup_manager import process_delete_entries
|
| 5 |
from asset_manager import get_content_strategy_lib
|
| 6 |
+
from src.config import get_config_value, set_config_value
|
| 7 |
from utils import logger
|
| 8 |
|
| 9 |
async def run_content_strategy_workflow(commit=False, job_index=None, total_jobs=None):
|
src/workflows/plain_video_workflow.py
CHANGED
|
@@ -2,7 +2,7 @@ import uuid
|
|
| 2 |
from execution_tracker import load_executed_from_gsheet, log_progress_to_gsheet
|
| 3 |
from pipeline_processor import process_single_row, configure_row_settings
|
| 4 |
from cleanup_manager import process_delete_entries
|
| 5 |
-
from config import get_config_value
|
| 6 |
from utils import logger
|
| 7 |
|
| 8 |
async def run_plain_video_workflow(commit=False, job_index=None, total_jobs=None):
|
|
|
|
| 2 |
from execution_tracker import load_executed_from_gsheet, log_progress_to_gsheet
|
| 3 |
from pipeline_processor import process_single_row, configure_row_settings
|
| 4 |
from cleanup_manager import process_delete_entries
|
| 5 |
+
from src.config import get_config_value
|
| 6 |
from utils import logger
|
| 7 |
|
| 8 |
async def run_plain_video_workflow(commit=False, job_index=None, total_jobs=None):
|