topcoderkz commited on
Commit
b620472
·
0 Parent(s):

Initial commit: Content automation system framework

Browse files
.env.example ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # API Keys - Fill these with your actual keys
2
+ GEMINI_API_KEY=your_gemini_api_key_here
3
+ RUNWAYML_API_KEY=your_runwayml_api_key_here
4
+ TTS_API_KEY=your_tts_api_key_here
5
+ GCS_BUCKET_NAME=your_bucket_name_here
6
+
7
+ # Configuration
8
+ AUDIO_LIBRARY_SIZE=27
9
+ VIDEO_LIBRARY_SIZE=47
10
+ DEFAULT_VOICE=en-US-AriaNeural
.gitattributes ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.jpg filter=lfs diff=lfs merge=lfs -text
2
+ *.jpeg filter=lfs diff=lfs merge=lfs -text
3
+ *.png filter=lfs diff=lfs merge=lfs -text
4
+ *.gif filter=lfs diff=lfs merge=lfs -text
5
+ *.mp3 filter=lfs diff=lfs merge=lfs -text
6
+ *.mp4 filter=lfs diff=lfs merge=lfs -text
7
+ *.wav filter=lfs diff=lfs merge=lfs -text
8
+ *.ttf filter=lfs diff=lfs merge=lfs -text
9
+ *.db filter=lfs diff=lfs merge=lfs -text
10
+ sliding_puzzle filter=lfs diff=lfs merge=lfs -text
11
+ stockfish/stockfish-ubuntu-x86-64-avx2 filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Environment variables
2
+ .env
3
+ *.env
4
+ .venv
5
+
6
+ # API keys
7
+ *_key.txt
8
+ *_secret.yaml
9
+
10
+ # Output files
11
+ outputs/videos/*
12
+ !outputs/videos/.gitkeep
13
+ outputs/logs/*.log
14
+
15
+ # Temporary files
16
+ *.tmp
17
+ *.temp
18
+ __pycache__/
19
+ *.pyc
20
+ *.pyo
21
+ *.pyd
22
+ .DS_Store
23
+ .thumbs.db
24
+
25
+ # Large files
26
+ *.mp4
27
+ *.mp3
28
+ *.wav
29
+ *.avi
README.md ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Content Automation System
2
+ A Python-based automated video content creation system that generates videos using AI APIs, selects relevant footage from a library, adds text-to-speech audio, and produces finished videos with subtitles.
3
+
4
+ ## Quick Start
5
+
6
+ ### Prerequisites
7
+ - Python 3.8+
8
+ - API keys for:
9
+ - Google Gemini
10
+ - RunwayML
11
+ - Text-to-Speech service (Azure/Google/Amazon)
12
+ - Google Cloud Storage
13
+
14
+ ### Installation
15
+
16
+ ```bash
17
+ git clone <your-repo>
18
+ cd content-automation
19
+ python -m venv venv
20
+ source venv/bin/activate
21
+ pip install -r requirements.txt
22
+ cp .env.example .env
23
+ # Edit .env with your actual API keys
24
+ python src/main.py
25
+ ```
config/api_keys.yaml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # API Configuration
2
+ gemini:
3
+ base_url: "https://generativelanguage.googleapis.com/v1beta"
4
+ model: "gemini-pro"
5
+
6
+ runwayml:
7
+ base_url: "https://api.runwayml.com/v1"
8
+ timeout: 300
9
+
10
+ tts:
11
+ provider: "azure" # or "google", "amazon"
12
+ voice: "en-US-AriaNeural"
13
+ rate: "medium"
14
+
15
+ gcs:
16
+ bucket: "somira-videos"
17
+ video_prefix: "automated-content/"
config/content_strategies.yaml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Content Strategies Template
2
+ commercial:
3
+ gemini_prompt_template: |
4
+ A photorealistic, comical yet painfully real depiction of {subject}
5
+ in a {setting}. {style_notes}. Format: {aspect_ratio}.
6
+
7
+ runway_prompt_template: |
8
+ {camera_movement}: {action}. {scene_description}.
9
+ {style_notes}. Vertical {aspect_ratio}.
10
+
11
+ styles:
12
+ - name: "commercial"
13
+ camera_movement: "Slow push-in camera"
14
+ style_notes: "Photorealistic, cinematic, bright high-key lighting"
15
+
16
+ - name: "educational"
17
+ camera_movement: "Static shot"
18
+ style_notes: "Clean, professional, even lighting"
19
+
20
+ # Video library metadata
21
+ video_categories:
22
+ product_demo:
23
+ tags: ["somira massager", "product", "demo"]
24
+ usage: "When script mentions product features or demonstration"
25
+
26
+ solution_highlight:
27
+ tags: ["solution", "relief", "comfort"]
28
+ usage: "When script discusses problem-solving or benefits"
29
+
30
+ customer_experience:
31
+ tags: ["satisfaction", "experience", "testimonial"]
32
+ usage: "When script shares customer stories or results"
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ aiohttp>=3.8.0
2
+ google-cloud-storage>=2.0.0
3
+ moviepy>=1.0.3
4
+ openai>=1.0.0
5
+ python-dotenv>=1.0.0
6
+ pyyaml>=6.0
7
+ asyncio>=3.4.3
8
+ pillow>=9.0.0
9
+ numpy>=1.21.0
setup.sh ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ echo "Setting up Content Automation System..."
3
+
4
+ # Create directories
5
+ mkdir -p config src assets/video_library assets/audio_library outputs/videos outputs/logs
6
+
7
+ # Run all the creation commands from above (you'd paste all the cat commands here)
8
+ # [Paste all the file creation commands from above here]
9
+
10
+ echo "✅ Setup complete!"
11
+ echo "📝 Next steps:"
12
+ echo "1. Edit .env with your API keys"
13
+ echo "2. Run: pip install -r requirements.txt"
14
+ echo "3. Run: python src/main.py"
src/api_clients.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ API clients for external services
3
+ """
4
+ import aiohttp
5
+ import json
6
+ from utils import logger
7
+
8
+ class APIClients:
9
+ def __init__(self, config):
10
+ self.config = config
11
+
12
+ async def enhance_prompt(self, prompt):
13
+ """Enhance prompt using Gemini API"""
14
+ # Simplified implementation - replace with actual API call
15
+ logger.info(f"Enhancing prompt: {prompt[:100]}...")
16
+ return prompt # Placeholder
17
+
18
+ async def generate_video(self, prompt):
19
+ """Generate video using RunwayML API"""
20
+ # Simplified implementation - replace with actual API call
21
+ logger.info(f"Generating video with prompt: {prompt[:100]}...")
22
+ return "generated_video_url" # Placeholder
23
+
24
+ async def generate_tts(self, text):
25
+ """Generate TTS audio"""
26
+ # Simplified implementation - replace with actual API call
27
+ logger.info(f"Generating TTS for text: {text[:100]}...")
28
+ return {
29
+ 'audio_url': 'generated_audio_url',
30
+ 'lip_sync_data': {'timestamps': []} # Placeholder
31
+ }
32
+
33
+ async def select_videos(self, tts_script, count=3):
34
+ """AI agent selects videos based on script"""
35
+ keywords = self._extract_keywords(tts_script)
36
+ logger.info(f"Selecting {count} videos for keywords: {keywords}")
37
+
38
+ # Simplified video selection logic
39
+ selected_videos = []
40
+ for i in range(min(count, 3)): # Max 3 videos
41
+ video_id = (hash(tts_script) + i) % self.config['video_library_size'] + 1
42
+ selected_videos.append({
43
+ 'id': video_id,
44
+ 'url': f'gs://somira-videos/library/video{video_id}.mp4',
45
+ 'reason': f'Matches keyword: {keywords[i % len(keywords)] if keywords else "general"}'
46
+ })
47
+
48
+ return selected_videos
49
+
50
+ async def store_in_gcs(self, file_path):
51
+ """Store file in Google Cloud Storage"""
52
+ logger.info(f"Storing file in GCS: {file_path}")
53
+ # Simplified implementation
54
+ return f"gs://{self.config['gcs_bucket']}/videos/{hash(file_path)}.mp4"
55
+
56
+ def _extract_keywords(self, text):
57
+ """Extract keywords from TTS script"""
58
+ text_lower = text.lower()
59
+ keywords = []
60
+
61
+ key_phrases = [
62
+ 'somira massager', 'neck pain', 'product', 'massager',
63
+ 'solution', 'comfort', 'using the product', 'relaxation'
64
+ ]
65
+
66
+ for phrase in key_phrases:
67
+ if phrase in text_lower:
68
+ keywords.append(phrase)
69
+
70
+ return keywords if keywords else ['general']
src/automation.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Main automation orchestrator
3
+ """
4
+ import asyncio
5
+ from api_clients import APIClients
6
+ from video_renderer import VideoRenderer
7
+ from utils import logger
8
+
9
+ class ContentAutomation:
10
+ def __init__(self, config):
11
+ self.config = config
12
+ self.api_clients = APIClients(config)
13
+ self.video_renderer = VideoRenderer(config)
14
+ self.current_audio_index = 0
15
+
16
+ async def execute_pipeline(self, content_strategy, tts_script):
17
+ """Execute the complete automation pipeline"""
18
+ logger.info("Starting automation pipeline...")
19
+
20
+ # Step 1: Simultaneous execution
21
+ assets = await self.execute_step_1(content_strategy, tts_script)
22
+
23
+ # Step 2: Merge and render
24
+ rendered_video = await self.video_renderer.render_video(assets)
25
+
26
+ # Step 3: Add subtitles
27
+ subtitled_video = await self.video_renderer.add_subtitles(rendered_video, tts_script)
28
+
29
+ # Step 4: Store in GCS
30
+ final_url = await self.api_clients.store_in_gcs(subtitled_video)
31
+
32
+ logger.info(f"Pipeline completed. Video stored at: {final_url}")
33
+ return final_url
34
+
35
+ async def execute_step_1(self, content_strategy, tts_script):
36
+ """Execute all step 1 processes simultaneously"""
37
+ tasks = [
38
+ self.generate_hook_video(content_strategy),
39
+ self.select_background_music(),
40
+ self.select_videos_from_library(tts_script),
41
+ self.generate_tts_audio(tts_script)
42
+ ]
43
+
44
+ results = await asyncio.gather(*tasks, return_exceptions=True)
45
+
46
+ return {
47
+ 'hook_video': results[0],
48
+ 'background_music': results[1],
49
+ 'selected_videos': results[2],
50
+ 'tts_audio': results[3]
51
+ }
52
+
53
+ async def generate_hook_video(self, strategy):
54
+ """Generate hook video using AI APIs"""
55
+ try:
56
+ # Enhance prompt with Gemini
57
+ enhanced_prompt = await self.api_clients.enhance_prompt(strategy['gemini_prompt'])
58
+
59
+ # Generate video with RunwayML
60
+ video_url = await self.api_clients.generate_video(enhanced_prompt)
61
+ return video_url
62
+
63
+ except Exception as e:
64
+ logger.error(f"Hook video generation failed: {e}")
65
+ return None
66
+
67
+ async def select_background_music(self):
68
+ """Select background music linearly"""
69
+ audio_index = self.current_audio_index
70
+ self.current_audio_index = (self.current_audio_index + 1) % self.config['audio_library_size']
71
+
72
+ audio_url = f"https://storage.googleapis.com/somira/{audio_index + 1}.mp3"
73
+ logger.info(f"Selected background music: {audio_url}")
74
+ return audio_url
75
+
76
+ async def select_videos_from_library(self, tts_script):
77
+ """AI agent selects 3 videos based on TTS script"""
78
+ try:
79
+ selected_videos = await self.api_clients.select_videos(tts_script, count=3)
80
+ return selected_videos
81
+ except Exception as e:
82
+ logger.error(f"Video selection failed: {e}")
83
+ return []
84
+
85
+ async def generate_tts_audio(self, tts_script):
86
+ """Generate TTS audio with lip-sync data"""
87
+ try:
88
+ tts_result = await self.api_clients.generate_tts(tts_script)
89
+ return tts_result
90
+ except Exception as e:
91
+ logger.error(f"TTS generation failed: {e}")
92
+ return None
src/main.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Main entry point for Content Automation System
4
+ """
5
+ import asyncio
6
+ import os
7
+ from dotenv import load_dotenv
8
+ from automation import ContentAutomation
9
+
10
+ # Load environment variables
11
+ load_dotenv()
12
+
13
+ async def main():
14
+ """Main execution function"""
15
+ print("🚀 Starting Content Automation System...")
16
+
17
+ # Configuration
18
+ config = {
19
+ 'gemini_api_key': os.getenv('GEMINI_API_KEY'),
20
+ 'runwayml_api_key': os.getenv('RUNWAYML_API_KEY'),
21
+ 'tts_api_key': os.getenv('TTS_API_KEY'),
22
+ 'gcs_bucket': os.getenv('GCS_BUCKET_NAME'),
23
+ 'audio_library_size': int(os.getenv('AUDIO_LIBRARY_SIZE', 27)),
24
+ 'video_library_size': int(os.getenv('VIDEO_LIBRARY_SIZE', 47))
25
+ }
26
+
27
+ # Initialize automation system
28
+ automation = ContentAutomation(config)
29
+
30
+ # Example content strategy
31
+ content_strategy = {
32
+ 'gemini_prompt': 'A photorealistic, comical yet painfully real depiction of an attractive blond, blue-eyed female stuck in a neck spasm nightmare in a luxurious home setting.',
33
+ 'runway_prompt': 'Slow push-in camera: a blond woman suddenly tilts her head stiffly to the side and blinks in surprise, face frozen like mid-sneeze.',
34
+ 'style': 'commercial',
35
+ 'aspect_ratio': '9:16'
36
+ }
37
+
38
+ # Example TTS script
39
+ tts_script = """
40
+ I heard a pop, and suddenly my neck was stuck. I looked like I was mid-sneeze all day.
41
+ After one minute with the Somira massager it was gone. If you ever feel neck pain,
42
+ you'll wish you bought one, because the moment I turned my head.
43
+ """
44
+
45
+ try:
46
+ # Execute automation pipeline
47
+ final_video_url = await automation.execute_pipeline(content_strategy, tts_script)
48
+ print(f"✅ Automation completed! Final video: {final_video_url}")
49
+
50
+ except Exception as e:
51
+ print(f"❌ Automation failed: {e}")
52
+
53
+ if __name__ == "__main__":
54
+ asyncio.run(main())
src/utils.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Utility functions and logging
3
+ """
4
+ import logging
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ # Setup logging
9
+ def setup_logging():
10
+ """Configure logging"""
11
+ log_dir = Path("outputs/logs")
12
+ log_dir.mkdir(parents=True, exist_ok=True)
13
+
14
+ logging.basicConfig(
15
+ level=logging.INFO,
16
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
17
+ handlers=[
18
+ logging.FileHandler(log_dir / 'automation.log'),
19
+ logging.StreamHandler(sys.stdout)
20
+ ]
21
+ )
22
+
23
+ setup_logging()
24
+ logger = logging.getLogger(__name__)
25
+
26
+ def validate_environment():
27
+ """Validate that required environment variables are set"""
28
+ required_vars = ['GEMINI_API_KEY', 'RUNWAYML_API_KEY', 'TTS_API_KEY']
29
+ missing_vars = [var for var in required_vars if not os.getenv(var)]
30
+
31
+ if missing_vars:
32
+ raise EnvironmentError(f"Missing required environment variables: {', '.join(missing_vars)}")
33
+
34
+ logger.info("Environment validation passed")
src/video_renderer.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Video rendering and subtitle engine
3
+ """
4
+ import os
5
+ from utils import logger
6
+
7
+ class VideoRenderer:
8
+ def __init__(self, config):
9
+ self.config = config
10
+
11
+ async def render_video(self, assets):
12
+ """Render final video by merging all assets"""
13
+ logger.info("Rendering video with assets...")
14
+
15
+ # Simplified implementation - replace with actual video rendering
16
+ # This would use moviepy or similar library
17
+
18
+ hook_video = assets.get('hook_video')
19
+ background_music = assets.get('background_music')
20
+ selected_videos = assets.get('selected_videos', [])
21
+ tts_audio = assets.get('tts_audio')
22
+
23
+ logger.info(f"Merging {len(selected_videos)} selected videos")
24
+ logger.info(f"Using hook video: {hook_video}")
25
+ logger.info(f"Using background music: {background_music}")
26
+
27
+ # Placeholder for actual video rendering logic
28
+ output_path = "outputs/videos/rendered_video.mp4"
29
+ logger.info(f"Video rendered to: {output_path}")
30
+
31
+ return output_path
32
+
33
+ async def add_subtitles(self, video_path, tts_script):
34
+ """Add subtitles to video"""
35
+ logger.info("Adding subtitles to video...")
36
+
37
+ # Simplified implementation - replace with actual subtitle engine
38
+ # This would add subtitles in the middle of the screen
39
+
40
+ subtitles = self._generate_subtitle_segments(tts_script)
41
+ logger.info(f"Generated {len(subtitles)} subtitle segments")
42
+
43
+ # Placeholder for actual subtitle rendering
44
+ output_path = video_path.replace('.mp4', '_subtitled.mp4')
45
+ logger.info(f"Subtitled video saved to: {output_path}")
46
+
47
+ return output_path
48
+
49
+ def _generate_subtitle_segments(self, text):
50
+ """Generate subtitle segments from text"""
51
+ sentences = [s.strip() + '.' for s in text.split('.') if s.strip()]
52
+ segments = []
53
+
54
+ for i, sentence in enumerate(sentences):
55
+ segments.append({
56
+ 'text': sentence,
57
+ 'start_time': i * 3, # 3 seconds per segment
58
+ 'end_time': (i + 1) * 3,
59
+ 'position': 'middle' # Your nuance: middle of screen
60
+ })
61
+
62
+ return segments