Sada8888 commited on
Commit
77c432f
·
verified ·
1 Parent(s): 0ebd19e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +171 -8
app.py CHANGED
@@ -3,7 +3,10 @@ import uuid
3
  import requests
4
  import threading
5
  import time
6
- from flask import Flask, request, jsonify, render_template, send_from_directory
 
 
 
7
  from deep_translator import GoogleTranslator
8
 
9
  app = Flask(__name__)
@@ -14,12 +17,46 @@ GITHUB_REPO = os.environ.get("GITHUB_REPO", "Action")
14
  SPACE_HOST = os.environ.get("SPACE_HOST", "")
15
  SPACE_URL = f"https://{SPACE_HOST}"
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  def delete_github_run(gh_run_id):
18
  time.sleep(20)
19
  url = f"https://api.github.com/repos/{GITHUB_USER}/{GITHUB_REPO}/actions/runs/{gh_run_id}"
20
  headers = {"Accept": "application/vnd.github.v3+json", "Authorization": f"token {GITHUB_TOKEN}"}
21
  requests.delete(url, headers=headers)
22
 
 
 
 
 
 
 
 
 
23
  @app.route('/')
24
  def index():
25
  return render_template('fluxpro.html')
@@ -109,7 +146,7 @@ def edit_image():
109
  else:
110
  return jsonify({"status": "error", "message": r.text}), 400
111
 
112
- # --- API جدید مخصوص تبدیل عکس به ویدیو (Image to Video) ---
113
  @app.route('/api/generate-video', methods=['POST'])
114
  def generate_video():
115
  if not GITHUB_TOKEN: return jsonify({"status": "error", "message": "Token missing"}), 500
@@ -152,12 +189,142 @@ def generate_video():
152
  else:
153
  return jsonify({"status": "error", "message": r.text}), 400
154
 
155
- # وب‌هوک برای دریافت فایل نهایی از گیت‌هاب (با پشتیبانی از فرمت‌های مختلف)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  @app.route('/api/webhook/upload', methods=['POST'])
157
  def webhook_upload():
158
  run_id = request.form.get('run_id')
159
  gh_run_id = request.form.get('github_run_id')
160
- ext = request.form.get('ext', 'webp') # پسوند پیش‌فرض webp برای عکس و mp4 برای ویدیو
161
 
162
  if 'file' not in request.files or not run_id:
163
  return "Invalid request", 400
@@ -168,19 +335,15 @@ def webhook_upload():
168
  if gh_run_id: threading.Thread(target=delete_github_run, args=(gh_run_id,)).start()
169
  return "OK", 200
170
 
171
- # چک کردن وضعیت توسط مرورگر
172
  @app.route('/api/status/<run_id>')
173
  def check_status(run_id):
174
- # چک کردن فایل ویدیویی
175
  if os.path.exists(f"static/images/{run_id}.mp4"):
176
  return jsonify({"status": "ready", "url": f"/static/images/{run_id}.mp4"})
177
- # چک کردن فایل تصویری
178
  if os.path.exists(f"static/images/{run_id}.webp"):
179
  return jsonify({"status": "ready", "url": f"/static/images/{run_id}.webp"})
180
 
181
  return jsonify({"status": "processing"})
182
 
183
- # دسترسی دادن به پوشه تصاویر و ویدیوها
184
  @app.route('/static/images/<filename>')
185
  def serve_file(filename):
186
  return send_from_directory('static/images', filename)
 
3
  import requests
4
  import threading
5
  import time
6
+ import sqlite3
7
+ import tempfile
8
+ import ffmpeg
9
+ from flask import Flask, request, jsonify, render_template, send_from_directory, send_file, after_this_request
10
  from deep_translator import GoogleTranslator
11
 
12
  app = Flask(__name__)
 
17
  SPACE_HOST = os.environ.get("SPACE_HOST", "")
18
  SPACE_URL = f"https://{SPACE_HOST}"
19
 
20
+ # --- تنظیمات دیتابیس SQLite ---
21
+ # در هاگینگ فیس باکت‌ها معمولاً در مسیر /data مونت می‌شوند
22
+ DB_DIR = "/data" if os.path.exists("/data") else "."
23
+ DB_PATH = os.path.join(DB_DIR, "users.db")
24
+ TEMP_DIR = "tmp"
25
+ USAGE_LIMIT = 5
26
+ ONE_WEEK_SECONDS = 7 * 24 * 60 * 60
27
+
28
+ os.makedirs(TEMP_DIR, exist_ok=True)
29
+
30
+ def init_db():
31
+ conn = sqlite3.connect(DB_PATH)
32
+ c = conn.cursor()
33
+ c.execute('''CREATE TABLE IF NOT EXISTS users
34
+ (id TEXT PRIMARY KEY, count INTEGER, week_start REAL)''')
35
+ conn.commit()
36
+ conn.close()
37
+
38
+ init_db()
39
+
40
+ def get_db_connection():
41
+ conn = sqlite3.connect(DB_PATH)
42
+ conn.row_factory = sqlite3.Row
43
+ return conn
44
+
45
+ # --- توابع کمکی ---
46
  def delete_github_run(gh_run_id):
