Spaces:
Sleeping
Sleeping
Upload 77 files
Browse files- .gitattributes +2 -0
- app_database.db +0 -0
- backend.py +47 -6
- feedback/fake/ChatGPT Image May 17, 2026, 06_10_56 PM.png +2 -2
- feedback/fake/Gemini_Generated_Image_ (1).png +2 -2
- feedback/fake/Gemini_Generated_Image_ (3).png +2 -2
- feedback/fake/Gemini_Generated_Image_.png +2 -2
- feedback/fake/ai-2.jpeg +0 -0
- feedback/fake/ai-3.jpeg +0 -0
- feedback/fake/ai.jpeg +2 -2
- feedback/fake/ai9.png +3 -0
- feedback/pending/695d9e42_Gemini_Generated_Image_ (3).png +3 -0
- feedback/pending/6d2b097f_ai.jpeg +0 -0
- feedback/real/Joko.jpg +0 -0
- feedback/real/real-2.jpeg +0 -0
- feedback/real/real-3.jpg +2 -2
- feedback/real/real-4.jpg +0 -0
- feedback/real/real-5.jpg +2 -2
- feedback/real/real-6.jpg +2 -2
- feedback/real/real.jpeg +2 -2
- index.html +6 -0
- script.js +77 -1
.gitattributes
CHANGED
|
@@ -69,3 +69,5 @@ feedback/real/real9.jpg filter=lfs diff=lfs merge=lfs -text
|
|
| 69 |
feedback/real/Screenshot_20260430_064350.jpg filter=lfs diff=lfs merge=lfs -text
|
| 70 |
feedback/real/WhatsApp[[:space:]]Image[[:space:]]2026-05-12[[:space:]]at[[:space:]]14.58.23.jpeg filter=lfs diff=lfs merge=lfs -text
|
| 71 |
feedback/real/WhatsApp[[:space:]]Image[[:space:]]2026-05-17[[:space:]]at[[:space:]]10.20.34.jpeg filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 69 |
feedback/real/Screenshot_20260430_064350.jpg filter=lfs diff=lfs merge=lfs -text
|
| 70 |
feedback/real/WhatsApp[[:space:]]Image[[:space:]]2026-05-12[[:space:]]at[[:space:]]14.58.23.jpeg filter=lfs diff=lfs merge=lfs -text
|
| 71 |
feedback/real/WhatsApp[[:space:]]Image[[:space:]]2026-05-17[[:space:]]at[[:space:]]10.20.34.jpeg filter=lfs diff=lfs merge=lfs -text
|
| 72 |
+
feedback/fake/ai9.png filter=lfs diff=lfs merge=lfs -text
|
| 73 |
+
feedback/pending/695d9e42_Gemini_Generated_Image_[[:space:]](3).png filter=lfs diff=lfs merge=lfs -text
|
app_database.db
CHANGED
|
Binary files a/app_database.db and b/app_database.db differ
|
|
|
backend.py
CHANGED
|
@@ -157,11 +157,19 @@ def api_scan_image(file: UploadFile = File(...), username: str = Form(...)):
|
|
| 157 |
|
| 158 |
result = resp.json()
|
| 159 |
prediction = result.get("prediction", "REAL")
|
| 160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
|
| 162 |
is_ai = prediction.upper() in ("AI", "FAKE")
|
| 163 |
source = "Pollinations AI (Stable Diffusion)" if is_ai else "Kamera/Foto Digital Asli"
|
| 164 |
-
accuracy = round(
|
| 165 |
|
| 166 |
feedback_path = f"{PENDING_DIR}/{uid}_{safe_name}"
|
| 167 |
save_compressed_image(temp_path, feedback_path)
|
|
@@ -177,7 +185,9 @@ def api_scan_image(file: UploadFile = File(...), username: str = Form(...)):
|
|
| 177 |
is_outlier = 1 if similarity < 0.88 else 0
|
| 178 |
|
| 179 |
# Detect low-light and monochrome conditions
|
| 180 |
-
is_dark,
|
|
|
|
|
|
|
| 181 |
|
| 182 |
# Check for Trap image (Solusi 3)
|
| 183 |
is_trap = 1 if "trap" in file.filename.lower() else 0
|
|
@@ -229,7 +239,9 @@ def api_scan_image(file: UploadFile = File(...), username: str = Form(...)):
|
|
| 229 |
"trust_score": trust_score,
|
| 230 |
"is_dark": is_dark,
|
| 231 |
"is_grayscale": is_grayscale,
|
| 232 |
-
"
|
|
|
|
|
|
|
| 233 |
}
|
| 234 |
finally:
|
| 235 |
if os.path.exists(temp_path): os.remove(temp_path)
|
|
@@ -288,9 +300,18 @@ async def api_batch_scan(files: list[UploadFile] = File(...), username: str = Fo
|
|
| 288 |
|
| 289 |
result = resp.json()
|
| 290 |
prediction = result.get("prediction", "REAL")
|
| 291 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
prediction_label = "AI" if prediction.upper() in ("AI", "FAKE") else "REAL"
|
| 293 |
-
confidence_pct = round(
|
| 294 |
|
| 295 |
is_mismatch = 0
|
| 296 |
if folder_label:
|
|
@@ -489,6 +510,26 @@ def api_correction_single(data: dict):
|
|
| 489 |
"new_trust": new_trust
|
| 490 |
}
|
| 491 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 492 |
@app.get("/api/history/{username}")
|
| 493 |
def api_get_history(username: str):
|
| 494 |
return {"history": database.get_user_history(username)}
|
|
|
|
| 157 |
|
| 158 |
result = resp.json()
|
| 159 |
prediction = result.get("prediction", "REAL")
|
| 160 |
+
confidence_str = result.get("confidence", "0.0%")
|
| 161 |
+
|
| 162 |
+
try:
|
| 163 |
+
if isinstance(confidence_str, str):
|
| 164 |
+
confidence_val = float(confidence_str.replace("%", "").strip()) / 100.0
|
| 165 |
+
else:
|
| 166 |
+
confidence_val = float(confidence_str)
|
| 167 |
+
except Exception:
|
| 168 |
+
confidence_val = 0.85 # fallback
|
| 169 |
|
| 170 |
is_ai = prediction.upper() in ("AI", "FAKE")
|
| 171 |
source = "Pollinations AI (Stable Diffusion)" if is_ai else "Kamera/Foto Digital Asli"
|
| 172 |
+
accuracy = round(confidence_val * 100, 1)
|
| 173 |
|
| 174 |
feedback_path = f"{PENDING_DIR}/{uid}_{safe_name}"
|
| 175 |
save_compressed_image(temp_path, feedback_path)
|
|
|
|
| 185 |
is_outlier = 1 if similarity < 0.88 else 0
|
| 186 |
|
| 187 |
# Detect low-light and monochrome conditions
|
| 188 |
+
is_dark, is_grayscale_local, avg_brightness = analyze_image_conditions(temp_path)
|
| 189 |
+
is_monochrome_detected = bool(result.get("is_monochrome_detected", False))
|
| 190 |
+
is_grayscale = is_monochrome_detected
|
| 191 |
|
| 192 |
# Check for Trap image (Solusi 3)
|
| 193 |
is_trap = 1 if "trap" in file.filename.lower() else 0
|
|
|
|
| 239 |
"trust_score": trust_score,
|
| 240 |
"is_dark": is_dark,
|
| 241 |
"is_grayscale": is_grayscale,
|
| 242 |
+
"is_monochrome_detected": is_monochrome_detected,
|
| 243 |
+
"avg_brightness": avg_brightness,
|
| 244 |
+
"forensic_analysis_logs": result.get("forensic_analysis_logs", {})
|
| 245 |
}
|
| 246 |
finally:
|
| 247 |
if os.path.exists(temp_path): os.remove(temp_path)
|
|
|
|
| 300 |
|
| 301 |
result = resp.json()
|
| 302 |
prediction = result.get("prediction", "REAL")
|
| 303 |
+
confidence_str = result.get("confidence", "0.0%")
|
| 304 |
+
|
| 305 |
+
try:
|
| 306 |
+
if isinstance(confidence_str, str):
|
| 307 |
+
confidence_val = float(confidence_str.replace("%", "").strip()) / 100.0
|
| 308 |
+
else:
|
| 309 |
+
confidence_val = float(confidence_str)
|
| 310 |
+
except Exception:
|
| 311 |
+
confidence_val = 0.85 # fallback
|
| 312 |
+
|
| 313 |
prediction_label = "AI" if prediction.upper() in ("AI", "FAKE") else "REAL"
|
| 314 |
+
confidence_pct = round(confidence_val * 100, 1)
|
| 315 |
|
| 316 |
is_mismatch = 0
|
| 317 |
if folder_label:
|
|
|
|
| 510 |
"new_trust": new_trust
|
| 511 |
}
|
| 512 |
|
| 513 |
+
@app.post("/save-feedback")
|
| 514 |
+
async def save_feedback_direct(file: UploadFile = File(...), correct_label: str = Form(...)):
|
| 515 |
+
# Standardize correct label to folder target
|
| 516 |
+
folder = "real" if correct_label.lower() in ("real", "true", "asli") else "fake"
|
| 517 |
+
target_dir = os.path.join(FEEDBACK_DIR, folder)
|
| 518 |
+
os.makedirs(target_dir, exist_ok=True)
|
| 519 |
+
|
| 520 |
+
safe_name = file.filename.replace("\\", "/").split("/")[-1]
|
| 521 |
+
target_path = os.path.join(target_dir, safe_name)
|
| 522 |
+
|
| 523 |
+
# Save uploaded file content directly
|
| 524 |
+
contents = await file.read()
|
| 525 |
+
with open(target_path, "wb") as f:
|
| 526 |
+
f.write(contents)
|
| 527 |
+
|
| 528 |
+
return {
|
| 529 |
+
"status": "saved",
|
| 530 |
+
"path": f"feedback/{folder}/{safe_name}"
|
| 531 |
+
}
|
| 532 |
+
|
| 533 |
@app.get("/api/history/{username}")
|
| 534 |
def api_get_history(username: str):
|
| 535 |
return {"history": database.get_user_history(username)}
|
feedback/fake/ChatGPT Image May 17, 2026, 06_10_56 PM.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
feedback/fake/Gemini_Generated_Image_ (1).png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
feedback/fake/Gemini_Generated_Image_ (3).png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
feedback/fake/Gemini_Generated_Image_.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
feedback/fake/ai-2.jpeg
CHANGED
|
|
feedback/fake/ai-3.jpeg
CHANGED
|
|
feedback/fake/ai.jpeg
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
feedback/fake/ai9.png
ADDED
|
Git LFS Details
|
feedback/pending/695d9e42_Gemini_Generated_Image_ (3).png
ADDED
|
Git LFS Details
|
feedback/pending/6d2b097f_ai.jpeg
ADDED
|
feedback/real/Joko.jpg
CHANGED
|
|
feedback/real/real-2.jpeg
CHANGED
|
|
feedback/real/real-3.jpg
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
feedback/real/real-4.jpg
CHANGED
|
|
feedback/real/real-5.jpg
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
feedback/real/real-6.jpg
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
feedback/real/real.jpeg
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
index.html
CHANGED
|
@@ -110,6 +110,9 @@
|
|
| 110 |
</div>
|
| 111 |
<button class="btn-scan" style="width: 100%; margin-top: 15px;" onclick="scanLandingFile()">🔍 PINDAI GAMBAR</button>
|
| 112 |
</div>
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
<!-- Result Box for Landing Page -->
|
| 115 |
<div id="landing-result-image" class="result-box hidden" style="margin-top: 20px;"></div>
|
|
@@ -226,6 +229,9 @@
|
|
| 226 |
</div>
|
| 227 |
<button class="btn-scan" onclick="scanFile()">🔍 SCAN GAMBAR</button>
|
| 228 |
</div>
|
|
|
|
|
|
|
|
|
|
| 229 |
<div id="result-image" class="result-box hidden"></div>
|
| 230 |
</section>
|
| 231 |
|
|
|
|
| 110 |
</div>
|
| 111 |
<button class="btn-scan" style="width: 100%; margin-top: 15px;" onclick="scanLandingFile()">🔍 PINDAI GAMBAR</button>
|
| 112 |
</div>
|
| 113 |
+
<div id="landing-warning-banner-monokrom" style="display: none; background: rgba(230,126,34,0.15); border: 1px dashed rgba(230,126,34,0.3); border-radius: 8px; padding: 12px; margin-top: 15px; font-size: 12px; color: #f39c12; line-height: 1.5; text-align: left;">
|
| 114 |
+
<b>⚠️ Peringatan Citra Monokrom:</b> Gambar hitam-putih terdeteksi. Kehilangan informasi warna RGB dapat menurunkan keakuratan klasifikasi model AI. Disarankan menggunakan foto penuh warna.
|
| 115 |
+
</div>
|
| 116 |
|
| 117 |
<!-- Result Box for Landing Page -->
|
| 118 |
<div id="landing-result-image" class="result-box hidden" style="margin-top: 20px;"></div>
|
|
|
|
| 229 |
</div>
|
| 230 |
<button class="btn-scan" onclick="scanFile()">🔍 SCAN GAMBAR</button>
|
| 231 |
</div>
|
| 232 |
+
<div id="warning-banner-monokrom" style="display: none; background: rgba(230,126,34,0.15); border: 1px dashed rgba(230,126,34,0.3); border-radius: 8px; padding: 12px; margin-top: 15px; font-size: 12px; color: #f39c12; line-height: 1.5; text-align: left;">
|
| 233 |
+
<b>⚠️ Peringatan Citra Monokrom:</b> Gambar hitam-putih terdeteksi. Kehilangan informasi warna RGB dapat menurunkan keakuratan klasifikasi model AI. Disarankan menggunakan foto penuh warna.
|
| 234 |
+
</div>
|
| 235 |
<div id="result-image" class="result-box hidden"></div>
|
| 236 |
</section>
|
| 237 |
|
script.js
CHANGED
|
@@ -323,6 +323,18 @@ async function simulateForensicAnalysis(resultBox, fetchPromise) {
|
|
| 323 |
container.scrollTop = container.scrollHeight;
|
| 324 |
}
|
| 325 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
async function scanFile() {
|
| 327 |
const fileInput = document.getElementById("input-image");
|
| 328 |
const resultBox = document.getElementById("result-image");
|
|
@@ -353,6 +365,16 @@ async function scanFile() {
|
|
| 353 |
const res = await fetchPromise;
|
| 354 |
const data = await res.json();
|
| 355 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
if (res.ok) {
|
| 357 |
if (currentUser === "__guest__") {
|
| 358 |
let count = parseInt(localStorage.getItem("guest_scan_count") || "0");
|
|
@@ -365,6 +387,26 @@ async function scanFile() {
|
|
| 365 |
const statusText = data.is_ai ? "⚠️ GAMBAR PALSU (AI)" : "✅ GAMBAR ASLI (REAL)";
|
| 366 |
const statusColor = data.is_ai ? "var(--danger)" : "var(--success)";
|
| 367 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 368 |
resultBox.className = `result-box ${statusClass}`;
|
| 369 |
resultBox.innerHTML = `
|
| 370 |
<div class="result-title" style="color: ${statusColor}">
|
|
@@ -399,12 +441,14 @@ async function scanFile() {
|
|
| 399 |
<b>⚠️ Rekomendasi Kondisi Deteksi:</b><br/>
|
| 400 |
${data.is_dark ? '• Cahaya terdeteksi rendah (kecerahan rata-rata: ' + data.avg_brightness + '/255). Hal ini memicu noise sensor kamera yang dapat mengganggu keakuratan forensik AI.<br/>' : ''}
|
| 401 |
${data.is_grayscale ? '• Gambar monokrom/hitam-putih terdeteksi. Kehilangan informasi kromatik (saluran warna RGB) secara drastis dapat menurunkan performa klasifikasi model AI.<br/>' : ''}
|
| 402 |
-
<i style="display:block;margin-top:6px;color:rgba(255,255,255,0.7)">Disarankan untuk melakukan scan ulang menggunakan foto dengan pencahayaan cukup
|
| 403 |
</div>
|
| 404 |
`
|
| 405 |
: ''
|
| 406 |
}
|
| 407 |
|
|
|
|
|
|
|
| 408 |
<div class="details-grid">
|
| 409 |
<div class="detail-item">
|
| 410 |
<div class="detail-label">Nama File</div>
|
|
@@ -1010,6 +1054,16 @@ async function scanLandingFile() {
|
|
| 1010 |
const res = await fetchPromise;
|
| 1011 |
const data = await res.json();
|
| 1012 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1013 |
if (res.ok) {
|
| 1014 |
let count = parseInt(localStorage.getItem("guest_scan_count") || "0");
|
| 1015 |
count++;
|
|
@@ -1020,6 +1074,26 @@ async function scanLandingFile() {
|
|
| 1020 |
const statusText = data.is_ai ? "⚠️ GAMBAR PALSU (AI)" : "✅ GAMBAR ASLI (REAL)";
|
| 1021 |
const statusColor = data.is_ai ? "var(--danger)" : "var(--success)";
|
| 1022 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1023 |
resultBox.className = `result-box ${statusClass}`;
|
| 1024 |
resultBox.innerHTML = `
|
| 1025 |
<div class="result-title" style="color: ${statusColor}">
|
|
@@ -1060,6 +1134,8 @@ async function scanLandingFile() {
|
|
| 1060 |
: ''
|
| 1061 |
}
|
| 1062 |
|
|
|
|
|
|
|
| 1063 |
<div class="details-grid">
|
| 1064 |
<div class="detail-item">
|
| 1065 |
<div class="detail-label">Nama File</div>
|
|
|
|
| 323 |
container.scrollTop = container.scrollHeight;
|
| 324 |
}
|
| 325 |
|
| 326 |
+
function toggleForensicLogs(btn) {
|
| 327 |
+
const content = btn.nextElementSibling;
|
| 328 |
+
const arrow = btn.querySelector("span:last-child");
|
| 329 |
+
if (content.classList.contains("hidden")) {
|
| 330 |
+
content.classList.remove("hidden");
|
| 331 |
+
arrow.innerText = "▲";
|
| 332 |
+
} else {
|
| 333 |
+
content.classList.add("hidden");
|
| 334 |
+
arrow.innerText = "▼";
|
| 335 |
+
}
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
async function scanFile() {
|
| 339 |
const fileInput = document.getElementById("input-image");
|
| 340 |
const resultBox = document.getElementById("result-image");
|
|
|
|
| 365 |
const res = await fetchPromise;
|
| 366 |
const data = await res.json();
|
| 367 |
|
| 368 |
+
// CEK OTOMATIS GAMBAR HITAM PUTIH UNTUK TAMPILKAN BANNER ALERT
|
| 369 |
+
const warningBanner = document.getElementById("warning-banner-monokrom");
|
| 370 |
+
if (warningBanner) {
|
| 371 |
+
if (data.is_monochrome_detected === true) {
|
| 372 |
+
warningBanner.style.display = "block";
|
| 373 |
+
} else {
|
| 374 |
+
warningBanner.style.display = "none";
|
| 375 |
+
}
|
| 376 |
+
}
|
| 377 |
+
|
| 378 |
if (res.ok) {
|
| 379 |
if (currentUser === "__guest__") {
|
| 380 |
let count = parseInt(localStorage.getItem("guest_scan_count") || "0");
|
|
|
|
| 387 |
const statusText = data.is_ai ? "⚠️ GAMBAR PALSU (AI)" : "✅ GAMBAR ASLI (REAL)";
|
| 388 |
const statusColor = data.is_ai ? "var(--danger)" : "var(--success)";
|
| 389 |
|
| 390 |
+
let logsHtml = '';
|
| 391 |
+
if (data.forensic_analysis_logs && Object.keys(data.forensic_analysis_logs).length > 0) {
|
| 392 |
+
logsHtml = `
|
| 393 |
+
<div class="forensic-logs-detail" style="margin-top: 15px; margin-bottom: 15px; text-align: left;">
|
| 394 |
+
<button class="btn-refresh" style="width: 100%; text-align: left; padding: 10px 15px; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,215,0,0.25); border-radius: 8px; color: var(--yellow-main); cursor: pointer; font-size: 13px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 0;" onclick="toggleForensicLogs(this)">
|
| 395 |
+
<span>🔍 Lihat Log Detil Forensik (12 Langkah)</span>
|
| 396 |
+
<span style="font-size:10px">▼</span>
|
| 397 |
+
</button>
|
| 398 |
+
<div class="logs-content hidden" style="background: rgba(0,0,0,0.5); border: 1px solid rgba(255,215,0,0.15); border-top: none; border-radius: 0 0 8px 8px; padding: 15px; font-family: monospace; font-size: 12px; color: #2ed573; line-height: 1.6; max-height: 250px; overflow-y: auto;">
|
| 399 |
+
`;
|
| 400 |
+
for (let i = 1; i <= 12; i++) {
|
| 401 |
+
const stepText = data.forensic_analysis_logs[`step_${i}`] || `[Step ${i}/12] Analisis selesai.`;
|
| 402 |
+
logsHtml += `<div style="margin-bottom: 6px; border-bottom: 1px dashed rgba(255,255,255,0.05); padding-bottom: 4px;">${stepText}</div>`;
|
| 403 |
+
}
|
| 404 |
+
logsHtml += `
|
| 405 |
+
</div>
|
| 406 |
+
</div>
|
| 407 |
+
`;
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
resultBox.className = `result-box ${statusClass}`;
|
| 411 |
resultBox.innerHTML = `
|
| 412 |
<div class="result-title" style="color: ${statusColor}">
|
|
|
|
| 441 |
<b>⚠️ Rekomendasi Kondisi Deteksi:</b><br/>
|
| 442 |
${data.is_dark ? '• Cahaya terdeteksi rendah (kecerahan rata-rata: ' + data.avg_brightness + '/255). Hal ini memicu noise sensor kamera yang dapat mengganggu keakuratan forensik AI.<br/>' : ''}
|
| 443 |
${data.is_grayscale ? '• Gambar monokrom/hitam-putih terdeteksi. Kehilangan informasi kromatik (saluran warna RGB) secara drastis dapat menurunkan performa klasifikasi model AI.<br/>' : ''}
|
| 444 |
+
<i style="display:block;margin-top:6px;color:rgba(255,255,255,0.7)">Disarankan untuk melakukan scan ulang menggunakan foto dengan pencahayaan cukup and penuh warna (Full RGB).</i>
|
| 445 |
</div>
|
| 446 |
`
|
| 447 |
: ''
|
| 448 |
}
|
| 449 |
|
| 450 |
+
${logsHtml}
|
| 451 |
+
|
| 452 |
<div class="details-grid">
|
| 453 |
<div class="detail-item">
|
| 454 |
<div class="detail-label">Nama File</div>
|
|
|
|
| 1054 |
const res = await fetchPromise;
|
| 1055 |
const data = await res.json();
|
| 1056 |
|
| 1057 |
+
// CEK OTOMATIS GAMBAR HITAM PUTIH UNTUK TAMPILKAN BANNER ALERT
|
| 1058 |
+
const warningBanner = document.getElementById("landing-warning-banner-monokrom");
|
| 1059 |
+
if (warningBanner) {
|
| 1060 |
+
if (data.is_monochrome_detected === true) {
|
| 1061 |
+
warningBanner.style.display = "block";
|
| 1062 |
+
} else {
|
| 1063 |
+
warningBanner.style.display = "none";
|
| 1064 |
+
}
|
| 1065 |
+
}
|
| 1066 |
+
|
| 1067 |
if (res.ok) {
|
| 1068 |
let count = parseInt(localStorage.getItem("guest_scan_count") || "0");
|
| 1069 |
count++;
|
|
|
|
| 1074 |
const statusText = data.is_ai ? "⚠️ GAMBAR PALSU (AI)" : "✅ GAMBAR ASLI (REAL)";
|
| 1075 |
const statusColor = data.is_ai ? "var(--danger)" : "var(--success)";
|
| 1076 |
|
| 1077 |
+
let logsHtml = '';
|
| 1078 |
+
if (data.forensic_analysis_logs && Object.keys(data.forensic_analysis_logs).length > 0) {
|
| 1079 |
+
logsHtml = `
|
| 1080 |
+
<div class="forensic-logs-detail" style="margin-top: 15px; margin-bottom: 15px; text-align: left;">
|
| 1081 |
+
<button class="btn-refresh" style="width: 100%; text-align: left; padding: 10px 15px; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,215,0,0.25); border-radius: 8px; color: var(--yellow-main); cursor: pointer; font-size: 13px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 0;" onclick="toggleForensicLogs(this)">
|
| 1082 |
+
<span>🔍 Lihat Log Detil Forensik (12 Langkah)</span>
|
| 1083 |
+
<span style="font-size:10px">▼</span>
|
| 1084 |
+
</button>
|
| 1085 |
+
<div class="logs-content hidden" style="background: rgba(0,0,0,0.5); border: 1px solid rgba(255,215,0,0.15); border-top: none; border-radius: 0 0 8px 8px; padding: 15px; font-family: monospace; font-size: 12px; color: #2ed573; line-height: 1.6; max-height: 250px; overflow-y: auto;">
|
| 1086 |
+
`;
|
| 1087 |
+
for (let i = 1; i <= 12; i++) {
|
| 1088 |
+
const stepText = data.forensic_analysis_logs[`step_${i}`] || `[Step ${i}/12] Analisis selesai.`;
|
| 1089 |
+
logsHtml += `<div style="margin-bottom: 6px; border-bottom: 1px dashed rgba(255,255,255,0.05); padding-bottom: 4px;">${stepText}</div>`;
|
| 1090 |
+
}
|
| 1091 |
+
logsHtml += `
|
| 1092 |
+
</div>
|
| 1093 |
+
</div>
|
| 1094 |
+
`;
|
| 1095 |
+
}
|
| 1096 |
+
|
| 1097 |
resultBox.className = `result-box ${statusClass}`;
|
| 1098 |
resultBox.innerHTML = `
|
| 1099 |
<div class="result-title" style="color: ${statusColor}">
|
|
|
|
| 1134 |
: ''
|
| 1135 |
}
|
| 1136 |
|
| 1137 |
+
${logsHtml}
|
| 1138 |
+
|
| 1139 |
<div class="details-grid">
|
| 1140 |
<div class="detail-item">
|
| 1141 |
<div class="detail-label">Nama File</div>
|