floodd commited on
Commit
4901939
Β·
verified Β·
1 Parent(s): 0d4c15d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +313 -186
app.py CHANGED
@@ -12,9 +12,9 @@ from datetime import datetime, timedelta
12
  import csv
13
  import pandas as pd
14
  import threading
15
- from PIL import Image, ImageEnhance
16
 
17
- # --- LIBRARY TAMBAHAN UNTUK FITUR BARU ---
18
  try:
19
  import psutil
20
  import platform
@@ -34,8 +34,92 @@ except ImportError:
34
  print("❌ Peringatan: Library 'google-generativeai' tidak ditemukan. Fitur Chatbot & Prompt Enhancer tidak akan berfungsi.")
35
  genai = None
36
 
37
- # --- HEAD_HTML tetap sama (potong di sini untuk ringkas) ---
38
- HEAD_HTML = """...""" # keep your original HEAD_HTML here
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
  # --- PENGATURAN & LOGIC GLOBAL ---
41
  VISITOR_LOG_FILE = "visitor_log.csv"
@@ -43,14 +127,6 @@ file_lock = threading.Lock()
43
  device = "cuda" if torch.cuda.is_available() else "cpu"
44
  print(f"➑️ Menggunakan device: {device.upper()}")
45
 
46
- # MODEL PLACEHOLDERS (lazy load)
47
- pipe = None
48
- pipe_lock = threading.Lock()
49
-
50
- upscaler_model = None
51
- upscaler_processor = None
52
- upscaler_lock = threading.Lock()
53
-
54
  # --- Inisialisasi File Log Pengunjung ---
55
  def initialize_log_file():
56
  if not os.path.exists(VISITOR_LOG_FILE):
@@ -63,62 +139,29 @@ def initialize_log_file():
63
 
64
  initialize_log_file()
65
 
66
- # --- FUNGSI UNTUK MEMUAT MODEL SECARA LAZY (agar UI tidak blank saat startup) ---
67
- def load_pipe_if_needed():
68
- global pipe
69
- if pipe is not None:
70
- return
71
- with pipe_lock:
72
- if pipe is not None:
73
- return
74
- try:
75
- print("➑️ Memuat model SDXL-Turbo (lazy load)... ini akan memakan waktu.")
76
- torch_dtype = torch.float16 if device == "cuda" else torch.float32
77
- # Sesuaikan variant agar tidak meng-crash saat cpu
78
- variant = "fp16" if device == "cuda" else None
79
- pipe_local = DiffusionPipeline.from_pretrained(
80
- "stabilityai/sdxl-turbo",
81
- torch_dtype=torch_dtype,
82
- variant=variant,
83
- use_safetensors=True
84
- )
85
- pipe_local = pipe_local.to(device)
86
- try:
87
- if torch.cuda.is_available():
88
- pipe_local.enable_xformers_memory_efficient_attention()
89
- except Exception:
90
- # optional if xformers not available
91
- pass
92
- pipe = pipe_local
93
- print("βœ… Model SDXL-Turbo berhasil dimuat (lazy).")
94
- except Exception as e:
95
- pipe = None
96
- print(f"❌ Gagal memuat model SDXL-Turbo: {e}")
97
-
98
- def load_upscaler_if_needed():
99
- global upscaler_model, upscaler_processor
100
- if Swin2SRForImageSuperResolution is None:
101
- return
102
- if upscaler_model is not None and upscaler_processor is not None:
103
- return
104
- with upscaler_lock:
105
- if upscaler_model is not None and upscaler_processor is not None:
106
- return
107
- try:
108
- print("➑️ Memuat model AI Upscaler (lazy)...")
109
- upscaler_model = Swin2SRForImageSuperResolution.from_pretrained(
110
- "caidas/swin2sr-realworld-sr-x4-64-bsrgan-psnr"
111
- ).to(device)
112
- upscaler_processor = Swin2SRImageProcessor.from_pretrained(
113
- "caidas/swin2sr-realworld-sr-x4-64-bsrgan-psnr"
114
- )
115
- print("βœ… Model AI Upscaler berhasil dimuat (lazy).")
116
- except Exception as e:
117
- print(f"❌ Gagal memuat model Upscaler: {e}. Fitur upscale akan dinonaktifkan.")
118
- upscaler_model = None
119
- upscaler_processor = None
120
 
121
- # --- KELAS UNTUK CHATBOT GEMINI (tetap sama) ---
122
  class GeminiChat:
123
  def __init__(self):
124
  self.api_keys = []
@@ -136,7 +179,7 @@ class GeminiChat:
136
  print("❌ PERINGATAN: Tidak ada API Key Gemini yang ditemukan. Fitur AI Chat & Prompt Enhancer tidak akan berfungsi.")
137
 
138
  def chat(self, message, history, system_prompt=None):
139
- if not self.is_configured: return "Maaf, fitur ini tidak terkonfigurasi karena tidak ada API Key yang valid."
140
  try:
141
  selected_key = random.choice(self.api_keys)
142
  genai.configure(api_key=selected_key)
@@ -147,71 +190,33 @@ class GeminiChat:
147
  return response.text
148
  except Exception as e:
149
  print(f"❌ Terjadi error pada API Key Gemini: {e}")
150
- return "Terjadi kesalahan saat menghubungi API AI. Mungkin salah satu API Key tidak valid, kuota habis, atau ada masalah jaringan. Silakan coba lagi."
151
 
152
  gemini_bot = GeminiChat()
153
 
154
- # --- FUNGSI-FUNGSI INTI & FITUR ---
155
- def log_visitor(request: gr.Request = None):
156
- # Catat pengunjung hanya jika request tersedia (tidak memaksa)
157
- if not request:
158
- return
159
- try:
160
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
161
- ip_address = request.client.host if hasattr(request, "client") else "Unknown"
162
- user_agent = request.headers.get("user-agent", "Unknown") if hasattr(request, "headers") else "Unknown"
163
- with file_lock:
164
- with open(VISITOR_LOG_FILE, mode='a', newline='', encoding='utf-8') as f:
165
- writer = csv.writer(f)
166
- writer.writerow([timestamp, ip_address, user_agent])
167
- print(f"βœ… Pengunjung baru tercatat: IP {ip_address}")
168
- except Exception as e:
169
- print(f"❌ Gagal mencatat pengunjung: {e}")
170
 
171
  def generate_images(prompt, negative_prompt, steps, seed, num_images):