47
  time.sleep(20)
48
  url = f"https://api.github.com/repos/{GITHUB_USER}/{GITHUB_REPO}/actions/runs/{gh_run_id}"
49
  headers = {"Accept": "application/vnd.github.v3+json", "Authorization": f"token {GITHUB_TOKEN}"}
50
  requests.delete(url, headers=headers)
51
 
52
+ def get_user_identifier(data):
53
+ fingerprint = data.get('fingerprint')
54
+ if fingerprint: return str(fingerprint)
55
+ if request.headers.getlist("X-Forwarded-For"):
56
+ return request.headers.getlist("X-Forwarded-For")[0].split(',')[0].strip()
57
+ return request.remote_addr
58
+
59
+ # --- مسیرهای صفحات HTML ---
60
  @app.route('/')
61
  def index():
62
  return render_template('fluxpro.html')
 
146
  else:
147
  return jsonify({"status": "error", "message": r.text}), 400
148
 
149
+ # --- API مخصوص تبدیل عکس به ویدیو ---
150
  @app.route('/api/generate-video', methods=['POST'])
151
  def generate_video():
152
  if not GITHUB_TOKEN: return jsonify({"status": "error", "message": "Token missing"}), 500
 
189
  else:
190
  return jsonify({"status": "error", "message": r.text}), 400
191
 
