File size: 4,544 Bytes
ebb2801
0616ade
ebb2801
 
 
 
 
 
 
0616ade
ebb2801
 
 
 
0616ade
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23b4a3c
0616ade
 
23b4a3c
0616ade
 
 
 
 
23b4a3c
 
0616ade
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ebb2801
 
0616ade
ebb2801
c4aaa51
 
0616ade
 
c4aaa51
 
 
 
 
 
 
 
 
0616ade
 
 
 
 
 
 
 
23b4a3c
0616ade
 
 
 
 
 
23b4a3c
0616ade
 
 
23b4a3c
 
 
c4aaa51
 
0616ade
c4aaa51
0616ade
c4aaa51
0616ade
 
 
c4aaa51
 
 
0616ade
 
 
 
c4aaa51
 
 
 
 
ebb2801
 
 
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
from flask import Flask, render_template, request, jsonify, send_file
from werkzeug.exceptions import HTTPException
import os
import subprocess
import uuid

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['OUTPUT_FOLDER'] = 'outputs'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024  # 100MB limit to prevent server overload

os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['OUTPUT_FOLDER'], exist_ok=True)

import shutil
import time
import traceback

# Check if ffmpeg is installed
FFMPEG_AVAILABLE = shutil.which('ffmpeg') is not None

def cleanup_old_files():
    """Cleanup files older than 1 hour"""
    try:
        now = time.time()
        for folder in [app.config['UPLOAD_FOLDER'], app.config['OUTPUT_FOLDER']]:
            if not os.path.exists(folder):
                continue
            for filename in os.listdir(folder):
                filepath = os.path.join(folder, filename)
                # Remove if older than 1 hour
                if os.path.isfile(filepath) and os.path.getmtime(filepath) < now - 3600:
                    try:
                        os.remove(filepath)
                    except Exception:
                        pass
    except Exception:
        pass

@app.errorhandler(500)
def internal_error(error):
    return jsonify({
        "error": "服务器内部错误 (Internal Server Error)",
        "details": str(error),
        "trace": traceback.format_exc(),
        "suggestion": "请尝试上传较小的文件,或检查文件格式是否正确。"
    }), 500

@app.errorhandler(413)
def request_entity_too_large(error):
    return jsonify({
        "error": "文件过大 (File Too Large)",
        "details": "上传的文件超过了服务器限制 (100MB)。"
    }), 413

@app.errorhandler(Exception)
def handle_exception(e):
    # Pass through HTTP errors
    if isinstance(e, HTTPException):
        return e
    
    # Log the full traceback
    print("Error occurred:")
    traceback.print_exc()
    
    # Now you're handling non-HTTP exceptions only
    return jsonify({
        "error": "Unexpected Error",
        "details": str(e),
        "trace": traceback.format_exc() # Return trace to client for debugging
    }), 500

@app.route('/')
def index():
    return render_template('index.html', ffmpeg_available=FFMPEG_AVAILABLE)

@app.route('/convert', methods=['POST'])
def convert():
    cleanup_old_files()
    
    if 'video' not in request.files:
        return jsonify({"error": "No video file"}), 400
    
    video = request.files['video']
    filename = str(uuid.uuid4())
    input_path = os.path.join(app.config['UPLOAD_FOLDER'], filename + '.webm')
    output_path = os.path.join(app.config['OUTPUT_FOLDER'], filename + '.mp4')
    
    try:
        video.save(input_path)
        
        if not FFMPEG_AVAILABLE:
            return jsonify({"error": "FFmpeg not installed on server", "fallback": True}), 503

        # FFmpeg conversion
        # -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" ensures dimensions are even for libx264
        # -pix_fmt yuv420p is often required for broad compatibility
        # Added -movflags +faststart for better web playback
        cmd = [
            'ffmpeg', '-y', '-i', input_path,
            '-c:v', 'libx264', '-preset', 'fast', '-crf', '23',
            '-pix_fmt', 'yuv420p',
            '-c:a', 'aac', '-b:a', '192k',
            '-vf', 'pad=ceil(iw/2)*2:ceil(ih/2)*2', 
            '-movflags', '+faststart',
            output_path
        ]
        
        # Log command for debugging
        print(f"Running command: {' '.join(cmd)}")
        
        subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        return jsonify({"url": f"/download/{filename}.mp4"})
        
    except subprocess.CalledProcessError as e:
        print(f"FFmpeg Error: {e.stderr.decode() if e.stderr else str(e)}")
        return jsonify({"error": "Conversion failed", "details": str(e)}), 500
    except Exception as e:
        print(f"General Error: {str(e)}")
        return jsonify({"error": "Internal server error", "details": str(e)}), 500
    finally:
        # Cleanup input
        if os.path.exists(input_path):
            try:
                os.remove(input_path)
            except:
                pass

@app.route('/download/<filename>')
def download_file(filename):
    return send_file(os.path.join(app.config['OUTPUT_FOLDER'], filename), as_attachment=True)


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=7860)