fugthchat commited on
Commit
a6c33cc
·
verified ·
1 Parent(s): 1e7aa4d

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +189 -0
app.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import threading
4
+ import time
5
+ from flask import Flask, request, jsonify, send_file
6
+ from flask_cors import CORS
7
+ from werkzeug.utils import secure_filename
8
+ from moviepy.editor import VideoFileClip
9
+ import yt_dlp
10
+
11
+ app = Flask(__name__)
12
+ # Allow your GitHub frontend to talk to this backend
13
+ CORS(app)
14
+
15
+ app.secret_key = "hugspace-backend-secret"
16
+
17
+ # Configuration
18
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
19
+ UPLOAD_FOLDER = os.path.join(BASE_DIR, "uploads")
20
+ USER_FOLDERS = os.path.join(BASE_DIR, "user_files")
21
+ ALLOWED_EXTENSIONS = {"mp4", "avi", "mov", "mkv", "webm", "flv", "3gp", "wmv"}
22
+
23
+ # Ensure directories exist with write permissions
24
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
25
+ os.makedirs(USER_FOLDERS, exist_ok=True)
26
+
27
+ # In-memory progress tracking (simple for demo purposes)
28
+ compression_progress = {}
29
+ download_progress = {}
30
+
31
+ def allowed_file(filename):
32
+ return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
33
+
34
+ def get_user_folder(session_id):
35
+ user_path = os.path.join(USER_FOLDERS, session_id)
36
+ os.makedirs(user_path, exist_ok=True)
37
+ return user_path
38
+
39
+ # --- COMPRESSOR LOGIC ---
40
+ def compress_video_task(input_path, output_path, quality, session_id):
41
+ try:
42
+ compression_progress[session_id] = {"status": "processing", "progress": 10}
43
+
44
+ clip = VideoFileClip(input_path)
45
+
46
+ # Bitrate settings
47
+ presets = {
48
+ "low": {"bitrate": "500k", "height": 480},
49
+ "medium": {"bitrate": "1000k", "height": 720},
50
+ "high": {"bitrate": "2000k", "height": 1080},
51
+ }
52
+ settings = presets.get(quality, presets["medium"])
53
+
54
+ # Resize if input is larger than target
55
+ if clip.h > settings["height"]:
56
+ clip = clip.resize(height=settings["height"])
57
+
58
+ # Processing... (Simplified progress for stability)
59
+ compression_progress[session_id]["progress"] = 50
60
+
61
+ clip.write_videofile(
62
+ output_path,
63
+ bitrate=settings["bitrate"],
64
+ codec="libx264",
65
+ audio_codec="aac",
66
+ preset="ultrafast", # Fast compression for free tier CPU
67
+ logger=None
68
+ )
69
+
70
+ clip.close()
71
+ compression_progress[session_id] = {"status": "completed", "progress": 100}
72
+
73
+ if os.path.exists(input_path):
74
+ os.remove(input_path)
75
+
76
+ except Exception as e:
77
+ compression_progress[session_id] = {"status": "error", "error": str(e)}
78
+ if os.path.exists(input_path):
79
+ os.remove(input_path)
80
+
81
+ # --- DOWNLOADER LOGIC ---
82
+ def download_video_task(url, output_path, session_id):
83
+ try:
84
+ download_progress[session_id] = {"status": "processing", "progress": 0}
85
+
86
+ def progress_hook(d):
87
+ if d['status'] == 'downloading':
88
+ p = d.get('_percent_str', '0%').replace('%','')
89
+ try:
90
+ download_progress[session_id]["progress"] = float(p)
91
+ except:
92
+ pass
93
+
94
+ ydl_opts = {
95
+ 'outtmpl': os.path.join(output_path, '%(title)s.%(ext)s'),
96
+ 'format': 'best[height<=1080]',
97
+ 'progress_hooks': [progress_hook],
98
+ }
99
+
100
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
101
+ ydl.download([url])
102
+
103
+ download_progress[session_id] = {"status": "completed", "progress": 100}
104
+
105
+ except Exception as e:
106
+ download_progress[session_id] = {"status": "error", "error": str(e)}
107
+
108
+ # --- ENDPOINTS ---
109
+
110
+ @app.route('/')
111
+ def health_check():
112
+ return jsonify({"status": "active", "service": "HugSpace Backend"})
113
+
114
+ @app.route('/upload', methods=['POST'])
115
+ def upload_file():
116
+ if 'file' not in request.files:
117
+ return jsonify({"error": "No file part"}), 400
118
+
119
+ file = request.files['file']
120
+ quality = request.form.get('quality', 'medium')
121
+ # Use provided session ID or generate one
122
+ session_id = request.form.get('session_id', str(uuid.uuid4()))
123
+
124
+ if file and allowed_file(file.filename):
125
+ filename = secure_filename(file.filename)
126
+ unique_name = f"{uuid.uuid4()}_{filename}"
127
+ input_path = os.path.join(UPLOAD_FOLDER, unique_name)
128
+ file.save(input_path)
129
+
130
+ user_folder = get_user_folder(session_id)
131
+ output_path = os.path.join(user_folder, f"compressed_{filename}")
132
+
133
+ # Start compression in background thread
134
+ thread = threading.Thread(target=compress_video_task, args=(input_path, output_path, quality, session_id))
135
+ thread.start()
136
+
137
+ return jsonify({"success": True, "session_id": session_id})
138
+
139
+ return jsonify({"error": "Invalid file type"}), 400
140
+
141
+ @app.route('/download_video', methods=['POST'])
142
+ def start_download():
143
+ # Force JSON parsing even if content-type header is missing
144
+ data = request.get_json(force=True)
145
+ url = data.get('url')
146
+ session_id = data.get('session_id', str(uuid.uuid4()))
147
+
148
+ if not url:
149
+ return jsonify({"error": "No URL provided"}), 400
150
+
151
+ user_folder = get_user_folder(session_id)
152
+
153
+ thread = threading.Thread(target=download_video_task, args=(url, user_folder, session_id))
154
+ thread.start()
155
+
156
+ return jsonify({"success": True, "session_id": session_id})
157
+
158
+ @app.route('/progress/<task_type>/<session_id>', methods=['GET'])
159
+ def get_progress(task_type, session_id):
160
+ if task_type == 'compression':
161
+ data = compression_progress.get(session_id, {"status": "waiting"})
162
+ return jsonify(data)
163
+ elif task_type == 'download':
164
+ data = download_progress.get(session_id, {"status": "waiting"})
165
+ return jsonify(data)
166
+ return jsonify({"error": "Invalid task"}), 400
167
+
168
+ @app.route('/files/<session_id>', methods=['GET'])
169
+ def list_files(session_id):
170
+ user_folder = get_user_folder(session_id)
171
+ files = []
172
+ if os.path.exists(user_folder):
173
+ for f in os.listdir(user_folder):
174
+ path = os.path.join(user_folder, f)
175
+ size = os.path.getsize(path) / (1024 * 1024)
176
+ files.append({"name": f, "size": f"{size:.2f} MB"})
177
+ return jsonify({"files": files})
178
+
179
+ @app.route('/get_file/<session_id>/<filename>', methods=['GET'])
180
+ def get_file(session_id, filename):
181
+ user_folder = get_user_folder(session_id)
182
+ path = os.path.join(user_folder, secure_filename(filename))
183
+ if os.path.exists(path):
184
+ return send_file(path, as_attachment=True)
185
+ return jsonify({"error": "File not found"}), 404
186
+
187
+ if __name__ == "__main__":
188
+ # Hugging Face Spaces run on port 7860
189
+ app.run(host="0.0.0.0", port=7860)