fugthchat commited on
Commit
a7bd8c9
·
verified ·
1 Parent(s): 60b07c5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -56
app.py CHANGED
@@ -9,7 +9,6 @@ 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"
@@ -20,13 +19,12 @@ 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
@@ -36,60 +34,53 @@ def get_user_folder(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'),
@@ -100,70 +91,123 @@ def download_video_task(url, output_path, session_id):
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):
@@ -185,5 +229,4 @@ def get_file(session_id, filename):
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)
 
9
  import yt_dlp
10
 
11
  app = Flask(__name__)
 
12
  CORS(app)
13
 
14
  app.secret_key = "hugspace-backend-secret"
 
19
  USER_FOLDERS = os.path.join(BASE_DIR, "user_files")
20
  ALLOWED_EXTENSIONS = {"mp4", "avi", "mov", "mkv", "webm", "flv", "3gp", "wmv"}
21
 
22
+ # Ensure directories exist
23
  os.makedirs(UPLOAD_FOLDER, exist_ok=True)
24
  os.makedirs(USER_FOLDERS, exist_ok=True)
25
 
26
+ # Progress tracking
27
+ progress_store = {}
 
28
 
29
  def allowed_file(filename):
30
  return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
 
34
  os.makedirs(user_path, exist_ok=True)
35
  return user_path
36
 
37
+ # --- TASK: COMPRESS VIDEO ---
38
  def compress_video_task(input_path, output_path, quality, session_id):
39
  try:
40
+ progress_store[session_id] = {"status": "processing", "progress": 10}
 
41
  clip = VideoFileClip(input_path)
42
 
43
+ # Presets
44
  presets = {
45
  "low": {"bitrate": "500k", "height": 480},
46
  "medium": {"bitrate": "1000k", "height": 720},
47
+ "high": {"bitrate": "2500k", "height": 1080},
48
  }
49
  settings = presets.get(quality, presets["medium"])
50
 
 
51
  if clip.h > settings["height"]:
52
  clip = clip.resize(height=settings["height"])
53
 
54
+ progress_store[session_id]["progress"] = 30
 
55
 
56
  clip.write_videofile(
57
  output_path,
58
  bitrate=settings["bitrate"],
59
  codec="libx264",
60
  audio_codec="aac",
61
+ preset="ultrafast",
62
  logger=None
63
  )
64
 
65
  clip.close()
66
+ progress_store[session_id] = {"status": "completed", "progress": 100}
67
 
 
 
 
68
  except Exception as e:
69
+ progress_store[session_id] = {"status": "error", "error": str(e)}
70
+ finally:
71
+ if os.path.exists(input_path): os.remove(input_path)
72
 
73
+ # --- TASK: DOWNLOAD VIDEO ---
74
  def download_video_task(url, output_path, session_id):
75
  try:
76
+ progress_store[session_id] = {"status": "processing", "progress": 0}
77
 
78
  def progress_hook(d):
79
  if d['status'] == 'downloading':
80
  p = d.get('_percent_str', '0%').replace('%','')
81
  try:
82
+ progress_store[session_id]["progress"] = float(p)
83
+ except: pass
 
84
 