172
- # Pastikan prompt ada
173
- if not prompt: raise gr.Error("Prompt tidak boleh kosong!")
174
-
175
- # Pastikan model dimuat (lazy)
176
- load_pipe_if_needed()
177
- if pipe is None:
178
- raise gr.Error("Model generator belum tersedia. Periksa log server untuk error saat memuat model.")
179
-
180
- # Atur seed
181
- if seed is None or int(seed) == -1:
182
- seed = random.randint(0, 2**31 - 1)
183
- seed = int(seed)
184
-
185
- # Buat generator yang benar sesuai device
186
- try:
187
- gen = torch.Generator(device=device)
188
- except Exception:
189
- # fallback jika device string tidak diterima
190
- gen = torch.Generator()
191
- gen.manual_seed(seed)
192
-
193
- # Convert steps/num_images to int (safety)
194
- steps = int(steps) if steps else 20
195
- num_images = int(num_images) if num_images else 1
196
-
197
- # Jalankan pipeline (gunakan guidance_scale default yang wajar)
198
- try:
199
- images = pipe(prompt=prompt,
200
- negative_prompt=negative_prompt or None,
201
- generator=gen,
202
- num_inference_steps=steps,
203
- guidance_scale=7.5,
204
- num_images_per_prompt=num_images).images
205
- except Exception as e:
206
- raise gr.Error(f"Kesalahan saat proses generasi: {e}")
207
-
208
  return images, seed
209
 
210
  def genie_wrapper(prompt, negative_prompt, steps, seed, num_images):
211
- # generator function untuk update UI step-by-step
212
  yield gr.update(visible=False), gr.update(visible=True), gr.update(interactive=False), gr.update(visible=False)
213
  start_time = time.time()
214
- images, used_seed = generate_images(prompt, negative_prompt, steps, seed, num_images)
215
  end_time = time.time()
216
  generation_time = end_time - start_time
217
  info_text = f"Seed yang digunakan: {used_seed}\nTotal waktu generasi: {generation_time:.2f} detik"
@@ -223,128 +228,250 @@ def submit_report(name, email, message):
223
  return gr.update(visible=False)
224
  report_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
225
  report_content = f"--- Laporan Baru ({report_time}) ---\nNama: {name}\nEmail: {email}\nPesan: {message}\n\n"
226
- with open("reports.log", "a", encoding="utf-8") as f: f.write(report_content)
 
227
  print("βœ… Laporan baru telah disimpan ke reports.log")
228
  return gr.update(value="βœ… Terima kasih! Laporan Anda telah kami terima.", visible=True)
229
 
 
230
  def update_visitor_monitor(time_filter: str):
231
  try:
232
  with file_lock:
233
- if not os.path.exists(VISITOR_LOG_FILE) or os.path.getsize(VISITOR_LOG_FILE) < 10:
234
- return "## <i class='fa-solid fa-users'></i> Total Pengunjung: 0", pd.DataFrame()
235
  df = pd.read_csv(VISITOR_LOG_FILE)
236
- if df.empty:
237
- return "## <i class='fa-solid fa-users'></i> Total Pengunjung: 0", pd.DataFrame()
238
- df['Timestamp'] = pd.to_datetime(df['Timestamp'], errors='coerce'); df.dropna(subset=['Timestamp'], inplace=True)
239
- if df.empty: return "## <i class='fa-solid fa-users'></i> Total Pengunjung: 0", pd.DataFrame()
 
 
 
240
  total_overall_visitors = len(df)
241
- total_visitors_formatted = f"## <i class='fa-solid fa-users'></i> Total Pengunjung: {total_overall_visitors:,}"
242
  df['Total Pengunjung'] = np.arange(1, len(df) + 1)
243
- now = pd.to_datetime('now', utc=True).tz_localize(None); df['Timestamp'] = df['Timestamp'].dt.tz_localize(None)
 
 
 
 
244
  if time_filter == "1 Minggu Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(weeks=1)]
245
  elif time_filter == "2 Minggu Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(weeks=2)]
246
  elif time_filter == "3 Bulan Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(days=90)]
247
  else: df_plot = df
 
248
  if df_plot.empty: return total_visitors_formatted, pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
 
249
  return total_visitors_formatted, df_plot
250
  except Exception as e:
251
- error_message = f"Error saat memperbarui monitor: {e}"; print(f"❌ {error_message}")
252
- return f"## ⚠️ Error", pd.DataFrame()
 
 
253
 
 
254
  def enhance_prompt(simple_prompt):
255
  if not simple_prompt:
256
- yield gr.update(value="", interactive=True)
257
- return
258
  if not gemini_bot.is_configured:
259
- yield gr.update(value="Fitur tidak aktif (API Key Gemini belum dikonfigurasi).", interactive=True)
260
- return
261
- system_instruction = ("Anda adalah ahli prompt engineering untuk AI text-to-image. Ubah ide sederhana dari pengguna menjadi prompt yang kaya, deskriptif, dan artistik. "
262
- "Fokus pada detail visual: subjek, setting, pencahayaan, gaya seni, komposisi, dan kualitas (hyperdetailed, 4K, masterpiece). "
263
- "Hasilkan HANYA prompt-nya saja tanpa penjelasan.")
264
- yield gr.update(value="🧠 AI sedang meracik prompt ajaib...", interactive=False)
 
 
 
 
265
  enhanced_prompt = gemini_bot.chat(simple_prompt, [], system_prompt=system_instruction)
266
- yield gr.update(value=enhanced_prompt, interactive=True)
267
 
 
268
  def upscale_image(image_to_upscale, clarity_strength):
269
  if image_to_upscale is None:
270
  raise gr.Error("Silakan unggah gambar terlebih dahulu.")
271
- load_upscaler_if_needed()
272
  if upscaler_model is None or upscaler_processor is None:
273
- raise gr.Error("Fitur Upscaler tidak aktif karena model gagal dimuat.")
 
274
  yield None, "πŸš€ Memproses peningkatan resolusi 4x oleh AI..."
275
  try:
 
276
  with torch.no_grad():
277
  inputs = upscaler_processor(image_to_upscale, return_tensors="pt").to(device)
278
  outputs = upscaler_model(**inputs)
279
  output_image = outputs.reconstruction.data.squeeze().float().cpu().clamp_(0, 1).numpy()
280
- output_image = (np.moveaxis(output_image, 0, -1) * 255.0).round().astype(np.uint8)
 
281
  final_image = Image.fromarray(output_image)
 
 
282
  if clarity_strength > 1.0:
283
  yield final_image, f"✨ Menerapkan peningkatan kejernihan (Strength: {clarity_strength:.2f})..."
