Opera8 commited on
Commit
c3ba67c
·
verified ·
1 Parent(s): 9481e69

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -75
app.py CHANGED
@@ -33,8 +33,28 @@ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
33
 
34
  # --- تنظیمات سیستم اعتبار ---
35
  USAGE_LIMIT = 5
36
- usage_data_cache = {} # {fingerprint: {'count': 0, 'last_reset': 'YYYY-MM-DD'}}
37
- PREMIUM_PAGE_ID = '1149636' # شناسه محصول برای بررسی خرید
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  # --- بارگذاری مدل تشخیص محتوای نامناسب (NSFW) ---
40
  print("Loading Safety Checker...")
@@ -178,20 +198,26 @@ def get_success_html(message):
178
  def check_quota(fingerprint, subscription_status):
179
  """
180
  بررسی اعتبار کاربر.
181
- اگر پولی باشد -> True
182
- اگر رایگان باشد:
183
- - بررسی تاریخ
184
- - بررسی تعداد استفاده
185
  """
 
 
 
 
 
186
  if subscription_status == 'paid':
187
  return True, "unlimited"
188
 
189
- if not fingerprint:
190
- # اگر فینگرپرینت نرسید (مثلا جاوا اسکریپت لود نشد)، به عنوان مهمان یک بار اجازه دهیم یا خطا دهیم.
191
- # اینجا فرض می‌کنیم باید خطا دهیم یا اجازه محدود دهیم.
192
- return True, 0 # برای جلوگیری از خطا در اولین اجرا
 
193
 
194
  today_str = date.today().isoformat()
 
 
 
 
195
  user_record = usage_data_cache.get(fingerprint)
196
 
197
  # اگر کاربر جدید است یا روز عوض شده
@@ -203,8 +229,13 @@ def check_quota(fingerprint, subscription_status):
203
  if user_record["count"] >= USAGE_LIMIT:
204
  return False, user_record["count"]
205
 
206
- # کسر اعتبار (در واقع افزایش استفاده)
207
  user_record["count"] += 1
 
 
 
 
 
208
  return True, user_record["count"]
209
 
210
 
@@ -224,12 +255,13 @@ def infer(
224
  subscription_status,
225
  progress=gr.Progress(track_tqdm=True)
226
  ):
 
 
227
  # --- بررسی اعتبار ---
228
  is_allowed, usage_count = check_quota(fingerprint, subscription_status)
229
 
230
  if not is_allowed:
231
  msg = "شما به محدودیت ۵ تصویر رایگان روزانه خود رسیده‌اید. لطفاً برای استفاده نامحدود حساب خود را ارتقا دهید."
232
- # این پیام باعث نمایش مودال ارتقا می‌شود (به خاطر کلمه Quota یا Limited در JS)
233
  return None, seed, get_error_html(msg)
234
 
235
  if input_image is None:
