Opera8 commited on
Commit
0416926
·
verified ·
1 Parent(s): f02a91d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -54
app.py CHANGED
@@ -161,29 +161,62 @@ def get_quota_exceeded_html():
161
  </div>
162
  """
163
 
164
- def check_quota(fingerprint, subscription_status):
 
165
  global usage_data_cache
166
- if subscription_status == 'paid': return True, "unlimited"
167
 
168
- # اگر فینگرپرینت معتبر نبود (مثلا کاربر جاوا اسکریپت را بست)، جلوگیری کنیم
169
- if not fingerprint or len(fingerprint) < 5 or fingerprint == "unknown":
170
- return True, 0 # برای بار اول سختگیری نمی‌کنیم تا لود شود
 
 
 
 
 
 
 
 
 
171
 
 
 
 
172
  today_str = date.today().isoformat()
173
  usage_data_cache = load_usage_data()
 
174
  user_record = usage_data_cache.get(fingerprint)
175
-
176
  if not user_record or user_record.get("last_reset") != today_str:
177
  user_record = {"count": 0, "last_reset": today_str}
178
- usage_data_cache[fingerprint] = user_record
179
-
180
- if user_record["count"] >= USAGE_LIMIT:
181
- return False, user_record["count"]
182
 
183
  user_record["count"] += 1
184
  usage_data_cache[fingerprint] = user_record
185
  save_usage_data(usage_data_cache)
186
- return True, user_record["count"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
 
188
  @spaces.GPU(duration=30)
189
  def infer(
@@ -191,21 +224,19 @@ def infer(
191
  guidance_scale, steps, aspect_ratio_selection, custom_width, custom_height,
192
  fingerprint, subscription_status, progress=gr.Progress(track_tqdm=True)
193
  ):
194
- # --- بررسی اعتبار ---
195
- is_allowed, usage_count = check_quota(fingerprint, subscription_status)
196
-
197
- # اگر اعتبار تمام شده باشد:
198
- if not is_allowed:
199
- # خروجی‌ها: تصویر=هیچ، سید=سید، پیام=پیام زیبا، دکمه‌اجرا=مخفی، دکمه‌ارتقا=نمایان
200
- return (
201
- None,
202
- seed,
203
- get_quota_exceeded_html(),
204
- gr.update(visible=False), # Run Button -> Hide
205
- gr.update(visible=True) # Upgrade Button -> Show
206
- )
207
 
208
- # --- اگر اعتبار دارد، ادامه پردازش ---
209
  if input_image is None:
210
  return None, seed, get_error_html("لطفاً ابتدا یک تصویر بارگذاری کنید."), gr.update(visible=True), gr.update(visible=False)
211
 
@@ -240,8 +271,16 @@ def infer(
240
  if is_image_nsfw(result):
241
  return None, seed, get_error_html("تصویر خروجی نامناسب بود حذف شد."), gr.update(visible=True), gr.update(visible=False)
242
 
 
 
 
 
243
  success_msg = f"تصویر ساخته شد."
244
- if subscription_status != 'paid': success_msg += f" (باقی‌مانده: {USAGE_LIMIT - usage_count})"
 
 
 
 
245
 
246
  return result, seed, get_success_html(success_msg), gr.update(visible=True), gr.update(visible=False)
247
 
@@ -250,7 +289,6 @@ def infer(
250
 
251
  @spaces.GPU(duration=30)
252
  def infer_example(input_image, prompt, lora_adapter):
253
- # مثال‌ها اعتبار کم نمی‌کنند
254
  res, s, status, btn1, btn2 = infer(input_image, prompt, lora_adapter, 0, True, 1.0, 4, "خودکار (پیش‌فرض)", 1024, 1024, "example", "paid")
255
  return res, s, status
256
 
@@ -264,7 +302,6 @@ async (image) => {
264
  }
265
  """
266
 