284
- enhancer = ImageEnhance.Sharpness(final_image); final_image = enhancer.enhance(clarity_strength)
 
 
285
  yield final_image, f"βœ… Gambar berhasil ditingkatkan! Resolusi akhir: {final_image.width}x{final_image.height}px."
286
  except Exception as e:
287
  print(f"❌ Error saat upscaling: {e}")
288
- yield None, f"⚠️ Terjadi error: {e}"
289
 
290
  def update_system_info():
291
- if not psutil or not platform:
292
- return "Informasi sistem tidak tersedia (library `psutil` tidak ditemukan)."
293
  cpu_percent = psutil.cpu_percent(interval=None)
294
  ram = psutil.virtual_memory()
295
- gpu_info = "`Tidak terdeteksi (PyTorch tidak menemukan CUDA)`"
296
  if torch.cuda.is_available():
297
- try:
298
- gpu_name = torch.cuda.get_device_name(0)
299
- gpu_mem_used_gb = torch.cuda.memory_allocated(0)/(1024**3)
300
- gpu_mem_total_gb = torch.cuda.get_device_properties(0).total_memory/(1024**3)
301
- gpu_info = f"**GPU:** `{gpu_name}`\n\n<i class='fa-solid fa-memory'></i> **VRAM:** `{gpu_mem_used_gb:.2f} GB / {gpu_mem_total_gb:.2f} GB`"
302
- except Exception:
303
- gpu_info = "`Informasi GPU tidak dapat diambil`"
304
- return (f"<i class='fa-solid fa-server'></i> **CPU:** `{cpu_percent:.1f}%`\n\n"
305
- f"<i class='fa-solid fa-sd-card'></i> **RAM:** `{ram.percent:.1f}% ({ram.used/(1024**3):.2f} GB / {ram.total/(1024**3):.2f} GB)`\n\n"
306
- f"<i class='fa-solid fa-microchip'></i> {gpu_info}")
307
 
308
- # --- ANTARMUKA PENGGUNA (GRADIO UI) ---
309
- with gr.Blocks(theme=gr.themes.Base(primary_hue="sky"), head=HEAD_HTML) as demo:
310
- with gr.Row(elem_id="header-container"):
311
- gr.Markdown("<i id='main-logo' class='fa-solid fa-meteor'></i>", elem_id="main-logo-container")
312
- gr.Markdown("# RenXploit's Creative AI Suite", elem_id="main-title")
313
- gr.Markdown("Platform All-in-One untuk Imajinasi Tanpa Batas, Didukung oleh Kecerdasan Buatan.", elem_id="main-subtitle")
314
-
315
- loader_html = gr.HTML('<div class="loader"></div>', visible=False)
316
 
 
 
 
 
 
317
  with gr.Tabs() as tabs:
318
- with gr.TabItem("<i class='fa-solid fa-wand-magic-sparkles'></i> Image Generator", id=0):
 
319
  with gr.Row(variant='panel', equal_height=False):
320
  with gr.Column(scale=1):
321
- gr.Markdown("<h3 class='section-header'><i class='fa-solid fa-pen-ruler'></i>Kontrol Kreasi</h3>")
322
- prompt_input = gr.Textbox(label="Prompt Utama", placeholder="Contoh: Seekor kucing astronot, potret sinematik...", lines=3, info="Jelaskan visimu sedetail mungkin!")
323
- negative_prompt_input = gr.Textbox(label="Prompt Negatif", placeholder="Contoh: kualitas buruk, teks, buram...", lines=2, info="Hal yang ingin dihindari.")
324
  num_images_slider = gr.Slider(minimum=1, maximum=8, value=2, step=1, label="Jumlah Gambar")
325
- generate_btn = gr.Button("✨ Hasilkan Karya!", variant="primary")
326
- with gr.Accordion("<i class='fa-solid fa-sliders'></i> Opsi Lanjutan", open=False):
327
- steps_slider = gr.Slider(minimum=1, maximum=50, value=20, step=1, label="Langkah Generasi (Cepat vs Detail)")
328
  with gr.Row():
329
  seed_input = gr.Number(label="Seed", value=-1, precision=0, info="Gunakan -1 untuk acak.")
330
- random_seed_btn = gr.Button("🎲 Acak")
331
  with gr.Column(scale=2):
332
- gr.Markdown("<h3 class='section-header'><i class='fa-solid fa-image'></i>Galeri Hasil</h3>")
333
  output_gallery = gr.Gallery(label="Hasil Gambar", show_label=False, elem_id="gallery", columns=2, object_fit="contain", height="auto")
 
334
  info_box = gr.Textbox(label="Informasi Generasi", visible=False, interactive=False, lines=2)
335
-
336
- # ... (sisa UI tidak berubah, gunakan bagian UI dari skrip asli) ...
337
- # For brevity, include the rest of your TabItems here unchanged (chat, enhancer, upscaler, etc).
338
- # Make sure to reuse the same component variable names referenced below.
339
-
340
- gr.Markdown("<div class='footer'><p>Dibuat dengan <i class='fa-solid fa-heart' style='color: #ff4d4d;'></i> oleh <b>RenXploit</b>.</p></div>")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
 
342
  # --- PENANGANAN EVENT (EVENT HANDLERS) ---
 
343
  random_seed_btn.click(lambda: -1, outputs=seed_input)
344
  generate_btn.click(fn=genie_wrapper, inputs=[prompt_input, negative_prompt_input, steps_slider, seed_input, num_images_slider], outputs=[output_gallery, loader_html, generate_btn, info_box])
345
- # Tambahkan handler lain seperti pada skrip asal: report_btn, demo.load, dsb.
 
 
346
  demo.load(log_visitor, inputs=None, outputs=None)
347
- demo.load(fn=update_system_info, inputs=None, outputs=[ ]) # sesuaikan sesuai komponen system_info_md jika ada
 
 
 
 
 
 
 
 
 
 
 
348
 
349
  # --- Menjalankan Aplikasi ---
350
  if __name__ == "__main__":
 
12
  import csv
13
  import pandas as pd
14
  import threading
15
+ from PIL import Image, ImageEnhance # Ditambahkan untuk fitur kejernihan gambar
16
 
17
+ # --- LIBRARY BARU UNTUK FITUR UPGRADE ---
18
  try:
19
  import psutil
20
  import platform
 
34
  print("❌ Peringatan: Library 'google-generativeai' tidak ditemukan. Fitur Chatbot & Prompt Enhancer tidak akan berfungsi.")
35
  genai = None
36
 
