AbuAlone09 commited on
Commit
176ad2b
·
verified ·
1 Parent(s): 05fe832

Update licensing_client.py

Browse files
Files changed (1) hide show
  1. licensing_client.py +81 -80
licensing_client.py CHANGED
@@ -17,7 +17,6 @@ from loguru import logger
17
  STORAGE_FILE = "Hugging AbuAlone09/AI-Video-Engine-storage"
18
 
19
  # ĐỌC BIẾN MÔI TRƯỜNG AN TOÀN TỪ SECRET CỦA HUGGING FACE
20
- # Ông chỉ cần điền Link gốc và API Key vào ô Secret trên Settings của Space Tool chính, code tự động bốc lên chạy ngầm
21
  SERVER_URL = os.getenv("LICENSIFY_SERVER_URL", "https://abualone09-my-licensify-server.hf.space").strip()
22
  SECRET_API_KEY = os.getenv("SECRET_API_KEY", "YOUR_SECRET_API_KEY_HERE").strip()
23
 
@@ -28,7 +27,6 @@ if not os.path.exists(STORAGE_FILE):
28
  json.dump({"keys": {}, "free_devices": {}}, f, indent=4)
29
 
30
  # --- THÀNH PHẦN QUAN TRỌNG: QUẢN LÝ TRẠNG THÁI 6 LUỒNG TOÀN CỤC (MEMORY LOCK) ---