85
  ydl_opts = {
86
  'outtmpl': os.path.join(output_path, '%(title)s.%(ext)s'),
 
91
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
92
  ydl.download([url])
93
 
94
+ progress_store[session_id] = {"status": "completed", "progress": 100}
95
+
96
+ except Exception as e:
97
+ progress_store[session_id] = {"status": "error", "error": str(e)}
98
 
99
+ # --- TASK: AUDIO EXTRACTION ---
100
+ def extract_audio_task(input_path, output_path, session_id):
101
+ try:
102
+ progress_store[session_id] = {"status": "processing", "progress": 10}
103
+ clip = VideoFileClip(input_path)
104
+
105
+ progress_store[session_id]["progress"] = 50
106
+
107
+ # Write audio
108
+ clip.audio.write_audiofile(output_path, logger=None)
109
+
110
+ clip.close()
111
+ progress_store[session_id] = {"status": "completed", "progress": 100}
112
+
113
  except Exception as e:
114
+ progress_store[session_id] = {"status": "error", "error": str(e)}
115
+ finally:
116
+ if os.path.exists(input_path): os.remove(input_path)
117
+
118
+ # --- TASK: GIF MAKER ---
119
+ def create_gif_task(input_path, output_path, session_id):
120
+ try:
121
+ progress_store[session_id] = {"status": "processing", "progress": 10}
122
+ clip = VideoFileClip(input_path)
123
+
124
+ # Optimize for Free Tier CPU (Limit resolution and FPS)
125
+ if clip.w > 480:
126
+ clip = clip.resize(width=480)
127
+
128
+ # If video is longer than 10s, take first 10s to prevent crash
129
+ if clip.duration > 10:
130
+ clip = clip.subclip(0, 10)
131
+
132
+ progress_store[session_id]["progress"] = 40
133
+
134
+ clip.write_gif(output_path, fps=10, logger=None)
135
+
136
+ clip.close()
137
+ progress_store[session_id] = {"status": "completed", "progress": 100}
138
+
139
+ except Exception as e:
140
+ progress_store[session_id] = {"status": "error", "error": str(e)}
141
+ finally:
142
+ if os.path.exists(input_path): os.remove(input_path)
143
 
144
  # --- ENDPOINTS ---
145
 
146
  @app.route('/')
147
+ def home():
148
+ return jsonify({"status": "active", "service": "Fugth Video Backend"})
149
 
150
+ # UNIVERSAL UPLOAD HANDLER
151
+ @app.route('/process/<task_type>', methods=['POST'])
152
+ def process_media(task_type):
153
  if 'file' not in request.files:
154
+ return jsonify({"error": "No file"}), 400
155
 
156
  file = request.files['file']
 
 
157
  session_id = request.form.get('session_id', str(uuid.uuid4()))
158
+ quality = request.form.get('quality', 'medium')
159
 
160
  if file and allowed_file(file.filename):
161
  filename = secure_filename(file.filename)
162
+ input_path = os.path.join(UPLOAD_FOLDER, f"{uuid.uuid4()}_{filename}")
 
163
  file.save(input_path)
164
 
165
  user_folder = get_user_folder(session_id)
 
166
 
167
+ # Dispatch Thread based on Task Type
168
+ if task_type == 'compress':
169
+ out_name = f"compressed_{filename}"
170
+ target = compress_video_task
171
+ args = (input_path, os.path.join(user_folder, out_name), quality, session_id)
172
+
173
+ elif task_type == 'audio':
174
+ out_name = f"{os.path.splitext(filename)[0]}.mp3"
175
+ target = extract_audio_task
176
+ args = (input_path, os.path.join(user_folder, out_name), session_id)
177
+
178
+ elif task_type == 'gif':
179
+ out_name = f"{os.path.splitext(filename)[0]}.gif"
180
+ target = create_gif_task
181
+ args = (input_path, os.path.join(user_folder, out_name), session_id)
182
+
183
+ else:
184
+ return jsonify({"error": "Invalid task"}), 400
185
+
186
+ thread = threading.Thread(target=target, args=args)
187
  thread.start()
188
 
189
  return jsonify({"success": True, "session_id": session_id})
190
 
191
+ return jsonify({"error": "Invalid file"}), 400
192
 
193
  @app.route('/download_video', methods=['POST'])
194
  def start_download():
 
195
  data = request.get_json(force=True)
196
  url = data.get('url')
197
  session_id = data.get('session_id', str(uuid.uuid4()))
198
 
199
+ if not url: return jsonify({"error": "No URL"}), 400
 
 
 
200
 
201
+ user_folder = get_user_folder(session_id)
202
  thread = threading.Thread(target=download_video_task, args=(url, user_folder, session_id))
203
  thread.start()
204
 
205
  return jsonify({"success": True, "session_id": session_id})
206
 
207
+ @app.route('/progress/<session_id>', methods=['GET'])
208
+ def get_progress(session_id):
209
+ data = progress_store.get(session_id, {"status": "waiting"})
210
+ return jsonify(data)
 
 
 
 
 
211
 
212
  @app.route('/files/<session_id>', methods=['GET'])
213
  def list_files(session_id):
 
229
  return jsonify({"error": "File not found"}), 404
230
 
231
  if __name__ == "__main__":
 
232
  app.run(host="0.0.0.0", port=7860)