floodd commited on
Commit
5f4214a
·
verified ·
1 Parent(s): 82bcb16

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +320 -729
app.py CHANGED
@@ -1,732 +1,323 @@
1
-
2
- -- coding: utf-8 --
3
- --- IMPORT LIBRARY UTAMA & INTI ---
4
-
5
- import gradio as gr
6
- import torch
7
- import numpy as np
8
- from diffusers import DiffusionPipeline
9
- import random
10
- import time
11
- import os
12
- from datetime import datetime, timedelta
13
- import csv
14
- import pandas as pd
15
- import threading
16
- from PIL import Image, ImageEnhance
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  from pathlib import Path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- --- LIBRARY BARU UNTUK FITUR UPGRADE ---
20
-
21
- try:
22
- import psutil
23
- import platform
24
- from transformers import Swin2SRForImageSuperResolution, Swin2SRImageProcessor
25
- print("✅ Library tambahan (psutil, transformers) berhasil diimpor.")
26
- except ImportError:
27
- print("❌ Peringatan: Library 'psutil' atau 'transformers' tidak ditemukan. Fitur System Monitor & Upscaler tidak akan berfungsi.")
28
- psutil = None
29
- platform = None
30
- Swin2SRForImageSuperResolution = None
31
- Swin2SRImageProcessor = None
32
-
33
- try:
34
- import google.generativeai as genai
35
- print("✅ Library 'google-generativeai' berhasil diimpor.")
36
- except ImportError:
37
- print("❌ Peringatan: Library 'google-generativeai' tidak ditemukan. Fitur Chatbot & Prompt Enhancer tidak akan berfungsi.")
38
- genai = None
39
-
40
- --- HEAD HTML & CSS (Tampilan Profesional) ---
41
-
42
- HEAD_HTML = """
43
-
44
- <head>
45
- <meta charset="UTF-8">
46
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
47
- <title>RenXploit's Creative AI Suite</title>
48
- <link rel="preconnect" href="https://fonts.googleapis.com">
49
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
50
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code:wght@500&display=swap" rel="stylesheet">
51
- <script>
52
- function typewriterEffect(element, text, speed) {
53
- let i = 0;
54
- element.innerHTML = "";
55
- const cursor = document.createElement('span');
56
- cursor.className = 'typewriter-cursor';
57
- cursor.innerHTML = '|';
58
- element.appendChild(cursor);
59
- function type() {
60
- if (i < text.length) {
61
- element.insertBefore(document.createTextNode(text.charAt(i)), cursor);
62
- i++;
63
- setTimeout(type, speed);
64
- } else {
65
- cursor.style.animation = 'blink 1s step-end infinite';
66
- }
67
- }
68
- type();
69
- }
70
-
71
- code
72
- Code
73
- download
74
- content_copy
75
- expand_less
76
- document.addEventListener("DOMContentLoaded", function(event) {
77
- const titleElement = document.querySelector('#main-title h1');
78
- if (titleElement) {
79
- const titleText = "🚀 RenXploit's Creative AI Suite 🌌";
80
- titleElement.textContent = "";
81
- setTimeout(() => typewriterEffect(titleElement, titleText, 50), 500);
82
- }
83
-
84
- setInterval(() => {
85
- const triggerButton = document.querySelector('#system-info-trigger-btn button');
86
- if (triggerButton) {
87
- triggerButton.click();
88
- }
89
- }, 5000);
90
- });
91
- </script>
92
- <style>
93
- :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); }
94
- body, .gradio-container { font-family: var(--font-family-main); background-color: var(--background-color) !important; color: var(--text-color); }
95
- h1, h2, h3 { color: #ffffff; font-weight: 600; }
96
- #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; }
97
- #main-title .typewriter-cursor { color: var(--primary-color); font-weight: bold; }
98
- @keyframes blink { from, to { opacity: 1 } 50% { opacity: 0 } }
99
- #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; }
100
- .gradio-tabs { border: none !important; background-color: transparent !important; margin-bottom: 1.5rem; }
101
- .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; }
102
- .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; }
103
- .gradio-tabs > div[role="tablist"] > button:hover { background-color: var(--content-background-color) !important; color: var(--primary-color) !important; }
104
- .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; }
105
- .tabitem { background-color: transparent !important; border: none !important; padding: 0 !important; }
106
- .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); }
107
- .gradio-accordion { margin-top: 1rem; }
108
- .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); }
109
- .gradio-button.primary:hover { transform: translateY(-2px); box-shadow: var(--shadow-strong); }
110
- .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; }
111
- .gradio-button.secondary:hover { background-color: var(--primary-color) !important; color: #ffffff !important; }
112
- .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; }
113
- .gradio-textbox textarea, .gradio-number input { color: var(--text-color) !important; }
114
- .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); }
115
- div.info { color: var(--text-muted-color) !important; font-style: italic; }
116
- #gallery { border-radius: var(--border-radius) !important; overflow: hidden; border: 1px solid var(--border-color); background-color: #010409; }
117
- #gallery .gallery-item { transition: transform 0.3s ease, box-shadow 0.3s ease; }
118
- #gallery .gallery-item:hover { transform: scale(1.03); box-shadow: 0 8px 30px rgba(0, 170, 255, 0.3); }
119
- .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; }
120
- .loader-text { font-family: var(--font-family-mono); font-size: 1.1em; color: var(--primary-color); margin-bottom: 20px; }
121
- .loader-bar-container { height: 8px; background: var(--background-color); border-radius: 4px; overflow: hidden; }
122
- .loader-bar { width: 100%; height: 100%; background: linear-gradient(90deg, transparent, var(--primary-color), transparent); background-size: 200% 100%; animation: scan 2s linear infinite; }
123
- @keyframes scan { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
124
- .gradio-chatbot .message { border-radius: 12px !important; box-shadow: none !important; }
125
- .gradio-chatbot .user { background-color: var(--primary-color) !important; color: white !important; }
126
- .gradio-chatbot .bot { background-color: var(--content-background-color) !important; border: 1px solid var(--border-color) !important; }
127
- .footer { text-align: center; margin: 2rem auto; padding: 1rem; color: var(--text-muted-color); border-top: 1px solid var(--border-color); }
128
- .footer b { color: var(--primary-color); }
129
- </style>
130
- </head>
131
  """