267
- # جاوا اسکریپت برای دکمه ارتقا
268
  js_upgrade_func = """
269
  () => {
270
  window.parent.postMessage({ type: 'NAVIGATE_TO_PREMIUM' }, '*');
@@ -274,19 +311,13 @@ js_upgrade_func = """
274
  js_global_content = """
275
  <script>
276
  document.addEventListener('DOMContentLoaded', () => {
277
- // ---------------------------------------------
278
- // Fingerprint Logic (CANVAS + SCREEN + UA)
279
- // ---------------------------------------------
280
  async function getBrowserFingerprint() {
281
  const components = [];
282
- // 1. User Agent & Language
283
  components.push(navigator.userAgent);
284
  components.push(navigator.language);
285
- // 2. Screen Params
286
  components.push(screen.colorDepth);
287
  components.push(screen.width + 'x' + screen.height);
288
  components.push(new Date().getTimezoneOffset());
289
- // 3. Canvas Fingerprinting (Rendering differences)
290
  try {
291
  const canvas = document.createElement('canvas');
292
  const ctx = canvas.getContext('2d');
@@ -296,7 +327,6 @@ document.addEventListener('DOMContentLoaded', () => {
296
  components.push(canvas.toDataURL());
297
  } catch (e) { components.push("canvas-err"); }
298
 
299
- // Hashing
300
  const str = components.join('~~~');
301
  let hash = 0;
302
  for (let i = 0; i < str.length; i++) {
@@ -320,8 +350,14 @@ document.addEventListener('DOMContentLoaded', () => {
320
  function updateHiddenInputs(fingerprint, status) {
321
  const fpInput = document.querySelector('#fingerprint_storage textarea');
322
  const stInput = document.querySelector('#status_storage textarea');
323
- if(fpInput && fingerprint) { fpInput.value = fingerprint; fpInput.dispatchEvent(new Event('input', { bubbles: true })); }
324
- if(stInput && status) { stInput.value = status; stInput.dispatchEvent(new Event('input', { bubbles: true })); }
 
 
 
 
 
 
325
  }
326
 
327
  function updateSubscriptionBadge(status) {
@@ -343,10 +379,8 @@ document.addEventListener('DOMContentLoaded', () => {
343
  window.parent.postMessage({ type: 'REQUEST_USER_STATUS' }, '*');
344
  updateSubscriptionBadge('free');
345
 
346
- // Polling to make sure hidden inputs are always filled
347
- setInterval(() => {
348
- updateHiddenInputs(window.userFingerprint, window.userStatus);
349
- }, 1500);
350
  }
351
 
352
  window.addEventListener('message', (event) => {
@@ -356,13 +390,20 @@ document.addEventListener('DOMContentLoaded', () => {
356
  const status = isUserPaid(userObject) ? 'paid' : 'free';
357
  window.userStatus = status;
358
  updateSubscriptionBadge(status);
 
359
  } catch (e) { updateSubscriptionBadge('free'); }
360
  }
361
  });
362
 
363
  initUserIdentity();
 
 
 
 
 
 
 
364
 
365
- // UI Force Light Mode
366
  const forceLight = () => {
367
  const body = document.querySelector('body');
368
  if (body) { body.classList.remove('dark'); body.style.backgroundColor = '#f5f7fa'; body.style.color = '#333333'; }
@@ -408,9 +449,7 @@ with gr.Blocks() as demo:
408
  status_box = gr.HTML(label="وضعیت")
409
 
410
  # --- دکمه‌ها ---
411
- # د��مه ساخت (پیش‌فرض نمایان)
412
  run_button = gr.Button("✨ شروع پردازش و ساخت تصویر", variant="primary", elem_classes="primary-btn", elem_id="run-btn", visible=True)
413
- # دکمه ارتقا (پیش‌فرض مخفی)
414
  upgrade_button = gr.Button("💎 خرید نسخه نامحدود (کلیک کنید)", variant="primary", elem_classes="upgrade-btn", elem_id="upgrade-btn", visible=False)
415
 
416
  with gr.Column():
@@ -431,27 +470,26 @@ with gr.Blocks() as demo:
431
  def toggle_row(choice): return gr.update(visible=(choice == "شخصی‌سازی (Custom)"))
432
  aspect_ratio_selection.change(fn=toggle_row, inputs=aspect_ratio_selection, outputs=custom_dims_row)
433
 
434
- # --- Event Listeners ---
435
- # کلیک روی دکمه اجرا: خروجی شامل آپدیت دکمه‌هاست (run_button و upgrade_button)
 
 
 
 
 
 
436
  run_button.click(
437
  fn=infer,
438
  inputs=[input_image, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps, aspect_ratio_selection, custom_width, custom_height, fingerprint_box, status_box_input],
439
  outputs=[output_image, seed, status_box, run_button, upgrade_button]
440
  )
441
 
442
- # کلیک روی دکمه ارتقا: اجرای JS برای هدایت به صفحه خرید
443
  upgrade_button.click(fn=None, js=js_upgrade_func)
444
-
445
  download_button.click(fn=None, inputs=[output_image], js=js_download_func)
446
 
447
- # مثال‌ها
448
  gr.Examples(
449
- examples=[
450
- ["examples/1.jpg", "تبدیل به انیمه کن.", "تبدیل عکس به انیمه"],
451
- ["examples/5.jpg", "سایه‌ها را حذف کن...", "اصلاح نور و سایه"],
452
- ],
453
  inputs=[input_image, prompt, lora_adapter],
454
- # مثال‌ها دکمه‌ها را آپدیت نمی‌کنند که UI خراب نشود
455
  fn=infer_example
456
  )
457
 
 
161
  </div>
162
  """