192
+ # --- API میکس ویدیوها سمت سرور ---
193
+ @app.route('/api/merge-videos', methods=['POST'])
194
+ def merge_videos():
195
+ if 'base_video' not in request.files or 'new_clip' not in request.files:
196
+ return jsonify({"error": "هر دو فایل ویدیویی برای میکس لازم هستند."}), 400
197
+
198
+ base_video_file = request.files['base_video']
199
+ new_clip_file = request.files['new_clip']
200
+
201
+ temp_files_to_clean = []
202
+
203
+ try:
204
+ base_video_path = os.path.join(TEMP_DIR, f"{uuid.uuid4().hex}.mp4")
205
+ new_clip_path = os.path.join(TEMP_DIR, f"{uuid.uuid4().hex}.mp4")
206
+ output_path = os.path.join(TEMP_DIR, f"{uuid.uuid4().hex}.mp4")
207
+
208
+ temp_files_to_clean.extend([base_video_path, new_clip_path, output_path])
209
+
210
+ base_video_file.save(base_video_path)
211
+ new_clip_file.save(new_clip_path)
212
+
213
+ # بررسی رزولوشن و همسان‌سازی
214
+ probe = ffmpeg.probe(base_video_path)
215
+ video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
216
+ width = int(video_stream['width'])
217
+ height = int(video_stream['height'])
218
+
219
+ input1 = ffmpeg.input(base_video_path)
220
+ input2 = ffmpeg.input(new_clip_path).filter('scale', width, height).filter('setsar', '1')
221
+
222
+ merged_video = ffmpeg.concat(input1, input2, v=1, a=0).output(
223
+ output_path,
224
+ crf=18,
225
+ preset='slow',
226
+ pix_fmt='yuv420p'
227
+ )
228
+
229
+ merged_video.run(overwrite_output=True, quiet=True)
230
+
231
+ @after_this_request
232
+ def cleanup(response):
233
+ try:
234
+ for f_path in temp_files_to_clean:
235
+ if os.path.exists(f_path):
236
+ os.remove(f_path)
237
+ except Exception as e:
238
+ pass
239
+ return response
240
+
241
+ return send_file(output_path, mimetype='video/mp4', as_attachment=True, download_name='merged_video.mp4')
242
+
243
+ except ffmpeg.Error as e:
244
+ for f_path in temp_files_to_clean:
245
+ if os.path.exists(f_path):
246
+ os.remove(f_path)
247
+ return jsonify({"error": "خطا در پردازش و میکس ویدیو در سرور."}), 500
248
+ except Exception as e:
249
+ for f_path in temp_files_to_clean:
250
+ if os.path.exists(f_path):
251
+ os.remove(f_path)
252
+ return jsonify({"error": "خطای پیش‌بینی نشده در سرور."}), 500
253
+
254
+ # --- مدیریت کاربران و محدودیت با SQLite ---
255
+ @app.route('/api/check-credit', methods=['POST'])
256
+ def check_credit():
257
+ data = request.get_json()
258
+ if not data: return jsonify({"error": "Invalid request"}), 400
259
+ user_id = get_user_identifier(data)
260
+
261
+ conn = get_db_connection()
262
+ c = conn.cursor()
263
+ c.execute("SELECT * FROM users WHERE id = ?", (user_id,))
264
+ user_record = c.fetchone()
265
+
266
+ now = time.time()
267
+ credits_remaining = USAGE_LIMIT
268
+ limit_reached = False
269
+ reset_timestamp = 0
270
+
271
+ if user_record:
272
+ if user_record['week_start'] < (now - ONE_WEEK_SECONDS):
273
+ c.execute("UPDATE users SET count = 0, week_start = ? WHERE id = ?", (now, user_id))
274
+ conn.commit()
275
+ credits_remaining = USAGE_LIMIT
276
+ else:
277
+ credits_remaining = max(0, USAGE_LIMIT - user_record['count'])
278
+ if credits_remaining == 0:
279
+ limit_reached = True
280
+ reset_timestamp = user_record['week_start'] + ONE_WEEK_SECONDS
281
+
282
+ conn.close()
283
+ return jsonify({
284
+ "credits_remaining": credits_remaining,
285
+ "limit_reached": limit_reached,
286
+ "reset_timestamp": reset_timestamp
287
+ })
288
+
289
+ @app.route('/api/use-credit', methods=['POST'])
290
+ def use_credit():
291
+ data = request.get_json()
292
+ if not data: return jsonify({"error": "Invalid request"}), 400
293
+ user_id = get_user_identifier(data)
294
+
295
+ conn = get_db_connection()
296
+ c = conn.cursor()
297
+ c.execute("SELECT * FROM users WHERE id = ?", (user_id,))
298
+ user_record = c.fetchone()
299
+
300
+ now = time.time()
301
+ if user_record:
302
+ if user_record['week_start'] < (now - ONE_WEEK_SECONDS):
303
+ c.execute("UPDATE users SET count = 1, week_start = ? WHERE id = ?", (now, user_id))
304
+ else:
305
+ if user_record['count'] >= USAGE_LIMIT:
306
+ conn.close()
307
+ reset_timestamp = user_record['week_start'] + ONE_WEEK_SECONDS
308
+ return jsonify({"status": "limit_reached", "credits_remaining": 0, "reset_timestamp": reset_timestamp}), 429
309
+ c.execute("UPDATE users SET count = count + 1 WHERE id = ?", (user_id,))
310
+ else:
311
+ c.execute("INSERT INTO users (id, count, week_start) VALUES (?, 1, ?)", (user_id, now))
312
+
313
+ conn.commit()
314
+
315
+ c.execute("SELECT count FROM users WHERE id = ?", (user_id,))
316
+ new_count = c.fetchone()['count']
317
+ credits_remaining = USAGE_LIMIT - new_count
318
+
319
+ conn.close()
320
+ return jsonify({"status": "success", "credits_remaining": credits_remaining})
321
+
322
+ # --- دریافت و ارسال فایل ---
323
  @app.route('/api/webhook/upload', methods=['POST'])
324
  def webhook_upload():
325
  run_id = request.form.get('run_id')
326
  gh_run_id = request.form.get('github_run_id')
327
+ ext = request.form.get('ext', 'webp')
328
 
329
  if 'file' not in request.files or not run_id:
330
  return "Invalid request", 400
 
335
  if gh_run_id: threading.Thread(target=delete_github_run, args=(gh_run_id,)).start()
336
  return "OK", 200
337
 
 
338
  @app.route('/api/status/<run_id>')
339
  def check_status(run_id):
 
340
  if os.path.exists(f"static/images/{run_id}.mp4"):
341
  return jsonify({"status": "ready", "url": f"/static/images/{run_id}.mp4"})
 
342
  if os.path.exists(f"static/images/{run_id}.webp"):
343
  return jsonify({"status": "ready", "url": f"/static/images/{run_id}.webp"})
344
 
345
  return jsonify({"status": "processing"})
346
 
 
347
  @app.route('/static/images/<filename>')
348
  def serve_file(filename):
349
  return send_from_directory('static/images', filename)