@@ -296,8 +328,6 @@ def infer(
296
 
297
  @spaces.GPU(duration=30)
298
  def infer_example(input_image, prompt, lora_adapter):
299
- # برای مثال‌ها، فینگرپرینت ساختگی و وضعیت رایگان در نظر می‌گیریم اما معمولا مثال‌ها اعتبار کم نمی‌کنند
300
- # یا می‌توانید برای مثال‌ها سختگیری نکنید. اینجا سختگیری نمیکنیم.
301
  res, s, status = infer(input_image, prompt, lora_adapter, 0, True, 1.0, 4, "خودکار (پیش‌فرض)", 1024, 1024, "example_user", "paid")
302
  return res, s, status
303
 
@@ -321,7 +351,7 @@ async (image) => {
321
  }
322
  """
323
 
324
- # --- جاوااسکریپت جامع (Fingerprint, Subscription, UI, Errors) ---
325
  js_global_content = """
326
  <script>
327
  document.addEventListener('DOMContentLoaded', () => {
@@ -329,7 +359,6 @@ document.addEventListener('DOMContentLoaded', () => {
329
  // 1. Fingerprint & Subscription Logic
330
  // ---------------------------------------------
331
 
332
- // تابع تولید Fingerprint
333
  async function getBrowserFingerprint() {
334
  const components = [
335
  navigator.userAgent,
@@ -340,17 +369,12 @@ document.addEventListener('DOMContentLoaded', () => {
340
  try {
341
  const canvas = document.createElement('canvas');
342
  const ctx = canvas.getContext('2d');
343
- ctx.textBaseline = "top";
344
- ctx.font = "14px 'Arial'";
345
- ctx.textBaseline = "alphabetic";
346
- ctx.fillStyle = "#f60";
347
- ctx.fillRect(125, 1, 62, 20);
348
- ctx.fillStyle = "#069";
349
- ctx.fillText("a1b2c3d4e5f6g7h8i9j0_!@#$%^&*()", 2, 15);
350
  components.push(canvas.toDataURL());
351
- } catch (e) {
352
- components.push("canvas-error");
353
- }
354
  const fingerprintString = components.join('~~~');
355
  let hash = 0;
356
  for (let i = 0; i < fingerprintString.length; i++) {
@@ -361,36 +385,32 @@ document.addEventListener('DOMContentLoaded', () => {
361
  return 'fp_' + Math.abs(hash).toString(16);
362
  }
363
 
364
- // بررسی کاربر پولی بودن
365
  function isUserPaid(userObject) {
366
  const PREMIUM_PAGE_ID = '1149636';
367
  if (userObject && userObject.isLogin && userObject.accessible_pages) {
368
- // بررسی می‌کنیم آیا ID صفحه پرمیوم در لیست دسترسی‌ها هست یا نه
369
  if (Array.isArray(userObject.accessible_pages)) {
370
- return userObject.accessible_pages.some(page =>
371
- String(page) === String(PREMIUM_PAGE_ID)
372
- );
373
  }
374
  }
375
  return false;
376
  }
377
 
378
- // به‌روزرسانی UI badge
379
- function updateSubscriptionBadge(status) {
380
- const badge = document.getElementById('user-sub-badge');
381
  const fpInput = document.querySelector('#fingerprint_storage textarea');
382
  const stInput = document.querySelector('#status_storage textarea');
383
-
384
- // مقداردهی اینپوت‌های مخفی برای ارسال به پایتون
385
  if(fpInput) {
386
- fpInput.value = window.userFingerprint || 'unknown';
387
  fpInput.dispatchEvent(new Event('input', { bubbles: true }));
388
  }
389
  if(stInput) {
390
  stInput.value = status;
391
  stInput.dispatchEvent(new Event('input', { bubbles: true }));
392
  }
 
393
 
 
 
394
  if (!badge) return;
395
 
396
  if (status === 'paid') {
@@ -401,20 +421,24 @@ document.addEventListener('DOMContentLoaded', () => {
401
  badge.style.background = 'linear-gradient(45deg, #4b5563, #6b7280)';
402
  }
403
  badge.style.display = 'inline-block';
 
 
 
404
  }
405
 
406
- // شروع فرآیند شناسایی
407
  async function initUserIdentity() {
408
  window.userFingerprint = await getBrowserFingerprint();
 
409
 
410
- // درخواست وضعیت از Parent
411
  window.parent.postMessage({ type: 'REQUEST_USER_STATUS' }, '*');
412
-
413
- // پیش‌فرض: رایگان
414
  updateSubscriptionBadge('free');
 
 
 
 
 
415
  }
416
 
417
- // شنیدن پیام از Parent
418
  window.addEventListener('message', (event) => {
419
  if (event.data && event.data.type === 'USER_STATUS_RESPONSE') {
420
  try {
@@ -423,6 +447,7 @@ document.addEventListener('DOMContentLoaded', () => {
423
  : event.data.payload;
424
 
425
  const status = isUserPaid(userObject) ? 'paid' : 'free';
 
426
  updateSubscriptionBadge(status);
427
  } catch (e) {
428
  console.error("Error parsing user status:", e);
@@ -434,7 +459,7 @@ document.addEventListener('DOMContentLoaded', () => {
434
  initUserIdentity();
435
 
436
  // ---------------------------------------------
437
- // 2. UI Helper & Error Handling
438
  // ---------------------------------------------
439
  const forceLight = () => {
440
  const body = document.querySelector('body');
@@ -448,13 +473,6 @@ document.addEventListener('DOMContentLoaded', () => {
448
  forceLight();
449
  setInterval(forceLight, 1000);
450
 
451
- window.retryGeneration = function() {
452
- const modal = document.getElementById('custom-quota-modal');
453
- if (modal) modal.remove();
454
- const runBtn = document.getElementById('run-btn');
455
- if(runBtn) runBtn.click();
456
- };
457
-
458
  window.closeErrorModal = function() {
459
  const modal = document.getElementById('custom-quota-modal');
460
  if (modal) modal.remove();
@@ -469,25 +487,22 @@ document.addEventListener('DOMContentLoaded', () => {
469
  <div style="font-size: 2rem;">💎</div>
470
  <div>
471
  <h2>پایان اعتبار رایگان امروز</h2>
472
- <p>برای ادامه، حساب خود را ارتقا دهید یا فردا برگردید.</p>
473
  </div>
474
  </div>
475
-
476
  <div class="guide-content">
477
  <div class="info-card" style="border-color: #fbbf24; background: #fffbeb;">
478
- <p>شما از ۵ اعتبار رایگان امروز خود استفاده کرده‌اید. با خرید نسخه نامحدود، بدون هیچ محدودیتی تصویر بسازید.</p>
479
  </div>
480
-
481
  <div class="video-button-container">
482
- <button onclick="parent.postMessage({ type: 'NAVIGATE_TO_URL', url: '#/nav/online/news/getSingle/1149636/eyJpdiI6InZSVUdlLzBlR0FzOHZJdXFZeWhER0E9PSIsInZhbHVlIjoiWFhqRXBLc29vSFpHdk9nYmRjZGVuWHRHRHVSZHRlTG1BUENLaE5mNXBNVVRGWFg3ZWN0djJ5K1dIY1RqTHJGaCIsIm1hYyI6IjIzYzFlZTMwYmVmMTdkYjQ0YTQ4YWMxNmFhN2RmNWQ2OTc1NDIyNGVlZGI3ZjJjMjhkNmQxNjM4MDFlZTIxNmUiLCJ0YWciOiIifQ==/20934991' }, '*')" class="elegant-video-button" style="background: linear-gradient(135deg, #f59e0b, #d97706) !important; color: white !important;">
483
  <span>⭐️ خرید نسخه نامحدود</span>
484
  </button>
485
  </div>
486
  </div>
487
-
488
  <div class="guide-actions">
489
  <button class="action-button back-button" onclick="window.closeErrorModal()">
490
- <span>بازگشت</span>
491
  </button>
492
  </div>
493
  </div>
@@ -500,7 +515,6 @@ document.addEventListener('DOMContentLoaded', () => {
500
  const potentialErrors = document.querySelectorAll('.toast-body, .error, .toast-wrap, .eta-bar, div[class*="error"]');
501
  potentialErrors.forEach(el => {
502
  const text = el.innerText || "";
503
- // اگر ارور مربوط به Quota بود (که از سمت پایتون میاد)
504
  if (text.includes('محدودیت') && text.includes('رایگان')) {
505
  showQuotaModal();
506
  el.style.display = 'none';
@@ -524,19 +538,14 @@ css_code = """
524
  font-family: 'Vazirmatn', sans-serif !important;
525
  }
526
 
527
- /* Badge Style */
528
- #user-sub-badge {
529
- padding: 6px 16px;
530
- border-radius: 20px;
531
- font-size: 0.9em;
532
- color: white;
533
- margin-top: 10px;
534
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
535
- display: none; /* JS will show it */
536
- transition: all 0.3s ease;
537
  }
538
 
539
- /* Modal CSS (Reused from previous request mostly) */
540
  .ip-reset-guide-container {
541
  text-align: right;
542
  direction: rtl;
@@ -608,10 +617,9 @@ combined_html = css_code + js_global_content
608
  with gr.Blocks() as demo:
609
  gr.HTML(combined_html)
610
 
611
- # کامپوننت‌های مخفی برای انتقال اطلاعات JS به Python
612
- # مقدار این‌ها توسط JS پر می‌شود
613
- fingerprint_box = gr.Textbox(elem_id="fingerprint_storage", visible=False)
614
- status_box_input = gr.Textbox(elem_id="status_storage", visible=False)
615
 
616
  with gr.Column(elem_id="col-container"):
617
  gr.Markdown("# **ویرایشگر هوشمند آلفا**", elem_id="main-title")
@@ -706,14 +714,13 @@ with gr.Blocks() as demo:
706
  label="نمونه‌ها (برای تست کلیک کنید)"
707
  )
708
 
709
- # اتصال دکمه اجرا با ورودی‌های مخفی Fingerprint و Status
710
  run_button.click(
711
  fn=infer,
712
  inputs=[
713
  input_image, prompt, lora_adapter, seed, randomize_seed,
714
  guidance_scale, steps, aspect_ratio_selection,
715
  custom_width, custom_height,
716
- fingerprint_box, status_box_input # ارسال اطلاعات کاربر به تابع پایتون
717
  ],
718
  outputs=[output_image, seed, status_box],
719
  api_name="predict"
 
33
 
34
  # --- تنظیمات سیستم اعتبار ---
35
  USAGE_LIMIT = 5
36
+ DATA_FILE = "usage_data.json" # فایل ذخیره اطلاعات
37
+ PREMIUM_PAGE_ID = '1149636'
38
+
39
+ # --- توابع مدیریت فایل JSON ---
40
+ def load_usage_data():
41
+ if os.path.exists(DATA_FILE):
42
+ try:
43
+ with open(DATA_FILE, 'r') as f:
44
+ return json.load(f)
45
+ except:
46
+ return {}
47
+ return {}
48
+
49
+ def save_usage_data(data):
50
+ try:
51
+ with open(DATA_FILE, 'w') as f:
52
+ json.dump(data, f)
53
+ except Exception as e:
54
+ print(f"Error saving data: {e}")
55
+
56
+ # بارگذاری اولیه داده‌ها
57
+ usage_data_cache = load_usage_data()
58
 
59
  # --- بارگذاری مدل تشخیص محتوای نامناسب (NSFW) ---
60
  print("Loading Safety Checker...")
 
198
  def check_quota(fingerprint, subscription_status):
199
  """
200
  بررسی اعتبار کاربر.
 
 
 
 
201
  """
202
+ global usage_data_cache
203
+
204
+ # لاگ برای دیباگ
205
+ print(f"CHECK QUOTA: FP={fingerprint}, STATUS={subscription_status}")
206
+
207
  if subscription_status == 'paid':
208
  return True, "unlimited"
209
 
210
+ if not fingerprint or fingerprint == "unknown":
211
+ # اگر فینگرپرینت نرسید، موقتا اجازه ندهیم یا یک شناسه موقت بسازیم
212
+ # اینجا فرض می‌کنیم کاربر ناشناس است
213
+ print("Warning: No fingerprint received.")
214
+ return True, 0
215
 
216
  today_str = date.today().isoformat()
217
+
218
+ # رفرش کردن دیتا از فایل (در صورت تغییر توسط ترد دیگر)
219
+ usage_data_cache = load_usage_data()
220
+
221
  user_record = usage_data_cache.get(fingerprint)
222
 
223
  # اگر کاربر جدید است یا روز عوض شده
 
229
  if user_record["count"] >= USAGE_LIMIT:
230
  return False, user_record["count"]
231
 
232
+ # کسر اعتبار
233
  user_record["count"] += 1
234
+
235
+ # ذخیره در فایل
236
+ usage_data_cache[fingerprint] = user_record
237
+ save_usage_data(usage_data_cache)
238
+
239
  return True, user_record["count"]
240
 
241
 
 
255
  subscription_status,
256
  progress=gr.Progress(track_tqdm=True)
257
  ):
258
+ print(f"INFER CALLED: Fingerprint='{fingerprint}', Sub='{subscription_status}'")
259
+
260
  # --- بررسی اعتبار ---
261
  is_allowed, usage_count = check_quota(fingerprint, subscription_status)
262
 
263
  if not is_allowed:
264
  msg = "شما به محدودیت ۵ تصویر رایگان روزانه خود رسیده‌اید. لطفاً برای استفاده نامحدود حساب خود را ارتقا دهید."
 
265
  return None, seed, get_error_html(msg)
266
 
267
  if input_image is None:
 
328
 
329
  @spaces.GPU(duration=30)
330
  def infer_example(input_image, prompt, lora_adapter):
 
 
331
  res, s, status = infer(input_image, prompt, lora_adapter, 0, True, 1.0, 4, "خودکار (پیش‌فرض)", 1024, 1024, "example_user", "paid")
332
  return res, s, status
333
 
 
351
  }
352
  """
353
 
354
+ # --- جاوااسکریپت جامع ---
355
  js_global_content = """
356
  <script>
357
  document.addEventListener('DOMContentLoaded', () => {
 
359
  // 1. Fingerprint & Subscription Logic
360
  // ---------------------------------------------
361
 
 
362
  async function getBrowserFingerprint() {
363
  const components = [
364
  navigator.userAgent,
 
369
  try {
370
  const canvas = document.createElement('canvas');
371
  const ctx = canvas.getContext('2d');
372
+ ctx.textBaseline = "top"; ctx.font = "14px 'Arial'"; ctx.textBaseline = "alphabetic";
373
+ ctx.fillStyle = "#f60"; ctx.fillRect(125, 1, 62, 20);
374
+ ctx.fillStyle = "#069"; ctx.fillText("a1b2c3d4e5f6g7h8i9j0_!@#$%^&*()", 2, 15);
 
 
 
 
375
  components.push(canvas.toDataURL());
376
+ } catch (e) { components.push("canvas-error"); }
377
+
 
378
  const fingerprintString = components.join('~~~');
379
  let hash = 0;
380
  for (let i = 0; i < fingerprintString.length; i++) {
 
385
  return 'fp_' + Math.abs(hash).toString(16);
386
  }
387
 
 
388
  function isUserPaid(userObject) {
389
  const PREMIUM_PAGE_ID = '1149636';
390
  if (userObject && userObject.isLogin && userObject.accessible_pages) {
 
391
  if (Array.isArray(userObject.accessible_pages)) {
392
+ return userObject.accessible_pages.some(page => String(page) === String(PREMIUM_PAGE_ID));
 
 
393
  }
394
  }
395
  return false;
396
  }
397
 
398
+ function updateHiddenInputs(fingerprint, status) {
 
 
399
  const fpInput = document.querySelector('#fingerprint_storage textarea');
400
  const stInput = document.querySelector('#status_storage textarea');
401
+
 
402
  if(fpInput) {
403
+ fpInput.value = fingerprint;
404
  fpInput.dispatchEvent(new Event('input', { bubbles: true }));
405
  }
406
  if(stInput) {
407
  stInput.value = status;
408
  stInput.dispatchEvent(new Event('input', { bubbles: true }));
409
  }
410
+ }
411
 
412
+ function updateSubscriptionBadge(status) {
413
+ const badge = document.getElementById('user-sub-badge');
414
  if (!badge) return;
415
 
416
  if (status === 'paid') {
 
421
  badge.style.background = 'linear-gradient(45deg, #4b5563, #6b7280)';
422
  }
423
  badge.style.display = 'inline-block';
424
+
425
+ // اطمینان از آپدیت شدن اینپوت‌ها هر بار که وضعیت تغییر می‌کند
426
+ updateHiddenInputs(window.userFingerprint || 'unknown', status);
427
  }
428
 
 
429
  async function initUserIdentity() {
430
  window.userFingerprint = await getBrowserFingerprint();
431
+ window.userStatus = 'free'; // Default
432
 
 
433
  window.parent.postMessage({ type: 'REQUEST_USER_STATUS' }, '*');
 
 
434
  updateSubscriptionBadge('free');
435
+
436
+ // Polling to ensure inputs are populated even if DOM is slow
437
+ setInterval(() => {
438
+ updateHiddenInputs(window.userFingerprint || 'unknown', window.userStatus || 'free');
439
+ }, 2000);
440
  }
441
 
 
442
  window.addEventListener('message', (event) => {
443
  if (event.data && event.data.type === 'USER_STATUS_RESPONSE') {
444
  try {
 
447
  : event.data.payload;
448
 
449
  const status = isUserPaid(userObject) ? 'paid' : 'free';
450
+ window.userStatus = status;
451
  updateSubscriptionBadge(status);
452
  } catch (e) {
453
  console.error("Error parsing user status:", e);
 
459
  initUserIdentity();
460
 
461
  // ---------------------------------------------
462
+ // 2. UI Helper
463
  // ---------------------------------------------
464
  const forceLight = () => {
465
  const body = document.querySelector('body');
 
473
  forceLight();
474
  setInterval(forceLight, 1000);
475
 
 
 
 
 
 
 
 
476
  window.closeErrorModal = function() {
477
  const modal = document.getElementById('custom-quota-modal');
478
  if (modal) modal.remove();
 
487
  <div style="font-size: 2rem;">💎</div>
488
  <div>
489
  <h2>پایان اعتبار رایگان امروز</h2>
490
+ <p>برای ادامه، حساب خود را ارتقا دهید.</p>
491
  </div>
492
  </div>
 
493
  <div class="guide-content">
494
  <div class="info-card" style="border-color: #fbbf24; background: #fffbeb;">
495
+ <p>شما از ۵ اعتبار رایگان امروز خود استفاده کرده‌اید.</p>
496
  </div>
 
497
  <div class="video-button-container">
498
+ <button onclick="parent.postMessage({ type: 'NAVIGATE_TO_PREMIUM' }, '*')" class="elegant-video-button" style="background: linear-gradient(135deg, #f59e0b, #d97706) !important; color: white !important;">
499
  <span>⭐️ خرید نسخه نامحدود</span>
500
  </button>
501
  </div>
502
  </div>
 
503
  <div class="guide-actions">
504
  <button class="action-button back-button" onclick="window.closeErrorModal()">
505
+ <span>بستن</span>
506
  </button>
507
  </div>
508
  </div>
 
515
  const potentialErrors = document.querySelectorAll('.toast-body, .error, .toast-wrap, .eta-bar, div[class*="error"]');
516
  potentialErrors.forEach(el => {
517
  const text = el.innerText || "";
 
518
  if (text.includes('محدودیت') && text.includes('رایگان')) {
519
  showQuotaModal();
520
  el.style.display = 'none';
 
538
  font-family: 'Vazirmatn', sans-serif !important;
539
  }
540
 
541
+ /* Hide the storage inputs but keep them in DOM */
542
+ #fingerprint_storage, #status_storage {
543
+ display: none !important;
544
+ /* We use display:none with !important in CSS instead of visible=False in Python
545
+ to ensure the element exists in DOM for JS to find it. */
 
 
 
 
 
546
  }
547
 
548
+ /* Modal CSS */
549
  .ip-reset-guide-container {
550
  text-align: right;
551
  direction: rtl;
 
617
  with gr.Blocks() as demo:
618
  gr.HTML(combined_html)
619
 
620
+ # تغییر مهم: visible=True قرار دادیم تا در DOM باشد، اما با CSS مخفی کردیم
621
+ fingerprint_box = gr.Textbox(elem_id="fingerprint_storage", visible=True)
622
+ status_box_input = gr.Textbox(elem_id="status_storage", visible=True)
 
623
 
624
  with gr.Column(elem_id="col-container"):
625
  gr.Markdown("# **ویرایشگر هوشمند آلفا**", elem_id="main-title")
 
714
  label="نمونه‌ها (برای تست کلیک کنید)"
715
  )
716
 
 
717
  run_button.click(
718
  fn=infer,
719
  inputs=[
720
  input_image, prompt, lora_adapter, seed, randomize_seed,
721
  guidance_scale, steps, aspect_ratio_selection,
722
  custom_width, custom_height,
723
+ fingerprint_box, status_box_input
724
  ],
725
  outputs=[output_image, seed, status_box],
726
  api_name="predict"