31
- # Slot 1 -> 5: Ưu tiên VIP Premium. Slot 6: Dành riêng cho Free (VIP có quyền đẩy tiến trình)
32
  THREAD_POOL = {
33
  1: {"status": "idle", "key": None, "type": None, "pid": None, "device": None},
34
  2: {"status": "idle", "key": None, "type": None, "pid": None, "device": None},
@@ -38,6 +36,11 @@ THREAD_POOL = {
38
  6: {"status": "idle", "key": None, "type": None, "pid": None, "device": None},
39
  }
40
 
 
 
 
 
 
41
  # =========================================================
42
  # CORE 1: XỬ LÝ ĐỌC / GHI & XÁC THỰC API KEY TỪ XA TỚI SERVER
43
  # =========================================================
@@ -56,14 +59,12 @@ def clean_expired_keys():
56
  now_ts = time.time()
57
  changed = False
58
 
59
- # Quét sạch VIP Key hết hạn khỏi hệ thống lưu trữ
60
  for k, info in list(data["keys"].items()):
61
  if now_ts > info.get("expiry_timestamp", 0):
62
  del data["keys"][k]
63
  changed = True
64
  logger.warning(f"Key {k} expired and has been automatically purged from storage.")
65
 
66
- # Reset lượt Free nếu hệ thống chuyển dịch sang ngày mới
67
  today_str = datetime.now().strftime("%Y-%m-%d")
68
  for dev, info in list(data["free_devices"].items()):
69
  if info.get("last_date") != today_str:
@@ -75,51 +76,77 @@ def clean_expired_keys():
75
  _save_storage(data)
76
 
77
  def verify_and_get_license_info(key_input: str, device_id: str) -> tuple:
78
- """Kiểm tra cục bộ trước, nếu không gọi API sang Server cổng thanh toán đối soát"""
79
  clean_expired_keys()
80
 
81
- # 1. Cơ chế đăng nhập bằng mã Admin để Test nhanh hệ thống bypass
82
- if key_input.strip() == "ADMIN_SECRET_TEST_2026":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  return True, {
84
- "tier": "ADMIN", "tx_name": "System Internal Tester", "amount": "$0.00",
85
- "tx_date": "2026-05-19", "expiry": "2026-12-31", "days_left": 999,
86
- "msg": "📋 SYSTEM STATUS: Admin Test Bypass Active. All threads unlocked."
 
 
 
 
 
 
 
87
  }
88
 
89
  data = _load_storage()
90
 
91
- # 2. Tra cứu kho lưu trữ cục bộ trước để tăng tốc độ phản hồi cho giao diện điện thoại
92
- if key_input in data["keys"]:
93
- info = data["keys"][key_input]
94
  days_left = int((info["expiry_timestamp"] - time.time()) / 86400)
95
  return True, {
96
  "tier": "VIP", "tx_name": info["tx_name"], "amount": info["amount"],
97
  "tx_date": info["tx_date"], "expiry": info["expiry"], "days_left": max(0, days_left),
98
- "msg": f"👑 VIP ACTIVE | User: {info['tx_name']} | {max(0, days_left)} days remaining."
 
 
 
99
  }
100
 
101
- # 3. Giao tiếp API an toàn từ xa kết nối thẳng tới Kho lưu trữ của Cổng thanh toán dịch vụ
102
  try:
103
- # Gài mã bảo mật Secret nội bộ vào lớp vỏ Header để thông quan
104
  headers = {
105
  "X-API-Key": SECRET_API_KEY,
106
  "Content-Type": "application/json"
107
  }
108
- payload = {"key": key_input.strip(), "hwid": device_id}
109
-
110
- # Gọi lệnh POST gửi dữ liệu JSON thông mạch tới cổng dịch vụ của ông
111
  response = requests.post(f"{SERVER_URL}/api/verify-key", json=payload, headers=headers, timeout=10)
112
 
113
  if response.status_code == 200:
114
  res_data = response.json()
115
  if res_data.get("status") == "success":
116
- # Bóc tách chuẩn xác toàn bộ thông tin giao dịch cần giám sát từ Server dịch vụ trả về
117
  tx_info = res_data.get("data", {})
118
- expiry_str = tx_info.get("expiry_date") # Định dạng YYYY-MM-DD từ cổng thanh toán
119
  expiry_ts = time.mktime(time.strptime(expiry_str, "%Y-%m-%d"))
120
 
121
- # Lưu trữ đồng bộ vào file nội bộ để theo dõi tiến trình render
122
- data["keys"][key_input] = {
123
  "tx_name": tx_info.get("buyer_name", "Anonymous User"),
124
  "amount": tx_info.get("amount_paid", "$2.99"),
125
  "tx_date": tx_info.get("payment_date", datetime.now().strftime("%Y-%m-%d")),
@@ -132,19 +159,21 @@ def verify_and_get_license_info(key_input: str, device_id: str) -> tuple:
132
  return True, {
133
  "tier": "VIP", "tx_name": tx_info.get("buyer_name"), "amount": tx_info.get("amount_paid"),
134
  "tx_date": tx_info.get("payment_date"), "expiry": expiry_str, "days_left": max(0, days_left),
135
- "msg": f"👑 VIP KEY VERIFIED | Owner: {tx_info.get('buyer_name')} | Amount: {tx_info.get('amount_paid')} | Expiry: {expiry_str} ({max(0, days_left)} days left)."
 
 
 
136
  }
137
- return False, {"msg": "❌ Invalid License Key or communication failure with payment gateway!"}
138
  except Exception as e:
139
- return False, {"msg": f"⚠️ Connection error to Licensify Server! Verify your Secret URL configs. Info: {str(e)}"}
140
 
141
  # =========================================================
142
  # CORE 2: CƠ CHẾ ĐIỀU PHỐI 6 LUỒNG ĐỘNG & BIÊN TRỰC VIP / FREE
143
  # =========================================================
144
 
145
  def get_thread_status_json():
146
- """Trả về trạng thái phân bổ thời gian thực của 6 luồng theo từng giây lên UI"""
147
- busy_count = sum(1 for t in THREAD_POOL.values() if t["status"] == "rendering")
148
  vip_count = sum(1 for t in THREAD_POOL.values() if t["type"] == "VIP")
149
  free_count = sum(1 for t in THREAD_POOL.values() if t["type"] == "FREE")
150
  return {
@@ -155,71 +184,56 @@ def get_thread_status_json():
155
  }
156
 
157
  def allocate_render_thread(key_input: str, device_id: str, is_vip: bool) -> tuple:
158
- """
159
- Thuật toán phân phối 6 luồng động cao cấp:
160
- - Mỗi VIP Key chỉ được tạo duy nhất 1 luồng/thiết bị, chống cắm đa luồng trùng lặp.
161
- - VIP tự động đẩy tiến trình Free tại Slot 6 nếu hệ thống đầy.
162
- - Chặn đứng mọi k���t nối vượt ngưỡng (Người thứ 7 báo đầy luồng).
163
- """
164
  global THREAD_POOL
165
 
166
- # Khóa đơn luồng thiết bị: Kiểm tra trùng lặp tiến trình đang xử lý
167
  for slot, thread in THREAD_POOL.items():
168
  if thread["status"] == "rendering":
169
- if is_vip and thread["key"] == key_input:
170
- return False, "❌ This VIP Key is already running a rendering process on another device! Multi-thread allocation denied."
171
  if not is_vip and thread["device"] == device_id:
172
  return False, "❌ Your device is currently rendering a video. Please wait until it completes!"
173
 
174
  target_slot = None
175
 
176
  if is_vip:
177
- # VIP quét tìm không gian slot trống từ Slot 1 đến Slot 5
178
  for i in range(1, 6):
179
  if THREAD_POOL[i]["status"] == "idle":
180
  target_slot = i
181
  break
182
 
183
- # Nếu 5 slot VIP đã full, VIP tiến hành kiểm tra chiếm dụng Slot số 6
184
  if not target_slot:
185
  if THREAD_POOL[6]["status"] == "idle":
186
  target_slot = 6
187
  elif THREAD_POOL[6]["type"] == "FREE":
188
- # 🔥 CƠ CHẾ ĐẨY TIẾN TRÌNH: VIP chiếm chỗ, gửi tín hiệu hủy trực tiếp PID của Free
189
  free_pid = THREAD_POOL[6]["pid"]
190
- logger.warning(f"👑 VIP Eviction Triggered: Terminating Free PID {free_pid} at Slot 6 to reclaim resources.")
191
  try:
192
  if free_pid:
193
- os.kill(free_pid, signal.SIGKILL) # Chấm dứt ngầm tiến trình FFmpeg của tài khoản Free
194
  except ProcessLookupError:
195
  pass
196
 
197
  target_slot = 6
198
- # Trả Slot 6 về mặc định trống để nạp đè VIP vào luôn
199
  THREAD_POOL[6] = {"status": "idle", "key": None, "type": None, "pid": None, "device": None}
200
  else:
201
- # Tài khoản Free: Chỉ được phép nạp vào Slot số 6 nếu hoàn toàn trống
202
  if THREAD_POOL[6]["status"] == "idle":
203
  target_slot = 6
204
 
205
- # BIÊN CHẶN NGƯỜI THỨ 7: Toàn bộ 6 luồng hệ thống đã đạt đỉnh tải
206
  if not target_slot:
207
  return False, "⚠️ All rendering channels are currently full. Please try again in a few moments!"
208
 
209
  return True, target_slot
210
 
211
  def register_process_to_slot(slot: int, key_input: str, device_id: str, is_vip: bool, pid: int):
212
- """Ghi nhận định danh tiến trình hệ thống (PID) vào sơ đồ luồng"""
213
  THREAD_POOL[slot] = {
214
  "status": "rendering",
215
- "key": key_input if is_vip else None,
216
  "type": "VIP" if is_vip else "FREE",
217
  "pid": pid,
218
  "device": device_id
219
  }
220
 
221
  def release_thread_slot(slot: int):
222
- """Giải phóng trạng thái slot sau khi tiến trình hoàn tất hoặc xuất file thành công"""
223
  if slot in THREAD_POOL:
224
  THREAD_POOL[slot] = {"status": "idle", "key": None, "type": None, "pid": None, "device": None}
225
 
@@ -228,7 +242,6 @@ def release_thread_slot(slot: int):
228
  # =========================================================
229
 
230
  def check_generation_limits(key_input: str, device_id: str, is_vip: bool) -> tuple:
231
- """Kiểm tra hạn mức tạo video sát sao theo thời gian thực"""
232
  data = _load_storage()
233
  today_str = datetime.now().strftime("%Y-%m-%d")
234
  now_ts = time.time()
@@ -238,33 +251,31 @@ def check_generation_limits(key_input: str, device_id: str, is_vip: bool) -> tup
238
  return False, "License error: Key missing from verified list."
239
 
240
  vip_data = data["keys"][key_input]
241
- # Khởi tạo bộ đếm mới nếu bước sang ngày mới theo chu kỳ cổng thanh toán cung cấp
242
  if vip_data.get("last_date_used") != today_str:
243
  vip_data["last_date_used"] = today_str
244
- vip_data["daily_batches"] = 0 # Tối đa 5 lượt / ngày (tổng 10 video)
245
- vip_data["videos_this_batch"] = 0 # Tối đa 2 video / lượt tạo
246
  vip_data["cooldown_until"] = 0
247
 
248
  if now_ts < vip_data.get("cooldown_until", 0):
249
  wait_min = int((vip_data["cooldown_until"] - now_ts) / 60)
250
- return False, f"⏱️ Cooldown active! You have reached your limit for this batch. Please return in {wait_min} minutes."
251
 
252
  if vip_data.get("daily_batches", 0) >= 5:
253
- return False, "❌ You have exhausted your daily limit of 5 batches (10 videos maximum). Please return tomorrow!"
254
 
255
  remaining_batches = 5 - vip_data.get("daily_batches", 0)
256
  return True, {
257
  "videos_created": vip_data.get("videos_this_batch", 0),
258
  "batches_created": vip_data.get("daily_batches", 0),
259
  "remaining_batches": remaining_batches,
260
- "status_str": f"VIP Usage: Batch {vip_data.get('daily_batches', 0)}/5 | Current Batch: {vip_data.get('videos_this_batch', 0)}/2 Videos"
261
  }
262
  else:
263
- # Cơ chế nhận diện thiết bị chống spam cho khách hàng không dùng key (FREE)
264
  if device_id not in data["free_devices"]:
265
  data["free_devices"][device_id] = {
266
  "last_date": today_str,
267
- "daily_batches": 0, # Tối đa 3 lượt / ngày (1 video/lượt)
268
  "cooldown_until": 0
269
  }
270
 
@@ -279,7 +290,7 @@ def check_generation_limits(key_input: str, device_id: str, is_vip: bool) -> tup
279
  return False, f"⏱️ Free Tier Cooldown: Please wait {wait_hours + 1} hours before generating again, or upgrade to VIP Premium!"
280
 
281
  if free_data.get("daily_batches", 0) >= 3:
282
- return False, "❌ Free Tier Exhausted! Maximum 3 daily videos reached. Upgrade to VIP to continue rendering immediately!"
283
 
284
  remaining_free = 3 - free_data.get("daily_batches", 0)
285
  return True, {
@@ -290,26 +301,22 @@ def check_generation_limits(key_input: str, device_id: str, is_vip: bool) -> tup
290
  }
291
 
292
  def commit_generation_success(key_input: str, device_id: str, is_vip: bool):
293
- """
294
- HÀM GHI NHẬN HẠN MỨC: Chỉ kích hoạt ghi đè khi video đã thành công và xuất xưởng 100%.
295
- Nếu tài khoản Free bị VIP đẩy tiến trình giữa chừng -> Không chạy qua hàm này -> Bảo toàn lượt tạo.
296
- """
297
  data = _load_storage()
298
  now_ts = time.time()
299
 
300
  if is_vip:
301
- vip_data = data["keys"][key_input]
302
- vip_data["videos_this_batch"] = vip_data.get("videos_this_batch", 0) + 1
303
-
304
- # Nếu đã render đủ 2 video cho lượt tạo này của VIP
305
- if vip_data["videos_this_batch"] >= 2:
306
- vip_data["daily_batches"] = vip_data.get("daily_batches", 0) + 1
307
- vip_data["videos_this_batch"] = 0 # Reset đếm video lượt mới
308
- vip_data["cooldown_until"] = now_ts + 3600 # Bắt đầu tính hàng chờ khóa 1 tiếng
309
  else:
310
- free_data = data["free_devices"][device_id]
311
- free_data["daily_batches"] = free_data.get("daily_batches", 0) + 1
312
- free_data["cooldown_until"] = now_ts + (3 * 3600) # Khóa hàng chờ chặn tạo 3 tiếng đối với Free
 
313
 
314
  _save_storage(data)
315
 
@@ -320,22 +327,16 @@ def commit_generation_success(key_input: str, device_id: str, is_vip: bool):
320
  def execute_admin_diagnostic_test() -> str:
321
  """Khi Admin nhấn nút Test trên run_app.py, hàm này gọi ngầm file tester.py để hứng dữ liệu báo cáo trả lên UI"""
322
  test_script = "tester.py"
323
-
324
- # Kiểm tra sự tồn tại của file kiểm thử trên phân vùng hệ thống
325
  if not os.path.exists(test_script):
326
  return f"❌ FILE MISSING: Tệp '{test_script}' không tồn tại trong thư mục chạy hệ thống!"
327
 
328
  try:
329
- # Gọi tệp kiểm thử độc lập chạy ngầm thông qua subprocess hệ thống
330
  result = subprocess.run(["python", test_script], capture_output=True, text=True, timeout=15)
331
-
332
- # Hứng toàn bộ kết quả đầu ra (stdout) hoặc thông báo lỗi (stderr) để bắn về khung thông báo run_app
333
  if result.returncode == 0:
334
  return f"✅ [TESTER REPORT SUCCESS]:\n{result.stdout.strip()}"
335
  else:
336
  return f"❌ [TESTER REPORT CRASHED WITH EXIT CODE {result.returncode}]:\n{result.stderr.strip()}"
337
  except subprocess.TimeoutExpired:
338
- return "⚠️ [TESTER TIMEOUT]: Tiến trình kiểm thử của file tester.py chạy quá lâu (vượt ngưỡng 15 giây)!"
339
  except Exception as e:
340
  return f"❌ [SYSTEM CRASH]: Không thể kích hoạt được tiến trình kiểm thử. Lỗi: {str(e)}"
341
-
 
17
  STORAGE_FILE = "Hugging AbuAlone09/AI-Video-Engine-storage"
18
 
19
  # ĐỌC BIẾN MÔI TRƯỜNG AN TOÀN TỪ SECRET CỦA HUGGING FACE
 
20
  SERVER_URL = os.getenv("LICENSIFY_SERVER_URL", "https://abualone09-my-licensify-server.hf.space").strip()
21
  SECRET_API_KEY = os.getenv("SECRET_API_KEY", "YOUR_SECRET_API_KEY_HERE").strip()
22
 
 
27
  json.dump({"keys": {}, "free_devices": {}}, f, indent=4)
28
 
29
  # --- THÀNH PHẦN QUAN TRỌNG: QUẢN LÝ TRẠNG THÁI 6 LUỒNG TOÀN CỤC (MEMORY LOCK) ---
 
30
  THREAD_POOL = {
31
  1: {"status": "idle", "key": None, "type": None, "pid": None, "device": None},
32
  2: {"status": "idle", "key": None, "type": None, "pid": None, "device": None},
 
36
  6: {"status": "idle", "key": None, "type": None, "pid": None, "device": None},
37
  }
38
 
39
+ # --- BỔ SUNG HÀM ĐỂ BÊN RUN_APP.PY GỌI ĐỒNG BỘ REALTIME THEO GIÂY MÀ KHÔNG BỊ LỖI ---
40
+ def get_active_threads_count() -> int:
41
+ """Trả về tổng số luồng thực tế đang bận render trong hệ thống cluster"""
42
+ return sum(1 for t in THREAD_POOL.values() if t["status"] == "rendering")
43
+
44
  # =========================================================
45
  # CORE 1: XỬ LÝ ĐỌC / GHI & XÁC THỰC API KEY TỪ XA TỚI SERVER
46
  # =========================================================
 
59
  now_ts = time.time()
60
  changed = False
61
 
 
62
  for k, info in list(data["keys"].items()):
63
  if now_ts > info.get("expiry_timestamp", 0):
64
  del data["keys"][k]
65
  changed = True
66
  logger.warning(f"Key {k} expired and has been automatically purged from storage.")
67
 
 
68
  today_str = datetime.now().strftime("%Y-%m-%d")
69
  for dev, info in list(data["free_devices"].items()):
70
  if info.get("last_date") != today_str:
 
76
  _save_storage(data)
77
 
78
  def verify_and_get_license_info(key_input: str, device_id: str) -> tuple:
79
+ """Kiểm tra cục bộ qua Hugging Face Secret trước, nếu không khớp gọi API sang Server đối soát"""
80
  clean_expired_keys()
81
 
82
+ token = key_input.strip()
83
+
84
+ # LẤY VIP KEY VÀ ADMIN KEY AN TOÀN TRỰC TIẾP TỪ MỤC SECRET CỦA HUGGING FACE
85
+ admin_secret = os.getenv("ADMIN_SECRET_KEY", "ADMIN_SECRET_TEST_2026").strip()
86
+ vip_secret = os.getenv("VIP_SECRET_KEY", "VIP_EARLY_ACCESS_2026").strip()
87
+
88
+ # 1. Cơ chế đăng nhập bằng mã Admin tối cao lấy từ Secret (Không giới hạn, mở Panel Test)
89
+ if token == admin_secret:
90
+ return True, {
91
+ "tier": "ADMIN",
92
+ "tx_name": "System Master Administrator",
93
+ "amount": "$0.00 (Root Privilege)",
94
+ "tx_date": datetime.now().strftime("%Y-%m-%d"),
95
+ "expiry": "Permanent Access",
96
+ "days_left": 9999,
97
+ "msg": "📋 SYSTEM STATUS: Admin Master Active. Full hardware diagnostic test layer unlocked.",
98
+ "show_test_panel": True,
99
+ "remove_watermark": True,
100
+ "bypass_limits": True
101
+ }
102
+
103
+ # 2. Cơ chế đặc quyền ưu đãi dành cho VIP Key lấy từ Secret (Bypass giới hạn, không hiện Panel Test)
104
+ if token == vip_secret:
105
  return True, {
106
+ "tier": "VIP",
107
+ "tx_name": "VIP Promotional Access",
108
+ "amount": "$0.00 (Promo Tier)",
109
+ "tx_date": datetime.now().strftime("%Y-%m-%d"),
110
+ "expiry": "2026-12-31",
111
+ "days_left": 200,
112
+ "msg": "👑 VIP STATUS: Promo Key Verified. Watermarks and daily rendering limits removed.",
113
+ "show_test_panel": False,
114
+ "remove_watermark": True,
115
+ "bypass_limits": True
116
  }
117
 
118
  data = _load_storage()
119
 
120
+ # 3. Tra cứu kho lưu trữ cục bộ trước để tăng tốc độ phản hồi cho giao diện điện thoại
121
+ if token in data["keys"]:
122
+ info = data["keys"][token]
123
  days_left = int((info["expiry_timestamp"] - time.time()) / 86400)
124
  return True, {
125
  "tier": "VIP", "tx_name": info["tx_name"], "amount": info["amount"],
126
  "tx_date": info["tx_date"], "expiry": info["expiry"], "days_left": max(0, days_left),
127
+ "msg": f"👑 VIP ACTIVE | User: {info['tx_name']} | {max(0, days_left)} days remaining.",
128
+ "show_test_panel": False,
129
+ "remove_watermark": True,
130
+ "bypass_limits": False
131
  }
132
 
133
+ # 4. Giao tiếp API an toàn từ xa kết nối thẳng tới Kho lưu trữ của Cổng thanh toán dịch vụ công khai
134
  try:
 
135
  headers = {
136
  "X-API-Key": SECRET_API_KEY,
137
  "Content-Type": "application/json"
138
  }
139
+ payload = {"key": token, "hwid": device_id}
 
 
140
  response = requests.post(f"{SERVER_URL}/api/verify-key", json=payload, headers=headers, timeout=10)
141
 
142
  if response.status_code == 200:
143
  res_data = response.json()
144
  if res_data.get("status") == "success":
 
145
  tx_info = res_data.get("data", {})
146
+ expiry_str = tx_info.get("expiry_date")
147
  expiry_ts = time.mktime(time.strptime(expiry_str, "%Y-%m-%d"))
148
 
149
+ data["keys"][token] = {
 
150
  "tx_name": tx_info.get("buyer_name", "Anonymous User"),
151
  "amount": tx_info.get("amount_paid", "$2.99"),
152
  "tx_date": tx_info.get("payment_date", datetime.now().strftime("%Y-%m-%d")),
 
159
  return True, {
160
  "tier": "VIP", "tx_name": tx_info.get("buyer_name"), "amount": tx_info.get("amount_paid"),
161
  "tx_date": tx_info.get("payment_date"), "expiry": expiry_str, "days_left": max(0, days_left),
162
+ "msg": f"👑 VIP KEY VERIFIED | Owner: {tx_info.get('buyer_name')} | Expiry: {expiry_str}.",
163
+ "show_test_panel": False,
164
+ "remove_watermark": True,
165
+ "bypass_limits": False
166
  }
167
+ return False, {"msg": "❌ Invalid Access Key or communication failure with payment gateway!", "show_test_panel": False, "remove_watermark": False, "tier": "FREE"}
168
  except Exception as e:
169
+ return False, {"msg": f"⚠️ Connection error to Licensify Server! Info: {str(e)}", "show_test_panel": False, "remove_watermark": False, "tier": "FREE"}
170
 
171
  # =========================================================
172
  # CORE 2: CƠ CHẾ ĐIỀU PHỐI 6 LUỒNG ĐỘNG & BIÊN TRỰC VIP / FREE
173
  # =========================================================
174
 
175
  def get_thread_status_json():
176
+ busy_count = get_active_threads_count()
 
177
  vip_count = sum(1 for t in THREAD_POOL.values() if t["type"] == "VIP")
178
  free_count = sum(1 for t in THREAD_POOL.values() if t["type"] == "FREE")
179
  return {
 
184
  }
185
 
186
  def allocate_render_thread(key_input: str, device_id: str, is_vip: bool) -> tuple:
 
 
 
 
 
 
187
  global THREAD_POOL
188
 
 
189
  for slot, thread in THREAD_POOL.items():
190
  if thread["status"] == "rendering":
191
+ if is_vip and thread["key"] == key_input.strip():
192
+ return False, "❌ This VIP Key is already running a rendering process! Multi-thread allocation denied."
193
  if not is_vip and thread["device"] == device_id:
194
  return False, "❌ Your device is currently rendering a video. Please wait until it completes!"
195
 
196
  target_slot = None
197
 
198
  if is_vip:
 
199
  for i in range(1, 6):
200
  if THREAD_POOL[i]["status"] == "idle":
201
  target_slot = i
202
  break
203
 
 
204
  if not target_slot:
205
  if THREAD_POOL[6]["status"] == "idle":
206
  target_slot = 6
207
  elif THREAD_POOL[6]["type"] == "FREE":
 
208
  free_pid = THREAD_POOL[6]["pid"]
209
+ logger.warning(f"👑 VIP Eviction Triggered: Terminating Free PID {free_pid} at Slot 6.")
210
  try:
211
  if free_pid:
212
+ os.kill(free_pid, signal.SIGKILL)
213
  except ProcessLookupError:
214
  pass
215
 
216
  target_slot = 6
 
217
  THREAD_POOL[6] = {"status": "idle", "key": None, "type": None, "pid": None, "device": None}
218
  else:
 
219
  if THREAD_POOL[6]["status"] == "idle":
220
  target_slot = 6
221
 
 
222
  if not target_slot:
223
  return False, "⚠️ All rendering channels are currently full. Please try again in a few moments!"
224
 
225
  return True, target_slot
226
 
227
  def register_process_to_slot(slot: int, key_input: str, device_id: str, is_vip: bool, pid: int):
 
228
  THREAD_POOL[slot] = {
229
  "status": "rendering",
230
+ "key": key_input.strip() if is_vip else None,
231
  "type": "VIP" if is_vip else "FREE",
232
  "pid": pid,
233
  "device": device_id
234
  }
235
 
236
  def release_thread_slot(slot: int):
 
237
  if slot in THREAD_POOL:
238
  THREAD_POOL[slot] = {"status": "idle", "key": None, "type": None, "pid": None, "device": None}
239
 
 
242
  # =========================================================
243
 
244
  def check_generation_limits(key_input: str, device_id: str, is_vip: bool) -> tuple:
 
245
  data = _load_storage()
246
  today_str = datetime.now().strftime("%Y-%m-%d")
247
  now_ts = time.time()
 
251
  return False, "License error: Key missing from verified list."
252
 
253
  vip_data = data["keys"][key_input]
 
254
  if vip_data.get("last_date_used") != today_str:
255
  vip_data["last_date_used"] = today_str
256
+ vip_data["daily_batches"] = 0
257
+ vip_data["videos_this_batch"] = 0
258
  vip_data["cooldown_until"] = 0
259
 
260
  if now_ts < vip_data.get("cooldown_until", 0):
261
  wait_min = int((vip_data["cooldown_until"] - now_ts) / 60)
262
+ return False, f"⏱️ Cooldown active! Please return in {wait_min} minutes."
263
 
264
  if vip_data.get("daily_batches", 0) >= 5:
265
+ return False, "❌ You have exhausted your daily limit of 5 batches. Please return tomorrow!"
266
 
267
  remaining_batches = 5 - vip_data.get("daily_batches", 0)
268
  return True, {
269
  "videos_created": vip_data.get("videos_this_batch", 0),
270
  "batches_created": vip_data.get("daily_batches", 0),
271
  "remaining_batches": remaining_batches,
272
+ "status_str": f"VIP Usage: Batch {vip_data.get('daily_batches', 0)}/5 | Batch Videos: {vip_data.get('videos_this_batch', 0)}/2"
273
  }
274
  else:
 
275
  if device_id not in data["free_devices"]:
276
  data["free_devices"][device_id] = {
277
  "last_date": today_str,
278
+ "daily_batches": 0,
279
  "cooldown_until": 0
280
  }
281
 
 
290
  return False, f"⏱️ Free Tier Cooldown: Please wait {wait_hours + 1} hours before generating again, or upgrade to VIP Premium!"
291
 
292
  if free_data.get("daily_batches", 0) >= 3:
293
+ return False, "❌ Free Tier Exhausted! Maximum 3 daily videos reached."
294
 
295
  remaining_free = 3 - free_data.get("daily_batches", 0)
296
  return True, {
 
301
  }
302
 
303
  def commit_generation_success(key_input: str, device_id: str, is_vip: bool):
 
 
 
 
304
  data = _load_storage()
305
  now_ts = time.time()
306
 
307
  if is_vip:
308
+ if key_input in data["keys"]:
309
+ vip_data = data["keys"][key_input]
310
+ vip_data["videos_this_batch"] = vip_data.get("videos_this_batch", 0) + 1
311
+ if vip_data["videos_this_batch"] >= 2:
312
+ vip_data["daily_batches"] = vip_data.get("daily_batches", 0) + 1
313
+ vip_data["videos_this_batch"] = 0
314
+ vip_data["cooldown_until"] = now_ts + 3600
 
315
  else:
316
+ if device_id in data["free_devices"]:
317
+ free_data = data["free_devices"][device_id]
318
+ free_data["daily_batches"] = free_data.get("daily_batches", 0) + 1
319
+ free_data["cooldown_until"] = now_ts + (3 * 3600)
320
 
321
  _save_storage(data)
322
 
 
327
  def execute_admin_diagnostic_test() -> str:
328
  """Khi Admin nhấn nút Test trên run_app.py, hàm này gọi ngầm file tester.py để hứng dữ liệu báo cáo trả lên UI"""
329
  test_script = "tester.py"
 
 
330
  if not os.path.exists(test_script):
331
  return f"❌ FILE MISSING: Tệp '{test_script}' không tồn tại trong thư mục chạy hệ thống!"
332
 
333
  try:
 
334
  result = subprocess.run(["python", test_script], capture_output=True, text=True, timeout=15)
 
 
335
  if result.returncode == 0:
336
  return f"✅ [TESTER REPORT SUCCESS]:\n{result.stdout.strip()}"
337
  else:
338
  return f"❌ [TESTER REPORT CRASHED WITH EXIT CODE {result.returncode}]:\n{result.stderr.strip()}"
339
  except subprocess.TimeoutExpired:
340
+ return "⚠️ [TESTER TIMEOUT]: Tiến trình kiểm thử vượt ngưỡng 15 giây!"
341
  except Exception as e:
342
  return f"❌ [SYSTEM CRASH]: Không thể kích hoạt được tiến trình kiểm thử. Lỗi: {str(e)}"