diff --git a/.gitignore b/.gitignore index 3fdd994..1db840e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ backend/outputs/* # Generated platform specifications backend/platform_specs.json +backend/naming_conventions.json # macOS .DS_Store diff --git a/backend/app.py b/backend/app.py index 5f0b680..d35ecae 100644 --- a/backend/app.py +++ b/backend/app.py @@ -12,6 +12,8 @@ from datetime import datetime from video_processor import VideoProcessor from platform_specs import ( PLATFORM_SPECS, + FILENAME_PATTERNS, + ASPECT_RATIO_PATTERNS, detect_platform_from_filename, detect_aspect_ratio_from_filename, get_all_platforms, @@ -25,6 +27,8 @@ CORS(app) # Store factory defaults (original specs from platform_specs.py) import copy FACTORY_DEFAULTS = copy.deepcopy(PLATFORM_SPECS) +FACTORY_FILENAME_PATTERNS = copy.deepcopy(FILENAME_PATTERNS) +FACTORY_ASPECT_RATIO_PATTERNS = copy.deepcopy(ASPECT_RATIO_PATTERNS) # Configuration UPLOAD_FOLDER = os.path.join(os.path.dirname(__file__), 'uploads') @@ -278,6 +282,7 @@ def cleanup_files(file_id): # ============================================================================ SPECS_FILE = os.path.join(os.path.dirname(__file__), 'platform_specs.json') +NAMING_FILE = os.path.join(os.path.dirname(__file__), 'naming_conventions.json') def save_specs_to_file(specs): @@ -303,6 +308,33 @@ def load_specs_from_file(): return None +def save_naming_conventions(platform_patterns, aspect_ratio_patterns): + """Save naming conventions to JSON file""" + try: + data = { + 'platform_patterns': platform_patterns, + 'aspect_ratio_patterns': aspect_ratio_patterns + } + with open(NAMING_FILE, 'w') as f: + json.dump(data, f, indent=2) + return True + except Exception as e: + print(f"Error saving naming conventions: {e}") + return False + + +def load_naming_conventions(): + """Load naming conventions from JSON file""" + try: + if os.path.exists(NAMING_FILE): + with open(NAMING_FILE, 'r') as f: + return json.load(f) + return None + except Exception as e: + print(f"Error loading naming conventions: {e}") + return None + + @app.route('/api/admin/platforms', methods=['POST']) def admin_add_platform(): """Add a new platform configuration""" @@ -432,13 +464,21 @@ def admin_reset_factory(): PLATFORM_SPECS.clear() PLATFORM_SPECS.update(copy.deepcopy(FACTORY_DEFAULTS)) - # Delete the saved JSON file if it exists + FILENAME_PATTERNS.clear() + FILENAME_PATTERNS.update(copy.deepcopy(FACTORY_FILENAME_PATTERNS)) + + ASPECT_RATIO_PATTERNS.clear() + ASPECT_RATIO_PATTERNS.update(copy.deepcopy(FACTORY_ASPECT_RATIO_PATTERNS)) + + # Delete the saved JSON files if they exist if os.path.exists(SPECS_FILE): os.remove(SPECS_FILE) + if os.path.exists(NAMING_FILE): + os.remove(NAMING_FILE) return jsonify({ 'success': True, - 'message': 'Platform specifications reset to factory defaults', + 'message': 'Platform specifications and naming conventions reset to factory defaults', 'platforms_count': len(PLATFORM_SPECS) }) @@ -446,6 +486,47 @@ def admin_reset_factory(): return jsonify({'error': str(e)}), 500 +@app.route('/api/admin/naming-conventions', methods=['GET']) +def admin_get_naming_conventions(): + """Get current naming conventions""" + try: + return jsonify({ + 'success': True, + 'platform_patterns': FILENAME_PATTERNS, + 'aspect_ratio_patterns': ASPECT_RATIO_PATTERNS + }) + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +@app.route('/api/admin/naming-conventions', methods=['POST']) +def admin_save_naming_conventions(): + """Save naming conventions""" + try: + data = request.get_json() + + platform_patterns = data.get('platform_patterns', {}) + aspect_ratio_patterns = data.get('aspect_ratio_patterns', {}) + + # Update in-memory patterns + FILENAME_PATTERNS.clear() + FILENAME_PATTERNS.update(platform_patterns) + + ASPECT_RATIO_PATTERNS.clear() + ASPECT_RATIO_PATTERNS.update(aspect_ratio_patterns) + + # Save to file + save_naming_conventions(platform_patterns, aspect_ratio_patterns) + + return jsonify({ + 'success': True, + 'message': 'Naming conventions saved successfully' + }) + + except Exception as e: + return jsonify({'error': str(e)}), 500 + + if __name__ == '__main__': # Load specs from file if exists saved_specs = load_specs_from_file() @@ -454,6 +535,15 @@ if __name__ == '__main__': PLATFORM_SPECS.update(saved_specs) print(f"Loaded {len(saved_specs)} platform configurations from file") + # Load naming conventions from file if exists + saved_naming = load_naming_conventions() + if saved_naming: + FILENAME_PATTERNS.clear() + FILENAME_PATTERNS.update(saved_naming.get('platform_patterns', {})) + ASPECT_RATIO_PATTERNS.clear() + ASPECT_RATIO_PATTERNS.update(saved_naming.get('aspect_ratio_patterns', {})) + print(f"Loaded naming conventions from file") + # Check FFmpeg installation if not VideoProcessor.check_ffmpeg_installed(): print("WARNING: FFmpeg not found. Please install FFmpeg to use video conversion features.") diff --git a/frontend/admin.css b/frontend/admin.css index 056f952..f937534 100644 --- a/frontend/admin.css +++ b/frontend/admin.css @@ -227,6 +227,107 @@ background-color: rgba(255, 196, 7, 0.05); } +/* Naming Conventions Section */ +.naming-section { + background-color: var(--secondary-black); + border-radius: 12px; + padding: 2rem; + margin-bottom: 3rem; +} + +.naming-section h2 { + font-size: 1.75rem; + font-weight: 600; + color: var(--primary-yellow); + margin-bottom: 0.5rem; +} + +.section-description { + color: var(--text-secondary); + margin-bottom: 2rem; + font-size: 0.9375rem; +} + +.naming-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); + gap: 2rem; + margin-bottom: 2rem; +} + +.naming-panel { + background-color: var(--primary-black); + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 1.5rem; +} + +.naming-panel h3 { + color: var(--primary-yellow); + font-size: 1.25rem; + margin-bottom: 0.5rem; +} + +.naming-panel .hint { + color: var(--text-muted); + font-size: 0.8125rem; + margin-bottom: 1rem; + display: block; +} + +.pattern-item { + background-color: var(--secondary-black); + border: 1px solid var(--border-color); + border-radius: 6px; + padding: 1rem; + margin-bottom: 0.75rem; + display: flex; + align-items: center; + gap: 1rem; +} + +.pattern-item:hover { + border-color: var(--primary-yellow); +} + +.pattern-select { + flex: 0 0 180px; +} + +.pattern-input { + flex: 1; +} + +.pattern-remove { + flex: 0 0 auto; + background: none; + border: none; + color: #ff0000; + font-size: 1.25rem; + cursor: pointer; + padding: 0.25rem 0.5rem; +} + +.pattern-remove:hover { + color: #ff6666; +} + +.btn-sm { + padding: 0.5rem 1rem; + font-size: 0.875rem; +} + +.naming-actions { + display: flex; + gap: 1rem; + margin-top: 2rem; +} + +.naming-actions .btn-primary, +.naming-actions .btn-secondary { + flex: 1; +} + /* Modal */ .modal { position: fixed; diff --git a/frontend/admin.html b/frontend/admin.html index c22b0e5..9c14008 100644 --- a/frontend/admin.html +++ b/frontend/admin.html @@ -62,6 +62,39 @@ + +
+

Naming Conventions

+

Define filename patterns for automatic platform and aspect ratio detection. Use regular expressions or simple text patterns.

+ +
+ +
+

Platform Detection Patterns

+

Patterns to identify platform from filename (e.g., "_tiktok_", "_meta_")

+
+ +
+ +
+ + +
+

Aspect Ratio Detection Patterns

+

Patterns to identify aspect ratio from filename (e.g., "_16x9_", "_1x1_")

+
+ +
+ +
+
+ +
+ + +
+
+