ginipick commited on
Commit
0e941d0
ยท
verified ยท
1 Parent(s): 333aced

Upload 2 files

Browse files
Files changed (2) hide show
  1. app (28).py +719 -0
  2. requirements (9).txt +20 -0
app (28).py ADDED
@@ -0,0 +1,719 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import hashlib
3
+ import re
4
+ from datetime import datetime
5
+ import os
6
+ import json
7
+ import schedule
8
+ import threading
9
+ import time
10
+ import dns.resolver
11
+ from huggingface_hub import Repository
12
+ import spaces
13
+ import argparse
14
+ from os import path
15
+ import shutil
16
+ from safetensors.torch import load_file
17
+ from huggingface_hub import hf_hub_download
18
+ import torch
19
+ from diffusers import FluxPipeline
20
+ from diffusers.pipelines.stable_diffusion import safety_checker
21
+ from PIL import Image
22
+ from transformers import pipeline
23
+ import replicate
24
+ import logging
25
+ import requests
26
+ from pathlib import Path
27
+ import cv2
28
+ import numpy as np
29
+ import sys
30
+ import io
31
+
32
+ # ๋กœ๊น… ์„ค์ •
33
+ logging.basicConfig(level=logging.INFO)
34
+ logger = logging.getLogger(__name__)
35
+
36
+ # Hugging Face Dataset Repo ์„ค์ •
37
+ HF_TOKEN = os.getenv("HF_TOKEN")
38
+ REPO_ID = "aiqcamp/MEMBERDB"
39
+ LOCAL_DIR = "./my_dataset"
40
+
41
+ repo = Repository(
42
+ local_dir=LOCAL_DIR,
43
+ clone_from=REPO_ID,
44
+ use_auth_token=HF_TOKEN,
45
+ repo_type="dataset"
46
+ )
47
+
48
+ DATA_FILE = os.path.join(LOCAL_DIR, "data.json")
49
+
50
+ current_user = {"email": None, "points": 0}
51
+ ADMIN_EMAIL = "arxivgpt@gmail.com"
52
+ ADMIN_PASS = "Arxiv4837!@"
53
+
54
+ # Setup and initialization code
55
+ cache_path = path.join(path.dirname(path.abspath(__file__)), "models")
56
+ PERSISTENT_DIR = os.environ.get("PERSISTENT_DIR", ".")
57
+
58
+ # API ์„ค์ •
59
+ CATBOX_USER_HASH = "e7a96fc68dd4c7d2954040cd5"
60
+ REPLICATE_API_TOKEN = os.getenv("API_KEY")
61
+
62
+ # ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
63
+ os.environ["TRANSFORMERS_CACHE"] = cache_path
64
+ os.environ["HF_HUB_CACHE"] = cache_path
65
+ os.environ["HF_HOME"] = cache_path
66
+
67
+ # ๋ฒˆ์—ญ๊ธฐ ์ดˆ๊ธฐํ™”
68
+ translator = pipeline("translation", model="Helsinki-NLP/opus-mt-ko-en", device="cpu")
69
+
70
+ if not path.exists(cache_path):
71
+ os.makedirs(cache_path, exist_ok=True)
72
+
73
+
74
+
75
+ def load_data():
76
+ try:
77
+ if os.path.exists(DATA_FILE):
78
+ with open(DATA_FILE, 'r', encoding='utf-8') as f:
79
+ return json.load(f)
80
+ except Exception as e:
81
+ print(f"Load error: {e}")
82
+ return {"users": {}}
83
+
84
+ def save_data(data):
85
+ try:
86
+ with open(DATA_FILE, 'w', encoding='utf-8') as f:
87
+ json.dump(data, f, ensure_ascii=False, indent=4)
88
+ repo.git_add()
89
+ repo.git_commit("Update data.json")
90
+ repo.git_push()
91
+ return True
92
+ except Exception as e:
93
+ print(f"Save error: {e}")
94
+ return False
95
+
96
+ def init_db():
97
+ print("Initializing database...")
98
+ try:
99
+ data = load_data()
100
+ if ADMIN_EMAIL not in data["users"]:
101
+ data["users"][ADMIN_EMAIL] = {
102
+ "password": hash_password(ADMIN_PASS),
103
+ "registration_date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
104
+ "points": 999,
105
+ "is_admin": 1
106
+ }
107
+ save_data(data)
108
+ print("Admin account created")
109
+ print("Database initialized successfully")
110
+ return True
111
+ except Exception as e:
112
+ print(f"Init error: {e}")
113
+ return False
114
+
115
+ def hash_password(password: str) -> str:
116
+ if not password:
117
+ return ""
118
+ return hashlib.sha256(password.encode("utf-8")).hexdigest()
119
+
120
+ def is_valid_email(email: str) -> bool:
121
+ pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
122
+ return re.match(pattern, email) is not None
123
+
124
+ def is_email_domain_valid(email: str) -> bool:
125
+ try:
126
+ domain = email.split("@")[1]
127
+ except IndexError:
128
+ return False
129
+ try:
130
+ records = dns.resolver.resolve(domain, 'MX')
131
+ if len(records) > 0:
132
+ return True
133
+ except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.exception.Timeout):
134
+ pass
135
+ return False
136
+
137
+ def register(email, password):
138
+ if not email or not password:
139
+ return "Please fill all fields."
140
+ if not is_valid_email(email):
141
+ return "Invalid email format. Please try again."
142
+ if not is_email_domain_valid(email):
143
+ return "This email domain seems invalid. Please use a different address."
144
+ if len(password) < 6:
145
+ return "Password must be at least 6 characters long."
146
+
147
+ data = load_data()
148
+ if email.strip() in data["users"]:
149
+ return "This email is already registered."
150
+
151
+ data["users"][email.strip()] = {
152
+ "password": hash_password(password.strip()),
153
+ "registration_date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
154
+ "points": 15, # ์ดˆ๊ธฐ ํฌ์ธํŠธ 15๋กœ ์„ค์ •
155
+ "is_admin": 0
156
+ }
157
+ if save_data(data):
158
+ return "Registration successful!"
159
+ return "Error occurred during registration."
160
+
161
+ def login(email, password):
162
+ global current_user
163
+ if not email or not password:
164
+ return {"value": "Please enter both email and password."}
165
+
166
+ data = load_data()
167
+ user_data = data["users"].get(email.strip())
168
+ if user_data and user_data["password"] == hash_password(password.strip()):
169
+ current_user = {
170
+ "email": email.strip(),
171
+ "points": user_data["points"],
172
+ "is_admin": user_data.get("is_admin", 0)
173
+ }
174
+ if current_user["is_admin"] == 1:
175
+ return {"value": "Logged in as ADMIN."}
176
+ else:
177
+ return {"value": f"Welcome! You have {user_data['points']} points."}
178
+ current_user = {"email": None, "points": 0, "is_admin": 0}
179
+ return {"value": "Wrong email or password."}
180
+
181
+ def use_point(points=5):
182
+ if not current_user["email"]:
183
+ return "You need to log in first."
184
+ data = load_data()
185
+ user_data = data["users"][current_user["email"]]
186
+ if user_data["points"] < points:
187
+ return f"Not enough points. Required: {points} points"
188
+ user_data["points"] -= points
189
+ current_user["points"] = user_data["points"]
190
+ if save_data(data):
191
+ return f"Points used! Remaining points: {user_data['points']}"
192
+ return "Error occurred while using points."
193
+
194
+ def get_profile():
195
+ if not current_user["email"]:
196
+ return "", "", "You need to log in first."
197
+ data = load_data()
198
+ user_data = data["users"].get(current_user["email"])
199
+ if user_data:
200
+ return (
201
+ current_user["email"],
202
+ user_data["registration_date"],
203
+ f"Current points: {user_data['points']}"
204
+ )
205
+ return "", "", "Profile not found."
206
+
207
+ def delete_user(email):
208
+ if not is_admin():
209
+ return "Only admin can delete users."
210
+ if email == ADMIN_EMAIL:
211
+ return "Cannot delete the admin account."
212
+ data = load_data()
213
+ if email in data["users"]:
214
+ del data["users"][email]
215
+ if save_data(data):
216
+ return f"User {email} has been deleted."
217
+ return "User not found."
218
+
219
+ def is_admin():
220
+ return current_user.get("email") == ADMIN_EMAIL and current_user.get("is_admin") == 1
221
+
222
+ def get_all_users():
223
+ if not is_admin():
224
+ return "Only admin can access this page."
225
+ data = load_data()
226
+ users = data["users"]
227
+ if not users or len(users) <= 1:
228
+ return "No registered users."
229
+
230
+ result = "<table style='width:100%; border-collapse: collapse;'>"
231
+ result += "<tr style='background-color: #f2f2f2;'>"
232
+ result += "<th style='padding: 12px; text-align: left; border: 1px solid #ddd;'>Email</th>"
233
+ result += "<th style='padding: 12px; text-align: left; border: 1px solid #ddd;'>Registration Date</th>"
234
+ result += "<th style='padding: 12px; text-align: left; border: 1px solid #ddd;'>Points</th>"
235
+ result += "<th style='padding: 12px; text-align: center; border: 1px solid #ddd;'>Action</th></tr>"
236
+
237
+ for email, user_data in users.items():
238
+ if email != ADMIN_EMAIL:
239
+ result += f"<tr style='border: 1px solid #ddd;'>"
240
+ result += f"<td style='padding: 12px; border: 1px solid #ddd;'>{email}</td>"
241
+ result += f"<td style='padding: 12px; border: 1px solid #ddd;'>{user_data['registration_date']}</td>"
242
+ result += f"<td style='padding: 12px; border: 1px solid #ddd;'>{user_data['points']}</td>"
243
+ result += f"<td style='padding: 12px; text-align: center; border: 1px solid #ddd;'>"
244
+ result += f"<button onclick='deleteUser(\"{email}\")' style='padding: 5px 10px; background-color: #ff4444; color: white; border: none; border-radius: 3px; cursor: pointer;'>Delete</button></td>"
245
+ result += "</tr>"
246
+ result += "</table>"
247
+ return result
248
+
249
+ @spaces.GPU
250
+ def setup_torch():
251
+ torch.backends.cuda.matmul.allow_tf32 = True
252
+
253
+ def translate_if_korean(text):
254
+ if any(ord(char) >= 0xAC00 and ord(char) <= 0xD7A3 for char in text):
255
+ translation = translator(text)[0]['translation_text']
256
+ return translation
257
+ return text
258
+
259
+ def filter_prompt(prompt):
260
+ inappropriate_keywords = [
261
+ "nude", "naked", "nsfw", "porn", "sex", "explicit", "adult", "xxx",
262
+ "erotic", "sensual", "seductive", "provocative", "intimate",
263
+ "violence", "gore", "blood", "death", "kill", "murder", "torture",
264
+ "drug", "suicide", "abuse", "hate", "discrimination"
265
+ ]
266
+
267
+ prompt_lower = prompt.lower()
268
+ for keyword in inappropriate_keywords:
269
+ if keyword in prompt_lower:
270
+ return False, "๋ถ€์ ์ ˆํ•œ ๋‚ด์šฉ์ด ํฌํ•จ๋œ ํ”„๋กฌํ”„ํŠธ์ž…๋‹ˆ๋‹ค."
271
+ return True, prompt
272
+
273
+ def process_prompt(prompt):
274
+ translated_prompt = translate_if_korean(prompt)
275
+ is_safe, filtered_prompt = filter_prompt(translated_prompt)
276
+ return is_safe, filtered_prompt
277
+
278
+ def add_watermark(video_path):
279
+ try:
280
+ cap = cv2.VideoCapture(video_path)
281
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
282
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
283
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
284
+
285
+ text = "GiniGEN.AI"
286
+ font = cv2.FONT_HERSHEY_SIMPLEX
287
+ font_scale = height * 0.05 / 30
288
+ thickness = 2
289
+ color = (255, 255, 255)
290
+
291
+ (text_width, text_height), _ = cv2.getTextSize(text, font, font_scale, thickness)
292
+ margin = int(height * 0.02)
293
+ x_pos = width - text_width - margin
294
+ y_pos = height - margin
295
+
296
+ output_path = "watermarked_output.mp4"
297
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
298
+ out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
299
+
300
+ while cap.isOpened():
301
+ ret, frame = cap.read()
302
+ if not ret:
303
+ break
304
+ cv2.putText(frame, text, (x_pos, y_pos), font, font_scale, color, thickness)
305
+ out.write(frame)
306
+
307
+ cap.release()
308
+ out.release()
309
+
310
+ return output_path
311
+
312
+ except Exception as e:
313
+ logger.error(f"Error adding watermark: {str(e)}")
314
+ return video_path
315
+
316
+ def check_api_key():
317
+ api_key = os.getenv("API_KEY")
318
+ if not api_key:
319
+ logger.error("API_KEY environment variable not found")
320
+ return False
321
+
322
+ os.environ["REPLICATE_API_TOKEN"] = api_key
323
+
324
+ try:
325
+ response = requests.get(
326
+ "https://api.replicate.com/v1/account",
327
+ headers={"Authorization": f"Bearer {api_key}"}
328
+ )
329
+ if response.status_code == 200:
330
+ logger.info("Replicate API token validated successfully")
331
+ return True
332
+ else:
333
+ logger.error(f"API key validation failed with status code: {response.status_code}")
334
+ return False
335
+ except Exception as e:
336
+ logger.error(f"API key validation error: {str(e)}")
337
+ return False
338
+
339
+ def generate_video(image, prompt):
340
+ logger.info("Starting video generation")
341
+ try:
342
+ if not check_api_key():
343
+ return "Replicate API key not properly configured"
344
+
345
+ input_data = {
346
+ "prompt": prompt
347
+ }
348
+
349
+ if image:
350
+ try:
351
+ import base64
352
+ with open(image, 'rb') as img_file:
353
+ data = base64.b64encode(img_file.read()).decode('utf-8')
354
+ input_data["first_frame_image"] = f"data:image/png;base64,{data}"
355
+ except Exception as img_error:
356
+ logger.error(f"Error processing image: {str(img_error)}")
357
+ return f"Error processing image: {str(img_error)}"
358
+
359
+ try:
360
+ prediction = replicate.predictions.create(
361
+ model="minimax/video-01-live",
362
+ input=input_data
363
+ )
364
+
365
+ while prediction.status not in ["succeeded", "failed", "canceled"]:
366
+ prediction = replicate.predictions.get(prediction.id)
367
+ time.sleep(1)
368
+
369
+ if prediction.status == "succeeded" and prediction.output:
370
+ temp_file = "temp_output.mp4"
371
+ try:
372
+ response = requests.get(prediction.output, stream=True)
373
+ response.raise_for_status()
374
+
375
+ with open(temp_file, "wb") as f:
376
+ for chunk in response.iter_content(chunk_size=8192):
377
+ if chunk:
378
+ f.write(chunk)
379
+
380
+ final_video = add_watermark(temp_file)
381
+ return final_video
382
+
383
+ except Exception as download_error:
384
+ logger.error(f"Error downloading video: {str(download_error)}")
385
+ return f"Error downloading video: {str(download_error)}"
386
+ else:
387
+ error_msg = f"Prediction failed with status: {prediction.status}"
388
+ if hasattr(prediction, 'error'):
389
+ error_msg += f" Error: {prediction.error}"
390
+ logger.error(error_msg)
391
+ return error_msg
392
+
393
+ except Exception as api_error:
394
+ logger.error(f"API call failed: {str(api_error)}")
395
+ return f"API call failed: {str(api_error)}"
396
+
397
+ except Exception as e:
398
+ logger.error(f"Unexpected error: {str(e)}")
399
+ return f"Unexpected error: {str(e)}"
400
+ finally:
401
+ try:
402
+ if 'temp_file' in locals() and os.path.exists(temp_file):
403
+ os.remove(temp_file)
404
+ except Exception as cleanup_error:
405
+ logger.warning(f"Error cleaning up temporary file: {str(cleanup_error)}")
406
+
407
+ def process_and_generate_video(image, prompt):
408
+ result = use_point(5) # 5ํฌ์ธํŠธ ์ฐจ๊ฐ
409
+ if "Not enough points" in result or "need to log in" in result:
410
+ return result
411
+
412
+ is_safe, translated_prompt = process_prompt(prompt)
413
+ if not is_safe:
414
+ return "๋ถ€์ ์ ˆํ•œ ๋‚ด์šฉ์ด ํฌํ•จ๋œ ํ”„๋กฌํ”„ํŠธ์ž…๋‹ˆ๋‹ค."
415
+
416
+ try:
417
+ video_result = generate_video(image, translated_prompt)
418
+ if isinstance(video_result, str) and ("error" in video_result.lower() or "failed" in video_result.lower()):
419
+ # ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ํฌ์ธํŠธ ํ™˜๋ถˆ
420
+ data = load_data()
421
+ user_data = data["users"][current_user["email"]]
422
+ user_data["points"] += 5
423
+ current_user["points"] = user_data["points"]
424
+ save_data(data)
425
+ return f"Error: {video_result}"
426
+ return video_result
427
+ except Exception as e:
428
+ # ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ํฌ์ธํŠธ ํ™˜๋ถˆ
429
+ data = load_data()
430
+ user_data = data["users"][current_user["email"]]
431
+ user_data["points"] += 5
432
+ current_user["points"] = user_data["points"]
433
+ save_data(data)
434
+ return f"Error: {str(e)}"
435
+
436
+
437
+ # CSS ์Šคํƒ€์ผ ์ •์˜
438
+ CUSTOM_CSS = """
439
+ /* ํ•œ ์ค„์— Email + Password + (Login, Sign Up) ๋ฒ„ํŠผ 2๊ฐœ + ๊ฒฐ๊ณผ์ฐฝ */
440
+
441
+ /* ์ „์ฒด Row ์ปจํ…Œ์ด๋„ˆ */
442
+ #row-container {
443
+ display: flex;
444
+ align-items: center;
445
+ gap: 8px;
446
+ background: linear-gradient(135deg, #b2fefa 0%, #8fd3f4 100%);
447
+ padding: 10px;
448
+ border-radius: 8px;
449
+ box-shadow: 0 3px 8px rgba(0,0,0,0.15);
450
+ margin-bottom: 15px;
451
+ }
452
+
453
+ /* ํ…์ŠคํŠธ๋ฐ•์Šค, ๋ฒ„ํŠผ ํฌ๊ธฐ ํ†ต์ผ */
454
+ .same-size {
455
+ width: 130px !important;
456
+ height: 34px !important;
457
+ font-size: 0.9rem !important;
458
+ text-align: center !important;
459
+ padding: 5px 6px !important;
460
+ margin: 0 !important;
461
+ border-radius: 5px;
462
+ border: 1px solid #ddd;
463
+ }
464
+
465
+ /* ๋ฒ„ํŠผ์€ ์•ฝ๊ฐ„ ๋‹ค๋ฅธ ๋ฐฐ๊ฒฝ */
466
+ button.same-size {
467
+ background-color: #6666ff;
468
+ color: white;
469
+ border: none;
470
+ cursor: pointer;
471
+ }
472
+ button.same-size:hover {
473
+ background-color: #4a4acc;
474
+ }
475
+
476
+ /* ๊ฒฐ๊ณผ์ฐฝ๋„ ๋™์ผ ํฌ๊ธฐ */
477
+ .result-box {
478
+ width: 160px !important;
479
+ height: 34px !important;
480
+ font-size: 0.9rem !important;
481
+ padding: 5px 6px !important;
482
+ border: 1px solid #ccc;
483
+ border-radius: 5px;
484
+ background-color: #fff;
485
+ text-align: left;
486
+ overflow: hidden;
487
+ white-space: nowrap;
488
+ text-overflow: ellipsis;
489
+ }
490
+
491
+ /* ์ƒ๋‹จ ํ—ค๋” ์Šคํƒ€์ผ */
492
+ .header-container {
493
+ display: flex;
494
+ justify-content: space-between;
495
+ align-items: center;
496
+ padding: 6px;
497
+ background-color: #f5f5f5;
498
+ margin-bottom: 10px;
499
+ }
500
+ .user-badge {
501
+ padding: 3px 6px;
502
+ border-radius: 15px;
503
+ background-color: #4CAF50;
504
+ color: white;
505
+ font-size: 0.8em;
506
+ }
507
+ .admin-badge {
508
+ padding: 3px 6px;
509
+ border-radius: 15px;
510
+ background-color: #ff4444;
511
+ color: white;
512
+ font-size: 0.8em;
513
+ cursor: pointer;
514
+ }
515
+
516
+ .points-display {
517
+ background: linear-gradient(135deg, #6e8efb 0%, #5d7df9 100%);
518
+ padding: 20px;
519
+ border-radius: 15px;
520
+ margin: 20px 0;
521
+ box-shadow: 0 4px 15px rgba(110, 142, 251, 0.2);
522
+ }
523
+
524
+ .points-text {
525
+ color: white;
526
+ font-size: 1.5em;
527
+ text-align: center;
528
+ margin: 0;
529
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
530
+ }
531
+
532
+ footer {
533
+ visibility: hidden;
534
+ }
535
+
536
+ .gradio-container {
537
+ background: linear-gradient(135deg, #f6f8ff 0%, #e9f0ff 100%);
538
+ }
539
+
540
+ .gr-button {
541
+ border: 2px solid rgba(100, 100, 255, 0.2);
542
+ background: linear-gradient(135deg, #6e8efb 0%, #5d7df9 100%);
543
+ box-shadow: 0 4px 15px rgba(110, 142, 251, 0.2);
544
+ }
545
+
546
+ .gr-button:hover {
547
+ background: linear-gradient(135deg, #5d7df9 0%, #4a6af8 100%);
548
+ box-shadow: 0 4px 20px rgba(110, 142, 251, 0.3);
549
+ }
550
+
551
+ .gr-input, .gr-box {
552
+ border-radius: 12px;
553
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
554
+ border: 2px solid rgba(100, 100, 255, 0.1);
555
+ }
556
+ """
557
+
558
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
559
+ with gr.Blocks(theme="soft", css=CUSTOM_CSS) as demo:
560
+ gr.HTML("""<a href="https://visitorbadge.io/status?path=https%3A%2F%2Faiqcamp-Dokdo-membership.hf.space">
561
+ <img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Faiqcamp-Dokdo-membership.hf.space&countColor=%23263759" />
562
+ </a>""")
563
+
564
+ # ์ƒ๋‹จ ํ—ค๋”
565
+ with gr.Row(elem_classes="header-container"):
566
+ gr.Markdown("#### Member Management System")
567
+ with gr.Column(scale=1):
568
+ user_info = gr.HTML(value="", elem_classes="user-badge")
569
+ admin_button = gr.Button("Admin Page", visible=False, elem_classes="admin-badge")
570
+
571
+ # ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ์„น์…˜
572
+ with gr.Row(elem_id="row-container"):
573
+ email_box = gr.Textbox(
574
+ placeholder="Email",
575
+ show_label=False,
576
+ elem_classes=["same-size"]
577
+ )
578
+ pass_box = gr.Textbox(
579
+ placeholder="Password",
580
+ show_label=False,
581
+ type="password",
582
+ elem_classes=["same-size"]
583
+ )
584
+ login_btn = gr.Button(
585
+ "Login",
586
+ elem_classes=["same-size"]
587
+ )
588
+ signup_btn = gr.Button(
589
+ "Sign Up",
590
+ elem_classes=["same-size"]
591
+ )
592
+ auth_output = gr.Textbox(
593
+ show_label=False,
594
+ elem_classes=["result-box"],
595
+ interactive=False,
596
+ placeholder="Message"
597
+ )
598
+
599
+ # ๋กœ๊ทธ์ธ ํ›„ ํ™”๋ฉด
600
+ with gr.Column(elem_id="studio-section", visible=False) as studio_container:
601
+ gr.Markdown("### My Studio")
602
+ with gr.Group(elem_classes="points-display"):
603
+ points_display = gr.Markdown("", elem_classes="points-text")
604
+
605
+ # ๋น„๋””์˜ค ์ƒ์„ฑ ์„น์…˜
606
+ with gr.Row():
607
+ with gr.Column(scale=3):
608
+ video_prompt = gr.Textbox(
609
+ label="Video Description",
610
+ placeholder="๋น„๋””์˜ค ์„ค๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”... (ํ•œ๊ธ€ ์ž…๋ ฅ ๊ฐ€๋Šฅ)",
611
+ lines=3
612
+ )
613
+ upload_image = gr.Image(type="filepath", label="Upload First Frame Image")
614
+ video_generate_btn = gr.Button("๐ŸŽฌ Generate Video (5 points)")
615
+
616
+ with gr.Column(scale=4):
617
+ video_output = gr.Video(label="Generated Video")
618
+
619
+ refresh_profile_button = gr.Button("Refresh Points")
620
+
621
+ # ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€
622
+ with gr.Column(visible=False) as admin_container:
623
+ gr.Markdown("### Admin Page")
624
+ admin_refresh = gr.Button("Get All Users")
625
+ admin_output = gr.HTML()
626
+ delete_status = gr.Textbox(label="Result")
627
+
628
+ # ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
629
+ def login_and_update(email, password):
630
+ res = login(email, password)
631
+ msg = res["value"]
632
+ if "Welcome!" in msg or "ADMIN" in msg:
633
+ user_badge = f"<div>Logged in: {email}</div>"
634
+ is_admin_flag = "ADMIN" in msg
635
+ _, _, points_info = get_profile()
636
+ return {
637
+ user_info: user_badge,
638
+ studio_container: gr.Column(visible=True),
639
+ admin_button: gr.Button(visible=is_admin_flag),
640
+ points_display: f"### {points_info}",
641
+ auth_output: msg
642
+ }
643
+ else:
644
+ return {auth_output: msg}
645
+
646
+ # ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
647
+ login_btn.click(
648
+ fn=login_and_update,
649
+ inputs=[email_box, pass_box],
650
+ outputs=[
651
+ user_info,
652
+ studio_container,
653
+ admin_button,
654
+ points_display,
655
+ auth_output
656
+ ]
657
+ )
658
+
659
+ signup_btn.click(
660
+ fn=register,
661
+ inputs=[email_box, pass_box],
662
+ outputs=auth_output
663
+ )
664
+
665
+ video_generate_btn.click(
666
+ fn=process_and_generate_video,
667
+ inputs=[upload_image, video_prompt],
668
+ outputs=video_output
669
+ )
670
+
671
+ refresh_profile_button.click(
672
+ fn=lambda: get_profile()[2], # points_info๋งŒ ๋ฐ˜ํ™˜
673
+ outputs=[points_display]
674
+ )
675
+
676
+ def toggle_admin_page():
677
+ return {admin_container: gr.Column(visible=True)}
678
+
679
+ admin_button.click(fn=toggle_admin_page, outputs=[admin_container])
680
+ admin_refresh.click(fn=get_all_users, outputs=admin_output)
681
+
682
+ # ํšŒ์› ์‚ญ์ œ JS
683
+ gr.HTML("""
684
+ <script>
685
+ function deleteUser(email) {
686
+ if (confirm('Are you sure you want to delete this user?')) {
687
+ const deleteEmail = document.getElementById('delete_user_email');
688
+ if (deleteEmail) {
689
+ deleteEmail.value = email;
690
+ }
691
+ const deleteBtn = document.getElementById('delete_and_refresh');
692
+ if (deleteBtn) {
693
+ deleteBtn.click();
694
+ }
695
+ }
696
+ }
697
+ </script>
698
+ """)
699
+
700
+ delete_user_email = gr.Textbox(visible=False)
701
+ delete_and_refresh = gr.Button(visible=False)
702
+
703
+ def delete_user_and_refresh(email):
704
+ msg = delete_user(email)
705
+ return msg, get_all_users()
706
+
707
+ delete_and_refresh.click(
708
+ fn=delete_user_and_refresh,
709
+ inputs=delete_user_email,
710
+ outputs=[delete_status, admin_output]
711
+ )
712
+
713
+
714
+ # ๋ฉ”์ธ ์‹คํ–‰
715
+ if __name__ == "__main__":
716
+ print("\n=== Application Startup ===")
717
+ init_db()
718
+ demo.launch(debug=True)
719
+
requirements (9).txt ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ schedule
2
+ huggingface_hub
3
+ dnspython
4
+
5
+ accelerate
6
+ diffusers==0.30.0
7
+ invisible_watermark
8
+ torch
9
+ transformers==4.43.3
10
+ xformers
11
+ sentencepiece
12
+ peft
13
+ gradio
14
+ replicate
15
+ requests
16
+ python-dotenv
17
+ Pillow
18
+ opencv-python-headless
19
+ numpy
20
+ sacremoses