Complete video optimization tool with: - 21 platform configurations (Meta, TikTok, YouTube, Pinterest, Snapchat, Amazon) - FFmpeg-powered video conversion with H264, H265, and VP9 codecs - Python Flask backend with REST API - HTML/JS frontend with drag-drop interface - Black + #FFC407 color scheme with Montserrat font - Side-by-side video comparison player - Filename auto-detection for platform and aspect ratio - MAMP-compatible setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
306 lines
8.5 KiB
Python
306 lines
8.5 KiB
Python
"""
|
|
Platform specifications for video optimization
|
|
Based on L'Oreal CDMO Creative Optimization Documentation v1.1
|
|
"""
|
|
|
|
PLATFORM_SPECS = {
|
|
"meta": {
|
|
"name": "Meta (Facebook/Instagram)",
|
|
"codec": "libx264",
|
|
"container": "mp4",
|
|
"formats": [
|
|
{
|
|
"ratio": "1:1",
|
|
"size": "720x720",
|
|
"bitrate": "1000k",
|
|
"bitrate_min": "840k",
|
|
"bitrate_max": "1200k",
|
|
"audio": "128k"
|
|
},
|
|
{
|
|
"ratio": "16:9",
|
|
"size": "1280x720",
|
|
"bitrate": "1250k",
|
|
"bitrate_min": "1100k",
|
|
"bitrate_max": "1400k",
|
|
"audio": "128k"
|
|
},
|
|
{
|
|
"ratio": "4:5",
|
|
"size": "720x900",
|
|
"bitrate": "1000k",
|
|
"bitrate_min": "840k",
|
|
"bitrate_max": "1200k",
|
|
"audio": "128k"
|
|
},
|
|
{
|
|
"ratio": "9:16",
|
|
"size": "720x1280",
|
|
"bitrate": "1250k",
|
|
"bitrate_min": "1100k",
|
|
"bitrate_max": "1400k",
|
|
"audio": "128k"
|
|
}
|
|
]
|
|
},
|
|
"pinterest": {
|
|
"name": "Pinterest",
|
|
"codec": "libx264",
|
|
"container": "mp4",
|
|
"formats": [
|
|
{
|
|
"ratio": "1:1",
|
|
"size": "720x720",
|
|
"bitrate": "1250k",
|
|
"bitrate_min": "1100k",
|
|
"bitrate_max": "1400k",
|
|
"audio": "128k"
|
|
},
|
|
{
|
|
"ratio": "16:9",
|
|
"size": "1280x720",
|
|
"bitrate": "1495k",
|
|
"bitrate_min": "1300k",
|
|
"bitrate_max": "1690k",
|
|
"audio": "128k"
|
|
},
|
|
{
|
|
"ratio": "2:3",
|
|
"size": "1000x1500",
|
|
"bitrate": "1495k",
|
|
"bitrate_min": "1300k",
|
|
"bitrate_max": "1690k",
|
|
"audio": "128k",
|
|
"note": "Not tested - Reduce bitrate if possible + smaller size"
|
|
},
|
|
{
|
|
"ratio": "4:5",
|
|
"size": "720x900",
|
|
"bitrate": "1250k",
|
|
"bitrate_min": "1100k",
|
|
"bitrate_max": "1400k",
|
|
"audio": "128k"
|
|
},
|
|
{
|
|
"ratio": "9:16",
|
|
"size": "720x1280",
|
|
"bitrate": "1495k",
|
|
"bitrate_min": "1300k",
|
|
"bitrate_max": "1690k",
|
|
"audio": "128k"
|
|
}
|
|
]
|
|
},
|
|
"snapchat": {
|
|
"name": "Snapchat",
|
|
"codec": "libx264",
|
|
"container": "mp4",
|
|
"formats": [
|
|
{
|
|
"ratio": "16:9",
|
|
"size": "1280x720",
|
|
"bitrate": "1250k",
|
|
"bitrate_min": "1100k",
|
|
"bitrate_max": "1400k",
|
|
"audio": "128k"
|
|
},
|
|
{
|
|
"ratio": "9:16",
|
|
"size": "720x1280",
|
|
"bitrate": "1250k",
|
|
"bitrate_min": "1100k",
|
|
"bitrate_max": "1400k",
|
|
"audio": "128k"
|
|
}
|
|
]
|
|
},
|
|
"tiktok": {
|
|
"name": "TikTok",
|
|
"codec": "libx265",
|
|
"container": "mp4",
|
|
"formats": [
|
|
{
|
|
"ratio": "1:1",
|
|
"size": "640x640",
|
|
"bitrate": "1000k",
|
|
"bitrate_min": "840k",
|
|
"bitrate_max": "1200k",
|
|
"audio": "128k"
|
|
},
|
|
{
|
|
"ratio": "16:9",
|
|
"size": "960x540",
|
|
"bitrate": "1050k",
|
|
"bitrate_min": "840k",
|
|
"bitrate_max": "1300k",
|
|
"audio": "128k"
|
|
},
|
|
{
|
|
"ratio": "9:16",
|
|
"size": "540x960",
|
|
"bitrate": "1050k",
|
|
"bitrate_min": "840k",
|
|
"bitrate_max": "1300k",
|
|
"audio": "128k"
|
|
}
|
|
]
|
|
},
|
|
"youtube": {
|
|
"name": "YouTube & DV360 - All Devices",
|
|
"codec": "libvpx-vp9",
|
|
"container": "webm",
|
|
"formats": [
|
|
{
|
|
"ratio": "1:1",
|
|
"size": "720x720",
|
|
"bitrate": "1495k",
|
|
"bitrate_min": "1300k",
|
|
"bitrate_max": "1690k",
|
|
"audio": "128k",
|
|
"audio_codec": "libopus"
|
|
},
|
|
{
|
|
"ratio": "16:9",
|
|
"size": "1280x720",
|
|
"bitrate": "1650k",
|
|
"bitrate_min": "1300k",
|
|
"bitrate_max": "2000k",
|
|
"audio": "128k",
|
|
"audio_codec": "libopus"
|
|
},
|
|
{
|
|
"ratio": "4:5",
|
|
"size": "720x900",
|
|
"bitrate": "1495k",
|
|
"bitrate_min": "1300k",
|
|
"bitrate_max": "1690k",
|
|
"audio": "128k",
|
|
"audio_codec": "libopus"
|
|
},
|
|
{
|
|
"ratio": "9:16",
|
|
"size": "720x1280",
|
|
"bitrate": "1650k",
|
|
"bitrate_min": "1300k",
|
|
"bitrate_max": "2000k",
|
|
"audio": "128k",
|
|
"audio_codec": "libopus"
|
|
}
|
|
]
|
|
},
|
|
"youtube_ctv": {
|
|
"name": "YouTube - CTV Specific",
|
|
"codec": "libvpx-vp9",
|
|
"container": "webm",
|
|
"formats": [
|
|
{
|
|
"ratio": "16:9",
|
|
"size": "1920x1080",
|
|
"bitrate": "5150k",
|
|
"bitrate_min": "3300k",
|
|
"bitrate_max": "7000k",
|
|
"audio": "192k",
|
|
"audio_codec": "libopus"
|
|
}
|
|
]
|
|
},
|
|
"amazon_prime": {
|
|
"name": "Amazon Prime - CTV Specific",
|
|
"codec": "libx264",
|
|
"container": "mp4",
|
|
"formats": [
|
|
{
|
|
"ratio": "16:9",
|
|
"size": "1920x1080",
|
|
"bitrate": "15000k",
|
|
"bitrate_min": "15000k",
|
|
"bitrate_max": "15000k",
|
|
"audio": "192k",
|
|
"note": "Minimum Video Bitrate accepted by Prime is 15Mbit/s"
|
|
}
|
|
]
|
|
},
|
|
"amazon_freevee": {
|
|
"name": "Amazon Freevee - CTV Specific",
|
|
"codec": "libx264",
|
|
"container": "mp4",
|
|
"formats": [
|
|
{
|
|
"ratio": "16:9",
|
|
"size": "1920x1080",
|
|
"bitrate": "5750k",
|
|
"bitrate_min": "4500k",
|
|
"bitrate_max": "7000k",
|
|
"audio": "192k"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
# Filename pattern detection
|
|
FILENAME_PATTERNS = {
|
|
'meta': ['_meta_', '_fb_', '_ig_', '_facebook_', '_instagram_'],
|
|
'pinterest': ['_pinterest_', '_pin_'],
|
|
'snapchat': ['_snapchat_', '_snap_'],
|
|
'tiktok': ['_tiktok_', '_tt_'],
|
|
'youtube': ['_youtube_', '_yt_'],
|
|
'youtube_ctv': ['_youtube_ctv_', '_yt_ctv_', '_ctv_'],
|
|
'amazon_prime': ['_prime_', '_amazon_prime_'],
|
|
'amazon_freevee': ['_freevee_', '_amazon_freevee_']
|
|
}
|
|
|
|
# Aspect ratio patterns
|
|
ASPECT_RATIO_PATTERNS = {
|
|
'1:1': ['_1x1_', '_square_', '_1-1_'],
|
|
'16:9': ['_16x9_', '_landscape_', '_16-9_'],
|
|
'4:5': ['_4x5_', '_4-5_'],
|
|
'9:16': ['_9x16_', '_vertical_', '_9-16_', '_portrait_'],
|
|
'2:3': ['_2x3_', '_2-3_']
|
|
}
|
|
|
|
|
|
def detect_platform_from_filename(filename):
|
|
"""
|
|
Detect platform from filename patterns
|
|
Returns platform key or None
|
|
"""
|
|
filename_lower = filename.lower()
|
|
|
|
for platform, patterns in FILENAME_PATTERNS.items():
|
|
for pattern in patterns:
|
|
if pattern in filename_lower:
|
|
return platform
|
|
|
|
return None
|
|
|
|
|
|
def detect_aspect_ratio_from_filename(filename):
|
|
"""
|
|
Detect aspect ratio from filename patterns
|
|
Returns aspect ratio string or None
|
|
"""
|
|
filename_lower = filename.lower()
|
|
|
|
for ratio, patterns in ASPECT_RATIO_PATTERNS.items():
|
|
for pattern in patterns:
|
|
if pattern in filename_lower:
|
|
return ratio
|
|
|
|
return None
|
|
|
|
|
|
def get_all_platforms():
|
|
"""Return list of all platform keys"""
|
|
return list(PLATFORM_SPECS.keys())
|
|
|
|
|
|
def get_platform_formats(platform):
|
|
"""Get all available formats for a platform"""
|
|
if platform in PLATFORM_SPECS:
|
|
return PLATFORM_SPECS[platform]['formats']
|
|
return []
|
|
|
|
|
|
def get_platform_info(platform):
|
|
"""Get complete platform information"""
|
|
return PLATFORM_SPECS.get(platform, None)
|