163
 
164
+ def get_user_record(fingerprint):
165
+ """خواندن رکورد کاربر از دیتابیس"""
166
  global usage_data_cache
167
+ if not fingerprint: return None
168
 
169
+ # Reload to get fresh data
170
+ usage_data_cache = load_usage_data()
171
+ today_str = date.today().isoformat()
172
+
173
+ user_record = usage_data_cache.get(fingerprint)
174
+
175
+ # اگر رکوردی نیست یا مال دیروز است، ریست نمی‌کنیم (فقط می‌خوانیم)
176
+ # ریست فقط موقع "مصرف" انجام می‌شود
177
+ if not user_record or user_record.get("last_reset") != today_str:
178
+ return {"count": 0, "last_reset": today_str}
179
+
180
+ return user_record
181
 
182
+ def consume_quota(fingerprint):
183
+ """کسر اعتبار"""
184
+ global usage_data_cache
185
  today_str = date.today().isoformat()
186
  usage_data_cache = load_usage_data()
187
+
188
  user_record = usage_data_cache.get(fingerprint)
 
189
  if not user_record or user_record.get("last_reset") != today_str:
190
  user_record = {"count": 0, "last_reset": today_str}
 
 
 
 
191
 
192
  user_record["count"] += 1
193
  usage_data_cache[fingerprint] = user_record
194
  save_usage_data(usage_data_cache)
195
+ return user_record["count"]
196
+
197
+ # --- تابع جدید برای بررسی اولیه (بدون GPU) ---
198
+ def check_initial_quota(fingerprint, subscription_status):
199
+ """
200
+ این تابع فقط برای بررسی وضعیت دکمه‌ها در لحظه لود شدن صفحه است.
201
+ هیچ GPUای مصرف نمی‌کند.
202
+ """
203
+ if not fingerprint:
204
+ # هنوز فینگرپرینت لود نشده، حالت پیش‌فرض (دکمه اجرا فعال)
205
+ return gr.update(visible=True), gr.update(visible=False), None
206
+
207
+ if subscription_status == 'paid':
208
+ return gr.update(visible=True), gr.update(visible=False), None
209
+
210
+ user_record = get_user_record(fingerprint)
211
+ current_usage = user_record["count"] if user_record else 0
212
+
213
+ if current_usage >= USAGE_LIMIT:
214
+ # اعتبار تمام شده: دکمه اجرا مخفی، دکمه ارتقا نمایش، پیام نمایش
215
+ return gr.update(visible=False), gr.update(visible=True), get_quota_exceeded_html()
216
+ else:
217
+ # اعتبار دارد
218
+ return gr.update(visible=True), gr.update(visible=False), None
219
+
220
 
