import os import subprocess import uuid from flask import Flask, render_template, request, send_file, jsonify from werkzeug.utils import secure_filename import json import time app = Flask(__name__) app.config['UPLOAD_FOLDER'] = 'uploads' app.config['OUTPUT_FOLDER'] = 'outputs' app.config['MAX_CONTENT_LENGTH'] = 500 * 1024 * 1024 # 500MB max file size # Create necessary folders os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) os.makedirs(app.config['OUTPUT_FOLDER'], exist_ok=True) # Store original filenames original_filenames = {} ALLOWED_EXTENSIONS = {'mp4', 'avi', 'mov', 'mkv', 'wmv', 'flv', 'webm'} def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS def get_video_info(video_path): """Get video information using ffprobe""" try: cmd = [ 'ffprobe', '-v', 'quiet', '-print_format', 'json', '-show_format', '-show_streams', video_path ] result = subprocess.run(cmd, capture_output=True, text=True) info = json.loads(result.stdout) # Extract video stream info video_stream = next((s for s in info['streams'] if s['codec_type'] == 'video'), None) if video_stream: return { 'width': video_stream.get('width'), 'height': video_stream.get('height'), 'duration': float(info['format'].get('duration', 0)), 'codec': video_stream.get('codec_name') } return None except Exception as e: print(f"Error getting video info: {e}") return None @app.route('/') def index(): return render_template('index.html') @app.route('/upload', methods=['POST']) def upload_video(): if 'video' not in request.files: return jsonify({'error': 'No video file provided'}), 400 file = request.files['video'] if file.filename == '': return jsonify({'error': 'No file selected'}), 400 if file and allowed_file(file.filename): # Generate unique filename file_id = str(uuid.uuid4()) ext = file.filename.rsplit('.', 1)[1].lower() filename = f"{file_id}.{ext}" filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(filepath) # Get video information video_info = get_video_info(filepath) # Store original filename for later use original_filenames[file_id] = file.filename return jsonify({ 'file_id': file_id, 'filename': filename, 'original_name': file.filename, 'info': video_info }) return jsonify({'error': 'Invalid file type'}), 400 @app.route('/process', methods=['POST']) def process_video(): try: data = request.get_json() file_id = data.get('file_id') if not file_id: return jsonify({'error': 'No file ID provided'}), 400 # Find the uploaded file uploaded_files = [f for f in os.listdir(app.config['UPLOAD_FOLDER']) if f.startswith(file_id)] if not uploaded_files: return jsonify({'error': 'File not found'}), 404 input_path = os.path.join(app.config['UPLOAD_FOLDER'], uploaded_files[0]) output_filename = f"{file_id}_processed.mp4" output_path = os.path.join(app.config['OUTPUT_FOLDER'], output_filename) # Build ffmpeg command cmd = ['ffmpeg', '-i', input_path] # Add trim parameters if specified start_time = data.get('start_time') end_time = data.get('end_time') if start_time is not None and start_time > 0: cmd.extend(['-ss', str(start_time)]) if end_time is not None: duration = end_time - (start_time if start_time else 0) if duration > 0: cmd.extend(['-t', str(duration)]) # Build filter complex for crop and scale filters = [] # Add rotation filter if specified rotation = data.get('rotation', 0) if rotation == 90: filters.append('transpose=1') elif rotation == 180: filters.append('transpose=2,transpose=2') elif rotation == 270: filters.append('transpose=2') # Add crop filter if specified crop = data.get('crop') if crop: x = crop.get('x', 0) y = crop.get('y', 0) width = crop.get('width') height = crop.get('height') if width and height: filters.append(f"crop={width}:{height}:{x}:{y}") # Add scale filter if scale percentage specified (not 100%) scale_percentage = data.get('scale_percentage', 100) if scale_percentage != 100: # Scale based on percentage - ffmpeg will apply this after crop # Force dimensions divisible by 2 for H.264 compatibility scale_filter = f"scale=trunc(iw*{scale_percentage/100}/2)*2:trunc(ih*{scale_percentage/100}/2)*2" filters.append(scale_filter) # Apply filters if any if filters: cmd.extend(['-vf', ','.join(filters)]) # Handle audio mute_audio = data.get('mute_audio', False) if mute_audio: cmd.extend(['-an']) # Remove audio # Get quality (CRF value) quality = data.get('quality', 23) # Ensure quality is within valid range (18-32) quality = max(18, min(32, int(quality))) # Output settings for H.264 MP4 cmd.extend([ '-c:v', 'libx264', '-preset', 'medium', '-crf', str(quality), '-c:a', 'aac', '-b:a', '128k', '-movflags', '+faststart', '-y', # Overwrite output file output_path ]) # Execute ffmpeg print(f"Executing: {' '.join(cmd)}") result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: print(f"FFmpeg error: {result.stderr}") return jsonify({'error': 'Video processing failed', 'details': result.stderr}), 500 return jsonify({ 'success': True, 'output_file': output_filename, 'file_id': file_id }) except Exception as e: print(f"Processing error: {e}") return jsonify({'error': str(e)}), 500 @app.route('/download/') def download_video(file_id): try: output_filename = f"{file_id}_processed.mp4" output_path = os.path.join(app.config['OUTPUT_FOLDER'], output_filename) if not os.path.exists(output_path): return jsonify({'error': 'File not found'}), 404 # Get original filename and create download name original_name = original_filenames.get(file_id, 'video.mp4') name_without_ext = os.path.splitext(original_name)[0] timestamp = int(time.time() * 1000) % 100000 # Last 5 digits of timestamp download_name = f"{name_without_ext}_processed_{timestamp}.mp4" return send_file(output_path, as_attachment=True, download_name=download_name) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/cleanup/', methods=['DELETE']) def cleanup_files(file_id): """Clean up uploaded and processed files""" try: # Clean up upload folder for f in os.listdir(app.config['UPLOAD_FOLDER']): if f.startswith(file_id): os.remove(os.path.join(app.config['UPLOAD_FOLDER'], f)) # Clean up output folder for f in os.listdir(app.config['OUTPUT_FOLDER']): if f.startswith(file_id): os.remove(os.path.join(app.config['OUTPUT_FOLDER'], f)) # Clean up stored original filename if file_id in original_filenames: del original_filenames[file_id] return jsonify({'success': True}) except Exception as e: return jsonify({'error': str(e)}), 500 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)