File size: 4,788 Bytes
ed85b4d
 
 
 
 
 
 
 
 
 
 
f76d838
 
ed85b4d
56af65e
ed85b4d
 
 
 
 
 
 
56af65e
ed85b4d
 
 
 
 
 
 
 
 
f912c03
ed85b4d
 
 
f912c03
ed85b4d
 
 
 
 
56af65e
ed85b4d
 
 
 
 
56af65e
ed85b4d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56af65e
ed85b4d
 
 
 
 
 
 
 
 
 
fc0bc36
 
ed85b4d
 
56af65e
ed85b4d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
"""
Common utilities and constants
Stable functions that rarely change
"""

import os
import time
from pathlib import Path
from typing import Dict, Any, List, Set

# Constants
IMG_EXTS: Set[str] = {'.jpg', '.jpeg', '.png', '.bmp', '.webp'}
VID_EXTS: Set[str] = {'.mp4', '.avi', '.mov', '.mkv'}

ASPECT: Dict[str, str] = {
    'youtube': '1920:1080',
    'reels': '1080:1920', 
    'tiktok': '1080:1920',
    'square': '1080:1080',
    'widescreen': '1920:1080'
}

QUALITY: Dict[str, Dict[str, str]] = {
    'fast': {'preset': 'ultrafast', 'crf': '28'},
    'balanced': {'preset': 'fast', 'crf': '23'},
    'quality': {'preset': 'medium', 'crf': '20'}, 
    'best': {'preset': 'slow', 'crf': '18'}
}

# Utility functions
def is_image(file_path: str) -> bool:
    """Check if file is supported image format"""
    return Path(file_path).suffix.lower() in IMG_EXTS

def is_video(file_path: str) -> bool:
    """Check if file is supported video format"""
    return Path(file_path).suffix.lower() in VID_EXTS

def is_media(file_path: str) -> bool:
    """Check if file is supported media format"""
    return is_image(file_path) or is_video(file_path)

def size_mb(file_path: str) -> float:
    """Get file size in MB"""
    if not os.path.exists(file_path):
        return 0.0
    return round(os.path.getsize(file_path) / (1024 * 1024), 2)

def unique_name(prefix: str, extension: str = '.mp4') -> str:
    """Generate unique filename with timestamp and PID"""
    return f"{prefix}_{int(time.time())}_{os.getpid()}{extension}"

def safe_filename(name: str) -> str:
    """Make filename safe for filesystem"""
    invalid_chars = '<>:"/\\|?*'
    for char in invalid_chars:
        name = name.replace(char, '_')
    return name[:100]  # Limit length

def format_duration(seconds: int) -> str:
    """Format seconds to human readable duration"""
    if seconds < 60:
        return f"{seconds}s"
    elif seconds < 3600:
        return f"{seconds // 60}m {seconds % 60}s"
    else:
        h = seconds // 3600
        m = (seconds % 3600) // 60
        return f"{h}h {m}m"

def estimate_output(input_files: List[str], duration_per_file: int = 4) -> Dict[str, Any]:
    """Estimate output video characteristics"""
    total_files = len(input_files)
    total_duration = total_files * duration_per_file
    
    # Rough size estimation (varies greatly by content)
    estimated_size_mb = total_duration * 2  # ~2MB per second for balanced quality
    
    return {
        'files': total_files,
        'duration': total_duration,
        'duration_fmt': format_duration(total_duration),
        'size_mb': round(estimated_size_mb, 1)
    }

def valid_template(template: Dict[str, Any]) -> bool:
    """Validate motion template structure"""
    required_fields = ['name', 'scale', 'pan', 'rotate', 'duration']
    
    if not all(field in template for field in required_fields):
        return False
    
    # Validate data types and ranges
    if not isinstance(template['duration'], (int, float)) or template['duration'] <= 0:
        return False
        
    if not (isinstance(template['scale'], list) and len(template['scale']) == 2):
        return False
        
    if not (isinstance(template['pan'], list) and len(template['pan']) == 4):
        return False
        
    if not (isinstance(template['rotate'], list) and len(template['rotate']) == 2):
        return False
    
    return True

def get_platform_info(aspect_key: str) -> Dict[str, str]:
    """Get platform information and recommendations"""
    platform_info = {
        'youtube': {
            'name': 'YouTube',
            'ratio': '16:9',
            'optimal_duration': '15-60s',
            'description': 'Horizontal format for YouTube Shorts and regular videos'
        },
        'reels': {
            'name': 'Instagram Reels',
            'ratio': '9:16', 
            'optimal_duration': '15-30s',
            'description': 'Vertical format optimized for mobile viewing'
        },
        'tiktok': {
            'name': 'TikTok',
            'ratio': '9:16',
            'optimal_duration': '15-60s', 
            'description': 'Vertical format for TikTok content'
        },
        'square': {
            'name': 'Instagram Post',
            'ratio': '1:1',
            'optimal_duration': '15-30s',
            'description': 'Square format for Instagram feed posts'
        },
        'widescreen': {
            'name': 'Widescreen',
            'ratio': '16:9',
            'optimal_duration': '30-120s',
            'description': 'Standard widescreen format for presentations'
        }
    }
    
    return platform_info.get(aspect_key, {
        'name': 'Custom',
        'ratio': 'Custom',
        'optimal_duration': 'Variable',
        'description': 'Custom aspect ratio'
    })