221
  @spaces.GPU(duration=30)
222
  def infer(
 
224
  guidance_scale, steps, aspect_ratio_selection, custom_width, custom_height,
225
  fingerprint, subscription_status, progress=gr.Progress(track_tqdm=True)
226
  ):
227
+ # بررسی مجدد اعتبار در لحظه اجرا (جهت اطمینان)
228
+ if subscription_status != 'paid':
229
+ user_record = get_user_record(fingerprint)
230
+ if user_record and user_record["count"] >= USAGE_LIMIT:
231
+ return (
232
+ None, seed, get_quota_exceeded_html(),
233
+ gr.update(visible=False), gr.update(visible=True)
234
+ )
235
+
236
+ # اگر اعتبار داشت، حالا کم می‌کنیم
237
+ consume_quota(fingerprint)
 
 
238
 
239
+ # --- ادامه منطق ساخت تصویر ---
240
  if input_image is None:
241
  return None, seed, get_error_html("لطفاً ابتدا یک تصویر بارگذاری کنید."), gr.update(visible=True), gr.update(visible=False)
242
 
 
271
  if is_image_nsfw(result):
272
  return None, seed, get_error_html("تصویر خروجی نامناسب بود حذف شد."), gr.update(visible=True), gr.update(visible=False)
273
 
274
+ # دریافت تعداد باقی‌مانده برای نمایش
275
+ user_record = get_user_record(fingerprint)
276
+ remaining = USAGE_LIMIT - user_record["count"] if user_record else 0
277
+
278
  success_msg = f"تصویر ساخته شد."
279
+ if subscription_status != 'paid': success_msg += f" (باقی‌مانده: {remaining})"
280
+
281
+ # اگر این آخرین اعتبار بود، دکمه‌ها را آپدیت کن
282
+ if subscription_status != 'paid' and remaining <= 0:
283
+ return result, seed, get_success_html(success_msg), gr.update(visible=False), gr.update(visible=True)
284
 
285
  return result, seed, get_success_html(success_msg), gr.update(visible=True), gr.update(visible=False)
286
 
 
289
 
290
  @spaces.GPU(duration=30)
291
  def infer_example(input_image, prompt, lora_adapter):
 
292
  res, s, status, btn1, btn2 = infer(input_image, prompt, lora_adapter, 0, True, 1.0, 4, "خودکار (پیش‌فرض)", 1024, 1024, "example", "paid")
293
  return res, s, status
294
 
 
302
  }
303
  """
304
 
 
305
  js_upgrade_func = """
