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 @@
+
+ Define filename patterns for automatic platform and aspect ratio detection. Use regular expressions or simple text patterns. Patterns to identify platform from filename (e.g., "_tiktok_", "_meta_") Patterns to identify aspect ratio from filename (e.g., "_16x9_", "_1x1_")Naming Conventions
+ Platform Detection Patterns
+ Aspect Ratio Detection Patterns
+