37
+ # --- HEAD HTML & CSS (Tampilan Profesional) ---
38
+ HEAD_HTML = """
39
+ <head>
40
+ <meta charset="UTF-8">
41
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
42
+ <title>RenXploit's Creative AI Suite</title>
43
+ <link rel="preconnect" href="https://fonts.googleapis.com">
44
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
45
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code:wght@500&display=swap" rel="stylesheet">
46
+ <script>
47
+ function typewriterEffect(element, text, speed) {
48
+ let i = 0;
49
+ element.innerHTML = "";
50
+ const cursor = document.createElement('span');
51
+ cursor.className = 'typewriter-cursor';
52
+ cursor.innerHTML = '|';
53
+ element.appendChild(cursor);
54
+ function type() {
55
+ if (i < text.length) {
56
+ element.insertBefore(document.createTextNode(text.charAt(i)), cursor);
57
+ i++;
58
+ setTimeout(type, speed);
59
+ } else {
60
+ cursor.style.animation = 'blink 1s step-end infinite';
61
+ }
62
+ }
63
+ type();
64
+ }
65
+
66
+ document.addEventListener("DOMContentLoaded", function(event) {
67
+ const titleElement = document.querySelector('#main-title h1');
68
+ if (titleElement) {
69
+ const titleText = "πŸš€ RenXploit's Creative AI Suite 🌌";
70
+ titleElement.textContent = "";
71
+ setTimeout(() => typewriterEffect(titleElement, titleText, 50), 500);
72
+ }
73
+
74
+ // --- JAVASCRIPT UNTUK TRIGGER PERIODIK YANG KOMPATIBEL ---
75
+ setInterval(() => {
76
+ const triggerButton = document.querySelector('#system-info-trigger-btn button');
77
+ if (triggerButton) {
78
+ triggerButton.click();
79
+ }
80
+ }, 5000); // Interval 5000 milidetik = 5 detik
81
+ });
82
+ </script>
83
+ <style>
84
+ :root { --primary-color: #00aaff; --primary-hover-color: #0088cc; --background-color: #0d1117; --content-background-color: #161b22; --border-color: #30363d; --text-color: #c9d1d9; --text-muted-color: #8b949e; --font-family-main: 'Inter', sans-serif; --font-family-mono: 'Fira Code', monospace; --border-radius: 12px; --shadow-light: 0 4px 14px 0 rgba(0, 170, 255, 0.1); --shadow-strong: 0 6px 20px 0 rgba(0, 170, 255, 0.15); }
85
+ body, .gradio-container { font-family: var(--font-family-main); background-color: var(--background-color) !important; color: var(--text-color); }
86
+ h1, h2, h3 { color: #ffffff; font-weight: 600; }
87
+ #main-title h1 { font-size: 2.5em !important; font-weight: 700 !important; text-align: center; padding: 2rem 1rem; color: #ffffff; background: linear-gradient(90deg, #00aaff, #00ffaa); -webkit-background-clip: text; -webkit-text-fill-color: transparent; display: inline-block; }
88
+ #main-title .typewriter-cursor { color: var(--primary-color); font-weight: bold; }
89
+ @keyframes blink { from, to { opacity: 1 } 50% { opacity: 0 } }
90
+ #main-subtitle p { font-size: 1.2em !important; text-align: center; color: var(--text-muted-color); margin-top: -1.5rem !important; margin-bottom: 2rem !important; }
91
+ .gradio-tabs { border: none !important; background-color: transparent !important; margin-bottom: 1.5rem; }
92
+ .gradio-tabs > div[role="tablist"] { display: flex; justify-content: center; flex-wrap: wrap; gap: 10px; border-bottom: 2px solid var(--border-color); padding-bottom: 10px; }
93
+ .gradio-tabs > div[role="tablist"] > button { background-color: transparent !important; color: var(--text-muted-color) !important; border: none !important; border-bottom: 3px solid transparent !important; font-size: 1.1em !important; font-weight: 600 !important; padding: 12px 20px !important; border-radius: 8px 8px 0 0 !important; transition: all 0.3s ease; }
94
+ .gradio-tabs > div[role="tablist"] > button:hover { background-color: var(--content-background-color) !important; color: var(--primary-color) !important; }
95
+ .gradio-tabs > div[role="tablist"] > button.selected { color: var(--primary-color) !important; border-bottom: 3px solid var(--primary-color) !important; background-color: var(--content-background-color) !important; }
96
+ .tabitem { background-color: transparent !important; border: none !important; padding: 0 !important; }
97
+ .gradio-row.panel, .gradio-accordion { background-color: var(--content-background-color) !important; border: 1px solid var(--border-color) !important; border-radius: var(--border-radius) !important; padding: 24px !important; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
98
+ .gradio-accordion { margin-top: 1rem; }
99
+ .gradio-button.primary { background: linear-gradient(90deg, var(--primary-color), #00c6ff) !important; color: #ffffff !important; font-weight: 600 !important; font-size: 1.1em !important; border-radius: 8px !important; border: none !important; padding: 12px 24px !important; transition: all 0.3s ease; box-shadow: var(--shadow-light); }
100
+ .gradio-button.primary:hover { transform: translateY(-2px); box-shadow: var(--shadow-strong); }
101
+ .gradio-button.secondary { background-color: var(--content-background-color) !important; border: 1px solid var(--primary-color) !important; color: var(--primary-color) !important; transition: all 0.3s ease; }
102
+ .gradio-button.secondary:hover { background-color: var(--primary-color) !important; color: #ffffff !important; }
103
+ .gradio-textbox, .gradio-number, .gradio-image { background-color: var(--background-color) !important; border: 1px solid var(--border-color) !important; color: var(--text-color) !important; border-radius: 8px !important; }
104
+ .gradio-textbox textarea, .gradio-number input { color: var(--text-color) !important; }
105
+ .gradio-textbox:focus-within, .gradio-number:focus-within { border-color: var(--primary-color) !important; box-shadow: 0 0 0 2px rgba(0, 170, 255, 0.3); }
106
+ div.info { color: var(--text-muted-color) !important; font-style: italic; }
107
+ #gallery { border-radius: var(--border-radius) !important; overflow: hidden; border: 1px solid var(--border-color); background-color: #010409; }
108
+ #gallery .gallery-item { transition: transform 0.3s ease, box-shadow 0.3s ease; }
109
+ #gallery .gallery-item:hover { transform: scale(1.03); box-shadow: 0 8px 30px rgba(0, 170, 255, 0.3); }
110
+ .loader-container { border: 1px solid var(--border-color); border-radius: var(--border-radius); padding: 30px; background: var(--content-background-color); text-align: center; overflow: hidden; }
111
+ .loader-text { font-family: var(--font-family-mono); font-size: 1.1em; color: var(--primary-color); margin-bottom: 20px; }
112
+ .loader-bar-container { height: 8px; background: var(--background-color); border-radius: 4px; overflow: hidden; }
113
+ .loader-bar { width: 100%; height: 100%; background: linear-gradient(90deg, transparent, var(--primary-color), transparent); background-size: 200% 100%; animation: scan 2s linear infinite; }
114
+ @keyframes scan { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
115
+ .gradio-chatbot .message { border-radius: 12px !important; box-shadow: none !important; }
116
+ .gradio-chatbot .user { background-color: var(--primary-color) !important; color: white !important; }
117
+ .gradio-chatbot .bot { background-color: var(--content-background-color) !important; border: 1px solid var(--border-color) !important; }
118
+ .footer { text-align: center; margin: 2rem auto; padding: 1rem; color: var(--text-muted-color); border-top: 1px solid var(--border-color); }
119
+ .footer b { color: var(--primary-color); }
120
+ </style>
121
+ </head>
122
+ """
123
 