306
  () => {
307
  window.parent.postMessage({ type: 'NAVIGATE_TO_PREMIUM' }, '*');
 
311
  js_global_content = """
312
  <script>
313
  document.addEventListener('DOMContentLoaded', () => {
 
 
 
314
  async function getBrowserFingerprint() {
315
  const components = [];
 
316
  components.push(navigator.userAgent);
317
  components.push(navigator.language);
 
318
  components.push(screen.colorDepth);
319
  components.push(screen.width + 'x' + screen.height);
320
  components.push(new Date().getTimezoneOffset());
 
321
  try {
322
  const canvas = document.createElement('canvas');
323
  const ctx = canvas.getContext('2d');
 
327
  components.push(canvas.toDataURL());
328
  } catch (e) { components.push("canvas-err"); }
329
 
 
330
  const str = components.join('~~~');
331
  let hash = 0;
332
  for (let i = 0; i < str.length; i++) {
 
350
  function updateHiddenInputs(fingerprint, status) {
351
  const fpInput = document.querySelector('#fingerprint_storage textarea');
352
  const stInput = document.querySelector('#status_storage textarea');
353
+ if(fpInput && fingerprint && fpInput.value !== fingerprint) {
354
+ fpInput.value = fingerprint;
355
+ fpInput.dispatchEvent(new Event('input', { bubbles: true }));
356
+ }
357
+ if(stInput && status && stInput.value !== status) {
358
+ stInput.value = status;
359
+ stInput.dispatchEvent(new Event('input', { bubbles: true }));
360
+ }
361
  }
362
 
363
  function updateSubscriptionBadge(status) {
 
379
  window.parent.postMessage({ type: 'REQUEST_USER_STATUS' }, '*');
380
  updateSubscriptionBadge('free');
381
 
382
+ // Initial Update
383
+ updateHiddenInputs(window.userFingerprint, window.userStatus);
 
 
384
  }
385
 
386
  window.addEventListener('message', (event) => {
 
390
  const status = isUserPaid(userObject) ? 'paid' : 'free';
391
  window.userStatus = status;
392
  updateSubscriptionBadge(status);
393
+ updateHiddenInputs(window.userFingerprint, status);
394
  } catch (e) { updateSubscriptionBadge('free'); }
395
  }
396
  });
397
 
398
  initUserIdentity();
399
+
400
+ // Polling to ensure sync
401
+ setInterval(() => {
402
+ if(window.userFingerprint) {
403
+ updateHiddenInputs(window.userFingerprint, window.userStatus || 'free');
404
+ }
405
+ }, 2000);
406
 
 
407
  const forceLight = () => {
408
  const body = document.querySelector('body');
409
  if (body) { body.classList.remove('dark'); body.style.backgroundColor = '#f5f7fa'; body.style.color = '#333333'; }
 
449
  status_box = gr.HTML(label="وضعیت")
450
 
451
  # --- دکمه‌ها ---
 
452
  run_button = gr.Button("✨ شروع پردازش و ساخت تصویر", variant="primary", elem_classes="primary-btn", elem_id="run-btn", visible=True)
 
453
  upgrade_button = gr.Button("💎 خرید نسخه نامحدود (کلیک کنید)", variant="primary", elem_classes="upgrade-btn", elem_id="upgrade-btn", visible=False)
454
 
455
  with gr.Column():
 
470
  def toggle_row(choice): return gr.update(visible=(choice == "شخصی‌سازی (Custom)"))
471
  aspect_ratio_selection.change(fn=toggle_row, inputs=aspect_ratio_selection, outputs=custom_dims_row)
472
 
473
+ # --- سیستم چک کردن خودکار اعتبار در هنگام لود صفحه ---
474
+ # به محض اینکه JS اثرانگشت را در باکس مخفی می‌ریزد، این تابع اجرا می‌شود
475
+ fingerprint_box.change(
476
+ fn=check_initial_quota,
477
+ inputs=[fingerprint_box, status_box_input],
478
+ outputs=[run_button, upgrade_button, status_box]
479
+ )
480
+
481
  run_button.click(
482
  fn=infer,
483
  inputs=[input_image, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps, aspect_ratio_selection, custom_width, custom_height, fingerprint_box, status_box_input],
484
  outputs=[output_image, seed, status_box, run_button, upgrade_button]
485
  )
486
 
 
487
  upgrade_button.click(fn=None, js=js_upgrade_func)
 
488
  download_button.click(fn=None, inputs=[output_image], js=js_download_func)
489
 
 
490
  gr.Examples(
491
+ examples=[["examples/1.jpg", "تبدیل به انیمه کن.", "تبدیل عکس به انیمه"]],
 
 
 
492
  inputs=[input_image, prompt, lora_adapter],
 
493
  fn=infer_example
494
  )
495