132
-
133
- --- PENGATURAN & LOGIC GLOBAL ---
134
-
135
- VISITOR_LOG_FILE = "visitor_log.csv"
136
- HISTORY_LOG_FILE = "generation_log.csv"
137
- IMAGE_HISTORY_DIR = Path("generated_images")
138
-
139
- file_lock = threading.Lock()
140
- device = "cuda" if torch.cuda.is_available() else "cpu"
141
- print(f"➡️ Menggunakan device: {device.upper()}")
142
-
143
- --- Inisialisasi File Log & Direktori ---
144
-
145
- def initialize_environment():
146
- if not os.path.exists(VISITOR_LOG_FILE):
147
- with file_lock:
148
- if not os.path.exists(VISITOR_LOG_FILE):
149
- with open(VISITOR_LOG_FILE, mode='w', newline='', encoding='utf-8') as f:
150
- writer = csv.writer(f)
151
- writer.writerow(["Timestamp", "IP Address", "User Agent"])
152
- print(f"✅ File log '{VISITOR_LOG_FILE}' berhasil dibuat.")
153
-
154
- code
155
- Code
156
- download
157
- content_copy
158
- expand_less
159
- IMAGE_HISTORY_DIR.mkdir(exist_ok=True)
160
- if not os.path.exists(HISTORY_LOG_FILE):
161
- with file_lock:
162
- if not os.path.exists(HISTORY_LOG_FILE):
163
- with open(HISTORY_LOG_FILE, mode='w', newline='', encoding='utf-8') as f:
164
- writer = csv.writer(f)
165
- writer.writerow(["Timestamp", "Filename", "Prompt", "NegativePrompt", "Seed", "Steps"])
166
- print(f" File log riwayat '{HISTORY_LOG_FILE}' dan direktori '{IMAGE_HISTORY_DIR}' siap.")
167
-
168
- initialize_environment()
169
-
170
- --- PEMUATAN MODEL-MODEL AI ---
171
-
172
- print("➡️ Memuat model SDXL-Turbo...")
173
- pipe = DiffusionPipeline.from_pretrained(
174
- "stabilityai/sdxl-turbo", torch_dtype=torch.float16 if device == "cuda" else torch.float32,
175
- variant="fp16" if device == "cuda" else None, use_safetensors=True
176
- ).to(device)
177
- if torch.cuda.is_available(): pipe.enable_xformers_memory_efficient_attention()
178
- print("✅ Model SDXL-Turbo berhasil dimuat.")
179
-
180
- upscaler_model = None
181
- upscaler_processor = None
182
- if Swin2SRForImageSuperResolution:
183
- try:
184
- print("➡️ Memuat model AI Upscaler (Swin2SR)...")
185
- upscaler_model = Swin2SRForImageSuperResolution.from_pretrained("caidas/swin2sr-realworld-sr-x4-64-bsrgan-psnr").to(device)
186
- upscaler_processor = Swin2SRImageProcessor.from_pretrained("caidas/swin2sr-realworld-sr-x4-64-bsrgan-psnr")
187
- print("✅ Model AI Upscaler berhasil dimuat.")
188
- except Exception as e:
189
- print(f"❌ Gagal memuat model Upscaler: {e}. Fitur upscale akan dinonaktifkan.")
190
-
191
- --- KELAS UNTUK CHATBOT GEMINI ---
192
-
193
- class GeminiChat:
194
- def init(self):
195
- self.api_keys = []
196
- self.is_configured = False
197
- if not genai: return
198
-
199
- code
200
- Code
201
- download
202
- content_copy
203
- expand_less
204
- i = 1
205
- while True:
206
- key = os.getenv(f"GEMINI_API_KEY_{i}")
207
- if key:
208
- self.api_keys.append(key)
209
- i += 1
210
- else:
211
- break
212
-
213
- if self.api_keys:
214
- print(f"✅ Berhasil memuat {len(self.api_keys)} API Key Gemini. Sistem rotasi aktif.")
215
- self.is_configured = True
216
- else:
217
- print("❌ PERINGATAN: Tidak ada API Key Gemini yang ditemukan. Fitur AI Chat & Prompt Enhancer tidak akan berfungsi.")
218
-
219
- def chat(self, message, history, system_prompt=None):
220
- if not self.is_configured:
221
- return "Maaf, fitur ini tidak terkonfigurasi karena tidak ada API Key."
222
- try:
223
- selected_key = random.choice(self.api_keys)
224
- genai.configure(api_key=selected_key)
225
- model = genai.GenerativeModel('gemini-2.5-flash')
226
- full_prompt = message
227
- if system_prompt:
228
- full_prompt = f"{system_prompt}\n\nUser query: {message}"
229
- response = model.generate_content(full_prompt)
230
- return response.text
231
- except Exception as e:
232
- print(f"❌ Terjadi error pada API Key Gemini: {e}")
233
- return "Terjadi kesalahan saat menghubungi API AI. Mungkin salah satu API Key tidak valid atau ada masalah jaringan. Silakan coba lagi."
234
-
235
- gemini_bot = GeminiChat()
236
-
237
- --- FUNGSI-FUNGSI INTI (GENERATOR & LAINNYA) ---
238
-
239
- def log_visitor(request: gr.Request):
240
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
241
- ip_address = request.client.host if request else "N/A"
242
- user_agent = request.headers.get("user-agent", "Unknown") if request else "N/A"
243
- with file_lock:
244
- with open(VISITOR_LOG_FILE, mode='a', newline='', encoding='utf-8') as f:
245
- writer = csv.writer(f)
246
- writer.writerow([timestamp, ip_address, user_agent])
247
- print(f"✅ Pengunjung baru tercatat: IP {ip_address}")
248
-
249
- def generate_images(prompt, negative_prompt, steps, seed, num_images):
250
- if not prompt:
251
- raise gr.Error("Prompt tidak boleh kosong!")
252
- if seed == -1:
253
- seed = random.randint(0, 2**32 - 1)
254
- generator = torch.manual_seed(seed)
255
- 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
256
- return images, seed
257
-
258
- def genie_wrapper(prompt, negative_prompt, steps, seed, num_images):
259
- yield gr.update(visible=False), gr.update(visible=True, value="<div class='loader-container'><div class='loader-text'>AI sedang melukis mahakarya Anda...</div><div class='loader-bar-container'><div class='loader-bar'></div></div></div>"), gr.update(interactive=False), gr.update(visible=False)
260
-
261
- code
262
- Code
263
- download
264
- content_copy
265
- expand_less
266
- start_time = time.time()
267
- images, used_seed = generate_images(prompt, negative_prompt, int(steps), int(seed), int(num_images))
268
- end_time = time.time()
269
-
270
- timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
271
- for i, img in enumerate(images):
272
- filename = f"{int(time.time())}_{used_seed}_{i}.png"
273
- filepath = IMAGE_HISTORY_DIR / filename
274
- img.save(filepath)
275
-
276
- with file_lock:
277
- with open(HISTORY_LOG_FILE, mode='a', newline='', encoding='utf-8') as f:
278
- writer = csv.writer(f)
279
- writer.writerow([timestamp_str, filename, prompt, negative_prompt, used_seed, int(steps)])
280
-
281
- generation_time = end_time - start_time
282
- info_text = f"Seed yang digunakan: {used_seed}\nTotal waktu generasi: {generation_time:.2f} detik"
283
- yield gr.update(value=images, visible=True), gr.update(visible=False), gr.update(interactive=True), gr.update(value=info_text, visible=True)
284
-
285
- def submit_report(name, email, message):
286
- if not name or not message:
287
- gr.Warning("Nama dan Pesan tidak boleh kosong!")
288
- return gr.update(visible=False)
289
- report_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
290
- report_content = f"--- Laporan Baru ({report_time}) ---\nNama: {name}\nEmail: {email}\nPesan: {message}\n\n"
291
- with open("reports.log", "a", encoding="utf-8") as f:
292
- f.write(report_content)
293
- print("✅ Laporan baru telah disimpan ke reports.log")
294
- return gr.update(value="✅ Terima kasih! Laporan Anda telah kami terima.", visible=True)
295
-
296
- --- FUNGSI VISITOR MONITOR (DENGAN PERBAIKAN FINAL) ---
297
-
298
- def update_visitor_monitor(time_filter: str):
299
- try:
300
- with file_lock:
301
- if not os.path.exists(VISITOR_LOG_FILE) or os.path.getsize(VISITOR_LOG_FILE) == 0:
302
- return "## 📈 0", pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
303
-
304
- code
305
- Code
306
- download
307
- content_copy
308
- expand_less
309
- # --- PERBAIKAN UTAMA ---
310
- # Secara eksplisit berikan nama kolom untuk menghindari KeyError
311
- # jika file tidak memiliki header.
312
- column_names = ["Timestamp", "IP Address", "User Agent"]
313
- df = pd.read_csv(VISITOR_LOG_FILE, header=None, names=column_names)
314
-
315
- # Jika baris header terbaca sebagai data, buang baris tersebut.
316
- if df.iloc[0]['Timestamp'] == 'Timestamp':
317
- df = df.iloc[1:].reset_index(drop=True)
318
-
319
- # Periksa lagi apakah DataFrame kosong setelah membuang header
320
- if df.empty:
321
- return "## 📈 0", pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
322
-
323
- df['Timestamp'] = pd.to_datetime(df['Timestamp'], errors='coerce')
324
- df.dropna(subset=['Timestamp'], inplace=True)
325
- if df.empty:
326
- return "## 📈 0", pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
327
-
328
- total_overall_visitors = len(df)
329
- total_visitors_formatted = f"## 📈 {total_overall_visitors:,}"
330
- df['Total Pengunjung'] = np.arange(1, len(df) + 1)
331
-
332
- now = datetime.now()
333
-
334
- if time_filter == "1 Minggu Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(weeks=1)]
335
- elif time_filter == "2 Minggu Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(weeks=2)]
336
- elif time_filter == "3 Bulan Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(days=90)]
337
- else: df_plot = df
338
-
339
- if df_plot.empty: return total_visitors_formatted, pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
340
-
341
- return total_visitors_formatted, df_plot
342
- except Exception as e:
343
- error_message = f"Error saat memperbarui monitor: {e}"
344
- print(f"❌ {error_message}")
345
- return f"## ⚠️ Error: {e}", pd.DataFrame({"Error": [error_message]})
346
- --- FUNGSI-FUNGSI FITUR BARU ---
347
-
348
- def enhance_prompt(simple_prompt):
349
- if not simple_prompt:
350
- gr.Warning("Tolong masukkan ide Anda terlebih dahulu.")
351
- return ""
352
- if not gemini_bot.is_configured:
353
- gr.Error("Fitur Prompt Enhancer tidak aktif karena API Key Gemini tidak diatur.")
354
- return "Fitur tidak aktif."
355
-
356
- code
357
- Code
358
- download
359
- content_copy
360
- expand_less
361
- system_instruction = (
362
- "Anda adalah seorang ahli prompt engineering untuk model AI text-to-image seperti Stable Diffusion. "
363
- "Tugas Anda adalah mengubah ide sederhana dari pengguna menjadi prompt yang kaya, deskriptif, dan artistik. "
364
- "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). "
365
- "Hasilkan HANYA prompt-nya saja dalam format teks panjang, tanpa penjelasan atau kalimat pembuka/penutup."
366
- )
367
- yield "🧠 AI sedang meracik prompt ajaib untuk Anda..."
368
- enhanced_prompt = gemini_bot.chat(simple_prompt, [], system_prompt=system_instruction)
369
- yield enhanced_prompt
370
-
371
- def upscale_image(image_to_upscale, clarity_strength):
372
- if image_to_upscale is None:
373
- raise gr.Error("Silakan unggah gambar terlebih dahulu.")
374
- if upscaler_model is None or upscaler_processor is None:
375
- raise gr.Error("Fitur Upscaler tidak aktif karena model gagal dimuat. Periksa log saat startup.")
376
-
377
- code
378
- Code
379
- download
380
- content_copy
381
- expand_less
382
- yield None, "🚀 Memproses peningkatan resolusi 4x oleh AI..."
383
- try:
384
- with torch.no_grad():
385
- inputs = upscaler_processor(image_to_upscale, return_tensors="pt").to(device)
386
- outputs = upscaler_model(**inputs)
387
- output_image = outputs.reconstruction.data.squeeze().float().cpu().clamp_(0, 1).numpy()
388
- output_image = np.moveaxis(output_image, source=0, destination=-1)
389
- output_image = (output_image * 255.0).round().astype(np.uint8)
390
- final_image = Image.fromarray(output_image)
391
-
392
- if clarity_strength > 1.0:
393
- yield final_image, f"✨ Menerapkan peningkatan kejernihan (Strength: {clarity_strength:.2f})..."
394
- enhancer = ImageEnhance.Sharpness(final_image)
395
- final_image = enhancer.enhance(clarity_strength)
396
-
397
- yield final_image, f"✅ Gambar berhasil ditingkatkan! Resolusi akhir: {final_image.width}x{final_image.height}px."
398
- except Exception as e:
399
- print(f"❌ Error saat upscaling: {e}")
400
- yield None, f"⚠️ Terjadi error saat upscaling: {e}"
401
-
402
- def update_system_info():
403
- if not psutil or not platform: return "Informasi sistem tidak tersedia (library psutil tidak ditemukan)."
404
- cpu_percent = psutil.cpu_percent(interval=None)
405
- ram = psutil.virtual_memory()
406
- gpu_info = "Tidak terdeteksi (PyTorch tidak menemukan CUDA)"
407
- if torch.cuda.is_available():
408
- gpu_name = torch.cuda.get_device_name(0)
409
- gpu_mem_used_gb = torch.cuda.memory_allocated(0) / (10243)
410
- gpu_mem_total_gb = torch.cuda.get_device_properties(0).total_memory / (10243)
411
- gpu_info = f"GPU: {gpu_name}\nVRAM Terpakai: {gpu_mem_used_gb:.2f} GB / {gpu_mem_total_gb:.2f} GB"
412
-
413
- code
414
- Code
415
- download
416
- content_copy
417
- expand_less
418
- sys_info = f"**Platform:** `{platform.system()} {platform.release()}`"
419
- return (f"**CPU Terpakai:** `{cpu_percent:.1f}%`\n"
420
- f"**RAM Terpakai:** `{ram.percent:.1f}% ({ram.used / (1024**3):.2f} GB / {ram.total / (1024**3):.2f} GB)`\n"
421
- f"{gpu_info}\n---\n{sys_info}")
422
- --- FUNGSI-FUNGSI UNTUK MENU BARU ---
423
-
424
- def load_history():
425
- try:
426
- with file_lock:
427
- if not os.path.exists(HISTORY_LOG_FILE):
428
- return [], pd.DataFrame(), "### 📂 Riwayat Kosong\nBelum ada gambar yang dihasilkan."
429
- df = pd.read_csv(HISTORY_LOG_FILE)
430
-
431
- code
432
- Code
433
- download
434
- content_copy
435
- expand_less
436
- if df.empty:
437
- return [], df, "### 📂 Riwayat Kosong\nBelum ada gambar yang dihasilkan."
438
-
439
- df_sorted = df.sort_values(by="Timestamp", ascending=False)
440
- image_paths = [str(IMAGE_HISTORY_DIR / fname) for fname in df_sorted['Filename'] if (IMAGE_HISTORY_DIR / fname).exists()]
441
- return image_paths, df_sorted, "### ⓘ Detail Gambar\nKlik pada sebuah gambar untuk melihat detailnya."
442
- except Exception as e:
443
- print(f"❌ Error memuat riwayat: {e}")
444
- return [], pd.DataFrame(), f"### ⚠️ Error\nTidak dapat memuat riwayat: {e}"
445
-
446
- def show_history_details(evt: gr.SelectData, history_df: pd.DataFrame):
447
- if not evt.selected or history_df.empty:
448
- return "### ⓘ Detail Gambar\nKlik pada sebuah gambar untuk melihat detailnya.", gr.update(visible=False)
449
-
450
- code
451
- Code
452
- download
453
- content_copy
454
- expand_less
455
- selected_row = history_df.iloc[evt.index]
456
- details = f"""
457
- **Prompt:** `{selected_row['Prompt']}`
458
- **Negative Prompt:** `{selected_row.get('NegativePrompt', 'N/A')}`
459
- ---
460
- **Seed:** `{selected_row['Seed']}` | **Steps:** `{selected_row['Steps']}`
461
- **File:** `{selected_row['Filename']}` | **Dibuat:** `{selected_row['Timestamp']}`
462
- """
463
- return details, gr.update(visible=True)
464
-
465
- def send_history_to_generator(evt: gr.SelectData, history_df: pd.DataFrame):
466
- if not evt.selected or history_df.empty:
467
- return gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
468
-
469
- code
470
- Code
471
- download
472
- content_copy
473
- expand_less
474
- selected_row = history_df.iloc[evt.index]
475
- return (
476
- selected_row['Prompt'],
477
- selected_row.get('NegativePrompt', ''),
478
- selected_row['Seed'],
479
- selected_row['Steps'],
480
- gr.Tabs(selected=0)
481
- )
482
-
483
- def send_history_to_editor(evt: gr.SelectData, history_df: pd.DataFrame):
484
- if not evt.selected or history_df.empty:
485
- return gr.update(), gr.update()
486
-
487
- code
488
- Code
489
- download
490
- content_copy
491
- expand_less
492
- selected_row = history_df.iloc[evt.index]
493
- image_path = str(IMAGE_HISTORY_DIR / selected_row['Filename'])
494
- return Image.open(image_path), gr.Tabs(selected=5)
495
-
496
- def apply_image_edits(image, brightness, contrast, saturation, sharpness, filter_choice):
497
- if image is None: return None
498
- output_image = image.copy()
499
- if filter_choice == "Grayscale":
500
- output_image = output_image.convert("L").convert("RGB")
501
- elif filter_choice == "Sepia":
502
- pixels = output_image.load()
503
- width, height = output_image.size
504
- for y in range(height):
505
- for x in range(width):
506
- r, g, b = output_image.getpixel((x, y))
507
- tr, tg, tb = int(0.393 * r + 0.769 * g + 0.189 * b), int(0.349 * r + 0.686 * g + 0.168 * b), int(0.272 * r + 0.534 * g + 0.131 * b)
508
- pixels[x, y] = (min(255, tr), min(255, tg), min(255, tb))
509
-
510
- code
511
- Code
512
- download
513
- content_copy
514
- expand_less
515
- enhancer = ImageEnhance.Brightness(output_image); output_image = enhancer.enhance(brightness)
516
- enhancer = ImageEnhance.Contrast(output_image); output_image = enhancer.enhance(contrast)
517
- enhancer = ImageEnhance.Color(output_image); output_image = enhancer.enhance(saturation)
518
- enhancer = ImageEnhance.Sharpness(output_image); output_image = enhancer.enhance(sharpness)
519
- return output_image
520
- --- ANTARMUKA PENGGUNA (GRADIO UI) ---
521
-
522
- with gr.Blocks(theme=gr.themes.Base(), head=HEAD_HTML) as demo:
523
- gr.Markdown("# 🚀 RenXploit's Creative AI Suite 🌌", elem_id="main-title")
524
- gr.Markdown("Sebuah platform lengkap untuk kreativitas Anda, ditenagai oleh AI.", elem_id="main-subtitle")
525
-
526
- code
527
- Code
528
- download
529
- content_copy
530
- expand_less
531
- with gr.Tabs() as tabs:
532
- # --- TAB 1: IMAGE GENERATOR (INTI) ---
533
- with gr.TabItem("🎨 Image Generator", id=0):
534
- with gr.Row(variant='panel', equal_height=False):
535
- with gr.Column(scale=1):
536
- gr.Markdown("### 📝 **Masukan Perintah Anda**")
537
- prompt_input = gr.Textbox(label="Prompt", placeholder="Contoh: Cinematic photo, seekor rubah merah...", lines=3, info="Jadilah sangat spesifik! Atau gunakan Prompt Enhancer.")
538
- negative_prompt_input = gr.Textbox(label="Prompt Negatif", placeholder="Contoh: blurry, low quality, bad hands...", lines=2, info="Hal-hal yang TIDAK Anda inginkan.")
539
- num_images_slider = gr.Slider(minimum=1, maximum=8, value=2, step=1, label="Jumlah Gambar")
540
- generate_btn = gr.Button("✨ Hasilkan Gambar!", variant="primary")
541
- with gr.Accordion("⚙️ Opsi Lanjutan", open=False):
542
- steps_slider = gr.Slider(minimum=1, maximum=5, value=2, step=1, label="Langkah Iterasi (Kualitas vs Kecepatan)")
543
- with gr.Row():
544
- seed_input = gr.Number(label="Seed", value=-1, precision=0, info="Gunakan -1 untuk acak.")
545
- random_seed_btn = gr.Button("🎲 Acak", variant="secondary")
546
- with gr.Column(scale=2):
547
- gr.Markdown("### 🖼️ **Hasil Generasi**")
548
- output_gallery = gr.Gallery(label="Hasil Gambar", show_label=False, elem_id="gallery", columns=2, object_fit="contain", height="auto")
549
- loader_html = gr.HTML(visible=False)
550
- info_box = gr.Textbox(label="Informasi Generasi", visible=False, interactive=False, lines=2)
551
-
552
- # --- TAB 2: CHAT WITH AI (INTI) ---
553
- with gr.TabItem("💬 Chat with AI", id=1):
554
- with gr.Row(variant='panel'):
555
- with gr.Column():
556
- gr.Markdown("### 🤖 **Asisten AI Flood**")
557
- if not gemini_bot.is_configured:
558
- gr.Warning("Fitur Chatbot dinonaktifkan. API Key Gemini tidak terkonfigurasi.")
559
- else:
560
- gr.ChatInterface(
561
- gemini_bot.chat,
562
- chatbot=gr.Chatbot(height=500, label="Flood AI", value=[(None, "Halo! Saya adalah asisten AI dari RenXploit. Ada yang bisa saya bantu?")]),
563
- title=None,
564
- description="Tanyakan apa saja!",
565
- examples=["Apa itu SDXL-Turbo?", "Buatkan saya puisi tentang AI", "Jelaskan konsep lubang hitam dengan sederhana"],
566
- )
567
-
568
- # --- TAB 3: PROMPT ENHANCER ---
569
- with gr.TabItem("✨ Prompt Enhancer", id=2):
570
- with gr.Row(variant='panel'):
571
- with gr.Column():
572
- gr.Markdown("### 🪄 **Ubah Ide Jadi Prompt Ajaib**\nCukup tulis ide sederhana, dan biarkan AI menyempurnakannya menjadi prompt yang detail dan artistik.")
573
- simple_prompt_input = gr.Textbox(label="Ide Sederhana Anda", placeholder="Contoh: seekor astronot di hutan alien", lines=3)
574
- enhance_btn = gr.Button("Buat Prompt Ajaib!", variant="primary")
575
- enhanced_prompt_output = gr.Textbox(label="Prompt yang Disempurnakan", lines=5, interactive=True, show_copy_button=True)
576
- send_to_gen_btn = gr.Button("➡️ Kirim & Pindah ke Generator")
577
-
578
- # --- TAB 4: AI IMAGE UPSCALER ---
579
- with gr.TabItem("🚀 AI Image Upscaler", id=3):
580
- with gr.Row(variant='panel', equal_height=False):
581
- with gr.Column():
582
- gr.Markdown("### **Tingkatkan Resolusi Gambar**\nUnggah gambar untuk meningkatkan kualitas dan ukurannya hingga 4x lipat menggunakan AI.")
583
- image_to_upscale_input = gr.Image(type="pil", label="Unggah Gambar Anda di Sini")
584
- 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.")
585
- upscale_btn = gr.Button("Tingkatkan Resolusi!", variant="primary")
586
- with gr.Column():
587
- gr.Markdown("### **Hasil Peningkatan Resolusi**")
588
- upscaled_image_output = gr.Image(label="Gambar Hasil Upscale", interactive=False, show_download_button=True)
589
- upscale_status_text = gr.Markdown("Status: Menunggu gambar...")
590
-
591
- # --- TAB 5 (BARU): GALERI & RIWAYAT ---
592
- with gr.TabItem("🖼️ Galeri & Riwayat", id=4) as history_tab:
593
- with gr.Row(variant='panel'):
594
- with gr.Column(scale=2):
595
- gr.Markdown("### **Galeri Hasil Generasi Anda**")
596
- history_gallery = gr.Gallery(label="Riwayat Gambar", show_label=False, columns=4, object_fit="contain", height="auto")
597
- history_df_state = gr.State()
598
- with gr.Column(scale=1):
599
- gr.Markdown("### **Detail & Aksi**")
600
- history_details_md = gr.Markdown("### ⓘ Detail Gambar\nKlik pada sebuah gambar untuk melihat detailnya.")
601
- refresh_history_btn = gr.Button("🔄 Segarkan Galeri", variant="secondary")
602
- with gr.Row(visible=False) as history_action_buttons:
603
- history_to_gen_btn = gr.Button("Kirim ke Generator")
604
- history_to_editor_btn = gr.Button("Kirim ke Editor")
605
-
606
- # --- TAB 6 (BARU): IMAGE EDITOR ---
607
- with gr.TabItem("🎨 Image Editor", id=5):
608
- with gr.Row(variant='panel'):
609
- with gr.Column(scale=1):
610
- gr.Markdown("### **Toolkit Pasca-Produksi**")
611
- editor_input_image = gr.Image(type="pil", label="Unggah Gambar atau Kirim dari Riwayat")
612
- with gr.Accordion("Penyesuaian", open=True):
613
- brightness_slider = gr.Slider(minimum=0.5, maximum=1.5, value=1.0, step=0.05, label="Kecerahan")
614
- contrast_slider = gr.Slider(minimum=0.5, maximum=1.5, value=1.0, step=0.05, label="Kontras")
615
- saturation_slider = gr.Slider(minimum=0.0, maximum=2.0, value=1.0, step=0.05, label="Saturasi Warna")
616
- sharpness_slider = gr.Slider(minimum=0.0, maximum=3.0, value=1.0, step=0.1, label="Ketajaman")
617
- with gr.Accordion("Filter Cepat", open=True):
618
- filter_radio = gr.Radio(["None", "Grayscale", "Sepia"], label="Pilih Filter", value="None")
619
- with gr.Column(scale=1):
620
- gr.Markdown("### **Hasil Editing**")
621
- editor_output_image = gr.Image(label="Hasil Akhir", interactive=False, show_download_button=True)
622
-
623
- # --- TAB 7: VISITOR MONITOR ---
624
- with gr.TabItem("📊 Visitor Monitor", id=6):
625
- with gr.Row(variant='panel'):
626
- with gr.Column():
627
- gr.Markdown("### 📈 **Live Visitor Monitor**\nPantau jumlah total pengunjung aplikasi Anda secara real-time.")
628
- with gr.Row():
629
- with gr.Column(scale=3):
630
- visitor_count_display = gr.Markdown("## 📈 Memuat data...")
631
- with gr.Column(scale=2):
632
- time_filter_radio = gr.Radio(["Semua Waktu", "1 Minggu Terakhir", "2 Minggu Terakhir", "3 Bulan Terakhir"], label="Tampilkan data untuk", value="Semua Waktu")
633
- refresh_btn = gr.Button("🔄 Segarkan Manual", variant="secondary")
634
- visitor_plot = gr.LinePlot(x="Timestamp", y="Total Pengunjung", title="Grafik Pertumbuhan Pengunjung", tooltip=['Timestamp', 'Total Pengunjung'], height=500, interactive=True)
635
-
636
- # --- TAB 8: SYSTEM & SETTINGS ---
637
- with gr.TabItem("⚙️ System & Settings", id=7):
638
- with gr.Row(variant='panel'):
639
- with gr.Column():
640
- gr.Markdown("### **Live System Monitor**")
641
- system_info_md = gr.Markdown("Memuat info sistem...")
642
- system_info_trigger_btn = gr.Button("Trigger System Info", visible=False, elem_id="system-info-trigger-btn")
643
- with gr.Column():
644
- gr.Markdown("### **Pengaturan Aplikasi**")
645
- with gr.Accordion("Kualitas Model", open=True):
646
- gr.Radio(["FP16 (Cepat, Kualitas Baik)", "FP32 (Lambat, Kualitas Terbaik)"],
647
- value="FP16 (Cepat, Kualitas Baik)" if device == "cuda" else "FP32 (Lambat, Kualitas Terbaik)",
648
- label="Presisi Model Generator",
649
- interactive=False,
650
- info="Terkunci. Pengaturan ini ditentukan saat aplikasi dimulai berdasarkan ketersediaan GPU Anda.")
651
-
652
- # --- TAB-TAB STATIS ---
653
- with gr.TabItem("💡 Panduan Prompting", id=8):
654
- with gr.Row(variant='panel'): gr.Markdown("""## Cara Menjadi "Art Director" yang Hebat untuk AI...\n (Konten panduan Anda di sini)""")
655
- with gr.TabItem("📖 Blog & Updates", id=9):
656
- with gr.Row(variant='panel'): gr.Markdown("""### Perkembangan Terbaru dari RenXploit's AI Suite
657
-
658
- v2.6 (Pembaruan Terkini): Perbaikan final untuk bug KeyError: 'Timestamp' pada Visitor Monitor, membuatnya lebih tahan banting terhadap format file CSV.
659
-
660
- v2.5: Menambahkan Galeri & Riwayat Generasi dan Image Editor untuk pasca-produksi.
661
-
662
- v2.4: Upscaler di-upgrade dengan kontrol kejernihan, Visitor Monitor diperbaiki, dan UI Settings diperjelas.
663
-
664
- v2.1 - v2.3: Perbaikan bug dan penambahan fitur inti seperti Prompt Enhancer, Upscaler, dan System Monitor.
665
-
666
- v2.0: Perombakan Desain Total.
667
-
668
- Rencana Berikutnya: Menjajaki model generator gambar yang berbeda dan fitur Inpainting/Outpainting. Nantikan!""")
669
- with gr.TabItem("ℹ️ About & Support", id=10):
670
- with gr.Row(variant='panel'):
671
- with gr.Column():
672
- gr.Markdown("### Tentang Proyek dan Dukungan")
673
- with gr.Accordion("Tentang RenXploit's Creative AI Suite", open=True):
674
- gr.Markdown("""
675
- RenXploit's Creative AI Suite adalah proyek pribadi yang dibuat untuk mengeksplorasi kemampuan AI generatif dalam sebuah antarmuka yang mudah digunakan dan profesional.
676
- Aplikasi ini terus dikembangkan dengan fitur-fitur baru untuk memberdayakan kreativitas Anda.
677
-
678
- Jika Anda memiliki masukan, menemukan bug, atau ingin berdiskusi, jangan ragu untuk menghubungi saya melalui website portofolio di: ngoprek.xyz/contact
679
- """)
680
- with gr.Accordion("Laporkan Masalah atau Beri Masukan"):
681
- report_name = gr.Textbox(label="Nama Anda")
682
- report_email = gr.Textbox(label="Email Anda (Opsional)")
683
- report_message = gr.Textbox(label="Pesan Anda", lines=5, placeholder="Jelaskan masalah yang Anda temui atau ide yang Anda miliki...")
684
- report_btn = gr.Button("Kirim Laporan", variant="primary")
685
- report_status = gr.Markdown(visible=False)
686
-
687
- code
688
- Code
689
- download
690
- content_copy
691
- expand_less
692
- gr.Markdown("---\n<div class='footer'><p>Dibuat dengan ❤️ oleh <b>RenXploit</b>.</p></div>", elem_classes="footer")
693
-
694
- # --- PENANGANAN EVENT (EVENT HANDLERS) ---
695
- # 1. Event Inti
696
- random_seed_btn.click(lambda: -1, outputs=seed_input)
697
- 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])
698
- report_btn.click(fn=submit_report, inputs=[report_name, report_email, report_message], outputs=[report_status])
699
-
700
- # 2. Event Visitor Monitor
701
- demo.load(log_visitor, inputs=None, outputs=None)
702
- demo.load(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
703
- refresh_btn.click(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
704
- time_filter_radio.change(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
705
-
706
- # 3. Event Fitur Lainnya
707
- enhance_btn.click(fn=enhance_prompt, inputs=[simple_prompt_input], outputs=[enhanced_prompt_output])
708
- send_to_gen_btn.click(fn=lambda prompt: (prompt, gr.Tabs(selected=0)), inputs=[enhanced_prompt_output], outputs=[prompt_input, tabs])
709
- upscale_btn.click(fn=upscale_image, inputs=[image_to_upscale_input, clarity_slider], outputs=[upscaled_image_output, upscale_status_text])
710
-
711
- system_info_trigger_btn.click(fn=update_system_info, inputs=None, outputs=system_info_md)
712
- demo.load(fn=update_system_info, inputs=None, outputs=system_info_md)
713
-
714
- # 4. Event untuk Fitur BARU (Galeri & Editor)
715
-
716
- # Galeri & Riwayat
717
- history_tab.select(fn=load_history, inputs=None, outputs=[history_gallery, history_df_state, history_details_md])
718
- refresh_history_btn.click(fn=load_history, inputs=None, outputs=[history_gallery, history_df_state, history_details_md])
719
- history_gallery.select(fn=show_history_details, inputs=[history_df_state], outputs=[history_details_md, history_action_buttons])
720
- history_to_gen_btn.click(fn=send_history_to_generator, inputs=[history_df_state], outputs=[prompt_input, negative_prompt_input, seed_input, steps_slider, tabs])
721
- history_to_editor_btn.click(fn=send_history_to_editor, inputs=[history_df_state], outputs=[editor_input_image, tabs])
722
-
723
- # Image Editor
724
- editor_inputs = [editor_input_image, brightness_slider, contrast_slider, saturation_slider, sharpness_slider, filter_radio]
725
- for slider in [brightness_slider, contrast_slider, saturation_slider, sharpness_slider]:
726
- slider.release(fn=apply_image_edits, inputs=editor_inputs, outputs=editor_output_image)
727
- filter_radio.change(fn=apply_image_edits, inputs=editor_inputs, outputs=editor_output_image)
728
- editor_input_image.change(fn=apply_image_edits, inputs=editor_inputs, outputs=editor_output_image)
729
- --- Menjalankan Aplikasi ---
730
-
731
- if name == "main":
732
- demo.launch(debug=True)
 
1
+ Ap='FP32 (Lambat, Kualitas Terbaik)'
2
+ Ao='FP16 (Cepat, Kualitas Baik)'
3
+ An='Semua Waktu'
4
+ Am='contain'
5
+ Al='http://127.0.0.1:7860/'
6
+ Ak='Grayscale'
7
+ Aj='### Detail Gambar\nKlik pada sebuah gambar untuk melihat detailnya.'
8
+ Ai='3 Bulan Terakhir'
9
+ Ah='2 Minggu Terakhir'
10
+ Ag='1 Minggu Terakhir'
11
+ Af='caidas/swin2sr-realworld-sr-x4-64-bsrgan-psnr'
12
+ Ae='User Agent'
13
+ Ad='IP Address'
14
+ Ac=ImportError
15
+ AJ='%Y-%m-%d %H:%M:%S'
16
+ y='secondary'
17
+ x='N/A'
18
+ w='Filename'
19
+ v='cuda'
20
+ m='primary'
21
+ l='Steps'
22
+ k='NegativePrompt'
23
+ j='utf-8'
24
+ i=str
25
+ h=Exception
26
+ g=open
27
+ b='Seed'
28
+ a='Prompt'
29
+ Z=len
30
+ T='history'
31
+ S=1.
32
+ R='Total Pengunjung'
33
+ O='title'
34
+ N=''
35
+ M=int
36
+ G='panel'
37
+ F='Timestamp'
38
+ E=print
39
+ D=True
40
+ C=False
41
+ B=None
42
+ import gradio as A,torch as H,numpy as z
43
+ from diffusers import DiffusionPipeline as Aq
44
+ import random as AK,time as A0,os as K
45
+ from datetime import datetime as n,timedelta as A1
46
+ import csv as o,pandas as I,threading as Ar
47
+ from PIL import Image as AL,ImageEnhance as c
48
  from pathlib import Path
49
+ import uuid,urllib.parse
50
+ try:import psutil as p,platform as q;from transformers import Swin2SRForImageSuperResolution as A2,Swin2SRImageProcessor as AM;E('✅ Library tambahan (psutil, transformers) berhasil diimpor.')
51
+ except Ac:E("❌ Peringatan: Library 'psutil' atau 'transformers' tidak ditemukan. Fitur System Monitor & Upscaler tidak akan berfungsi.");p=B;q=B;A2=B;AM=B
52
+ try:import google.generativeai as r;E("✅ Library 'google-generativeai' berhasil diimpor.")
53
+ except Ac:E("❌ Peringatan: Library 'google-generativeai' tidak ditemukan. Fitur Chatbot & Prompt Enhancer tidak akan berfungsi.");r=B
54
+ As='\n<head>\n <meta charset="UTF-8">\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <title>RenXploit\'s Creative AI Suite</title>\n <link rel="preconnect" href="https://fonts.googleapis.com">\n <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>\n <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code:wght@500&display=swap" rel="stylesheet">\n <script>\n function typewriterEffect(element, text, speed) {\n let i = 0;\n element.innerHTML = ""; \n const cursor = document.createElement(\'span\');\n cursor.className = \'typewriter-cursor\';\n cursor.innerHTML = \'|\';\n element.appendChild(cursor);\n function type() {\n if (i < text.length) {\n element.insertBefore(document.createTextNode(text.charAt(i)), cursor);\n i++;\n setTimeout(type, speed);\n } else {\n cursor.style.animation = \'blink 1s step-end infinite\';\n }\n }\n type();\n }\n\n document.addEventListener("DOMContentLoaded", function(event) {\n const titleElement = document.querySelector(\'#main-title h1\');\n if (titleElement) {\n const titleText = "🚀 RenXploit\'s Creative AI Suite 🌌";\n titleElement.textContent = "";\n setTimeout(() => typewriterEffect(titleElement, titleText, 50), 500);\n }\n \n setInterval(() => {\n const triggerButton = document.querySelector(\'#system-info-trigger-btn button\');\n if (triggerButton) {\n triggerButton.click();\n }\n }, 5000);\n });\n </script>\n <style>\n :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); }\n body, .gradio-container { font-family: var(--font-family-main); background-color: var(--background-color) !important; color: var(--text-color); }\n h1, h2, h3 { color: #ffffff; font-weight: 600; }\n #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; }\n #main-title .typewriter-cursor { color: var(--primary-color); font-weight: bold; }\n @keyframes blink { from, to { opacity: 1 } 50% { opacity: 0 } }\n #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; }\n .gradio-tabs { border: none !important; background-color: transparent !important; margin-bottom: 1.5rem; }\n .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; }\n .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; }\n .gradio-tabs > div[role="tablist"] > button:hover { background-color: var(--content-background-color) !important; color: var(--primary-color) !important; }\n .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; }\n .tabitem { background-color: transparent !important; border: none !important; padding: 0 !important; }\n .gradio-row.panel, .gradio-accordion, .gradio-group { 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); }\n .gradio-accordion { margin-top: 1rem; }\n .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); }\n .gradio-button.primary:hover { transform: translateY(-2px); box-shadow: var(--shadow-strong); }\n .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; }\n .gradio-button.secondary:hover { background-color: var(--primary-color) !important; color: #ffffff !important; }\n .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; }\n .gradio-textbox textarea, .gradio-number input { color: var(--text-color) !important; }\n .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); }\n div.info { color: var(--text-muted-color) !important; font-style: italic; }\n #gallery, #history_gallery { border-radius: var(--border-radius) !important; overflow: hidden; border: 1px solid var(--border-color); background-color: #010409; }\n #gallery .gallery-item, #history_gallery .gallery-item { transition: transform 0.3s ease, box-shadow 0.3s ease; }\n #gallery .gallery-item:hover, #history_gallery .gallery-item:hover { transform: scale(1.03); box-shadow: 0 8px 30px rgba(0, 170, 255, 0.3); }\n .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; }\n .loader-text { font-family: var(--font-family-mono); font-size: 1.1em; color: var(--primary-color); margin-bottom: 20px; }\n .loader-bar-container { height: 8px; background: var(--background-color); border-radius: 4px; overflow: hidden; }\n .loader-bar { width: 100%; height: 100%; background: linear-gradient(90deg, transparent, var(--primary-color), transparent); background-size: 200% 100%; animation: scan 2s linear infinite; }\n @keyframes scan { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }\n #chatbot-container { display: flex; flex-direction: column; height: 70vh; max-height: 750px; }\n #chatbot-display { flex-grow: 1; overflow-y: auto; padding: 10px; border-bottom: 1px solid var(--border-color); }\n #chatbot-display .message { border-radius: 18px !important; padding: 12px 18px !important; box-shadow: none !important; max-width: 80%; }\n #chatbot-display .user { background-color: var(--primary-color) !important; color: white !important; align-self: flex-end; border-bottom-right-radius: 5px !important; }\n #chatbot-display .bot { background-color: var(--content-background-color) !important; border: 1px solid var(--border-color) !important; align-self: flex-start; border-bottom-left-radius: 5px !important; }\n #chat-history-sidebar { background-color: transparent !important; border: none !important; padding: 0 !important; box-shadow: none !important; }\n #chat-history-sidebar .gradio-radio > div { display: flex; flex-direction: column; gap: 8px; }\n #chat-history-sidebar .gradio-radio label { width: 100%; text-align: left; padding: 10px; border: 1px solid var(--border-color); border-radius: 8px; transition: all 0.2s ease; }\n #chat-history-sidebar .gradio-radio input:checked + label { border-color: var(--primary-color); background-color: rgba(0, 170, 255, 0.1); color: var(--primary-color); font-weight: 600; }\n .footer { text-align: center; margin: 2rem auto; padding: 1rem; color: var(--text-muted-color); border-top: 1px solid var(--border-color); }\n .footer b { color: var(--primary-color); }\n </style>\n</head>\n'
55
+ L='visitor_log.csv'
56
+ P='generation_log.csv'
57
+ U=Path('generated_images')
58
+ V=Ar.Lock()
59
+ Q=v if H.cuda.is_available()else'cpu'
60
+ E(f"➡️ Menggunakan device: {Q.upper()}")
61
+ def At():
62
+ if not K.path.exists(L):
63
+ with V:
64
+ if not K.path.exists(L):
65
+ with g(L,mode='w',newline=N,encoding=j)as A:B=o.writer(A);B.writerow([F,Ad,Ae])
66
+ E(f"✅ File log '{L}' berhasil dibuat.")
67
+ U.mkdir(exist_ok=D)
68
+ if not K.path.exists(P):
69
+ with V:
70
+ if not K.path.exists(P):
71
+ with g(P,mode='w',newline=N,encoding=j)as A:B=o.writer(A);B.writerow([F,w,a,k,b,l])
72
+ E(f"✅ File log riwayat '{P}' dan direktori '{U}' siap.")
73
+ At()
74
+ E('➡️ Memuat model SDXL-Turbo...')
75
+ AN=Aq.from_pretrained('stabilityai/sdxl-turbo',torch_dtype=H.float16 if Q==v else H.float32,variant='fp16'if Q==v else B,use_safetensors=D).to(Q)
76
+ if H.cuda.is_available():AN.enable_xformers_memory_efficient_attention()
77
+ E('✅ Model SDXL-Turbo berhasil dimuat.')
78
+ A3=B
79
+ A4=B
80
+ if A2:
81
+ try:E('➡️ Memuat model AI Upscaler (Swin2SR)...');A3=A2.from_pretrained(Af).to(Q);A4=AM.from_pretrained(Af);E('✅ Model AI Upscaler berhasil dimuat.')
82
+ except h as Au:E(f"❌ Gagal memuat model Upscaler: {Au}. Fitur upscale akan dinaktifkan.")
83
+ class Av:
84
+ def __init__(A):
85
+ A.api_keys=[];A.is_configured=C
86
+ if not r:return
87
+ B=1
88
+ while D:
89
+ F=K.getenv(f"GEMINI_API_KEY_{B}")
90
+ if F:A.api_keys.append(F);B+=1
91
+ else:break
92
+ if A.api_keys:E(f"✅ Berhasil memuat {Z(A.api_keys)} API Key Gemini. Sistem rotasi aktif.");A.is_configured=D
93
+ else:E('❌ PERINGATAN: Tidak ada API Key Gemini yang ditemukan. Fitur AI Chat & Prompt Enhancer tidak akan berfungsi.')
94
+ def chat(B,message,history,system_prompt=B):
95
+ K='parts';J='role';F=system_prompt;D=history;C=message
96
+ if not B.is_configured:return'Maaf, fitur ini tidak terkonfigurasi karena tidak ada API Key.'
97
+ try:
98
+ L=AK.choice(B.api_keys);r.configure(api_key=L);M=r.GenerativeModel('gemini-2.5-flash');A=[]
99
+ for(G,H)in D:
100
+ if G:A.append({J:'user',K:[G]})
101
+ if H:A.append({J:'model',K:[H]})
102
+ N=M.start_chat(history=A);I=C
103
+ if F and not D:I=f"{F}\n\nUser query: {C}"
104
+ O=N.send_message(I);return O.text
105
+ except h as P:E(f"❌ Terjadi error pada API Key Gemini: {P}");return'Terjadi kesalahan saat menghubungi API AI. Mungkin salah satu API Key tidak valid atau ada masalah jaringan. Silakan coba lagi.'
106
+ s=Av()
107
+ def Aw(request):
108
+ A=request;C=n.now().strftime(AJ);B=A.client.host if A else x;D=A.headers.get('user-agent','Unknown')if A else x
109
+ with V:
110
+ with g(L,mode='a',newline=N,encoding=j)as F:G=o.writer(F);G.writerow([C,B,D])
111
+ E(f"✅ Pengunjung baru tercatat: IP {B}")
112
+ def Ax(prompt,negative_prompt,steps,seed,num_images):
113
+ C=prompt;B=seed
114
+ if not C:raise A.Error('Prompt tidak boleh kosong!')
115
+ if B==-1:B=AK.randint(0,2**32-1)
116
+ D=H.manual_seed(B);E=AN(prompt=C,negative_prompt=negative_prompt,generator=D,num_inference_steps=steps,guidance_scale=.0,num_images_per_prompt=num_images).images;return E,B
117
+ def Ay(prompt,negative_prompt,steps,seed,num_images):
118
+ G=steps;F=negative_prompt;E=prompt;yield(A.update(visible=C),A.update(visible=D,value="<div class='loader-container'><div class='loader-text'>AI sedang melukis mahakarya Anda...</div><div class='loader-bar-container'><div class='loader-bar'></div></div></div>"),A.update(interactive=C),A.update(visible=C));J=A0.time();H,B=Ax(E,F,M(G),M(seed),M(num_images));K=A0.time();L=n.now().strftime(AJ)
119
+ for(O,Q)in enumerate(H):
120
+ I=f"{M(A0.time())}_{B}_{O}.png";R=U/I;Q.save(R)
121
+ with V:
122
+ with g(P,mode='a',newline=N,encoding=j)as S:T=o.writer(S);T.writerow([L,I,E,F,B,M(G)])
123
+ W=K-J;X=f"Seed yang digunakan: {B}\nTotal waktu generasi: {W:.2f} detik";yield(A.update(value=H,visible=D),A.update(visible=C),A.update(interactive=D),A.update(value=X,visible=D))
124
+ def Az(name,email,message):
125
+ B=message
126
+ if not name or not B:A.Warning('Nama dan Pesan tidak boleh kosong!');return A.update(visible=C)
127
+ F=n.now().strftime(AJ);G=f"""--- Laporan Baru ({F}) ---
128
+ Nama: {name}
129
+ Email: {email}
130
+ Pesan: {B}
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  """
133
+ with g('reports.log','a',encoding=j)as H:H.write(G)
134
+ E('✅ Laporan baru telah disimpan ke reports.log');return A.update(value='✅ Terima kasih! Laporan Anda telah kami terima.',visible=D)
135
+ def A5(time_filter):
136
+ J='## 📈 0';G=time_filter
137
+ try:
138
+ with V:
139
+ if not K.path.exists(L)or K.path.getsize(L)==0:return J,I.DataFrame({F:[],R:[]})
140
+ P=[F,Ad,Ae];A=I.read_csv(L,header=B,names=P)
141
+ if not A.empty and A.iloc[0][F]==F:A=A.iloc[1:].reset_index(drop=D)
142
+ if A.empty:return J,I.DataFrame({F:[],R:[]})
143
+ A[F]=I.to_datetime(A[F],errors='coerce');A.dropna(subset=[F],inplace=D)
144
+ if A.empty:return J,I.DataFrame({F:[],R:[]})
145
+ Q=Z(A);M=f"## 📈 {Q:,}";A[R]=z.arange(1,Z(A)+1);H=n.now()
146
+ if G==Ag:C=A[A[F]>=H-A1(weeks=1)]
147
+ elif G==Ah:C=A[A[F]>=H-A1(weeks=2)]
148
+ elif G==Ai:C=A[A[F]>=H-A1(days=90)]
149
+ else:C=A
150
+ if C.empty:return M,I.DataFrame({F:[],R:[]})
151
+ return M,C
152
+ except h as N:O=f"Error saat memperbarui monitor: {N}";E(f"❌ {O}");return f"## ⚠️ Error: {N}",I.DataFrame({'Error':[O]})
153
+ def A_(simple_prompt):
154
+ B=simple_prompt
155
+ if not B:A.Warning('Tolong masukkan ide Anda terlebih dahulu.');return N
156
+ if not s.is_configured:A.Error('Fitur Prompt Enhancer tidak aktif karena API Key Gemini tidak diatur.');return'Fitur tidak aktif.'
157
+ C='Anda adalah seorang ahli prompt engineering untuk model AI text-to-image seperti Stable Diffusion. Tugas Anda adalah mengubah ide sederhana dari pengguna menjadi prompt yang kaya, deskriptif, dan artistik. Fokus pada detail visual: subjek, setting, pencahayaan, gaya seni, komposisi, dan kualitas. Hasilkan HANYA prompt-nya saja dalam format teks panjang, tanpa penjelasan atau kalimat pembuka/penutup.';yield'🧠 AI sedang meracik prompt ajaib untuk Anda...';D=s.chat(B,[],system_prompt=C);yield D
158
+ def B0(image_to_upscale,clarity_strength):
159
+ G=image_to_upscale;F=clarity_strength
160
+ if G is B:raise A.Error('Silakan unggah gambar terlebih dahulu.')
161
+ if A3 is B or A4 is B:raise A.Error('Fitur Upscaler tidak aktif karena model gagal dimuat.')
162
+ yield(B,'🚀 Memproses peningkatan resolusi 4x oleh AI...')
163
+ try:
164
+ with H.no_grad():J=A4(G,return_tensors='pt').to(Q);K=A3(**J);D=K.reconstruction.data.squeeze().float().cpu().clamp_(0,1).numpy();D=z.moveaxis(D,source=0,destination=-1);D=(D*255.).round().astype(z.uint8);C=AL.fromarray(D)
165
+ if F>S:yield(C,f"✨ Menerapkan peningkatan kejernihan (Strength: {F:.2f})...");L=c.Sharpness(C);C=L.enhance(F)
166
+ yield(C,f" Gambar berhasil ditingkatkan! Resolusi akhir: {C.width}x{C.height}px.")
167
+ except h as I:E(f" Error saat upscaling: {I}");yield(B,f"⚠️ Terjadi error saat upscaling: {I}")
168
+ def AO():
169
+ if not p or not q:return'Informasi sistem tidak tersedia (library psutil tidak ditemukan).'
170
+ D=p.cpu_percent(interval=B);A=p.virtual_memory();C='Tidak terdeteksi (PyTorch tidak menemukan CUDA)'
171
+ if H.cuda.is_available():E=H.cuda.get_device_name(0);F=H.cuda.memory_allocated(0)/1024**3;G=H.cuda.get_device_properties(0).total_memory/1024**3;C=f"GPU: {E}\nVRAM Terpakai: {F:.2f} GB / {G:.2f} GB"
172
+ I=f"**Platform:** `{q.system()} {q.release()}`";return f"**CPU Terpakai:** `{D:.1f}%`\n**RAM Terpakai:** `{A.percent:.1f}% ({A.used/1024**3:.2f} GB / {A.total/1024**3:.2f} GB)`\n{C}\n---\n{I}"
173
+ def AP():
174
+ G='### 📂 Riwayat Kosong\nBelum ada gambar yang dihasilkan.'
175
+ try:
176
+ with V:
177
+ if not K.path.exists(P):return[],I.DataFrame(),G
178
+ A=I.read_csv(P)
179
+ if A.empty:return[],A,G
180
+ B=A.sort_values(by=F,ascending=C);H=[i(U/A)for A in B[w]if(U/A).exists()];return H,B,Aj
181
+ except h as D:E(f"❌ Error memuat riwayat: {D}");return[],I.DataFrame(),f"### ⚠️ Error\nTidak dapat memuat riwayat: {D}"
182
+ def B1(evt,history_df):
183
+ G=history_df
184
+ if not evt.selected or G.empty:return'### ⓘ Detail Gambar\nKlik gambar untuk melihat detailnya.',A.update(visible=C),A.update(visible=C),B
185
+ H=evt.index;E=G.iloc[H];I=f"""
186
+ **Prompt:** `{E[a]}`
187
+ **Negative Prompt:** `{E.get(k,x)}`
188
+ ---
189
+ **Seed:** `{E[b]}` | **Steps:** `{E[l]}`
190
+ **File:** `{E[w]}` | **Dibuat:** `{E[F]}`
191
+ """;return I,A.update(visible=D),A.update(visible=C),H
192
+ def B2(selected_index,history_df):
193
+ E=history_df;D=selected_index
194
+ if D is B or E.empty:return A.update(),A.update(),A.update(),A.update(),A.update()
195
+ C=E.iloc[D];return C[a],C.get(k,N),C[b],C[l],A.Tabs(selected=0)
196
+ def B3(selected_index,history_df):
197
+ D=history_df;C=selected_index
198
+ if C is B or D.empty:return A.update(),A.update()
199
+ E=D.iloc[C];F=i(U/E[w]);return AL.open(F),A.Tabs(selected=5)
200
+ def A6(image,brightness,contrast,saturation,sharpness,filter_choice):
201
+ H=filter_choice;G=image
202
+ if G is B:return
203
+ A=G.copy()
204
+ if H==Ak:A=A.convert('L').convert('RGB')
205
+ elif H=='Sepia':
206
+ K=A.load();L,N=A.size
207
+ for I in range(N):
208
+ for J in range(L):D,E,F=A.getpixel((J,I));O,P,Q=M(.393*D+.769*E+.189*F),M(.349*D+.686*E+.168*F),M(.272*D+.534*E+.131*F);K[J,I]=min(255,O),min(255,P),min(255,Q)
209
+ C=c.Brightness(A);A=C.enhance(brightness);C=c.Contrast(A);A=C.enhance(contrast);C=c.Color(A);A=C.enhance(saturation);C=c.Sharpness(A);A=C.enhance(sharpness);return A
210
+ def B4(selected_index,history_df,request):
211
+ H=history_df;G=selected_index;F=request
212
+ if G is B or H.empty:A.Warning('Pilih gambar terlebih dahulu sebelum membagikan.');return A.update(visible=C)
213
+ E=H.iloc[G];I=i(F.url).split('?')[0]if F and hasattr(F,'url')else Al;J={'prompt':E[a],'neg':E.get(k,N),'seed':E[b],'steps':E[l]};K=urllib.parse.urlencode(J);L=f"{I}?__theme=dark&{K}";M=f"""
214
+ **URL untuk Berbagi & Isi Ulang Generator:**
215
+ `{L}`
216
+ ---
217
+ **Detail untuk Disalin:**
218
+ - **Prompt:** {E[a]}
219
+ - **Negative:** {E.get(k,x)}
220
+ - **Seed:** {E[b]}
221
+ - **Steps:** {E[l]}
222
+ """;return A.update(value=M,visible=D)
223
+ def B5(chat_id,all_chats_history,request):
224
+ G=request;F=all_chats_history;B=chat_id
225
+ if not B or B not in F:return A.update(visible=C)
226
+ H=F[B][T]
227
+ if not H:return A.update(visible=C)
228
+ I=i(G.url).split('?')[0]if G and hasattr(G,'url')else Al;L=F[B][O];M=f"{I}chat/{B}?__theme=dark";E=f"### Riwayat Chat: {L}\n";E+=f"**URL Sesi:** `{I}`\n---\n"
229
+ for(J,K)in H:
230
+ if J:E+=f"\n**👤 Anda:**\n{J}\n"
231
+ if K:E+=f"\n**🤖 AI:**\n{K}\n"
232
+ return A.update(value=E,visible=D)
233
+ def A7(all_chats_history):C=all_chats_history;D=i(uuid.uuid4());C[D]={O:'Percakapan Baru',T:[(B,'Halo! Saya Flood, asisten AI dari RenXploit. Ada yang bisa saya bantu?')]};E=[A[O]for A in C.values()];return C,D,C[D][T],A.update(choices=E,value=C[D][O])
234
+ def AQ(user_message,chat_id,all_chats_history):
235
+ H=user_message;G=chat_id;F=all_chats_history
236
+ if not G or G not in F:F,G,E,L=A7({})
237
+ E=F[G][T]
238
+ if Z(E)==1 and E[0][0]is B:E=[]
239
+ E.append((H,B));F[G][T]=E
240
+ if Z(E)==1:I=H[:30]+'...'if Z(H)>30 else H;F[G][O]=I
241
+ J=[A[O]for A in F.values()];yield(F,E,A.update(choices=J,value=F[G][O]),A.update(value=N),A.update(visible=C));K=s.chat(H,[A for A in E if A[0]is not B]);E[-1]=H,K;F[G][T]=E;yield(F,E,A.update(),A.update(),A.update(visible=D))
242
+ def B6(selected_title,all_chats_history):
243
+ G=all_chats_history;F=selected_title
244
+ if not F:return B,[],A.update(visible=C)
245
+ E=B
246
+ for(H,I)in G.items():
247
+ if I[O]==F:E=H;break
248
+ if E:return E,G[E][T],A.update(visible=D)
249
+ return B,[],A.update(visible=C)
250
+ with A.Blocks(theme=A.themes.Base(),head=As)as d:
251
+ A.Markdown("# 🚀 RenXploit's Creative AI Suite 🌌",elem_id='main-title');A.Markdown('Sebuah platform lengkap untuk kreativitas Anda, ditenagai oleh AI.',elem_id='main-subtitle');J=A.State({});W=A.State(B);t=A.State(B)
252
+ with A.Tabs()as A8:
253
+ with A.TabItem('🎨 Image Generator',id=0):
254
+ with A.Row(variant=G,equal_height=C):
255
+ with A.Column(scale=1):
256
+ A.Markdown('### 📝 **Masukan Perintah Anda**');A9=A.Textbox(label=a,placeholder='Contoh: Cinematic photo, seekor rubah merah...',lines=3,info='Jadilah sangat spesifik! Atau gunakan Prompt Enhancer.');AR=A.Textbox(label='Prompt Negatif',placeholder='Contoh: blurry, low quality, bad hands...',lines=2,info='Hal-hal yang TIDAK Anda inginkan.');B7=A.Slider(minimum=1,maximum=8,value=2,step=1,label='Jumlah Gambar');AS=A.Button('✨ Hasilkan Gambar!',variant=m)
257
+ with A.Accordion('⚙️ Opsi Lanjutan',open=C):
258
+ AT=A.Slider(minimum=1,maximum=5,value=2,step=1,label='Langkah Iterasi (Kualitas vs Kecepatan)')
259
+ with A.Row():AA=A.Number(label=b,value=-1,precision=0,info='Gunakan -1 untuk acak.');B8=A.Button('🎲 Acak',variant=y)
260
+ with A.Column(scale=2):A.Markdown('### 🖼️ **Hasil Generasi**');B9=A.Gallery(label='Hasil Gambar',show_label=C,elem_id='gallery',columns=2,object_fit=Am,height='auto');BA=A.HTML(visible=C);BB=A.Textbox(label='Informasi Generasi',visible=C,interactive=C,lines=2)
261
+ with A.TabItem('💬 Chat with AI',id=1):
262
+ with A.Row(variant=G):
263
+ with A.Column(scale=1,min_width=250)as Bc:
264
+ A.Markdown('### 🗂️ Riwayat Chat');BC=A.Button('➕ Obrolan Baru',variant=m)
265
+ with A.Group(elem_id='chat-history-sidebar'):X=A.Radio(label='Pilih Obrolan',choices=[],interactive=D)
266
+ with A.Row(visible=C)as AB:BD=A.Button('🔗 Bagikan Chat')
267
+ BE=A.Textbox(label='Salin Konten Chat',lines=10,interactive=D,show_copy_button=D,visible=C)
268
+ with A.Column(scale=3):
269
+ A.Markdown('### 🤖 **Asisten AI Flood**')
270
+ if not s.is_configured:A.Warning('Fitur Chatbot dinonaktifkan. API Key Gemini tidak terkonfigurasi.')
271
+ else:
272
+ with A.Column(elem_id='chatbot-container'):
273
+ e=A.Chatbot(elem_id='chatbot-display',label='Flood AI',bubble_full_width=C)
274
+ with A.Row():f=A.Textbox(show_label=C,placeholder='Ketik pesan Anda di sini...',scale=5);BF=A.Button('Kirim',variant=y,scale=1)
275
+ with A.TabItem('✨ Prompt Enhancer',id=2):
276
+ with A.Row(variant=G):
277
+ with A.Column():A.Markdown('### 🪄 **Ubah Ide Jadi Prompt Ajaib**\nCukup tulis ide sederhana, dan biarkan AI menyempurnakannya menjadi prompt yang detail dan artistik.');BG=A.Textbox(label='Ide Sederhana Anda',placeholder='Contoh: seekor astronot di hutan alien',lines=3);BH=A.Button('Buat Prompt Ajaib!',variant=m);AU=A.Textbox(label='Prompt yang Disempurnakan',lines=5,interactive=D,show_copy_button=D);BI=A.Button('➡️ Kirim & Pindah ke Generator')
278
+ with A.TabItem('🚀 AI Image Upscaler',id=3):
279
+ with A.Row(variant=G,equal_height=C):
280
+ with A.Column():A.Markdown('### **Tingkatkan Resolusi Gambar**\nUnggah gambar untuk meningkatkan kualitas dan ukurannya hingga 4x lipat menggunakan AI.');BJ=A.Image(type='pil',label='Unggah Gambar Anda di Sini');BK=A.Slider(minimum=S,maximum=3.,value=S,step=.1,label='Tingkat Peningkatan Kejernihan',info='Setelah di-upscale 4x, atur kejernihan gambar di sini. 1.0 = Tanpa efek.');BL=A.Button('Tingkatkan Resolusi!',variant=m)
281
+ with A.Column():A.Markdown('### **Hasil Peningkatan Resolusi**');BM=A.Image(label='Gambar Hasil Upscale',interactive=C,show_download_button=D);BN=A.Markdown('Status: Menunggu gambar...')
282
+ with A.TabItem('🖼️ Galeri & Riwayat',id=4)as BO:
283
+ with A.Row(variant=G):
284
+ with A.Column(scale=2):A.Markdown('### **Galeri Hasil Generasi Anda**');AC=A.Gallery(label='Riwayat Gambar',show_label=C,columns=4,object_fit=Am,height='auto',elem_id='history_gallery');Y=A.State()
285
+ with A.Column(scale=1):
286
+ A.Markdown('### **Detail & Aksi**');AD=A.Markdown(Aj);BP=A.Button('🔄 Segarkan Galeri',variant=y)
287
+ with A.Row(visible=C)as BQ:BR=A.Button('Kirim ke Generator');BS=A.Button('Kirim ke Editor');BT=A.Button('🔗 Bagikan Detail')
288
+ AV=A.Textbox(label='URL & Detail untuk Dibagikan',lines=8,interactive=D,show_copy_button=D,visible=C)
289
+ with A.TabItem('🎨 Image Editor',id=5):
290
+ with A.Row(variant=G):
291
+ with A.Column(scale=1):
292
+ A.Markdown('### **Toolkit Pasca-Produksi**');AE=A.Image(type='pil',label='Unggah Gambar atau Kirim dari Riwayat')
293
+ with A.Accordion('Penyesuaian',open=D):AW=A.Slider(minimum=.5,maximum=1.5,value=S,step=.05,label='Kecerahan');AX=A.Slider(minimum=.5,maximum=1.5,value=S,step=.05,label='Kontras');AY=A.Slider(minimum=.0,maximum=2.,value=S,step=.05,label='Saturasi Warna');AZ=A.Slider(minimum=.0,maximum=3.,value=S,step=.1,label='Ketajaman')
294
+ with A.Accordion('Filter Cepat',open=D):Aa=A.Radio(['None',Ak,'Sepia'],label='Pilih Filter',value='None')
295
+ with A.Column(scale=1):A.Markdown('### **Hasil Editing**');AF=A.Image(label='Hasil Akhir',interactive=C,show_download_button=D)
296
+ with A.TabItem('📊 Visitor Monitor',id=6):
297
+ with A.Row(variant=G):
298
+ with A.Column():
299
+ A.Markdown('### 📈 **Live Visitor Monitor**\nPantau jumlah total pengunjung aplikasi Anda secara real-time.')
300
+ with A.Row():
301
+ with A.Column(scale=3):AG=A.Markdown('## 📈 Memuat data...')
302
+ with A.Column(scale=2):u=A.Radio([An,Ag,Ah,Ai],label='Tampilkan data untuk',value=An);BU=A.Button('🔄 Segarkan Manual',variant=y)
303
+ AH=A.LinePlot(x=F,y=R,title='Grafik Pertumbuhan Pengunjung',tooltip=[F,R],height=500,interactive=D)
304
+ with A.TabItem('⚙️ System & Settings',id=7):
305
+ with A.Row(variant=G):
306
+ with A.Column():A.Markdown('### **Live System Monitor**');Ab=A.Markdown('Memuat info sistem...');BV=A.Button('Trigger System Info',visible=C,elem_id='system-info-trigger-btn')
307
+ with A.Column():
308
+ A.Markdown('### **Pengaturan Aplikasi**')
309
+ with A.Accordion('Kualitas Model',open=D):A.Radio([Ao,Ap],value=Ao if Q==v else Ap,label='Presisi Model Generator',interactive=C,info='Terkunci. Ditentukan saat aplikasi dimulai.')
310
+ with A.TabItem('💡 Panduan Prompting',id=8):
311
+ with A.Row(variant=G):A.Markdown('## Cara Menjadi "Art Director" yang Hebat untuk AI...\n (Konten panduan Anda di sini)')
312
+ with A.TabItem('📖 Blog & Updates',id=9):
313
+ with A.Row(variant=G):A.Markdown("### Perkembangan Terbaru dari RenXploit's AI Suite\nv3.0 (Perbaikan Kritis): Memperbaiki bug pada fitur Share URL dan Share Detail Galeri yang disebabkan oleh perbedaan versi Gradio dan logika event yang salah.\nv2.8: Mengganti komponen `gr.Box` dengan `gr.Group` untuk kompatibilitas.\nv2.7: Perombakan total UI Chatbot dengan fitur riwayat, new chat, dan share.\nRencana Berikutnya: Menjajaki model generator gambar yang berbeda dan fitur Inpainting/Outpainting.")
314
+ with A.TabItem('ℹ️ About & Support',id=10):
315
+ with A.Row(variant=G):
316
+ with A.Column():
317
+ A.Markdown('### Tentang Proyek dan Dukungan')
318
+ with A.Accordion("Tentang RenXploit's Creative AI Suite",open=D):A.Markdown("\n RenXploit's Creative AI Suite adalah proyek pribadi untuk mengeksplorasi AI generatif.\n Hubungi saya melalui: ngoprek.xyz/contact\n ")
319
+ with A.Accordion('Laporkan Masalah atau Beri Masukan'):BW=A.Textbox(label='Nama Anda');BX=A.Textbox(label='Email Anda (Opsional)');BY=A.Textbox(label='Pesan Anda',lines=5,placeholder='Jelaskan masalah atau ide Anda...');BZ=A.Button('Kirim Laporan',variant=m);Ba=A.Markdown(visible=C)
320
+ A.Markdown("---\n<div class='footer'><p>Dibuat dengan ❤️ oleh <b>RenXploit</b>.</p></div>",elem_classes='footer');d.load(Aw,inputs=B,outputs=B);d.load(fn=A7,inputs=[J],outputs=[J,W,e,X]);B8.click(lambda:-1,outputs=AA);AS.click(fn=Ay,inputs=[A9,AR,AT,AA,B7],outputs=[B9,BA,AS,BB]);BC.click(fn=A7,inputs=[J],outputs=[J,W,e,X]);BF.click(fn=AQ,inputs=[f,W,J],outputs=[J,e,X,f,AB]);f.submit(fn=AQ,inputs=[f,W,J],outputs=[J,e,X,f,AB]);X.change(fn=B6,inputs=[X,J],outputs=[W,e,AB]);BD.click(fn=B5,inputs=[W,J],outputs=[BE]);BH.click(fn=A_,inputs=[BG],outputs=[AU]);BI.click(fn=lambda prompt:(prompt,A.Tabs(selected=0)),inputs=[AU],outputs=[A9,A8]);BL.click(fn=B0,inputs=[BJ,BK],outputs=[BM,BN]);BO.select(fn=AP,inputs=B,outputs=[AC,Y,AD]);BP.click(fn=AP,inputs=B,outputs=[AC,Y,AD]);AC.select(fn=B1,inputs=[Y],outputs=[AD,BQ,AV,t]);BR.click(fn=B2,inputs=[t,Y],outputs=[A9,AR,AA,AT,A8]);BS.click(fn=B3,inputs=[t,Y],outputs=[AE,A8]);BT.click(fn=B4,inputs=[t,Y],outputs=[AV]);AI=[AE,AW,AX,AY,AZ,Aa]
321
+ for Bb in[AW,AX,AY,AZ]:Bb.release(fn=A6,inputs=AI,outputs=AF)
322
+ Aa.change(fn=A6,inputs=AI,outputs=AF);AE.change(fn=A6,inputs=AI,outputs=AF);d.load(fn=A5,inputs=[u],outputs=[AG,AH]);BU.click(fn=A5,inputs=[u],outputs=[AG,AH]);u.change(fn=A5,inputs=[u],outputs=[AG,AH]);BV.click(fn=AO,inputs=B,outputs=Ab);d.load(fn=AO,inputs=B,outputs=Ab);BZ.click(fn=Az,inputs=[BW,BX,BY],outputs=[Ba])
323
+ if __name__=='__main__':d.launch(debug=D)