124
  # --- PENGATURAN & LOGIC GLOBAL ---
125
  VISITOR_LOG_FILE = "visitor_log.csv"
 
127
  device = "cuda" if torch.cuda.is_available() else "cpu"
128
  print(f"➑️ Menggunakan device: {device.upper()}")
129
 
 
 
 
 
 
 
 
 
130
  # --- Inisialisasi File Log Pengunjung ---
131
  def initialize_log_file():
132
  if not os.path.exists(VISITOR_LOG_FILE):
 
139
 
140
  initialize_log_file()
141
 
142
+ # --- PEMUATAN MODEL-MODEL AI ---
143
+ # 1. Model Generator Gambar (Inti)
144
+ print("➑️ Memuat model SDXL-Turbo...")
145
+ pipe = DiffusionPipeline.from_pretrained(
146
+ "stabilityai/sdxl-turbo", torch_dtype=torch.float16 if device == "cuda" else torch.float32,
147
+ variant="fp16" if device == "cuda" else None, use_safetensors=True
148
+ ).to(device)
149
+ if torch.cuda.is_available(): pipe.enable_xformers_memory_efficient_attention()
150
+ print("βœ… Model SDXL-Turbo berhasil dimuat.")
151
+
152
+ # 2. Model Peningkatan Resolusi (Fitur Baru)
153
+ upscaler_model = None
154
+ upscaler_processor = None
155
+ if Swin2SRForImageSuperResolution:
156
+ try:
157
+ print("➑️ Memuat model AI Upscaler (Swin2SR)...")
158
+ upscaler_model = Swin2SRForImageSuperResolution.from_pretrained("caidas/swin2sr-realworld-sr-x4-64-bsrgan-psnr").to(device)
159
+ upscaler_processor = Swin2SRImageProcessor.from_pretrained("caidas/swin2sr-realworld-sr-x4-64-bsrgan-psnr")
160
+ print("βœ… Model AI Upscaler berhasil dimuat.")
161
+ except Exception as e:
162
+ print(f"❌ Gagal memuat model Upscaler: {e}. Fitur upscale akan dinonaktifkan.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
+ # --- KELAS UNTUK CHATBOT GEMINI ---
165
  class GeminiChat:
166
  def __init__(self):
167
  self.api_keys = []
 
179
  print("❌ PERINGATAN: Tidak ada API Key Gemini yang ditemukan. Fitur AI Chat & Prompt Enhancer tidak akan berfungsi.")
180
 
181
  def chat(self, message, history, system_prompt=None):
182
+ if not self.is_configured: return "Maaf, fitur ini tidak terkonfigurasi karena tidak ada API Key."
183
  try:
184
  selected_key = random.choice(self.api_keys)
185
  genai.configure(api_key=selected_key)
 
190
  return response.text
191
  except Exception as e:
192
  print(f"❌ Terjadi error pada API Key Gemini: {e}")
193
+ return "Terjadi kesalahan saat menghubungi API AI. Mungkin salah satu API Key tidak valid atau ada masalah jaringan. Silakan coba lagi."
194
 
195
  gemini_bot = GeminiChat()
196
 
197
+ # --- FUNGSI-FUNGSI INTI (GENERATOR & LAINNYA) ---
198
+ def log_visitor(request: gr.Request):
199
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
200
+ ip_address = request.client.host if request else "N/A"
201
+ user_agent = request.headers.get("user-agent", "Unknown") if request else "N/A"
202
+ with file_lock:
203
+ with open(VISITOR_LOG_FILE, mode='a', newline='', encoding='utf-8') as f:
204
+ writer = csv.writer(f)
205
+ writer.writerow([timestamp, ip_address, user_agent])
206
+ print(f"βœ… Pengunjung baru tercatat: IP {ip_address}")
 
 
 
 
 
 
207
 
208
  def generate_images(prompt, negative_prompt, steps, seed, num_images):
209
+ if not prompt:
210
+ raise gr.Error("Prompt tidak boleh kosong!")
211
+ if seed == -1: seed = random.randint(0, 2**32 - 1)
212
+ generator = torch.manual_seed(seed)
213
+ images = pipe(prompt=prompt, negative_prompt=negative_prompt, generator=generator, num_inference_steps=steps, guidance_scale=0.0, num_images_per_prompt=num_images).images
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  return images, seed
215
 
216
  def genie_wrapper(prompt, negative_prompt, steps, seed, num_images):
 
217
  yield gr.update(visible=False), gr.update(visible=True), gr.update(interactive=False), gr.update(visible=False)
218
  start_time = time.time()
219
+ images, used_seed = generate_images(prompt, negative_prompt, steps, int(seed), int(num_images))
220
  end_time = time.time()
221
  generation_time = end_time - start_time
222
  info_text = f"Seed yang digunakan: {used_seed}\nTotal waktu generasi: {generation_time:.2f} detik"
 
228
  return gr.update(visible=False)
229
  report_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
230
  report_content = f"--- Laporan Baru ({report_time}) ---\nNama: {name}\nEmail: {email}\nPesan: {message}\n\n"
231
+ with open("reports.log", "a", encoding="utf-8") as f:
232
+ f.write(report_content)
233
  print("βœ… Laporan baru telah disimpan ke reports.log")
234
  return gr.update(value="βœ… Terima kasih! Laporan Anda telah kami terima.", visible=True)
235
 
236
+ # --- PERBAIKAN: Fungsi Visitor Monitor yang lebih robust ---
237
  def update_visitor_monitor(time_filter: str):
238
  try:
239
  with file_lock:
240
+ if not os.path.exists(VISITOR_LOG_FILE) or os.path.getsize(VISITOR_LOG_FILE) < 10: # Check for header
241
+ return "## πŸ“ˆ 0", pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
242
  df = pd.read_csv(VISITOR_LOG_FILE)
243
+ if df.empty: return "## πŸ“ˆ 0", pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
244
+
245
+ # Konversi ke datetime dan tangani error dengan mengubahnya menjadi NaT (Not a Time)
246
+ df['Timestamp'] = pd.to_datetime(df['Timestamp'], errors='coerce')
247
+ df.dropna(subset=['Timestamp'], inplace=True) # Hapus baris yang gagal dikonversi
248
+ if df.empty: return "## πŸ“ˆ 0", pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
249
+
250
  total_overall_visitors = len(df)
251
+ total_visitors_formatted = f"## πŸ“ˆ {total_overall_visitors:,}"
252
  df['Total Pengunjung'] = np.arange(1, len(df) + 1)
253
+
254
+ # Membuat semua data waktu menjadi timezone-naive untuk perbandingan yang konsisten
255
+ now = pd.to_datetime('now', utc=True).tz_localize(None)
256
+ df['Timestamp'] = df['Timestamp'].dt.tz_localize(None)
257
+
258
  if time_filter == "1 Minggu Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(weeks=1)]
259
  elif time_filter == "2 Minggu Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(weeks=2)]
260
  elif time_filter == "3 Bulan Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(days=90)]
261
  else: df_plot = df
262
+
263
  if df_plot.empty: return total_visitors_formatted, pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
264
+
265
  return total_visitors_formatted, df_plot
266
  except Exception as e:
267
+ error_message = f"Error saat memperbarui monitor: {e}"
268
+ print(f"❌ {error_message}")
269
+ return f"## ⚠️ Error", pd.DataFrame({"Error": [error_message]})
270
+
271
 
272
+ # --- FUNGSI-FUNGSI FITUR BARU ---
273
  def enhance_prompt(simple_prompt):
274
  if not simple_prompt:
275
+ gr.Warning("Tolong masukkan ide Anda terlebih dahulu.")
276
+ return ""
277
  if not gemini_bot.is_configured:
278
+ gr.Error("Fitur Prompt Enhancer tidak aktif karena API Key Gemini tidak diatur.")
279
+ return "Fitur tidak aktif."
280
+
281
+ system_instruction = (
282
+ "Anda adalah seorang ahli prompt engineering untuk model AI text-to-image seperti Stable Diffusion. "
283
+ "Tugas Anda adalah mengubah ide sederhana dari pengguna menjadi prompt yang kaya, deskriptif, dan artistik. "
284
+ "Fokus pada detail visual: subjek, setting, pencahayaan (cinematic lighting, soft light, dll), gaya seni (photorealistic, anime style, oil painting, dll), komposisi, dan kualitas (hyperdetailed, 4K, masterpiece, trending on artstation). "
285
+ "Hasilkan HANYA prompt-nya saja dalam format teks panjang, tanpa penjelasan atau kalimat pembuka/penutup."
286
+ )
287
+ yield "🧠 AI sedang meracik prompt ajaib untuk Anda..."
288
  enhanced_prompt = gemini_bot.chat(simple_prompt, [], system_prompt=system_instruction)
289
+ yield enhanced_prompt
290
 
291
+ # --- PERBAIKAN: Fungsi Upscaler dengan kontrol kejernihan ---
292
  def upscale_image(image_to_upscale, clarity_strength):
293
  if image_to_upscale is None:
294
  raise gr.Error("Silakan unggah gambar terlebih dahulu.")
 
295
  if upscaler_model is None or upscaler_processor is None:
296
+ raise gr.Error("Fitur Upscaler tidak aktif karena model gagal dimuat. Periksa log saat startup.")
297
+
298
  yield None, "πŸš€ Memproses peningkatan resolusi 4x oleh AI..."
299
  try:
300
+ # Langkah 1: Upscale dengan AI
301
  with torch.no_grad():
302
  inputs = upscaler_processor(image_to_upscale, return_tensors="pt").to(device)
303
  outputs = upscaler_model(**inputs)
304
  output_image = outputs.reconstruction.data.squeeze().float().cpu().clamp_(0, 1).numpy()
305
+ output_image = np.moveaxis(output_image, source=0, destination=-1)
306
+ output_image = (output_image * 255.0).round().astype(np.uint8)
307
  final_image = Image.fromarray(output_image)
308
+
309
+ # Langkah 2: Terapkan peningkatan kejernihan jika diminta
310
  if clarity_strength > 1.0:
311
  yield final_image, f"✨ Menerapkan peningkatan kejernihan (Strength: {clarity_strength:.2f})..."
312
+ enhancer = ImageEnhance.Sharpness(final_image)
313
+ final_image = enhancer.enhance(clarity_strength)
314
+
315
  yield final_image, f"βœ… Gambar berhasil ditingkatkan! Resolusi akhir: {final_image.width}x{final_image.height}px."
316
  except Exception as e:
317
  print(f"❌ Error saat upscaling: {e}")
318
+ yield None, f"⚠️ Terjadi error saat upscaling: {e}"
319
 
320
  def update_system_info():
321
+ if not psutil or not platform: return "Informasi sistem tidak tersedia (library `psutil` tidak ditemukan)."
 
322
  cpu_percent = psutil.cpu_percent(interval=None)
323
  ram = psutil.virtual_memory()
324
+ gpu_info = "Tidak terdeteksi (PyTorch tidak menemukan CUDA)"
325
  if torch.cuda.is_available():
326
+ gpu_name = torch.cuda.get_device_name(0)
327
+ gpu_mem_used_gb = torch.cuda.memory_allocated(0) / (1024**3)
328
+ gpu_mem_total_gb = torch.cuda.get_device_properties(0).total_memory / (1024**3)
329
+ gpu_info = f"**GPU:** `{gpu_name}`\n**VRAM Terpakai:** `{gpu_mem_used_gb:.2f} GB / {gpu_mem_total_gb:.2f} GB`"
330
+
331
+ sys_info = f"**Platform:** `{platform.system()} {platform.release()}`"
332
+ return (f"**CPU Terpakai:** `{cpu_percent:.1f}%`\n"
333
+ f"**RAM Terpakai:** `{ram.percent:.1f}% ({ram.used / (1024**3):.2f} GB / {ram.total / (1024**3):.2f} GB)`\n"
334
+ f"{gpu_info}\n---\n{sys_info}")
 
335
 
 
 
 
 
 
 
 
 
336
 
337
+ # --- ANTARMUKA PENGGUNA (GRADIO UI) ---
338
+ with gr.Blocks(theme=gr.themes.Base(), head=HEAD_HTML) as demo:
339
+ gr.Markdown("# πŸš€ RenXploit's Creative AI Suite 🌌", elem_id="main-title")
340
+ gr.Markdown("Sebuah platform lengkap untuk kreativitas Anda, ditenagai oleh AI.", elem_id="main-subtitle")
341
+
342
  with gr.Tabs() as tabs:
343
+ # --- TAB 1: IMAGE GENERATOR (INTI) ---
344
+ with gr.TabItem("🎨 Image Generator", id=0):
345
  with gr.Row(variant='panel', equal_height=False):
346
  with gr.Column(scale=1):
347
+ gr.Markdown("### πŸ“ **Masukan Perintah Anda**")
348
+ prompt_input = gr.Textbox(label="Prompt", placeholder="Contoh: Cinematic photo, seekor rubah merah...", lines=3, info="Jadilah sangat spesifik! Atau gunakan Prompt Enhancer.")
349
+ negative_prompt_input = gr.Textbox(label="Prompt Negatif", placeholder="Contoh: blurry, low quality, bad hands...", lines=2, info="Hal-hal yang TIDAK Anda inginkan.")
350
  num_images_slider = gr.Slider(minimum=1, maximum=8, value=2, step=1, label="Jumlah Gambar")
351
+ generate_btn = gr.Button("✨ Hasilkan Gambar!", variant="primary")
352
+ with gr.Accordion("βš™οΈ Opsi Lanjutan", open=False):
353
+ steps_slider = gr.Slider(minimum=1, maximum=5, value=2, step=1, label="Langkah Iterasi (Kualitas vs Kecepatan)")
354
  with gr.Row():
355
  seed_input = gr.Number(label="Seed", value=-1, precision=0, info="Gunakan -1 untuk acak.")
356
+ random_seed_btn = gr.Button("🎲 Acak", variant="secondary")
357
  with gr.Column(scale=2):
358
+ gr.Markdown("### πŸ–ΌοΈ **Hasil Generasi**")
359
  output_gallery = gr.Gallery(label="Hasil Gambar", show_label=False, elem_id="gallery", columns=2, object_fit="contain", height="auto")
360
+ loader_html = gr.HTML(visible=False)
361
  info_box = gr.Textbox(label="Informasi Generasi", visible=False, interactive=False, lines=2)
362
+
363
+ # --- TAB 2: CHAT WITH AI (INTI) ---
364
+ with gr.TabItem("πŸ’¬ Chat with AI", id=1):
365
+ with gr.Row(variant='panel'):
366
+ with gr.Column():
367
+ gr.Markdown("### πŸ€– **Asisten AI Flood**")
368
+ if not gemini_bot.is_configured:
369
+ gr.Warning("Fitur Chatbot dinonaktifkan. API Key Gemini tidak terkonfigurasi.")
370
+ else:
371
+ gr.ChatInterface(
372
+ gemini_bot.chat,
373
+ chatbot=gr.Chatbot(height=500, label="Flood AI", value=[(None, "Halo! Saya adalah asisten AI dari RenXploit. Ada yang bisa saya bantu?")]),
374
+ title=None,
375
+ description="Tanyakan apa saja!",
376
+ examples=["Apa itu SDXL-Turbo?", "Buatkan saya puisi tentang AI", "Jelaskan konsep lubang hitam dengan sederhana"],
377
+ )
378
+
379
+ # --- TAB 3: PROMPT ENHANCER (FITUR BARU) ---
380
+ with gr.TabItem("✨ Prompt Enhancer", id=2):
381
+ with gr.Row(variant='panel'):
382
+ with gr.Column():
383
+ gr.Markdown("### πŸͺ„ **Ubah Ide Jadi Prompt Ajaib**\nCukup tulis ide sederhana, dan biarkan AI menyempurnakannya menjadi prompt yang detail dan artistik.")
384
+ simple_prompt_input = gr.Textbox(label="Ide Sederhana Anda", placeholder="Contoh: seekor astronot di hutan alien", lines=3)
385
+ enhance_btn = gr.Button("Buat Prompt Ajaib!", variant="primary")
386
+ enhanced_prompt_output = gr.Textbox(label="Prompt yang Disempurnakan", lines=5, interactive=True, show_copy_button=True)
387
+ with gr.Row():
388
+ send_to_gen_btn = gr.Button("➑️ Kirim & Pindah ke Generator")
389
+
390
+ # --- TAB 4: AI IMAGE UPSCALER (FITUR BARU & DIPERBAIKI) ---
391
+ with gr.TabItem("πŸš€ AI Image Upscaler", id=3):
392
+ with gr.Row(variant='panel', equal_height=False): # Ubah equal_height agar slider tidak meregang
393
+ with gr.Column():
394
+ gr.Markdown("### **Tingkatkan Resolusi Gambar**\nUnggah gambar untuk meningkatkan kualitas dan ukurannya hingga 4x lipat menggunakan AI.")
395
+ image_to_upscale_input = gr.Image(type="pil", label="Unggah Gambar Anda di Sini")
396
+ # --- PERBAIKAN: Menambahkan slider untuk kontrol kejernihan ---
397
+ clarity_slider = gr.Slider(minimum=1.0, maximum=3.0, value=1.0, step=0.1, label="Tingkat Peningkatan Kejernihan", info="Setelah di-upscale 4x, atur kejernihan gambar di sini. 1.0 = Tanpa efek.")
398
+ upscale_btn = gr.Button("Tingkatkan Resolusi!", variant="primary")
399
+ with gr.Column():
400
+ gr.Markdown("### **Hasil Peningkatan Resolusi**")
401
+ upscaled_image_output = gr.Image(label="Gambar Hasil Upscale", interactive=False)
402
+ upscale_status_text = gr.Markdown("Status: Menunggu gambar...")
403
+
404
+ # --- TAB 5: VISITOR MONITOR (INTI & DIPERBAIKI) ---
405
+ with gr.TabItem("πŸ“Š Visitor Monitor", id=4):
406
+ with gr.Row(variant='panel'):
407
+ with gr.Column():
408
+ gr.Markdown("### πŸ“ˆ **Live Visitor Monitor**\nPantau jumlah total pengunjung aplikasi Anda secara real-time.")
409
+ with gr.Row():
410
+ with gr.Column(scale=3):
411
+ visitor_count_display = gr.Markdown("## πŸ“ˆ Memuat data...")
412
+ with gr.Column(scale=2):
413
+ time_filter_radio = gr.Radio(["Semua Waktu", "1 Minggu Terakhir", "2 Minggu Terakhir", "3 Bulan Terakhir"], label="Tampilkan data untuk", value="Semua Waktu")
414
+ refresh_btn = gr.Button("πŸ”„ Segarkan Manual", variant="secondary")
415
+ visitor_plot = gr.LinePlot(x="Timestamp", y="Total Pengunjung", title="Grafik Pertumbuhan Pengunjung", tooltip=['Timestamp', 'Total Pengunjung'], height=500, interactive=True)
416
+
417
+ # --- TAB 6: SYSTEM & SETTINGS (FITUR BARU & DIPERBAIKI) ---
418
+ with gr.TabItem("βš™οΈ System & Settings", id=5):
419
+ with gr.Row(variant='panel'):
420
+ with gr.Column():
421
+ gr.Markdown("### **Live System Monitor**")
422
+ system_info_md = gr.Markdown("Memuat info sistem...")
423
+ system_info_trigger_btn = gr.Button("Trigger System Info", visible=False, elem_id="system-info-trigger-btn")
424
+ with gr.Column():
425
+ gr.Markdown("### **Pengaturan Aplikasi**")
426
+ with gr.Accordion("Kualitas Model", open=True):
427
+ # --- PERBAIKAN: Mengubah teks info agar lebih jelas ---
428
+ gr.Radio(["FP16 (Cepat, Kualitas Baik)", "FP32 (Lambat, Kualitas Terbaik)"],
429
+ value="FP16 (Cepat, Kualitas Baik)" if device == "cuda" else "FP32 (Lambat, Kualitas Terbaik)",
430
+ label="Presisi Model Generator",
431
+ interactive=False,
432
+ info="Terkunci. Pengaturan ini ditentukan saat aplikasi dimulai berdasarkan ketersediaan GPU Anda.")
433
+
434
+ # --- TAB-TAB STATIS ---
435
+ # (Tidak ada perubahan di sini, konten tetap sama)
436
+ with gr.TabItem("πŸ’‘ Panduan Prompting", id=6):
437
+ with gr.Row(variant='panel'): gr.Markdown("""## Cara Menjadi "Art Director" yang Hebat untuk AI...""")
438
+ with gr.TabItem("πŸ“– Blog & Updates", id=7):
439
+ with gr.Row(variant='panel'): gr.Markdown("""### Perkembangan Terbaru dari RenXploit's AI Suite\n- **v2.4 (Pembaruan Terkini):** Upscaler di-upgrade dengan kontrol kejernihan, Visitor Monitor diperbaiki, dan UI Settings diperjelas.\n- **v2.3:** Perbaikan bug kritis untuk kompatibilitas dengan berbagai versi Gradio.\n- **v2.2:** Perbaikan bug untuk kompatibilitas Gradio.\n- **v2.1:** Menambahkan **Prompt Enhancer**, **AI Image Upscaler**, dan **Live System Monitor**.\n- **v2.0:** Perombakan Desain Total.\n- **Rencana Berikutnya:** Menjajaki model generator gambar yang berbeda dan fitur Inpainting/Outpainting. Nantikan!""")
440
+ with gr.TabItem("ℹ️ About & Support", id=8):
441
+ with gr.Row(variant='panel'):
442
+ with gr.Column():
443
+ gr.Markdown("### Tentang Proyek dan Dukungan")
444
+ with gr.Accordion("Tentang RenXploit's Creative AI Suite", open=True):
445
+ gr.Markdown("**RenXploit's Creative AI Suite** adalah proyek pribadi yang dibuat untuk mengeksplorasi kemampuan AI generatif dalam sebuah antarmuka yang mudah digunakan.\n **Bagian ini belum saya kerjakan dengan selesai** jadi jika anda ingin komplain atau ada hal apapun itu bisa report ke saya lewat website portofolio saya dengan url: https://ngoprek.xyz/contact")
446
+ with gr.Accordion("Laporkan Masalah atau Beri Masukan"):
447
+ report_name = gr.Textbox(label="Nama Anda")
448
+ report_email = gr.Textbox(label="Email Anda (Opsional)")
449
+ report_message = gr.Textbox(label="Pesan Anda", lines=5, placeholder="Jelaskan masalah yang Anda temui atau ide yang Anda miliki...")
450
+ report_btn = gr.Button("Kirim Laporan", variant="primary")
451
+ report_status = gr.Markdown(visible=False)
452
+
453
+ gr.Markdown("---\n<div class='footer'><p>Dibuat dengan ❀️ oleh <b>RenXploit</b>.</p></div>", elem_classes="footer")
454
 
455
  # --- PENANGANAN EVENT (EVENT HANDLERS) ---
456
+ # 1. Event Inti
457
  random_seed_btn.click(lambda: -1, outputs=seed_input)
458
  generate_btn.click(fn=genie_wrapper, inputs=[prompt_input, negative_prompt_input, steps_slider, seed_input, num_images_slider], outputs=[output_gallery, loader_html, generate_btn, info_box])
459
+ report_btn.click(fn=submit_report, inputs=[report_name, report_email, report_message], outputs=[report_status])
460
+
461
+ # 2. Event Visitor Monitor
462
  demo.load(log_visitor, inputs=None, outputs=None)
463
+ demo.load(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
464
+ refresh_btn.click(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
465
+ time_filter_radio.change(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
466
+
467
+ # 3. Event Fitur Baru
468
+ enhance_btn.click(fn=enhance_prompt, inputs=[simple_prompt_input], outputs=[enhanced_prompt_output])
469
+ send_to_gen_btn.click(fn=lambda prompt: (prompt, gr.Tabs(selected=0)), inputs=[enhanced_prompt_output], outputs=[prompt_input, tabs])
470
+ upscale_btn.click(fn=upscale_image, inputs=[image_to_upscale_input, clarity_slider], outputs=[upscaled_image_output, upscale_status_text]) # Input diperbarui
471
+
472
+ system_info_trigger_btn.click(fn=update_system_info, inputs=None, outputs=system_info_md)
473
+ demo.load(fn=update_system_info, inputs=None, outputs=system_info_md) # Jalankan sekali saat dimuat
474
+
475
 
476
  # --- Menjalankan Aplikasi ---
477
  if __name__ == "__main__":