Update app.py
Browse files
app.py
CHANGED
|
@@ -1,732 +1,323 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
if
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
).
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
if
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
with
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|