12097 lines
684 KiB
HTML
12097 lines
684 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Studio Monitor</title>
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
background: #0f0f0f;
|
||
color: #e0e0e0;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.header {
|
||
position: fixed;
|
||
top: 0; left: 0; right: 0;
|
||
background: rgba(15, 15, 15, 0.95);
|
||
backdrop-filter: blur(10px);
|
||
border-bottom: 1px solid #333;
|
||
padding: 16px 24px;
|
||
z-index: 100;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.status {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 13px;
|
||
color: #888;
|
||
}
|
||
|
||
.status-dot {
|
||
width: 8px; height: 8px;
|
||
border-radius: 50%;
|
||
background: #22c55e;
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.4; }
|
||
}
|
||
|
||
.status-dot.updating {
|
||
background: #f59e0b;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
.controls {
|
||
display: flex;
|
||
gap: 12px;
|
||
align-items: center;
|
||
}
|
||
|
||
.btn {
|
||
background: #1a1a1a;
|
||
border: 1px solid #333;
|
||
color: #ccc;
|
||
padding: 6px 14px;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.btn:hover {
|
||
background: #252525;
|
||
border-color: #444;
|
||
color: #fff;
|
||
}
|
||
|
||
.btn.primary {
|
||
background: #2563eb;
|
||
border-color: #2563eb;
|
||
color: #fff;
|
||
}
|
||
|
||
.btn.primary:hover {
|
||
background: #3b82f6;
|
||
}
|
||
|
||
.btn.danger {
|
||
background: #991b1b;
|
||
border-color: #991b1b;
|
||
color: #fff;
|
||
}
|
||
.btn.danger:hover {
|
||
background: #b91c1c;
|
||
}
|
||
|
||
.image-actions {
|
||
position: absolute;
|
||
top: 8px;
|
||
right: 8px;
|
||
display: flex;
|
||
gap: 4px;
|
||
opacity: 0;
|
||
transition: opacity 0.2s;
|
||
z-index: 20;
|
||
}
|
||
|
||
.selectable .image-actions {
|
||
display: none !important;
|
||
}
|
||
|
||
.image-card:hover .image-actions {
|
||
opacity: 1;
|
||
}
|
||
|
||
.image-card.highlight-prev {
|
||
border: 2px solid #f59e0b !important;
|
||
box-shadow: 0 0 15px rgba(245, 158, 11, 0.4);
|
||
}
|
||
|
||
.archive-confirm-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0,0,0,0.85);
|
||
backdrop-filter: blur(4px);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 50;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 0.2s;
|
||
padding: 12px;
|
||
text-align: center;
|
||
}
|
||
|
||
.image-card.confirming-archive .archive-confirm-overlay {
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.archive-confirm-overlay h3 {
|
||
font-size: 14px;
|
||
margin: 0 0 12px 0;
|
||
color: #eee;
|
||
}
|
||
|
||
.archive-confirm-buttons {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
|
||
.pad-preview-overlay {
|
||
position: absolute;
|
||
background: rgba(245,158,11,0.4);
|
||
border: 1px dashed #f59e0b;
|
||
pointer-events: none;
|
||
z-index: 10;
|
||
}
|
||
|
||
.action-btn {
|
||
background: rgba(0,0,0,0.6);
|
||
backdrop-filter: blur(4px);
|
||
border: 1px solid rgba(255,255,255,0.1);
|
||
color: #fff;
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: 6px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.action-btn:hover {
|
||
background: rgba(0,0,0,0.8);
|
||
border-color: rgba(255,255,255,0.3);
|
||
}
|
||
|
||
.action-btn.delete:hover {
|
||
background: #991b1b;
|
||
border-color: #991b1b;
|
||
}
|
||
|
||
.prompt-bar {
|
||
display: flex;
|
||
gap: 8px;
|
||
align-items: center;
|
||
}
|
||
|
||
.prompt-input {
|
||
background: #1a1a1a;
|
||
border: 1px solid #333;
|
||
color: #e0e0e0;
|
||
padding: 6px 12px;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
width: 320px;
|
||
outline: none;
|
||
transition: border-color 0.2s;
|
||
}
|
||
|
||
.prompt-input:focus {
|
||
border-color: #2563eb;
|
||
}
|
||
|
||
.prompt-input::placeholder {
|
||
color: #555;
|
||
}
|
||
|
||
.count {
|
||
font-size: 13px;
|
||
color: #666;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.gallery {
|
||
padding: 100px 24px 40px;
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||
gap: 20px;
|
||
max-width: 1800px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.image-card {
|
||
background: #1a1a1a;
|
||
border: 1px solid #2a2a2a;
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
transition: transform 0.2s, border-color 0.2s;
|
||
position: relative;
|
||
}
|
||
|
||
.image-card:hover {
|
||
transform: translateY(-2px);
|
||
border-color: #444;
|
||
}
|
||
|
||
.image-card.new {
|
||
border-color: #2563eb;
|
||
animation: highlight 1s ease;
|
||
}
|
||
|
||
@keyframes highlight {
|
||
from { box-shadow: 0 0 0 2px #2563eb; }
|
||
to { box-shadow: none; }
|
||
}
|
||
|
||
.image-wrapper {
|
||
position: relative;
|
||
padding-top: 133%; /* 4:3 portrait */
|
||
background: #111;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.image-wrapper img {
|
||
position: absolute;
|
||
top: 0; left: 0;
|
||
width: 100%; height: 100%;
|
||
object-fit: contain;
|
||
transition: opacity 0.3s;
|
||
}
|
||
|
||
.variant-count {
|
||
position: absolute;
|
||
bottom: 8px; left: 8px;
|
||
background: rgba(0,0,0,0.65);
|
||
color: #fff;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
padding: 2px 7px;
|
||
border-radius: 10px;
|
||
z-index: 5;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.variant-dots {
|
||
position: absolute;
|
||
bottom: 8px; left: 50%; transform: translateX(-50%);
|
||
display: flex; gap: 4px;
|
||
z-index: 5;
|
||
}
|
||
|
||
.dot {
|
||
width: 6px; height: 6px;
|
||
border-radius: 50%;
|
||
background: rgba(255,255,255,0.35);
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.dot.active { background: #fff; }
|
||
|
||
.image-info {
|
||
padding: 14px 16px;
|
||
}
|
||
|
||
.image-name {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: #fff;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.image-meta {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
font-size: 12px;
|
||
color: #666;
|
||
}
|
||
|
||
.image-time {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.badge {
|
||
background: #2563eb;
|
||
color: #fff;
|
||
font-size: 10px;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.badge.recent {
|
||
background: #22c55e;
|
||
}
|
||
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 120px 20px;
|
||
color: #555;
|
||
}
|
||
|
||
.empty-state svg {
|
||
width: 64px; height: 64px;
|
||
margin-bottom: 16px;
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.empty-state h2 {
|
||
font-size: 20px;
|
||
color: #777;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.empty-state p {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.toast {
|
||
position: fixed;
|
||
bottom: 24px; right: 24px;
|
||
background: #1a1a1a;
|
||
border: 1px solid #333;
|
||
padding: 12px 20px;
|
||
border-radius: 8px;
|
||
font-size: 13px;
|
||
color: #ccc;
|
||
transform: translateY(100px);
|
||
opacity: 0;
|
||
transition: all 0.3s;
|
||
z-index: 200;
|
||
}
|
||
|
||
.toast.show {
|
||
transform: translateY(0);
|
||
opacity: 1;
|
||
}
|
||
|
||
.toast.success { border-left: 3px solid #22c55e; }
|
||
.toast.info { border-left: 3px solid #2563eb; }
|
||
|
||
.btn.active {
|
||
background: #2563eb;
|
||
border-color: #2563eb;
|
||
color: #fff;
|
||
}
|
||
|
||
.image-card.selectable {
|
||
cursor: pointer;
|
||
user-select: none;
|
||
}
|
||
|
||
.image-card.selected {
|
||
border-color: #2563eb;
|
||
box-shadow: 0 0 0 2px rgba(37,99,235,0.35);
|
||
}
|
||
|
||
.select-indicator {
|
||
position: absolute;
|
||
top: 8px; left: 8px;
|
||
width: 22px; height: 22px;
|
||
border-radius: 50%;
|
||
border: 2px solid rgba(255,255,255,0.5);
|
||
background: rgba(0,0,0,0.45);
|
||
display: none;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 15;
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
transition: background 0.15s, border-color 0.15s;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.selectable .select-indicator { display: flex; }
|
||
|
||
.selected .select-indicator {
|
||
background: #2563eb;
|
||
border-color: #2563eb;
|
||
}
|
||
|
||
.selected .select-indicator::after { content: '✓'; }
|
||
|
||
.variant-picker {
|
||
display: none;
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0,0,0,0.80);
|
||
z-index: 350;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.variant-picker.open { display: flex; }
|
||
.variant-picker-inner {
|
||
background: #1a1a1a;
|
||
border: 1px solid #333;
|
||
border-radius: 14px;
|
||
padding: 22px;
|
||
max-width: 88vw;
|
||
max-height: 80vh;
|
||
overflow-y: auto;
|
||
box-shadow: 0 8px 40px rgba(0,0,0,0.7);
|
||
}
|
||
.variant-picker-title {
|
||
color: #ccc;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
margin-bottom: 14px;
|
||
}
|
||
.variant-picker-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
|
||
gap: 10px;
|
||
}
|
||
.variant-thumb {
|
||
cursor: pointer;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
border: 2px solid transparent;
|
||
aspect-ratio: 3/4;
|
||
background: #111;
|
||
transition: border-color 0.15s, transform 0.15s;
|
||
}
|
||
.variant-thumb:hover { border-color: #2563eb; transform: scale(1.03); }
|
||
.variant-thumb.selected { border-color: #22c55e; }
|
||
.variant-thumb.hidden-img { opacity: 0.38; }
|
||
.variant-thumb.hidden-img::after {
|
||
content: '🚫';
|
||
position: absolute;
|
||
top: 4px; right: 4px;
|
||
font-size: 14px;
|
||
line-height: 1;
|
||
}
|
||
.variant-thumb { position: relative; }
|
||
|
||
.lb-hidden-badge {
|
||
position: absolute;
|
||
top: 12px; left: 50%; transform: translateX(-50%);
|
||
background: rgba(0,0,0,0.7);
|
||
color: #f87171;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
padding: 3px 12px;
|
||
border-radius: 20px;
|
||
pointer-events: none;
|
||
display: none;
|
||
}
|
||
.lightbox.img-hidden .lb-hidden-badge { display: block; }
|
||
.lightbox.img-hidden #lbImg { opacity: 0.45; }
|
||
.studio.st-hidden .lb-hidden-badge { display: block; }
|
||
.studio.st-hidden #lbImg { opacity: 0.45; }
|
||
.variant-thumb img {
|
||
width: 100%; height: 100%;
|
||
object-fit: contain;
|
||
display: block;
|
||
}
|
||
.variant-picker-actions {
|
||
display: flex;
|
||
gap: 8px;
|
||
margin-top: 16px;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.batch-bar {
|
||
position: fixed;
|
||
bottom: 0; left: 0; right: 0;
|
||
background: rgba(12,12,12,0.97);
|
||
backdrop-filter: blur(12px);
|
||
border-top: 1px solid #2a2a2a;
|
||
padding: 14px 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
z-index: 100;
|
||
transform: translateY(100%);
|
||
transition: transform 0.25s ease;
|
||
}
|
||
|
||
.batch-bar.visible { transform: translateY(0); }
|
||
|
||
.pose-menu {
|
||
position: absolute;
|
||
bottom: 100%;
|
||
left: 24px;
|
||
right: 24px;
|
||
background: #1a1a1a;
|
||
border: 1px solid #333;
|
||
border-bottom: none;
|
||
border-radius: 8px 8px 0 0;
|
||
padding: 16px;
|
||
display: none;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
box-shadow: 0 -10px 20px rgba(0,0,0,0.5);
|
||
}
|
||
|
||
.pose-menu.open {
|
||
display: flex;
|
||
}
|
||
|
||
.pose-item {
|
||
background: #2a2a2a;
|
||
border: 1px solid #444;
|
||
color: #ccc;
|
||
padding: 4px 10px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
|
||
.pose-item:hover {
|
||
background: #3a3a3a;
|
||
color: #fff;
|
||
}
|
||
|
||
.pose-item.active {
|
||
background: #2563eb;
|
||
border-color: #2563eb;
|
||
color: #fff;
|
||
}
|
||
|
||
.batch-count {
|
||
font-size: 13px;
|
||
color: #888;
|
||
min-width: 80px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.batch-prompt-wrap { flex: 1; position: relative; }
|
||
|
||
.batch-prompt {
|
||
width: 100%;
|
||
background: #1a1a1a;
|
||
border: 1px solid #333;
|
||
color: #e0e0e0;
|
||
padding: 7px 12px;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
outline: none;
|
||
transition: border-color 0.2s;
|
||
}
|
||
|
||
.batch-prompt:focus { border-color: #2563eb; }
|
||
.batch-prompt::placeholder { color: #555; }
|
||
|
||
.batch-progress {
|
||
font-size: 12px;
|
||
color: #666;
|
||
min-width: 90px;
|
||
text-align: right;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Lightbox */
|
||
.lightbox {
|
||
display: none;
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0,0,0,0.93);
|
||
z-index: 500;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 16px;
|
||
}
|
||
.lightbox.open { display: flex; }
|
||
|
||
.lightbox img {
|
||
max-width: 88vw;
|
||
max-height: 88vh;
|
||
object-fit: contain;
|
||
border-radius: 4px;
|
||
display: block;
|
||
}
|
||
|
||
.lb-btn {
|
||
background: rgba(255,255,255,0.08);
|
||
border: 1px solid rgba(255,255,255,0.15);
|
||
color: #fff;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
font-size: 20px;
|
||
padding: 10px 16px;
|
||
transition: background 0.2s;
|
||
flex-shrink: 0;
|
||
}
|
||
.lb-btn:hover { background: rgba(255,255,255,0.18); }
|
||
|
||
.lb-close {
|
||
position: absolute;
|
||
top: 16px; right: 20px;
|
||
font-size: 16px;
|
||
padding: 6px 12px;
|
||
}
|
||
|
||
.lb-info {
|
||
position: absolute;
|
||
bottom: 20px; left: 50%; transform: translateX(-50%);
|
||
display: flex; gap: 16px; align-items: center;
|
||
background: rgba(0,0,0,0.8);
|
||
padding: 10px 20px;
|
||
border-radius: 20px;
|
||
font-size: 14px;
|
||
color: white;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
#lbGroupNameInput, #lbNameInput {
|
||
background: rgba(255,255,255,0.1);
|
||
border: 1px solid rgba(255,255,255,0.2);
|
||
color: white;
|
||
padding: 5px 10px;
|
||
border-radius: 4px;
|
||
width: 150px;
|
||
font-size: 13px;
|
||
}
|
||
#lbGroupNameInput { border-color: rgba(255,255,255,0.35); }
|
||
|
||
#lbClipDesc {
|
||
font-size: 12px;
|
||
color: #aaa;
|
||
max-width: 300px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.upload-zone {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
margin-left: 20px;
|
||
padding-left: 20px;
|
||
border-left: 1px solid #333;
|
||
}
|
||
|
||
#uploadInput { display: none; }
|
||
|
||
/* --- lightbox variant strip --- */
|
||
#lbVariantStrip {
|
||
display: none;
|
||
flex-direction: row;
|
||
gap: 6px;
|
||
overflow-x: auto;
|
||
padding: 6px 2px 2px;
|
||
width: 100%;
|
||
scrollbar-width: thin;
|
||
scrollbar-color: #444 transparent;
|
||
}
|
||
.lb-var-thumb {
|
||
flex: 0 0 auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 2px;
|
||
cursor: pointer;
|
||
opacity: 0.5;
|
||
transition: opacity 0.15s;
|
||
max-width: 60px;
|
||
}
|
||
.lb-var-thumb:hover { opacity: 0.85; }
|
||
.lb-var-thumb.active { opacity: 1; outline: 2px solid #60a5fa; border-radius: 4px; }
|
||
.lb-var-thumb.ref-selected { opacity: 1; outline: 2px solid #f97316; border-radius: 4px; }
|
||
.lb-var-thumb.active.ref-selected { outline: 2px solid #f97316; }
|
||
.lb-var-ref-badge { position: absolute; top: 2px; left: 2px; background: #f97316; color: #fff; font-size: 9px; font-weight: bold; padding: 1px 4px; border-radius: 3px; line-height: 1.4; pointer-events: none; z-index: 2; }
|
||
.lb-var-thumb img {
|
||
width: 52px; height: 52px;
|
||
object-fit: cover;
|
||
border-radius: 4px;
|
||
display: block;
|
||
}
|
||
.lb-var-pose {
|
||
font-size: 7px;
|
||
color: #999;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
max-width: 58px;
|
||
}
|
||
.lb-var-thumb { position: relative; }
|
||
.lb-var-play {
|
||
position: absolute;
|
||
top: 50%; left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
font-size: 13px;
|
||
color: #fff;
|
||
text-shadow: 0 1px 3px rgba(0,0,0,0.8);
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* --- group card grid overview --- */
|
||
.card-grid {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
grid-template-rows: 1fr 1fr;
|
||
gap: 2px;
|
||
background: #111;
|
||
}
|
||
.card-grid-thumb {
|
||
position: relative;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
}
|
||
.card-grid-thumb img {
|
||
width: 100%; height: 100%;
|
||
object-fit: cover;
|
||
display: block;
|
||
}
|
||
.card-grid-thumb .thumb-pose {
|
||
position: absolute;
|
||
bottom: 0; left: 0; right: 0;
|
||
background: rgba(0,0,0,0.72);
|
||
font-size: 7px;
|
||
color: #ccc;
|
||
padding: 1px 3px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
text-align: center;
|
||
pointer-events: none;
|
||
}
|
||
.card-grid-more {
|
||
position: absolute;
|
||
inset: 0;
|
||
background: rgba(0,0,0,0.55);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* --- privacy overlay: disguised as an innocuous AI study assistant --- */
|
||
.privacy-overlay {
|
||
display: none;
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 999999;
|
||
background: #0a0a0a;
|
||
color: #e0e0e0;
|
||
flex-direction: column;
|
||
cursor: default;
|
||
user-select: none;
|
||
font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||
}
|
||
.priv-topbar {
|
||
display: flex; align-items: center; gap: 10px;
|
||
padding: 12px 18px;
|
||
border-bottom: 1px solid #333;
|
||
background: #1a1a1a;
|
||
font-size: 14px; font-weight: 600; color: #e0e0e0;
|
||
}
|
||
.priv-topbar .priv-logo {
|
||
width: 26px; height: 26px; border-radius: 6px;
|
||
background: linear-gradient(135deg,#2563eb,#1d4ed8);
|
||
display: flex; align-items: center; justify-content: center;
|
||
color: #fff; font-size: 14px; font-weight: 700;
|
||
}
|
||
.priv-topbar .priv-sub { font-weight: 400; color: #8a8a8a; font-size: 12px; margin-left: 2px; }
|
||
.priv-chat {
|
||
flex: 1; overflow-y: auto;
|
||
padding: 26px 0;
|
||
display: flex; flex-direction: column; align-items: center;
|
||
}
|
||
.priv-msg-row { width: 100%; padding: 18px 0; }
|
||
.priv-msg-row.assistant { background: #1a1a1a; border-top: 1px solid #333; border-bottom: 1px solid #333; }
|
||
.priv-msg-inner {
|
||
max-width: 720px; margin: 0 auto; padding: 0 24px;
|
||
display: flex; gap: 16px; align-items: flex-start;
|
||
}
|
||
.priv-avatar {
|
||
flex: 0 0 28px; width: 28px; height: 28px; border-radius: 4px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 12px; font-weight: 700; color: #fff;
|
||
}
|
||
.priv-avatar.user { background: #8b5cf6; }
|
||
.priv-avatar.bot { background: #2563eb; }
|
||
.priv-text { font-size: 15px; line-height: 1.7; color: #e0e0e0; }
|
||
.priv-text .eq { font-family: "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace; background: #2d333a; padding: 1px 5px; border-radius: 4px; }
|
||
.priv-inputbar {
|
||
border-top: 1px solid #333; background: #1a1a1a;
|
||
padding: 14px 0 22px;
|
||
}
|
||
.priv-inputbox {
|
||
max-width: 720px; margin: 0 auto; padding: 0 24px;
|
||
display: flex; align-items: center; gap: 10px;
|
||
}
|
||
.priv-fakeinput {
|
||
flex: 1; border: 1px solid #333; border-radius: 12px;
|
||
padding: 12px 16px; font-size: 14px; color: #888;
|
||
box-shadow: 0 1px 4px rgba(0,0,0,0.04);
|
||
background: #151515;
|
||
}
|
||
.priv-send { width: 34px; height: 34px; border-radius: 8px; background: #10a37f; color:#fff; display:flex; align-items:center; justify-content:center; font-size:15px; }
|
||
.priv-hint { text-align:center; font-size: 11px; color: #b0b0b0; margin-top: 10px; }
|
||
|
||
/* --- preferred star badge in variant picker --- */
|
||
.variant-thumb.preferred::before {
|
||
content: '★';
|
||
position: absolute;
|
||
top: 3px; left: 5px;
|
||
font-size: 14px;
|
||
color: #f59e0b;
|
||
text-shadow: 0 0 4px #000;
|
||
pointer-events: none;
|
||
z-index: 2;
|
||
}
|
||
.set-preferred-btn {
|
||
position: absolute;
|
||
bottom: 3px; right: 3px;
|
||
background: rgba(0,0,0,0.7);
|
||
color: #999;
|
||
border: none;
|
||
border-radius: 3px;
|
||
font-size: 10px;
|
||
padding: 1px 4px;
|
||
cursor: pointer;
|
||
z-index: 2;
|
||
}
|
||
.set-preferred-btn:hover { color: #f59e0b; }
|
||
|
||
/* ---- professional UI polish ---- */
|
||
body { background: #0a0a0a; }
|
||
|
||
.header {
|
||
background: rgba(10,10,10,0.96);
|
||
border-bottom: 1px solid #1e1e1e;
|
||
padding: 14px 20px;
|
||
gap: 12px;
|
||
}
|
||
.header h1 { font-size: 16px; letter-spacing: -0.2px; }
|
||
.header h1 span { color: #555; font-weight: 400; font-size: 13px; margin-left: 4px; }
|
||
|
||
.btn {
|
||
background: #181818;
|
||
border: 1px solid #2a2a2a;
|
||
color: #aaa;
|
||
padding: 5px 12px;
|
||
border-radius: 7px;
|
||
font-size: 12px;
|
||
height: 30px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
.btn:hover { background: #222; border-color: #3a3a3a; color: #eee; }
|
||
.btn.primary { background: #1d4ed8; border-color: #2563eb; color: #fff; }
|
||
.btn.primary:hover { background: #2563eb; }
|
||
|
||
.prompt-input { background: #111; border-color: #2a2a2a; border-radius: 7px; }
|
||
.prompt-input:focus { border-color: #2563eb; box-shadow: 0 0 0 2px rgba(37,99,235,0.2); }
|
||
|
||
.image-card {
|
||
background: #111;
|
||
border: 1px solid #1a1a1a;
|
||
border-radius: 14px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.4);
|
||
}
|
||
.image-card:hover {
|
||
border-color: #2e2e2e;
|
||
box-shadow: 0 8px 24px rgba(0,0,0,0.6);
|
||
transform: translateY(-3px);
|
||
}
|
||
.image-info { padding: 12px 14px; }
|
||
.image-name { font-size: 13px; }
|
||
.image-meta { font-size: 11px; }
|
||
|
||
.gallery { padding-top: 96px; gap: 16px; }
|
||
|
||
.batch-bar {
|
||
background: rgba(8,8,8,0.98);
|
||
border-top: 1px solid #1e1e1e;
|
||
}
|
||
.batch-prompt { background: #111; border-color: #2a2a2a; border-radius: 7px; }
|
||
|
||
/* ---- video card support ---- */
|
||
.image-wrapper video {
|
||
position: absolute;
|
||
top: 0; left: 0;
|
||
width: 100%; height: 100%;
|
||
object-fit: cover;
|
||
display: block;
|
||
border-radius: 0;
|
||
}
|
||
.video-play-overlay {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
opacity: 0;
|
||
transition: opacity 0.2s;
|
||
z-index: 4;
|
||
background: rgba(0,0,0,0.25);
|
||
}
|
||
.image-card:hover .video-play-overlay { opacity: 1; }
|
||
.video-play-icon {
|
||
width: 42px; height: 42px;
|
||
background: rgba(255,255,255,0.88);
|
||
border-radius: 50%;
|
||
display: flex; align-items: center; justify-content: center;
|
||
box-shadow: 0 2px 12px rgba(0,0,0,0.4);
|
||
}
|
||
.badge.video { background: #6d28d9; }
|
||
.badge.fs { background: #0f766e; }
|
||
|
||
/* ---- faceswap modal ---- */
|
||
.faceswap-modal {
|
||
display: none;
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0,0,0,0.88);
|
||
z-index: 600;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.faceswap-modal.open { display: flex; }
|
||
.faceswap-modal-inner {
|
||
background: #111;
|
||
border: 1px solid #252525;
|
||
border-radius: 16px;
|
||
padding: 22px 28px 24px;
|
||
max-width: 640px;
|
||
width: 92vw;
|
||
max-height: 90vh;
|
||
overflow-y: auto;
|
||
box-shadow: 0 24px 72px rgba(0,0,0,0.9);
|
||
}
|
||
/* ---- modal header ---- */
|
||
.fs-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-bottom: 14px;
|
||
}
|
||
.fs-model-thumb {
|
||
width: 52px; height: 52px;
|
||
object-fit: cover;
|
||
border-radius: 8px;
|
||
border: 1px solid #2a2a2a;
|
||
flex-shrink: 0;
|
||
}
|
||
.fs-header-info { min-width: 0; }
|
||
.faceswap-modal-title {
|
||
font-size: 15px;
|
||
font-weight: 650;
|
||
color: #fff;
|
||
margin-bottom: 2px;
|
||
}
|
||
.faceswap-modal-sub {
|
||
font-size: 11px;
|
||
color: #555;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
/* ---- tabs ---- */
|
||
.fs-tabs {
|
||
display: flex;
|
||
gap: 0;
|
||
margin-bottom: 14px;
|
||
border-bottom: 1px solid #222;
|
||
}
|
||
.fs-tab {
|
||
background: none;
|
||
border: none;
|
||
color: #555;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
padding: 7px 16px;
|
||
cursor: pointer;
|
||
border-bottom: 2px solid transparent;
|
||
margin-bottom: -1px;
|
||
transition: color 0.15s, border-color 0.15s;
|
||
}
|
||
.fs-tab:hover { color: #aaa; }
|
||
.fs-tab.active { color: #e4e4e7; border-bottom-color: #7c3aed; }
|
||
.fs-tab-content { display: none; }
|
||
.fs-tab-content.active { display: block; }
|
||
/* ---- poses grid (generate tab) ---- */
|
||
.fs-poses-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 6px;
|
||
max-height: 200px;
|
||
overflow-y: auto;
|
||
padding: 2px 0 10px;
|
||
}
|
||
.fs-pose-btn {
|
||
padding: 5px 11px;
|
||
border-radius: 6px;
|
||
border: 1px solid #2a2a2a;
|
||
background: #18181b;
|
||
color: #888;
|
||
font-size: 11px;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
transition: all 0.13s;
|
||
}
|
||
.fs-pose-btn:hover { border-color: #444; color: #ccc; }
|
||
.fs-pose-btn.done {
|
||
border-color: #14532d;
|
||
background: #052e16;
|
||
color: #4ade80;
|
||
}
|
||
.fs-pose-btn.done::before { content: '✓ '; opacity: 0.8; }
|
||
.fs-pose-btn.selected {
|
||
border-color: #6d28d9;
|
||
background: #1e1040;
|
||
color: #c4b5fd;
|
||
font-weight: 600;
|
||
}
|
||
.fs-pose-btn.done.selected { border-color: #6d28d9; background: #1e1040; color: #c4b5fd; }
|
||
.fs-prompt-row { margin-top: 10px; }
|
||
.fs-prompt-input {
|
||
width: 100%; box-sizing: border-box;
|
||
background: #18181b;
|
||
border: 1px solid #2a2a2a;
|
||
border-radius: 7px;
|
||
color: #e4e4e7;
|
||
font-size: 12px;
|
||
padding: 8px 10px;
|
||
}
|
||
.fs-prompt-input:focus { outline: none; border-color: #7c3aed; }
|
||
.fs-prompt-input::placeholder { color: #3f3f46; }
|
||
/* ---- trim tool ---- */
|
||
.template-card .trim-btn {
|
||
position: absolute;
|
||
top: 5px; right: 5px;
|
||
width: 24px; height: 24px;
|
||
background: rgba(0,0,0,0.72);
|
||
border: 1px solid #333;
|
||
border-radius: 5px;
|
||
color: #888;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
opacity: 0;
|
||
transition: opacity 0.15s, color 0.1s;
|
||
z-index: 3;
|
||
}
|
||
.template-card:hover .trim-btn { opacity: 1; }
|
||
.template-card .trim-btn:hover { color: #fff; background: rgba(0,0,0,0.92); }
|
||
.fs-trim-panel {
|
||
background: #0d0d10;
|
||
border: 1px solid #222;
|
||
border-radius: 10px;
|
||
padding: 14px 16px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.fs-trim-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
font-size: 12px;
|
||
color: #aaa;
|
||
font-weight: 600;
|
||
}
|
||
.fs-trim-close {
|
||
background: none; border: none;
|
||
color: #555; cursor: pointer;
|
||
font-size: 15px; line-height: 1;
|
||
}
|
||
.fs-trim-close:hover { color: #ccc; }
|
||
.fs-trim-form {
|
||
display: flex;
|
||
gap: 12px;
|
||
flex-wrap: wrap;
|
||
align-items: flex-end;
|
||
}
|
||
.fs-trim-form label {
|
||
display: flex; flex-direction: column; gap: 4px;
|
||
font-size: 11px; color: #555;
|
||
}
|
||
.fs-trim-form input[type=number],
|
||
.fs-trim-form input[type=text] {
|
||
background: #18181b;
|
||
border: 1px solid #2a2a2a;
|
||
border-radius: 5px;
|
||
color: #e4e4e7;
|
||
font-size: 12px;
|
||
padding: 5px 8px;
|
||
width: 84px;
|
||
}
|
||
.fs-trim-form input[type=text] { width: 150px; }
|
||
.fs-trim-form input:focus { outline: none; border-color: #7c3aed; }
|
||
.fs-trim-actions {
|
||
display: flex;
|
||
gap: 8px;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
margin-top: 10px;
|
||
}
|
||
.fs-trim-status { font-size: 11px; color: #666; margin-right: auto; }
|
||
.template-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||
gap: 12px;
|
||
margin-bottom: 22px;
|
||
}
|
||
.template-card {
|
||
background: #181818;
|
||
border: 2px solid #222;
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s;
|
||
aspect-ratio: 16/9;
|
||
position: relative;
|
||
}
|
||
.template-card:hover {
|
||
border-color: #7c3aed;
|
||
transform: scale(1.03);
|
||
box-shadow: 0 4px 20px rgba(124,58,237,0.25);
|
||
}
|
||
.template-card.selected {
|
||
border-color: #22c55e;
|
||
box-shadow: 0 4px 20px rgba(34,197,94,0.2);
|
||
}
|
||
.template-card video {
|
||
width: 100%; height: 100%;
|
||
object-fit: cover;
|
||
display: block;
|
||
pointer-events: none;
|
||
}
|
||
.template-card-label {
|
||
position: absolute;
|
||
bottom: 0; left: 0; right: 0;
|
||
background: linear-gradient(transparent, rgba(0,0,0,0.8));
|
||
color: #ddd;
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
padding: 12px 8px 5px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.template-empty {
|
||
color: #444;
|
||
font-size: 13px;
|
||
text-align: center;
|
||
padding: 30px;
|
||
grid-column: 1/-1;
|
||
}
|
||
.faceswap-actions {
|
||
display: flex;
|
||
gap: 10px;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
}
|
||
.fs-job-status {
|
||
font-size: 12px;
|
||
color: #888;
|
||
margin-right: auto;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.fs-spinner {
|
||
width: 12px; height: 12px;
|
||
border: 2px solid #333;
|
||
border-top-color: #7c3aed;
|
||
border-radius: 50%;
|
||
animation: spin 0.8s linear infinite;
|
||
flex-shrink: 0;
|
||
will-change: transform;
|
||
}
|
||
.fs-options {
|
||
display: flex;
|
||
gap: 18px;
|
||
padding: 8px 4px 0;
|
||
align-items: center;
|
||
}
|
||
.fs-opt-label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 12px;
|
||
color: #aaa;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
}
|
||
.fs-opt-label input[type=checkbox] {
|
||
accent-color: #7c3aed;
|
||
width: 14px; height: 14px;
|
||
cursor: pointer;
|
||
}
|
||
.fs-opt-label input[type=checkbox]:disabled { opacity: 0.4; cursor: not-allowed; }
|
||
.fs-opt-badge {
|
||
font-size: 10px;
|
||
padding: 1px 6px;
|
||
border-radius: 8px;
|
||
background: #27272a;
|
||
color: #666;
|
||
border: 1px solid #333;
|
||
}
|
||
|
||
/* ---- lb-info tighter layout ---- */
|
||
.lb-info {
|
||
padding: 8px 16px;
|
||
border-radius: 14px;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.lb-section-sep {
|
||
width: 1px; height: 18px;
|
||
background: rgba(255,255,255,0.15);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* ===================== STUDIO VIEW ===================== */
|
||
.studio {
|
||
display: none;
|
||
position: fixed;
|
||
inset: 0;
|
||
background: #0a0a0a;
|
||
z-index: 500;
|
||
flex-direction: column;
|
||
}
|
||
.studio.open { display: flex; }
|
||
|
||
.studio-topbar {
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 8px 14px;
|
||
background: #0f0f0f;
|
||
border-bottom: 1px solid #1a1a1a;
|
||
height: 44px;
|
||
}
|
||
.studio-topbar-left { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
|
||
.studio-topbar-center{ flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; overflow: hidden; }
|
||
.studio-topbar-right { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
|
||
|
||
.studio-body {
|
||
flex: 1; min-height: 0;
|
||
display: flex; overflow: hidden;
|
||
}
|
||
.studio-main {
|
||
flex: 1; min-width: 0;
|
||
display: flex; flex-direction: column; overflow: hidden;
|
||
}
|
||
.studio-viewer-row {
|
||
flex: 1; min-height: 0;
|
||
display: flex; align-items: center; gap: 6px; padding: 10px 6px;
|
||
}
|
||
.studio-viewer {
|
||
flex: 1; min-width: 0; min-height: 0;
|
||
display: flex; align-items: center; justify-content: center;
|
||
position: relative; height: 100%; border-radius: 6px; overflow: hidden;
|
||
}
|
||
.studio-viewer.show-checker {
|
||
background-color: transparent;
|
||
}
|
||
#studioCheckerboard {
|
||
background-color: #888;
|
||
background-image:
|
||
linear-gradient(45deg,#555 25%,transparent 25%),
|
||
linear-gradient(-45deg,#555 25%,transparent 25%),
|
||
linear-gradient(45deg,transparent 75%,#555 75%),
|
||
linear-gradient(-45deg,transparent 75%,#555 75%);
|
||
background-size: 16px 16px;
|
||
background-position: 0 0, 0 8px, 8px -8px, -8px 0;
|
||
z-index: 1;
|
||
}
|
||
#lbImg {
|
||
max-width: 100%; max-height: 100%;
|
||
object-fit: contain; border-radius: 4px;
|
||
cursor: zoom-in; transition: transform 0.25s; display: block;
|
||
z-index: 2; position: relative;
|
||
}
|
||
#lbImg.zoomed { cursor: zoom-out; transform: scale(2.2); transform-origin: center; }
|
||
#lbVideo { max-width: 100%; max-height: 100%; border-radius: 4px; }
|
||
.studio-nav-btn {
|
||
flex-shrink: 0; background: rgba(255,255,255,0.06);
|
||
border: 1px solid rgba(255,255,255,0.1); color: #fff;
|
||
border-radius: 8px; cursor: pointer; font-size: 26px;
|
||
width: 38px; height: 56px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
transition: background 0.2s; user-select: none;
|
||
}
|
||
.studio-nav-btn:hover { background: rgba(255,255,255,0.14); }
|
||
|
||
/* Film strip */
|
||
.studio-filmstrip {
|
||
flex-shrink: 0; height: 76px;
|
||
display: flex; flex-direction: row; gap: 5px;
|
||
overflow-x: auto; padding: 8px 10px;
|
||
background: #0d0d0d; border-top: 1px solid #1a1a1a;
|
||
scrollbar-width: thin; scrollbar-color: #333 transparent;
|
||
align-items: center;
|
||
}
|
||
|
||
/* ===================== SIDEBAR ===================== */
|
||
.studio-sidebar {
|
||
flex-shrink: 0; width: 320px; min-width: 320px;
|
||
display: flex; flex-direction: column;
|
||
background: #0d0d10; border-left: 1px solid #1a1a1a;
|
||
transition: width 0.2s ease, min-width 0.2s ease;
|
||
overflow: hidden; position: relative;
|
||
}
|
||
.studio-sidebar.collapsed { width: 32px; min-width: 32px; }
|
||
|
||
.sidebar-toggle-btn {
|
||
position: absolute; top: 9px; left: 5px;
|
||
width: 22px; height: 22px;
|
||
background: #1a1a1a; border: 1px solid #2a2a2a;
|
||
border-radius: 4px; color: #666; font-size: 11px;
|
||
cursor: pointer; display: flex; align-items: center; justify-content: center;
|
||
z-index: 2; transition: color 0.15s;
|
||
}
|
||
.sidebar-toggle-btn:hover { color: #fff; border-color: #444; }
|
||
|
||
.sidebar-inner {
|
||
flex: 1; min-height: 0;
|
||
display: flex; flex-direction: column;
|
||
padding-top: 36px; overflow: hidden;
|
||
transition: opacity 0.15s;
|
||
}
|
||
.studio-sidebar.collapsed .sidebar-inner { opacity: 0; pointer-events: none; }
|
||
|
||
.sidebar-tabs {
|
||
display: flex; border-bottom: 1px solid #1a1a1a;
|
||
overflow-x: auto; flex-shrink: 0; scrollbar-width: none;
|
||
}
|
||
.sidebar-tabs::-webkit-scrollbar { display: none; }
|
||
.sb-tab {
|
||
background: none; border: none; color: #555;
|
||
font-size: 11px; font-weight: 500; padding: 6px 10px;
|
||
cursor: pointer; border-bottom: 2px solid transparent;
|
||
margin-bottom: -1px; white-space: nowrap;
|
||
transition: color 0.15s, border-color 0.15s;
|
||
}
|
||
.sb-tab:hover { color: #999; }
|
||
.sb-tab.active { color: #e4e4e7; border-bottom-color: #2563eb; }
|
||
|
||
.sb-panels { flex: 1; min-height: 0; overflow: hidden; position: relative; }
|
||
.sb-panel {
|
||
display: none; flex-direction: column;
|
||
height: 100%; overflow-y: auto;
|
||
padding: 12px 13px;
|
||
scrollbar-width: thin; scrollbar-color: #2a2a2a transparent;
|
||
}
|
||
.sb-panel.active { display: flex; }
|
||
|
||
.sb-label {
|
||
font-size: 10px; color: #4a4a4a; font-weight: 600;
|
||
text-transform: uppercase; letter-spacing: 0.6px;
|
||
margin-bottom: 5px;
|
||
}
|
||
.sb-input {
|
||
width: 100%; box-sizing: border-box;
|
||
background: #18181b; border: 1px solid #2a2a2a;
|
||
border-radius: 6px; color: #e4e4e7;
|
||
font-size: 12px; padding: 7px 10px; outline: none;
|
||
transition: border-color 0.15s;
|
||
}
|
||
.sb-input:focus { border-color: #2563eb; box-shadow: 0 0 0 2px rgba(37,99,235,0.15); }
|
||
.sb-input::placeholder { color: #3f3f46; }
|
||
.sb-sep { height: 1px; background: #1a1a1a; margin: 10px 0; flex-shrink: 0; }
|
||
.sb-actions { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 4px; }
|
||
.sb-btn {
|
||
background: #18181b; border: 1px solid #2a2a2a;
|
||
color: #999; font-size: 11px; padding: 5px 9px;
|
||
border-radius: 6px; cursor: pointer;
|
||
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
||
display: inline-flex; align-items: center; gap: 4px;
|
||
text-decoration: none;
|
||
}
|
||
.sb-btn:hover { background: #252525; color: #eee; border-color: #3a3a3a; }
|
||
.sb-btn.primary { background: #1d4ed8; border-color: #2563eb; color: #fff; }
|
||
.sb-btn.primary:hover { background: #2563eb; }
|
||
.sb-btn.danger { background: #7f1d1d; border-color: #991b1b; color: #fca5a5; }
|
||
.sb-btn.danger:hover { background: #991b1b; }
|
||
.sb-btn.teal { background: #0f4c46; border-color: #0f766e; color: #99f6e4; }
|
||
.sb-btn.teal:hover { background: #0f766e; }
|
||
.sb-btn.purple { background: #3b0764; border-color: #6d28d9; color: #c4b5fd; }
|
||
.sb-btn.purple:hover { background: #6d28d9; }
|
||
.sb-btn.amber { background: #431407; border-color: #92400e; color: #fcd34d; }
|
||
.sb-btn.amber:hover { background: #78350f; }
|
||
.sb-btn:disabled { opacity: 0.35; cursor: not-allowed; pointer-events: none; }
|
||
|
||
/* Orbit preview modal */
|
||
#orbitModal {
|
||
display: none; position: fixed; inset: 0; z-index: 9000;
|
||
background: rgba(0,0,0,0.85); align-items: center; justify-content: center;
|
||
}
|
||
#orbitModal.open { display: flex; }
|
||
#orbitModal .orbit-box {
|
||
background: #111; border: 1px solid #2a2a2a; border-radius: 10px;
|
||
padding: 20px; max-width: 600px; width: 90%; position: relative;
|
||
}
|
||
#orbitModal .orbit-title {
|
||
font-size: 13px; font-weight: 600; color: #ccc; margin-bottom: 12px;
|
||
}
|
||
#orbitModal video {
|
||
width: 100%; border-radius: 6px; background: #000;
|
||
}
|
||
#orbitModal .orbit-status {
|
||
font-size: 11px; color: #666; margin: 8px 0 0;
|
||
}
|
||
#orbitModal .orbit-status.ok { color: #4ade80; }
|
||
#orbitModal .orbit-status.err { color: #f87171; }
|
||
#orbitModal .orbit-close {
|
||
position: absolute; top: 10px; right: 12px;
|
||
background: none; border: none; color: #555; font-size: 18px;
|
||
cursor: pointer; line-height: 1;
|
||
}
|
||
#orbitModal .orbit-close:hover { color: #ccc; }
|
||
#orbitModal .orbit-actions {
|
||
display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; align-items: center;
|
||
}
|
||
.orbit-engine-toggle { display: flex; gap: 4px; margin-bottom: 10px; }
|
||
.orbit-engine-toggle button {
|
||
flex: 1; padding: 5px 10px; font-size: 11px; border-radius: 4px;
|
||
border: 1px solid #333; background: #1a1a1a; color: #777; cursor: pointer;
|
||
}
|
||
.orbit-engine-toggle button.active {
|
||
background: #2563eb; color: #fff; border-color: #2563eb;
|
||
}
|
||
.orbit-qwen-controls { display: none; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 6px; }
|
||
.orbit-qwen-controls.visible { display: flex; }
|
||
#turntableBgStatus {
|
||
font-size: 10px; color: #555; margin-top: 4px;
|
||
display: flex; align-items: center; gap: 4px;
|
||
}
|
||
#turntableBgStatus .tbs-dot {
|
||
width: 6px; height: 6px; border-radius: 50%; background: #555; flex-shrink: 0;
|
||
}
|
||
#turntableBgStatus .tbs-dot.active { background: #f59e0b; animation: pulse 1.2s ease-in-out infinite; }
|
||
#turntableBgStatus .tbs-dot.done { background: #4ade80; animation: none; }
|
||
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} }
|
||
.orbit-badge {
|
||
font-size: 9px; background: #f59e0b; color: #000; border-radius: 9px;
|
||
padding: 1px 5px; margin-left: 4px; font-weight: 700; vertical-align: middle;
|
||
}
|
||
/* Frame flipper player */
|
||
.orbit-player { display: none; flex-direction: column; align-items: stretch; gap: 0; }
|
||
.orbit-player.visible { display: flex; }
|
||
/* Fixed-height image container — prevents layout jumps when frames differ in size */
|
||
.orbit-img-wrap {
|
||
width: 100%; aspect-ratio: 3/4; position: relative; border-radius: 6px; overflow: hidden;
|
||
background: repeating-conic-gradient(#222 0% 25%, #2a2a2a 0% 50%) 0 0 / 16px 16px;
|
||
flex-shrink: 0;
|
||
}
|
||
.orbit-img-wrap img {
|
||
position: absolute; inset: 0; width: 100%; height: 100%;
|
||
object-fit: contain; display: block;
|
||
}
|
||
/* Controls bar is fixed height — never moves */
|
||
.orbit-player-controls {
|
||
display: flex; gap: 8px; align-items: center; flex-wrap: nowrap;
|
||
justify-content: space-between; width: 100%; padding: 6px 0 0;
|
||
flex-shrink: 0; min-height: 32px;
|
||
}
|
||
.orbit-player-controls button { padding: 3px 10px; font-size: 11px; flex-shrink: 0; }
|
||
.orbit-frame-counter { font-size: 10px; color: #555; min-width: 48px; text-align: center; flex-shrink: 0; }
|
||
/* Orbit sidebar tab */
|
||
#sbPanelOrbit { flex-direction: column; gap: 0; overflow-y: auto; }
|
||
.orbit-tab-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; padding: 6px 0; }
|
||
.orbit-tab-card {
|
||
background: #1a1a1a; border: 1px solid #2a2a2a; border-radius: 6px;
|
||
padding: 6px; cursor: pointer; transition: border-color .15s;
|
||
display: flex; flex-direction: column; gap: 4px; align-items: center;
|
||
}
|
||
.orbit-tab-card:hover { border-color: #f59e0b; }
|
||
/* Card thumbnails: fixed square with contain (shows whole body, no crop) */
|
||
.orbit-tab-card .otc-img-wrap {
|
||
width: 100%; aspect-ratio: 1/1; position: relative; border-radius: 4px; overflow: hidden;
|
||
background: repeating-conic-gradient(#222 0% 25%, #2a2a2a 0% 50%) 0 0 / 10px 10px;
|
||
flex-shrink: 0;
|
||
}
|
||
.orbit-tab-card .otc-img-wrap img {
|
||
position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
|
||
}
|
||
.orbit-tab-card .otc-name { font-size: 10px; color: #aaa; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 100%; }
|
||
.orbit-tab-card .otc-progress { font-size: 9px; color: #555; flex-shrink: 0; }
|
||
.orbit-tab-card.complete { border-color: #1a3a1a; }
|
||
.orbit-tab-card.complete:hover { border-color: #4ade80; }
|
||
/* Full viewer in sidebar tab */
|
||
.orbit-full-player { display: none; flex-direction: column; gap: 6px; padding: 0 0 8px; flex-shrink: 0; }
|
||
.orbit-full-player.visible { display: flex; }
|
||
.orbit-full-img-wrap {
|
||
width: 100%; aspect-ratio: 3/4; position: relative; border-radius: 6px; overflow: hidden;
|
||
background: repeating-conic-gradient(#222 0% 25%, #2a2a2a 0% 50%) 0 0 / 16px 16px;
|
||
flex-shrink: 0;
|
||
}
|
||
.orbit-full-img-wrap img {
|
||
position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
|
||
}
|
||
.sb-status { font-size: 11px; color: #555; margin: 4px 0; }
|
||
.sb-status.ok { color: #4ade80; }
|
||
.sb-status.err { color: #f87171; }
|
||
.sb-spinner {
|
||
width: 10px; height: 10px; border: 2px solid #333;
|
||
border-top-color: #2563eb; border-radius: 50%;
|
||
animation: spin 0.8s linear infinite; display: inline-block;
|
||
margin-right: 4px; vertical-align: middle;
|
||
will-change: transform;
|
||
}
|
||
.sb-source-refs { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
|
||
.sb-source-refs img {
|
||
width: 28px; height: 28px; object-fit: cover; border-radius: 3px;
|
||
cursor: pointer; opacity: 0.7; border: 1px solid #333;
|
||
transition: opacity 0.15s, border-color 0.15s;
|
||
}
|
||
.sb-source-refs img:hover { opacity: 1; border-color: #555; }
|
||
|
||
/* poses grid in Generate tab */
|
||
.sb-poses-grid { display: flex; flex-wrap: wrap; gap: 5px; padding-bottom: 8px; }
|
||
.sb-pose-btn {
|
||
padding: 4px 10px; border-radius: 5px;
|
||
border: 1px solid #2a2a2a; background: #18181b;
|
||
color: #777; font-size: 11px; cursor: pointer; user-select: none;
|
||
transition: all 0.12s;
|
||
}
|
||
.sb-pose-btn:hover { border-color: #444; color: #ccc; }
|
||
.sb-pose-btn.done { border-color: #14532d; background: #052e16; color: #4ade80; }
|
||
.sb-pose-btn.done::before { content: '✓ '; opacity: 0.8; }
|
||
.sb-pose-btn.selected { border-color: #6d28d9; background: #1e1040; color: #c4b5fd; font-weight: 600; }
|
||
.sb-pose-btn.beta-badge::after {
|
||
content: 'β'; font-size: 8px; background: #78350f; color: #fbbf24;
|
||
padding: 1px 3px; border-radius: 2px; margin-left: 3px; vertical-align: middle;
|
||
}
|
||
|
||
.sb-camera-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 4px; margin-bottom: 8px; }
|
||
.sb-angle-btn {
|
||
padding: 5px 2px; border-radius: 5px;
|
||
border: 1px solid #2a2a2a; background: #18181b;
|
||
color: #777; font-size: 11px; cursor: pointer; user-select: none;
|
||
text-align: center; transition: all 0.12s;
|
||
}
|
||
.sb-angle-btn:hover { border-color: #444; color: #ccc; }
|
||
.sb-angle-btn.done { border-color: #14532d; background: #052e16; color: #4ade80; position: relative; }
|
||
.sb-angle-btn.done::after { content: '✓'; position: absolute; top: 1px; right: 3px; font-size: 9px; color: #4ade80; opacity: 0.9; }
|
||
.sb-angle-btn.selected { border-color: #0e7490; background: #082f49; color: #7dd3fc; font-weight: 600; }
|
||
|
||
/* Generate panel: sticky footer (custom prompt + Generate) and selected-ref thumbnails */
|
||
.sb-gen-footer {
|
||
position: sticky; bottom: 0; margin: 8px -13px -12px; padding: 10px 13px;
|
||
background: #0d0d10; border-top: 1px solid #1f1f24;
|
||
}
|
||
.sb-gen-refs { display: flex; gap: 5px; margin-bottom: 8px; flex-wrap: wrap; }
|
||
.sb-gen-refs .sb-gen-ref { position: relative; width: 40px; height: 40px; flex-shrink: 0; }
|
||
.sb-gen-refs .sb-gen-ref img {
|
||
width: 40px; height: 40px; object-fit: cover; border-radius: 4px;
|
||
border: 1px solid #3d2008; background: #111;
|
||
}
|
||
.sb-gen-refs .sb-gen-ref-badge {
|
||
position: absolute; top: -4px; left: -4px; background: #f97316; color: #111;
|
||
font-size: 9px; font-weight: 700; border-radius: 50%; width: 15px; height: 15px;
|
||
display: flex; align-items: center; justify-content: center; line-height: 1;
|
||
}
|
||
.sb-gen-refs .sb-gen-ref-x {
|
||
position: absolute; top: -4px; right: -4px; background: #dc2626; color: #fff;
|
||
font-size: 11px; font-weight: 700; border-radius: 50%; width: 15px; height: 15px;
|
||
display: none; align-items: center; justify-content: center; line-height: 1;
|
||
}
|
||
.sb-gen-refs .sb-gen-ref:hover .sb-gen-ref-x { display: flex; }
|
||
.sb-gen-refs .sb-gen-ref:hover img { border-color: #dc2626; }
|
||
#studioAngleBar {
|
||
position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%);
|
||
display: flex; gap: 4px; opacity: 0; transition: opacity 0.2s;
|
||
z-index: 10; pointer-events: none;
|
||
}
|
||
.studio-viewer:hover #studioAngleBar { opacity: 1; pointer-events: all; }
|
||
#studioAngleBar button {
|
||
font-size: 15px; background: rgba(0,0,0,0.65); border: 1px solid rgba(255,255,255,0.18);
|
||
border-radius: 4px; color: #fff; width: 34px; height: 34px; cursor: pointer; line-height: 1;
|
||
transition: background 0.12s; position: relative;
|
||
}
|
||
#studioAngleBar button:hover { background: rgba(255,255,255,0.18); }
|
||
#studioAngleBar button[title]:hover::after {
|
||
content: attr(title); position: absolute; bottom: 38px; left: 50%; transform: translateX(-50%);
|
||
background: rgba(0,0,0,0.8); color: #fff; font-size: 10px; padding: 2px 6px;
|
||
border-radius: 3px; white-space: nowrap; pointer-events: none;
|
||
}
|
||
|
||
/* template grid (faceswap / scenery) */
|
||
.sb-template-grid {
|
||
display: grid; grid-template-columns: 1fr 1fr;
|
||
gap: 6px; margin-bottom: 8px;
|
||
}
|
||
.sb-template-card {
|
||
background: #181818; border: 2px solid #1e1e1e;
|
||
border-radius: 7px; overflow: hidden; cursor: pointer;
|
||
aspect-ratio: 16/9; position: relative;
|
||
transition: border-color 0.15s, transform 0.13s;
|
||
}
|
||
.sb-template-card:hover { border-color: #7c3aed; transform: scale(1.02); }
|
||
.sb-template-card.selected { border-color: #22c55e; }
|
||
.sb-template-card video,
|
||
.sb-template-card img { width:100%; height:100%; object-fit:cover; pointer-events:none; }
|
||
.sb-template-card video { position:absolute; inset:0; }
|
||
.sb-template-label {
|
||
position: absolute; bottom: 0; left: 0; right: 0;
|
||
background: linear-gradient(transparent,rgba(0,0,0,0.82));
|
||
color: #ccc; font-size: 9px; padding: 10px 5px 3px;
|
||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none;
|
||
}
|
||
.sb-template-trim-btn {
|
||
position: absolute; top: 3px; right: 3px;
|
||
width: 20px; height: 20px;
|
||
background: rgba(0,0,0,0.7); border: 1px solid #333;
|
||
border-radius: 4px; color: #888; font-size: 11px;
|
||
cursor: pointer; display: flex; align-items: center; justify-content: center;
|
||
opacity: 0; transition: opacity 0.15s, color 0.1s;
|
||
z-index: 2;
|
||
}
|
||
.sb-template-card:hover .sb-template-trim-btn { opacity: 1; }
|
||
.sb-template-trim-btn:hover { color: #fff; background: rgba(0,0,0,0.92); }
|
||
|
||
/* faceswap options */
|
||
.sb-opt-label {
|
||
display: flex; align-items: center; gap: 6px;
|
||
font-size: 11px; color: #888; cursor: pointer; user-select: none;
|
||
}
|
||
.sb-opt-label input[type=checkbox] { accent-color: #7c3aed; width: 13px; height: 13px; cursor: pointer; }
|
||
.sb-opt-label input[type=checkbox]:disabled { opacity: 0.4; cursor: not-allowed; }
|
||
.sb-opt-badge {
|
||
font-size: 9px; padding: 1px 5px; border-radius: 7px;
|
||
background: #27272a; color: #555; border: 1px solid #333;
|
||
}
|
||
|
||
/* ===================== CLIP TIMELINE ===================== */
|
||
.clt-wrapper {
|
||
background: #0d0d10; border: 1px solid #1e1e1e;
|
||
border-radius: 8px; overflow: hidden; margin: 8px 0;
|
||
}
|
||
.clt-video-preview {
|
||
width: 100%; aspect-ratio: 16/9; background: #000; display: block;
|
||
}
|
||
.clt-body { padding: 10px 12px 12px; }
|
||
.clt-track {
|
||
position: relative; height: 32px; background: #1a1a1a;
|
||
border-radius: 4px; cursor: pointer; user-select: none;
|
||
margin-bottom: 6px; overflow: visible;
|
||
}
|
||
.clt-bg { position: absolute; inset: 0; background: #222; border-radius: 4px; }
|
||
.clt-range {
|
||
position: absolute; top: 0; bottom: 0;
|
||
background: rgba(37,99,235,0.3); pointer-events: none;
|
||
}
|
||
.clt-handle {
|
||
position: absolute; top: 0; bottom: 0; width: 10px;
|
||
background: #2563eb; border-radius: 3px; cursor: ew-resize;
|
||
transform: translateX(-50%); z-index: 2;
|
||
display: flex; align-items: center; justify-content: center;
|
||
touch-action: none;
|
||
}
|
||
.clt-handle::after {
|
||
content: ''; width: 2px; height: 14px;
|
||
background: rgba(255,255,255,0.55); border-radius: 1px;
|
||
}
|
||
.clt-handle.end { background: #7c3aed; }
|
||
.clt-playhead {
|
||
position: absolute; top: -3px; bottom: -3px; width: 2px;
|
||
background: #f59e0b; pointer-events: none; z-index: 1;
|
||
transform: translateX(-50%);
|
||
}
|
||
.clt-labels {
|
||
display: flex; justify-content: space-between;
|
||
font-size: 10px; color: #555; margin-bottom: 8px;
|
||
}
|
||
.clt-name { font-size: 10px; color: #444; margin-bottom: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
|
||
/* ===================== SCENERY TAB ===================== */
|
||
.scene-frame-preview {
|
||
width: 100%; aspect-ratio: 16/9; background: #111;
|
||
border-radius: 6px; overflow: hidden; display: none;
|
||
margin-bottom: 8px; position: relative;
|
||
}
|
||
.scene-frame-preview img { width:100%; height:100%; object-fit:contain; display:block; }
|
||
.scene-scrubber { width: 100%; accent-color: #2563eb; margin: 6px 0 2px; cursor: pointer; height: 18px; }
|
||
.scene-scrubber:disabled { opacity: 0.4; cursor: not-allowed; }
|
||
.scene-captured-badge {
|
||
position: absolute; top: 6px; left: 6px;
|
||
background: rgba(34,197,94,0.9); color: #04210f;
|
||
border-radius: 5px; padding: 2px 8px; font-size: 11px; font-weight: 600;
|
||
}
|
||
/* 3 interactive reference slots (image1 / image2 / image3) */
|
||
.scene-slots { display: flex; gap: 6px; margin-bottom: 12px; }
|
||
.scene-slot {
|
||
flex: 1; min-width: 0; background: #141414; border: 1px solid #262626;
|
||
border-radius: 7px; overflow: hidden; text-align: center;
|
||
}
|
||
.scene-slot.filled { border-color: #2563eb; cursor: grab; }
|
||
.scene-slot.empty { border-style: dashed; }
|
||
.scene-slot.drop-hover { border-color: #60a5fa; background: #11203f; }
|
||
.scene-slot-thumb {
|
||
width: 100%; aspect-ratio: 1/1; background: #0c0c0c; position: relative;
|
||
display: flex; align-items: center; justify-content: center;
|
||
}
|
||
.scene-slot-thumb img { width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
|
||
.scene-slot-thumb .ph { font-size: 18px; color: #333; }
|
||
.scene-slot-x {
|
||
position: absolute; top: 2px; right: 2px; width: 16px; height: 16px; line-height: 14px;
|
||
background: rgba(0,0,0,0.7); border: 1px solid #444; border-radius: 4px;
|
||
color: #ddd; font-size: 10px; cursor: pointer; padding: 0;
|
||
}
|
||
.scene-slot-x:hover { background: #b91c1c; color: #fff; }
|
||
.scene-slot-kind { position: absolute; bottom: 1px; left: 2px; font-size: 10px; }
|
||
.scene-slot-cap { font-size: 8.5px; color: #888; padding: 3px 2px 2px; line-height: 1.25; }
|
||
.scene-slot-cap b { color: #bbb; display: block; font-size: 9px; }
|
||
|
||
/* ===================== SEGMENT TAB ===================== */
|
||
.segment-checker-hint { font-size: 10px; color: #444; margin-top: 6px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="privacyOverlay" class="privacy-overlay" onclick="if(privacyMode)disablePrivacyOverlay()">
|
||
<div class="priv-topbar">
|
||
<div class="priv-logo">S</div>
|
||
AI Assist <span class="priv-sub">· Qwen-Image-Edit Rapid-AIO v23</span>
|
||
</div>
|
||
<div class="priv-chat">
|
||
<div class="priv-msg-row">
|
||
<div class="priv-msg-inner">
|
||
<div class="priv-avatar user">You</div>
|
||
<div class="priv-text">What is this system and what does it run on?</div>
|
||
</div>
|
||
</div>
|
||
<div class="priv-msg-row assistant">
|
||
<div class="priv-msg-inner">
|
||
<div class="priv-avatar bot">AI</div>
|
||
<div class="priv-text">
|
||
This is a self-hosted AI for generative editing, multi-view character rendering, background removal, face swapping, and group-based asset management. It runs on a local RTX A6000 GPU (CUDA 12.4, 48 GB VRAM) using the <strong>Qwen 2.5 VL GGUF</strong> model (Rapid-NSFW-v23 Q8_0) via ComfyUI. The frontend is a single-page browser app (car.html) talking to a FastAPI backend on :8500.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="priv-msg-row">
|
||
<div class="priv-msg-inner">
|
||
<div class="priv-avatar user">You</div>
|
||
<div class="priv-text">Walk me through the image generation flow.</div>
|
||
</div>
|
||
</div>
|
||
<div class="priv-msg-row assistant">
|
||
<div class="priv-msg-inner">
|
||
<div class="priv-avatar bot">AI</div>
|
||
<div class="priv-text">
|
||
When a generation is triggered the pipeline is:<br><br>
|
||
<strong>1. Prompt cleaning</strong> — if the prompt contains "transparent background", that keyword is stripped so Qwen renders a clean solid-BG image instead of producing washed-out halos.<br><br>
|
||
<strong>2. ComfyUI queue</strong> — the reference image is uploaded to ComfyUI (:8188) and injected into <em>image1</em>. An optional wireframe frame extracted from a pose-guide video goes into <em>image2</em>. The workflow graph (workflow_qwen_edit.json) is patched with the prompt, dimensions, seed, and sampler params, then submitted via POST /prompt.<br><br>
|
||
<strong>3. Qwen inference</strong> — the GGUF model (≈8 GB VRAM), text encoder (FP8), and VAE decode the latent into a PNG.<br><br>
|
||
<strong>4. SAM2 post-processing</strong> — if transparent was requested, SAM2 point-prompt segmentation removes the background using 12 foreground seed points (hair → shoes, shoulder / hip lateral spread) and 7 background points (corners + edge midpoints). A coverage sanity check (5–92 %) falls back to rembg if the mask is implausible.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="priv-msg-row">
|
||
<div class="priv-msg-inner">
|
||
<div class="priv-avatar user">You</div>
|
||
<div class="priv-text">What are the main API endpoints?</div>
|
||
</div>
|
||
</div>
|
||
<div class="priv-msg-row assistant">
|
||
<div class="priv-msg-inner">
|
||
<div class="priv-avatar bot">AI</div>
|
||
<div class="priv-text">
|
||
The FastAPI service (:8500) exposes:<br><br>
|
||
<strong>GET /images</strong> — full image list with PostgreSQL metadata (group, prompt, pose, sort order, archived flag).<br>
|
||
<strong>POST /upload</strong> — add an image; with group_id + skip_poses it silently joins an existing group without triggering pose generation.<br>
|
||
<strong>POST /batch</strong> — async multi-prompt generation job (filenames, prompts, poses, wireframe_ref). Poll progress via GET /jobs/{id}.<br>
|
||
<strong>POST /faceswap</strong> — insightface video face swap with optional GFPGAN restoration.<br>
|
||
<strong>POST /remove-background-sam/{f}</strong> — SAM2 BG removal to a .nobg.png sidecar.<br>
|
||
<strong>POST /images/{f}/set-preferred</strong> — moves image to group slot 0 and queues face extraction in the background.<br>
|
||
<strong>GET /wireframe/frame/{name}?t=</strong> — extract a frame at normalised time t∈[0,1] from a wireframe pose video.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="priv-msg-row">
|
||
<div class="priv-msg-inner">
|
||
<div class="priv-avatar user">You</div>
|
||
<div class="priv-text">How does the face-book and multi-view workflow work?</div>
|
||
</div>
|
||
</div>
|
||
<div class="priv-msg-row assistant">
|
||
<div class="priv-msg-inner">
|
||
<div class="priv-avatar bot">AI</div>
|
||
<div class="priv-text">
|
||
<strong>Face-book:</strong> marking an image as "preferred" (star button) moves it to sort_order=0 in the group and triggers a background task. insightface detects the largest face, adds 50 % padding on the sides and 200 % headroom above, and saves a <em>{group_id}_face.png</em> crop. This appears as a 72 px thumbnail in the studio Info tab for fast face-reference lookup.<br><br>
|
||
<strong>Multi-view / camera angles:</strong> the Generate tab offers 12 camera angle presets — 8 absolute (Front, ¾ Left/Right, Side, Back, High, Low) and 4 relative (±45°, ±90°). Each fires a /batch request with the angle prompt injected and pose=null. The wireframe pose guide (a .mp4 video scrubbed to any frame) is passed as image2 in the Qwen workflow to constrain body layout without ControlNet — the controlnet model folder is empty by design.<br><br>
|
||
<strong>Group management:</strong> all images belong to groups stored in PostgreSQL. Clipboard paste (Ctrl+V) while a group is open offers "add to this group — no poses" or "new group with pose generation". Archive hides images from the default view without deleting them.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="priv-inputbar">
|
||
<div class="priv-inputbox">
|
||
<div class="priv-fakeinput">Ask a follow-up question…</div>
|
||
<div class="priv-send">➤</div>
|
||
</div>
|
||
<div class="priv-hint">Press P or click to resume • Qwen-Image-Edit Rapid-AIO v23</div>
|
||
</div>
|
||
|
||
<!-- Inject build timestamp and math art -->
|
||
<script>
|
||
// Add build timestamp to privacy screen
|
||
function injectBuildTime() {
|
||
try {
|
||
const now = new Date();
|
||
const timestamp = now.toLocaleString('en-US', {
|
||
year: 'numeric',
|
||
month: 'short',
|
||
day: 'numeric',
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
second: '2-digit',
|
||
timeZoneName: 'short'
|
||
});
|
||
|
||
// Add timestamp to the privacy screen title
|
||
const titleElement = document.querySelector('.priv-sub');
|
||
if (titleElement) {
|
||
titleElement.innerHTML += ` • Built: ${timestamp}`;
|
||
}
|
||
|
||
} catch (e) {
|
||
console.error('Error injecting build time or math art:', e);
|
||
}
|
||
}
|
||
|
||
// Call the function when page loads
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', injectBuildTime);
|
||
} else {
|
||
injectBuildTime();
|
||
}
|
||
</script>
|
||
|
||
</div>
|
||
|
||
<div class="header">
|
||
<h1>
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
||
<circle cx="8.5" cy="8.5" r="1.5"/>
|
||
<path d="M21 15l-5-5L5 21"/>
|
||
</svg>
|
||
Studio Monitor <span>AI</span>
|
||
</h1>
|
||
<div class="controls">
|
||
<span class="count" id="count">0 images</span>
|
||
<button class="btn" onclick="setIntervalTime(30)">30s</button>
|
||
<button class="btn" onclick="setIntervalTime(120)">2m</button>
|
||
<button class="btn primary" onclick="refreshNow()">Refresh Now</button>
|
||
<button class="btn" id="archiveViewBtn" onclick="toggleArchiveView()" title="View archived items">📦 Archive</button>
|
||
<a href="trash.html" class="btn" title="View database inconsistencies" style="text-decoration:none;display:inline-flex;align-items:center;">🗑️ Trash</a>
|
||
<button class="btn" id="selectBtn" onclick="toggleSelectMode()">Select</button>
|
||
<button class="btn" id="privacyBtn" onclick="togglePrivacyMode()" title="Privacy mode — hides content on focus loss (P)">🔓</button>
|
||
</div>
|
||
<div class="status">
|
||
<span class="status-dot" id="statusDot"></span>
|
||
<span id="statusText">Auto-refresh: 2m</span>
|
||
</div>
|
||
<div class="upload-zone">
|
||
<input type="file" id="uploadInput" multiple onchange="handleUpload(event)" />
|
||
<button class="btn" onclick="document.getElementById('uploadInput').click()" title="Or paste image from clipboard (Ctrl+V)">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12"/>
|
||
</svg>
|
||
Upload
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="gallery" id="gallery">
|
||
<div class="empty-state">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
||
<circle cx="8.5" cy="8.5" r="1.5"/>
|
||
<path d="M21 15l-5-5L5 21"/>
|
||
</svg>
|
||
<h2>No images found</h2>
|
||
<p>Place this HTML file in your image folder, or configure the path below.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="toast" id="toast"></div>
|
||
|
||
<!-- Studio view (replaces old lightbox + faceswap modal) -->
|
||
<div id="studio" class="studio">
|
||
<!-- Topbar -->
|
||
<div class="studio-topbar">
|
||
<div class="studio-topbar-left">
|
||
<button class="btn" onclick="closeStudio()" style="font-size:11px;padding:4px 10px">✕ Gallery</button>
|
||
<button class="btn" id="lbBackBtn" onclick="jumpToPrevGroup()" style="font-size:11px;padding:4px 10px;display:none" title="Go back to previously opened group">↺ Back</button>
|
||
</div>
|
||
<div class="studio-topbar-center">
|
||
<input type="text" id="lbGroupNameInput"
|
||
style="background:transparent;border:none;border-bottom:1px solid #2a2a2a;color:#fff;font-size:13px;font-weight:600;padding:2px 5px;min-width:80px;max-width:200px;outline:none"
|
||
placeholder="Group name…"
|
||
onblur="updateGroupName()" onkeydown="if(event.key==='Enter')this.blur()"
|
||
title="Group name (applies to all images in this group)" />
|
||
<span id="lbPoseTag" style="font-size:11px;color:#f59e0b;display:none"></span>
|
||
<span id="lbImgDims" style="font-size:11px;color:#444;margin:0 8px"></span>
|
||
<span id="lbCounter" style="font-size:12px;color:#555"></span>
|
||
</div>
|
||
<div class="studio-topbar-right">
|
||
<button class="btn" id="sidebarToggleTopBtn" onclick="toggleSidebar()" style="font-size:11px;padding:4px 10px" title="Toggle toolbox">Tools</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Body: main viewer + sidebar -->
|
||
<div class="studio-body">
|
||
|
||
<!-- Main area: viewer + filmstrip -->
|
||
<div class="studio-main">
|
||
<div class="studio-viewer-row">
|
||
<button class="studio-nav-btn" id="lbPrev" onclick="lbNav(-1)">‹</button>
|
||
<div class="studio-viewer" id="studioViewer">
|
||
<div id="studioCheckerboard" style="display:none;position:absolute;pointer-events:none;z-index:1"></div>
|
||
<img id="lbImg" src="" alt="" onclick="toggleImageZoom(this)" />
|
||
<video id="lbVideo" style="display:none;max-width:100%;max-height:100%;border-radius:4px" controls autoplay muted loop></video>
|
||
<div class="lb-hidden-badge">Hidden from preview</div>
|
||
<div id="studioAngleBar"></div>
|
||
</div>
|
||
<button class="studio-nav-btn" id="lbNext" onclick="lbNav(1)">›</button>
|
||
</div>
|
||
<!-- Film strip / variant thumbnails -->
|
||
<div class="studio-filmstrip" id="lbVariantStrip"></div>
|
||
<!-- Slideshow controls -->
|
||
<div id="slideshowBar" style="display:flex;align-items:center;gap:6px;padding:4px 8px;background:#111;border-top:1px solid #1a1a1a;flex-shrink:0">
|
||
<button onclick="toggleSlideshow()" id="slideshowBtn" style="background:none;border:1px solid #333;color:#888;font-size:11px;padding:2px 8px;border-radius:4px;cursor:pointer">▶ Slideshow</button>
|
||
<button onclick="toggleOrbitFast()" id="lbOrbitFastBtn" style="background:none;border:1px solid #333;color:#888;font-size:11px;padding:2px 8px;border-radius:4px;cursor:pointer" title="Loop current filmstrip at ~12fps">▶ Orbit</button>
|
||
<select id="slideshowInterval" onchange="updateSlideshowInterval()" style="background:#111;border:1px solid #333;color:#888;font-size:11px;padding:2px 4px;border-radius:4px">
|
||
<option value="2000">2s</option>
|
||
<option value="4000" selected>4s</option>
|
||
<option value="8000">8s</option>
|
||
<option value="15000">15s</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Sidebar toolbox -->
|
||
<div id="studioSidebar" class="studio-sidebar">
|
||
<button class="sidebar-toggle-btn" id="sidebarInnerToggle" onclick="toggleSidebar()" title="Collapse/expand toolbox">⊟</button>
|
||
<div id="sidebarInner" class="sidebar-inner">
|
||
<div class="sidebar-tabs">
|
||
<button class="sb-tab active" data-tab="info" onclick="switchSidebarTab('info')">Info</button>
|
||
<button class="sb-tab" data-tab="generate" onclick="switchSidebarTab('generate')">Generate</button>
|
||
<button class="sb-tab" data-tab="faceswap" onclick="switchSidebarTab('faceswap')">Face</button>
|
||
<button class="sb-tab" data-tab="scenery" onclick="switchSidebarTab('scenery')">Scene</button>
|
||
<button class="sb-tab" data-tab="segment" onclick="switchSidebarTab('segment')">SAM</button>
|
||
<button class="sb-tab" data-tab="orbit" onclick="switchSidebarTab('orbit')" id="sbTabOrbit">Orbit</button>
|
||
</div>
|
||
<div class="sb-panels">
|
||
<!-- Info panel: static HTML with existing lb* IDs so updateStudio() just works -->
|
||
<div class="sb-panel active" id="sbPanelInfo">
|
||
<div class="sb-label">Image name</div>
|
||
<input type="text" class="sb-input" id="lbNameInput"
|
||
placeholder="Image name…"
|
||
onblur="updateName()" onkeydown="if(event.key==='Enter')this.blur()"
|
||
style="margin-bottom:8px">
|
||
<div id="lbClipDesc" class="sb-status" style="margin-bottom:6px;font-size:11px;color:#555"></div>
|
||
<div id="lbGenPromptWrap" style="display:none;margin-bottom:8px">
|
||
<div class="sb-label" style="margin-bottom:3px">Generation prompt</div>
|
||
<div id="lbGenPrompt" style="font-size:11px;color:#888;line-height:1.4;word-break:break-word"></div>
|
||
</div>
|
||
<div id="lbFaceBook" style="display:none;margin-bottom:8px">
|
||
<div class="sb-label" style="margin-bottom:4px">Face reference</div>
|
||
<div style="display:flex;gap:8px;align-items:flex-start">
|
||
<img id="lbFaceThumb" style="width:72px;height:72px;object-fit:cover;border-radius:6px;border:1px solid #333;cursor:pointer;flex-shrink:0" onclick="window.open(this.src,'_blank')" title="Face crop — click to view full">
|
||
<button class="sb-btn" onclick="lbFindSimilarFaces()" title="Find groups with matching faces">Similar faces</button>
|
||
</div>
|
||
</div>
|
||
<div class="sb-sep"></div>
|
||
<div class="sb-label">Order & visibility</div>
|
||
<div class="sb-actions" style="margin-bottom:8px">
|
||
<button class="sb-btn" id="lbMoveUp" style="display:none" onclick="lbMoveInGroup(-1)" title="Move earlier in group">↑ Earlier</button>
|
||
<button class="sb-btn" id="lbMoveDown" style="display:none" onclick="lbMoveInGroup(1)" title="Move later in group">↓ Later</button>
|
||
<button class="sb-btn" id="lbPreferredBtn" style="display:none;color:#f59e0b" onclick="lbSetPreferred()" title="Set as preferred reference">★ Preferred</button>
|
||
<button class="sb-btn" id="lbEyeBtn" onclick="lbToggleHidden()" title="Hide/show in cycling">👁 Visibility</button>
|
||
</div>
|
||
<div class="sb-sep"></div>
|
||
<div class="sb-label">Actions</div>
|
||
<div class="sb-actions" style="margin-bottom:8px">
|
||
<button class="sb-btn" onclick="tagCurrentImage()" title="Re-run tagger">Re-tag</button>
|
||
<button class="sb-btn purple" id="lbUndressBtn" style="display:none" onclick="lbUndress()">Undress</button>
|
||
<button class="sb-btn teal" id="lbFaceswapBtn" onclick="lbFaceswap()">Faceswap →</button>
|
||
</div>
|
||
<div class="sb-actions" style="margin-bottom:8px">
|
||
<button class="sb-btn" id="lbExtract" style="display:none" onclick="extractCurrentImage()">Extract</button>
|
||
<button class="sb-btn" id="lbNoBgBtn" onclick="lbRemoveBg()">No BG</button>
|
||
<button class="sb-btn" id="lbInvertAlphaBtn" onclick="lbInvertAlpha()" title="Invert transparency — fixes when the wrong segment was kept">Invert α</button>
|
||
<button class="sb-btn" id="lbCropBtn" style="display:none" onclick="lbAutoCrop()" title="Crop away transparent border">Crop</button>
|
||
<button class="sb-btn" id="lbDuplicateBtn" onclick="lbDuplicate()" title="Duplicate image into same group">Duplicate</button>
|
||
<button class="sb-btn" id="lbManualCropBtn" onclick="startManualCrop()" title="Drag to crop image">Crop…</button>
|
||
<button class="sb-btn" id="lbManualPadBtn" onclick="startManualPad()" title="Expand canvas by padding each side">Expand…</button>
|
||
<button class="sb-btn" id="lbRotateLeftBtn" onclick="lbRotate(-90)" title="Rotate 90° counter-clockwise">⟲ 90°</button>
|
||
<button class="sb-btn" id="lbRotateRightBtn" onclick="lbRotate(90)" title="Rotate 90° clockwise">⟳ 90°</button>
|
||
<button class="sb-btn" id="lbPoseBtn" onclick="lbTogglePose()" title="Preview body pose skeleton">Pose</button>
|
||
<button class="sb-btn" id="lbMakeVideoBtn" onclick="lbMakeVideo()" title="Stitch group images (or Shift+Click selection) into a short MP4">Make video</button>
|
||
<button class="sb-btn amber" id="lbOrbitBtn" onclick="lbOrbit()" title="Generate orbit preview (Fast=depth-card, Quality=Qwen turntable)">Orbit 3D<span id="orbitBadge" class="orbit-badge" style="display:none"></span></button>
|
||
<a class="sb-btn" id="lbDownloadBtn" download style="text-decoration:none">Download</a>
|
||
<button class="sb-btn" id="lbArchiveBtn" onclick="lbArchive()" style="color:#f59e0b" title="Move to archive (recoverable)">Archive</button>
|
||
<button class="sb-btn danger" id="lbDeleteBtn" onclick="lbDeleteArm()" title="Click once to arm, then again to confirm">Delete</button>
|
||
</div>
|
||
<div id="lbSourceRefs" class="sb-source-refs" style="display:none"></div>
|
||
</div>
|
||
<!-- Other panels: rendered dynamically -->
|
||
<div class="sb-panel" id="sbPanelGenerate"></div>
|
||
<div class="sb-panel" id="sbPanelFaceswap"></div>
|
||
<div class="sb-panel" id="sbPanelScenery"></div>
|
||
<div class="sb-panel" id="sbPanelSegment"></div>
|
||
<div class="sb-panel" id="sbPanelOrbit"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Orbit preview modal -->
|
||
<div id="orbitModal">
|
||
<div class="orbit-box">
|
||
<button class="orbit-close" onclick="closeOrbitModal()" title="Close">×</button>
|
||
<div class="orbit-title">Orbit 3D preview</div>
|
||
|
||
<!-- === SETTINGS STATE === -->
|
||
<div id="orbitSettings">
|
||
<!-- Engine selector -->
|
||
<div class="orbit-engine-toggle">
|
||
<button id="orbitEngineDepth" class="active" onclick="orbitSetEngine('depth')">Fast — depth-card</button>
|
||
<button id="orbitEngineQwen" onclick="orbitSetEngine('qwen')">Quality — Qwen turntable</button>
|
||
</div>
|
||
|
||
<!-- Depth-card controls -->
|
||
<div id="orbitDepthControls" style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:6px">
|
||
<label style="font-size:11px;color:#666;display:flex;align-items:center;gap:6px">
|
||
Parallax
|
||
<input type="range" id="orbitParallaxSlider" min="2" max="20" value="8" step="1"
|
||
style="width:80px;accent-color:#f59e0b"
|
||
oninput="document.getElementById('orbitParallaxVal').textContent=this.value+'%'">
|
||
<span id="orbitParallaxVal">8%</span>
|
||
</label>
|
||
<label style="font-size:11px;color:#666;display:flex;align-items:center;gap:6px">
|
||
Mode
|
||
<select id="orbitModeSelect" style="background:#1a1a1a;border:1px solid #333;color:#ccc;font-size:11px;padding:2px 4px;border-radius:4px">
|
||
<option value="swing" selected>Swing</option>
|
||
<option value="orbit">Full 360°</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Qwen turntable controls -->
|
||
<div id="orbitQwenControls" class="orbit-qwen-controls">
|
||
<label style="font-size:11px;color:#666;display:flex;align-items:center;gap:6px">
|
||
Views
|
||
<select id="orbitNviewsSelect" style="background:#1a1a1a;border:1px solid #333;color:#ccc;font-size:11px;padding:2px 4px;border-radius:4px">
|
||
<option value="12">12 (fast ~4 min)</option>
|
||
<option value="24" selected>24 (smooth ~9 min)</option>
|
||
</select>
|
||
</label>
|
||
<label style="font-size:11px;color:#666;display:flex;align-items:center;gap:6px">
|
||
Quality
|
||
<select id="orbitStepsSelect" style="background:#1a1a1a;border:1px solid #333;color:#ccc;font-size:11px;padding:2px 4px;border-radius:4px">
|
||
<option value="4">4 steps (draft)</option>
|
||
<option value="8" selected>8 steps (good)</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
|
||
<div id="orbitQwenCacheStatus" style="font-size:10px;color:#f59e0b;margin-bottom:6px;display:none"></div>
|
||
|
||
<div style="display:flex;gap:8px;margin-top:10px;flex-wrap:wrap;align-items:center">
|
||
<button class="sb-btn amber" id="orbitGenerateBtn" onclick="orbitGenerate()">Generate</button>
|
||
<button class="sb-btn" id="orbitLoadCacheBtn" onclick="orbitLoadCache()" style="display:none">Load cached ✓</button>
|
||
</div>
|
||
|
||
<div id="turntableBgStatus" style="display:none;margin-top:8px">
|
||
<div class="tbs-dot" id="tbsDot"></div>
|
||
<span id="tbsText"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- === PLAYER STATE === -->
|
||
<div id="orbitPlayer" style="display:none">
|
||
<div id="orbitStatus" class="orbit-status" style="margin-bottom:8px">Ready</div>
|
||
|
||
<!-- Frame flipper (shows transparency via checkerboard bg) -->
|
||
<div class="orbit-player" id="orbitFlipperWrap">
|
||
<div class="orbit-img-wrap">
|
||
<img id="orbitFlipperImg" alt="orbit frame">
|
||
</div>
|
||
<div class="orbit-player-controls">
|
||
<button class="sb-btn" onclick="orbitFlipperToggle()" id="orbitFlipperPlayBtn">⏸ Pause</button>
|
||
<span class="orbit-frame-counter" id="orbitFrameCounter">0/0</span>
|
||
<label style="font-size:11px;color:#666;display:flex;align-items:center;gap:6px;flex-shrink:0">
|
||
Speed
|
||
<input type="range" id="orbitFpsSlider" min="1" max="30" value="1" step="1"
|
||
style="width:60px;accent-color:#f59e0b"
|
||
oninput="_orbitSetFps(+this.value);document.getElementById('orbitFpsVal').textContent=this.value+'fps'">
|
||
<span id="orbitFpsVal" style="min-width:30px">1fps</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Video fallback (depth-card MP4 — no transparency) -->
|
||
<video id="orbitVideo" controls loop autoplay muted playsinline style="width:100%;border-radius:6px;display:none"></video>
|
||
|
||
<div class="orbit-actions" style="margin-top:10px">
|
||
<button class="sb-btn" onclick="orbitDownloadFrames()" id="orbitDlFramesBtn" style="display:none">⬇ Frames</button>
|
||
<button class="sb-btn" onclick="orbitDownload()" id="orbitDlBtn" style="display:none">⬇ MP4</button>
|
||
<button class="sb-btn" onclick="orbitBackToSettings()" style="margin-left:auto">⚙ Settings</button>
|
||
</div>
|
||
|
||
<div id="orbitNobgHint" style="display:none;margin-top:8px;font-size:11px;color:#78716c;background:#1c1917;padding:6px 10px;border-radius:6px">
|
||
Tip: run <strong style="color:#d4a27a">No BG</strong> first — transparent background gives much better depth separation.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="batch-bar" id="batchBar">
|
||
<div id="poseMenu" class="pose-menu"></div>
|
||
<span class="batch-count" id="batchCount">0 selected</span>
|
||
<div class="batch-prompt-wrap">
|
||
<input class="batch-prompt" id="batchPromptInput" list="promptHistory"
|
||
placeholder="temporal prompt..."
|
||
onkeydown="if(event.key==='Enter')processSelected()" />
|
||
<datalist id="promptHistory"></datalist>
|
||
</div>
|
||
<button class="btn" id="poseBtn" onclick="togglePoseMenu()">Poses</button>
|
||
<button class="btn" onclick="selectAll()">All</button>
|
||
<button class="btn" onclick="deselectAll()">None</button>
|
||
<button class="btn" id="groupBtn" onclick="groupSelected()">Group</button>
|
||
<button class="btn" id="multiRefBtn" onclick="processMultiRef()" style="display:none" title="Use selected (2–3) as combined references → single output">Multi-ref</button>
|
||
<span id="multiRefLegend" style="display:none;font-size:10px;color:#888;white-space:nowrap"></span>
|
||
<button class="btn primary" id="processBtn" onclick="processSelected()">Process</button>
|
||
<span class="batch-progress" id="batchProgress"></span>
|
||
<button class="btn" onclick="toggleSelectMode()">Done</button>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="variant-picker" id="variantPicker" onclick="if(event.target===this)closeVariantPicker()">
|
||
<div class="variant-picker-inner">
|
||
<div class="variant-picker-title" id="variantPickerTitle">Select reference image</div>
|
||
<div class="variant-picker-grid" id="variantPickerGrid"></div>
|
||
<div class="variant-picker-actions">
|
||
<button class="btn danger" id="variantPickerDeselect" style="display:none" onclick="deselectGroupFromPicker()">Deselect</button>
|
||
<button class="btn" onclick="closeVariantPicker()">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// ============================================
|
||
// CONFIGURATION - EDIT THIS SECTION
|
||
// ============================================
|
||
|
||
// Set this to your image folder path relative to this HTML file
|
||
// Examples: './' (same folder), './images/', '../screenshots/'
|
||
const IMAGE_FOLDER = './';
|
||
|
||
// --- HYDRATION_START ---
|
||
const PRELOADED_IMAGES = [
|
||
"20260628_020853_pad_20260628_015651_img_12.nobg.png",
|
||
"20260628_020642_pad_20260628_015651_img_12.nobg.png",
|
||
"20260628_020610_pad_20260628_015651_img_12.nobg.png",
|
||
"20260628_020546_pad_20260628_015651_img_12.nobg.png",
|
||
"20260628_020519_pad_20260628_015651_img_12.nobg.png",
|
||
"20260628_020428_pad_20260628_015651_img_12.nobg.png",
|
||
"20260628_020302_pad_20260628_015651_img_12.nobg.png",
|
||
"20260628_020125_pad_20260628_015651_img_12.nobg.png",
|
||
"20260628_015815_pad_20260628_015651_img_12.nobg.png",
|
||
"20260628_015832_pad_20260628_015651_img_12.png",
|
||
"20260628_015815_pad_20260628_015651_img_12.png",
|
||
"20260628_015707_img_12.png",
|
||
"20260628_015651_img_12.png",
|
||
"20260615_153426_img_12.png",
|
||
"20260628_015423_img_12.png",
|
||
"cg_5991f3f2_face.png",
|
||
"20260628_012806_pad_20260624_143523_Screenshot_From_2026-06-17_07-41-27.nobg.nobg.png",
|
||
"20260628_014614_p10.nobg.png",
|
||
"p10.png_face.png",
|
||
"20260628_014614_p10.png",
|
||
"20260628_014603_p10.png",
|
||
"up_2687be75_face.png",
|
||
"20260628_013521_pad_20260627_213816_best_01_person_0.874_2026-06-27_12-18-49.nobg.png",
|
||
"20260628_013511_pad_20260627_213816_best_01_person_0.874_2026-06-27_12-18-49.nobg.png",
|
||
"20260628_013459_pad_20260627_213816_best_01_person_0.874_2026-06-27_12-18-49.nobg.png",
|
||
"20260628_013429_pad_20260627_213816_best_01_person_0.874_2026-06-27_12-18-49.nobg.png",
|
||
"20260628_013429_pad_20260627_213816_best_01_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260628_013412_pad_20260627_213816_best_01_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260628_012832_pad_20260624_143523_Screenshot_From_2026-06-17_07-41-27.nobg.nobg.png",
|
||
"_turntable/cg_5991f3f2/views/view_022_330deg.png",
|
||
"20260628_011700_pad_20260624_154318_img_72.png",
|
||
"20260628_012006_pad_20260624_154318_img_72.png",
|
||
"20260628_011837_pad_20260624_154318_img_72.png",
|
||
"20260628_012240_crop_20260628_011837_pad_20260624_154318_img_72.png",
|
||
"20260628_011227_dup_Pasted image (9).png",
|
||
"20260628_011118_pad_20260628_010755_pad_20260626_005644_dup_20260626_005504_Pasted image (9).nobg.nobg.png",
|
||
"20260628_010957_pad_20260628_010755_pad_20260626_005644_dup_20260626_005504_Pasted image (9).nobg.nobg.png",
|
||
"20260628_010947_pad_20260628_010755_pad_20260626_005644_dup_20260626_005504_Pasted image (9).nobg.nobg.png",
|
||
"20260628_010858_pad_20260628_010755_pad_20260626_005644_dup_20260626_005504_Pasted image (9).nobg.nobg.png",
|
||
"20260628_010858_pad_20260628_010755_pad_20260626_005644_dup_20260626_005504_Pasted image (9).nobg.png",
|
||
"20260628_010841_pad_20260628_010755_pad_20260626_005644_dup_20260626_005504_Pasted image (9).nobg.png",
|
||
"20260628_010755_pad_20260626_005644_dup_20260626_005504_Pasted image (9).nobg.png",
|
||
"20260628_010630_pad_20260626_005644_dup_20260626_005504_Pasted image (9).nobg.png",
|
||
"20260628_010408_image.png",
|
||
"20260627_203626_image.png",
|
||
"up_7b6eaabb_face.png",
|
||
"cg_809653a0_face.png",
|
||
"bb_01.png_face.png",
|
||
"_turntable/up_aa1add4d/views/view_023_345deg.png",
|
||
"_turntable/up_aa1add4d/views/view_003_045deg.png",
|
||
"_turntable/up_aa1add4d/views/view_001_015deg.png",
|
||
"_turntable/up_7b6eaabb/views/view_001_015deg.png",
|
||
"_turntable/up_7b6eaabb/views/view_000_000deg.png",
|
||
"_turntable/up_6ed4293c/views/view_023_345deg.png",
|
||
"_turntable/up_6ed4293c/views/view_022_330deg.png",
|
||
"_turntable/up_6ed4293c/views/view_021_315deg.png",
|
||
"_turntable/up_6ed4293c/views/view_020_300deg.png",
|
||
"_turntable/up_6ed4293c/views/view_019_285deg.png",
|
||
"_turntable/up_6ed4293c/views/view_018_270deg.png",
|
||
"_turntable/up_6ed4293c/views/view_017_255deg.png",
|
||
"_turntable/up_6ed4293c/views/view_016_240deg.png",
|
||
"_turntable/up_6ed4293c/views/view_015_225deg.png",
|
||
"_turntable/up_6ed4293c/views/view_014_210deg.png",
|
||
"_turntable/up_6ed4293c/views/view_013_195deg.png",
|
||
"_turntable/up_6ed4293c/views/view_012_180deg.png",
|
||
"_turntable/up_6ed4293c/views/view_011_165deg.png",
|
||
"_turntable/up_6ed4293c/views/view_010_150deg.png",
|
||
"_turntable/up_6ed4293c/views/view_009_135deg.png",
|
||
"_turntable/up_6ed4293c/views/view_008_120deg.png",
|
||
"_turntable/up_6ed4293c/views/view_007_105deg.png",
|
||
"_turntable/up_6ed4293c/views/view_006_090deg.png",
|
||
"_turntable/up_6ed4293c/views/view_005_075deg.png",
|
||
"_turntable/up_6ed4293c/views/view_004_060deg.png",
|
||
"_turntable/up_6ed4293c/views/view_003_045deg.png",
|
||
"_turntable/up_6ed4293c/views/view_002_030deg.png",
|
||
"_turntable/up_6ed4293c/views/view_001_015deg.png",
|
||
"_turntable/up_6ed4293c/views/view_000_000deg.png",
|
||
"20260628_001216_pad_20260626_035101_pad_20260626_034830_jb.nobg.png",
|
||
"20260627_232502_pad_20260626_035101_pad_20260626_034830_jb.nobg.png",
|
||
"20260627_232843_pad_20260626_035101_pad_20260626_034830_jb.nobg.png",
|
||
"20260627_232526_pad_20260626_035101_pad_20260626_034830_jb.nobg.png",
|
||
"20260627_232302_pad_20260627_232133_image.png",
|
||
"20260627_232238_pad_20260627_232133_image.png",
|
||
"up_6ed4293c_face.png",
|
||
"20260627_232209_image.png",
|
||
"20260627_232151_image.png",
|
||
"20260627_232133_image.png",
|
||
"20260627_230405_mr_image.png",
|
||
"20260627_222806_mr_image.png",
|
||
"20260627_230301_7_20260627_201343_image.nobg.png",
|
||
"20260627_201629_7_20260627_201343_image.nobg.png",
|
||
"20260627_201629_7_20260627_201343_image.png",
|
||
"20260627_225051_mr_image.png",
|
||
"20260627_224340_mr_image.png",
|
||
"20260627_224013_mr_image.png",
|
||
"20260627_223316_image.png",
|
||
"20260627_223304_image.png",
|
||
"20260627_223252_image.png",
|
||
"20260627_223028_image.png",
|
||
"20260627_223017_image.png",
|
||
"20260627_223005_image.png",
|
||
"20260627_222926_image.png",
|
||
"20260627_222915_image.png",
|
||
"20260627_222904_image.png",
|
||
"20260627_222321_image.png",
|
||
"20260627_222310_image.png",
|
||
"20260627_222259_image.png",
|
||
"20260627_222112_mr_image.png",
|
||
"20260627_222047_mr_image.png",
|
||
"20260627_222015_mr_image.png",
|
||
"20260626_034949_pose_jb.nobg.png",
|
||
"cg_0ed9a15a_face.png",
|
||
"_turntable/cg_7c7b22c9/views/view_022_330deg.png",
|
||
"20260618_015124_20260618_010617_20260615_153426_img_12.nobg.png",
|
||
"_turntable/cg_5991f3f2/views/view_002_030deg.png",
|
||
"20260627_213910_best_02_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213840_best_02_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213816_best_01_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213756_best_01_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213743_best_01_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213727_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213711_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213626_best_02_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213613_best_01_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213559_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213517_image.png",
|
||
"20260627_213504_image.png",
|
||
"20260627_213451_image.png",
|
||
"20260627_213241_12_20260627_212803_best_01_person_0.874_2026-06-27_20-05-05.jpg.png",
|
||
"20260627_213227_11_20260627_212803_best_01_person_0.874_2026-06-27_20-05-05.jpg.png",
|
||
"_turntable/cg_74544975/views/view_023_345deg.png",
|
||
"20260627_213215_10_20260627_212803_best_01_person_0.874_2026-06-27_20-05-05.jpg.png",
|
||
"_turntable/cg_74544975/views/view_022_330deg.png",
|
||
"_turntable/cg_74544975/views/view_021_315deg.png",
|
||
"_turntable/cg_74544975/views/view_020_300deg.png",
|
||
"_turntable/cg_74544975/views/view_019_285deg.png",
|
||
"_turntable/cg_74544975/views/view_018_270deg.png",
|
||
"20260627_213202_9_20260627_212803_best_01_person_0.874_2026-06-27_20-05-05.jpg.png",
|
||
"20260627_213149_8_20260627_212803_best_01_person_0.874_2026-06-27_20-05-05.jpg.png",
|
||
"_turntable/cg_74544975/views/view_004_060deg.png",
|
||
"_turntable/cg_74544975/views/view_001_015deg.png",
|
||
"_turntable/cg_74544975/views/view_000_000deg.png",
|
||
"20260627_213102_12_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213037_11_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_213013_10_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212949_9_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212937_3_20260627_212803_best_01_person_0.874_2026-06-27_20-05-05.jpg.png",
|
||
"20260627_212925_8_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212913_2_20260627_212803_best_01_person_0.874_2026-06-27_20-05-05.jpg.png",
|
||
"20260627_212901_7_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212848_1_20260627_212803_best_01_person_0.874_2026-06-27_20-05-05.jpg.png",
|
||
"20260627_212836_6_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212824_0_20260627_212803_best_01_person_0.874_2026-06-27_20-05-05.jpg.png",
|
||
"20260627_212814_best_02_person_0.874_2026-06-27_20-05-05.png",
|
||
"20260627_212814_best_02_person_0.874_2026-06-27_20-05-05.jpg",
|
||
"20260627_212814_best_01_person_0.874_2026-06-27_20-05-05.png",
|
||
"20260627_212814_best_00_person_0.874_2026-06-27_20-05-05.png",
|
||
"20260627_212814_best_00_person_0.874_2026-06-27_20-05-05.jpg",
|
||
"20260627_212812_5_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212803_best_01_person_0.874_2026-06-27_20-05-05.jpg",
|
||
"20260627_212800_4_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212748_3_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212735_2_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212726_best_02_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212726_best_01_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212726_best_01_person_0.874_2026-06-27_12-18-49.jpg",
|
||
"20260627_212726_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212726_best_00_person_0.874_2026-06-27_12-18-49.jpg",
|
||
"20260627_212723_1_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212710_0_20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_212658_best_00_person_0.874_2026-06-27_12-18-49.png",
|
||
"20260627_204815_image.png",
|
||
"20260627_204804_image.png",
|
||
"20260627_204756_image.png",
|
||
"_turntable/cg_f85e7ae1/views/view_023_345deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_013_195deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_012_180deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_010_150deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_001_015deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_000_000deg.png",
|
||
"_turntable/cg_75340e8e/views/view_017_255deg.png",
|
||
"_turntable/cg_75340e8e/views/view_016_240deg.png",
|
||
"_turntable/cg_75340e8e/views/view_014_210deg.png",
|
||
"_turntable/cg_75340e8e/views/view_013_195deg.png",
|
||
"_turntable/cg_75340e8e/views/view_012_180deg.png",
|
||
"_turntable/cg_75340e8e/views/view_011_165deg.png",
|
||
"_turntable/cg_75340e8e/views/view_010_150deg.png",
|
||
"_turntable/cg_75340e8e/views/view_009_135deg.png",
|
||
"_turntable/cg_75340e8e/views/view_008_120deg.png",
|
||
"_turntable/cg_75340e8e/views/view_007_105deg.png",
|
||
"_turntable/cg_75340e8e/views/view_023_345deg.png",
|
||
"_turntable/cg_75340e8e/views/view_001_015deg.png",
|
||
"_turntable/cg_75340e8e/views/view_000_000deg.png",
|
||
"20260627_203706_11_20260627_200330_image.png",
|
||
"20260627_203654_11_20260627_200330_image.png",
|
||
"20260627_203642_11_20260627_200330_image.png",
|
||
"20260627_203607_image.png",
|
||
"20260627_203549_image.png",
|
||
"20260627_203531_Screenshot_From_2026-06-27_20-04-11.png",
|
||
"20260627_203514_Screenshot_From_2026-06-27_20-04-11.png",
|
||
"20260627_203459_Screenshot_From_2026-06-27_20-04-11.png",
|
||
"20260627_203349_image.png",
|
||
"20260627_203337_image.png",
|
||
"20260627_203324_image.png",
|
||
"20260627_203312_image.png",
|
||
"20260627_203300_image.png",
|
||
"20260627_203247_image.png",
|
||
"up_c8be3aa7_face.png",
|
||
"20260627_203235_Screenshot_From_2026-06-27_19-59-43.png",
|
||
"20260627_203223_Screenshot_From_2026-06-27_19-59-43.png",
|
||
"20260627_203211_Screenshot_From_2026-06-27_19-59-43.png",
|
||
"20260627_203127_image.png",
|
||
"20260627_203105_Screenshot_From_2026-06-27_19-59-43.png",
|
||
"20260627_203054_Screenshot_From_2026-06-27_20-04-11.png",
|
||
"20260627_203044_image.png",
|
||
"20260627_203032_image.png",
|
||
"_turntable/up_c8be3aa7/views/view_002_030deg.png",
|
||
"_turntable/up_c8be3aa7/views/view_003_045deg.png",
|
||
"20260627_201807_12_20260627_201343_image.png",
|
||
"20260627_201753_Screenshot_From_2026-06-27_12-32-39.png",
|
||
"20260627_201751_11_20260627_201343_image.png",
|
||
"20260627_201733_10_20260627_201343_image.png",
|
||
"20260627_201711_9_20260627_201343_image.png",
|
||
"20260627_201648_8_20260627_201343_image.png",
|
||
"20260627_201622_Screenshot_From_2026-06-27_20-04-55.png",
|
||
"20260627_201614_Screenshot_From_2026-06-27_20-04-11.png",
|
||
"20260627_201558_Screenshot_From_2026-06-27_19-59-43.png",
|
||
"20260627_201548_5_20260627_201343_image.png",
|
||
"20260627_201518_image.png",
|
||
"20260627_201500_3_20260627_201343_image.png",
|
||
"20260627_201439_2_20260627_201343_image.png",
|
||
"20260627_201418_1_20260627_201343_image.png",
|
||
"20260627_201400_0_20260627_201343_image.png",
|
||
"20260627_201343_image.png",
|
||
"_turntable/up_c8be3aa7/views/view_007_105deg.png",
|
||
"_turntable/up_c8be3aa7/views/view_006_090deg.png",
|
||
"_turntable/up_c8be3aa7/views/view_005_075deg.png",
|
||
"_turntable/up_c8be3aa7/views/view_004_060deg.png",
|
||
"_turntable/up_c8be3aa7/views/view_001_015deg.png",
|
||
"20260627_200711_11_20260627_200330_image.png",
|
||
"20260627_200506_image.png",
|
||
"20260627_200422_2_20260627_200330_image.png",
|
||
"20260627_200414_image.png",
|
||
"20260627_200405_1_20260627_200330_image.png",
|
||
"20260627_200330_image.png",
|
||
"_turntable/up_c8be3aa7/views/view_000_000deg.png",
|
||
"20260627_200201_12_20260627_195835_image.png",
|
||
"20260627_200144_11_20260627_195835_image.png",
|
||
"20260627_200127_10_20260627_195835_image.png",
|
||
"20260627_200111_9_20260627_195835_image.png",
|
||
"20260627_200100_8_20260627_195835_image.png",
|
||
"20260627_200047_7_20260627_195835_image.png",
|
||
"20260627_200032_6_20260627_195835_image.png",
|
||
"20260627_200015_5_20260627_195835_image.png",
|
||
"20260627_200001_4_20260627_195835_image.png",
|
||
"20260627_195953_image.png",
|
||
"20260627_195945_3_20260627_195835_image.png",
|
||
"20260627_195929_2_20260627_195835_image.png",
|
||
"20260627_195915_1_20260627_195835_image.png",
|
||
"20260627_195858_0_20260627_195835_image.png",
|
||
"20260627_195835_image.png",
|
||
"_turntable/img_87.png/views/view_023_345deg.png",
|
||
"_turntable/img_87.png/views/view_022_330deg.png",
|
||
"_turntable/img_87.png/views/view_021_315deg.png",
|
||
"_turntable/img_87.png/views/view_020_300deg.png",
|
||
"_turntable/img_87.png/views/view_019_285deg.png",
|
||
"_turntable/img_87.png/views/view_018_270deg.png",
|
||
"_turntable/img_87.png/views/view_017_255deg.png",
|
||
"_turntable/img_87.png/views/view_016_240deg.png",
|
||
"_turntable/img_87.png/views/view_015_225deg.png",
|
||
"_turntable/up_aa1add4d/views/view_022_330deg.png",
|
||
"_turntable/up_aa1add4d/views/view_021_315deg.png",
|
||
"_turntable/up_aa1add4d/views/view_020_300deg.png",
|
||
"_turntable/up_aa1add4d/views/view_019_285deg.png",
|
||
"_turntable/up_aa1add4d/views/view_018_270deg.png",
|
||
"_turntable/up_aa1add4d/views/view_017_255deg.png",
|
||
"_turntable/up_aa1add4d/views/view_016_240deg.png",
|
||
"_turntable/up_aa1add4d/views/view_015_225deg.png",
|
||
"_turntable/up_aa1add4d/views/view_014_210deg.png",
|
||
"_turntable/up_aa1add4d/views/view_013_195deg.png",
|
||
"_turntable/up_aa1add4d/views/view_012_180deg.png",
|
||
"_turntable/up_aa1add4d/views/view_011_165deg.png",
|
||
"_turntable/up_aa1add4d/views/view_010_150deg.png",
|
||
"_turntable/up_aa1add4d/views/view_009_135deg.png",
|
||
"_turntable/up_aa1add4d/views/view_008_120deg.png",
|
||
"_turntable/up_aa1add4d/views/view_007_105deg.png",
|
||
"_turntable/up_aa1add4d/views/view_006_090deg.png",
|
||
"_turntable/up_aa1add4d/views/view_005_075deg.png",
|
||
"_turntable/up_aa1add4d/views/view_004_060deg.png",
|
||
"_turntable/up_aa1add4d/views/view_002_030deg.png",
|
||
"_turntable/up_aa1add4d/views/view_000_000deg.png",
|
||
"_turntable/up_ae825fce/views/view_023_345deg.png",
|
||
"_turntable/up_ae825fce/views/view_022_330deg.png",
|
||
"_turntable/up_ae825fce/views/view_021_315deg.png",
|
||
"_turntable/up_ae825fce/views/view_020_300deg.png",
|
||
"_turntable/up_ae825fce/views/view_019_285deg.png",
|
||
"_turntable/up_ae825fce/views/view_018_270deg.png",
|
||
"_turntable/up_ae825fce/views/view_017_255deg.png",
|
||
"_turntable/up_ae825fce/views/view_016_240deg.png",
|
||
"_turntable/up_ae825fce/views/view_015_225deg.png",
|
||
"_turntable/up_ae825fce/views/view_014_210deg.png",
|
||
"_turntable/up_ae825fce/views/view_013_195deg.png",
|
||
"_turntable/up_ae825fce/views/view_012_180deg.png",
|
||
"_turntable/up_ae825fce/views/view_011_165deg.png",
|
||
"_turntable/up_ae825fce/views/view_010_150deg.png",
|
||
"_turntable/up_ae825fce/views/view_009_135deg.png",
|
||
"_turntable/up_ae825fce/views/view_008_120deg.png",
|
||
"_turntable/up_ae825fce/views/view_007_105deg.png",
|
||
"_turntable/up_ae825fce/views/view_006_090deg.png",
|
||
"_turntable/up_ae825fce/views/view_005_075deg.png",
|
||
"_turntable/up_ae825fce/views/view_004_060deg.png",
|
||
"_turntable/up_ae825fce/views/view_003_045deg.png",
|
||
"_turntable/up_ae825fce/views/view_002_030deg.png",
|
||
"_turntable/up_ae825fce/views/view_001_015deg.png",
|
||
"_turntable/up_ae825fce/views/view_000_000deg.png",
|
||
"_turntable/up_78ae5271/views/view_023_345deg.png",
|
||
"_turntable/up_78ae5271/views/view_022_330deg.png",
|
||
"_turntable/up_78ae5271/views/view_021_315deg.png",
|
||
"_turntable/up_78ae5271/views/view_020_300deg.png",
|
||
"_turntable/up_78ae5271/views/view_019_285deg.png",
|
||
"_turntable/up_78ae5271/views/view_018_270deg.png",
|
||
"_turntable/up_78ae5271/views/view_017_255deg.png",
|
||
"_turntable/up_78ae5271/views/view_016_240deg.png",
|
||
"_turntable/up_78ae5271/views/view_015_225deg.png",
|
||
"_turntable/up_78ae5271/views/view_014_210deg.png",
|
||
"_turntable/up_78ae5271/views/view_013_195deg.png",
|
||
"_turntable/up_78ae5271/views/view_012_180deg.png",
|
||
"_turntable/up_78ae5271/views/view_011_165deg.png",
|
||
"_turntable/up_78ae5271/views/view_010_150deg.png",
|
||
"_turntable/up_78ae5271/views/view_009_135deg.png",
|
||
"_turntable/up_78ae5271/views/view_008_120deg.png",
|
||
"_turntable/up_78ae5271/views/view_007_105deg.png",
|
||
"_turntable/up_78ae5271/views/view_006_090deg.png",
|
||
"_turntable/up_78ae5271/views/view_005_075deg.png",
|
||
"_turntable/up_78ae5271/views/view_004_060deg.png",
|
||
"_turntable/up_78ae5271/views/view_003_045deg.png",
|
||
"_turntable/up_78ae5271/views/view_002_030deg.png",
|
||
"_turntable/up_78ae5271/views/view_001_015deg.png",
|
||
"_turntable/up_78ae5271/views/view_000_000deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_023_345deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_022_330deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_021_315deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_020_300deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_019_285deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_018_270deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_017_255deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_016_240deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_015_225deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_014_210deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_013_195deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_012_180deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_011_165deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_010_150deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_009_135deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_008_120deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_007_105deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_006_090deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_005_075deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_004_060deg.png",
|
||
"_turntable/up_5b23ff92/views/view_023_345deg.png",
|
||
"_turntable/up_5b23ff92/views/view_022_330deg.png",
|
||
"_turntable/up_5b23ff92/views/view_021_315deg.png",
|
||
"_turntable/up_5b23ff92/views/view_020_300deg.png",
|
||
"_turntable/up_5b23ff92/views/view_019_285deg.png",
|
||
"_turntable/up_5b23ff92/views/view_018_270deg.png",
|
||
"_turntable/up_5b23ff92/views/view_017_255deg.png",
|
||
"_turntable/cg_6f321af3/views/view_023_345deg.png",
|
||
"_turntable/cg_6f321af3/views/view_022_330deg.png",
|
||
"_turntable/cg_6f321af3/views/view_021_315deg.png",
|
||
"_turntable/cg_6f321af3/views/view_020_300deg.png",
|
||
"_turntable/cg_6f321af3/views/view_019_285deg.png",
|
||
"_turntable/cg_6f321af3/views/view_018_270deg.png",
|
||
"_turntable/cg_6f321af3/views/view_017_255deg.png",
|
||
"_turntable/cg_6f321af3/views/view_016_240deg.png",
|
||
"_turntable/cg_6f321af3/views/view_015_225deg.png",
|
||
"_turntable/cg_6f321af3/views/view_014_210deg.png",
|
||
"_turntable/cg_6f321af3/views/view_013_195deg.png",
|
||
"_turntable/cg_6f321af3/views/view_012_180deg.png",
|
||
"_turntable/cg_6f321af3/views/view_011_165deg.png",
|
||
"_turntable/cg_6f321af3/views/view_010_150deg.png",
|
||
"_turntable/cg_6f321af3/views/view_009_135deg.png",
|
||
"_turntable/cg_6f321af3/views/view_008_120deg.png",
|
||
"_turntable/cg_6f321af3/views/view_007_105deg.png",
|
||
"_turntable/cg_6f321af3/views/view_006_090deg.png",
|
||
"_turntable/cg_6f321af3/views/view_005_075deg.png",
|
||
"_turntable/cg_6f321af3/views/view_004_060deg.png",
|
||
"_turntable/cg_6f321af3/views/view_003_045deg.png",
|
||
"_turntable/cg_6f321af3/views/view_002_030deg.png",
|
||
"_turntable/cg_6f321af3/views/view_001_015deg.png",
|
||
"_turntable/cg_6f321af3/views/view_000_000deg.png",
|
||
"_turntable/up_1cff7d63/views/view_023_345deg.png",
|
||
"_turntable/up_1cff7d63/views/view_022_330deg.png",
|
||
"_turntable/up_1cff7d63/views/view_021_315deg.png",
|
||
"_turntable/up_1cff7d63/views/view_020_300deg.png",
|
||
"_turntable/up_1cff7d63/views/view_019_285deg.png",
|
||
"_turntable/up_1cff7d63/views/view_018_270deg.png",
|
||
"_turntable/up_1cff7d63/views/view_017_255deg.png",
|
||
"_turntable/up_1cff7d63/views/view_016_240deg.png",
|
||
"_turntable/up_1cff7d63/views/view_015_225deg.png",
|
||
"_turntable/up_1cff7d63/views/view_014_210deg.png",
|
||
"_turntable/up_1cff7d63/views/view_013_195deg.png",
|
||
"_turntable/up_1cff7d63/views/view_012_180deg.png",
|
||
"_turntable/up_1cff7d63/views/view_011_165deg.png",
|
||
"_turntable/up_1cff7d63/views/view_010_150deg.png",
|
||
"_turntable/up_1cff7d63/views/view_009_135deg.png",
|
||
"_turntable/up_1cff7d63/views/view_008_120deg.png",
|
||
"_turntable/up_1cff7d63/views/view_007_105deg.png",
|
||
"_turntable/up_1cff7d63/views/view_006_090deg.png",
|
||
"_turntable/up_1cff7d63/views/view_005_075deg.png",
|
||
"_turntable/up_1cff7d63/views/view_004_060deg.png",
|
||
"_turntable/up_1cff7d63/views/view_003_045deg.png",
|
||
"_turntable/up_1cff7d63/views/view_002_030deg.png",
|
||
"_turntable/up_1cff7d63/views/view_001_015deg.png",
|
||
"_turntable/up_1cff7d63/views/view_000_000deg.png",
|
||
"_turntable/cg_e964fb19/views/view_023_345deg.png",
|
||
"_turntable/cg_e964fb19/views/view_022_330deg.png",
|
||
"_turntable/cg_e964fb19/views/view_021_315deg.png",
|
||
"_turntable/cg_e964fb19/views/view_020_300deg.png",
|
||
"_turntable/cg_e964fb19/views/view_019_285deg.png",
|
||
"_turntable/cg_e964fb19/views/view_018_270deg.png",
|
||
"_turntable/cg_e964fb19/views/view_017_255deg.png",
|
||
"_turntable/cg_e964fb19/views/view_016_240deg.png",
|
||
"_turntable/cg_e964fb19/views/view_015_225deg.png",
|
||
"_turntable/cg_e964fb19/views/view_014_210deg.png",
|
||
"_turntable/cg_e964fb19/views/view_013_195deg.png",
|
||
"_turntable/cg_e964fb19/views/view_012_180deg.png",
|
||
"_turntable/cg_e964fb19/views/view_011_165deg.png",
|
||
"_turntable/cg_e964fb19/views/view_010_150deg.png",
|
||
"_turntable/cg_e964fb19/views/view_009_135deg.png",
|
||
"_turntable/cg_e964fb19/views/view_008_120deg.png",
|
||
"_turntable/cg_e964fb19/views/view_007_105deg.png",
|
||
"_turntable/cg_e964fb19/views/view_006_090deg.png",
|
||
"_turntable/cg_e964fb19/views/view_005_075deg.png",
|
||
"_turntable/cg_e964fb19/views/view_004_060deg.png",
|
||
"_turntable/cg_e964fb19/views/view_003_045deg.png",
|
||
"_turntable/cg_e964fb19/views/view_002_030deg.png",
|
||
"_turntable/cg_e964fb19/views/view_001_015deg.png",
|
||
"_turntable/cg_e964fb19/views/view_000_000deg.png",
|
||
"_turntable/cg_75340e8e/views/view_022_330deg.png",
|
||
"_turntable/cg_75340e8e/views/view_021_315deg.png",
|
||
"_turntable/cg_75340e8e/views/view_020_300deg.png",
|
||
"_turntable/cg_75340e8e/views/view_019_285deg.png",
|
||
"_turntable/cg_75340e8e/views/view_018_270deg.png",
|
||
"_turntable/cg_75340e8e/views/view_015_225deg.png",
|
||
"_turntable/cg_75340e8e/views/view_006_090deg.png",
|
||
"_turntable/cg_75340e8e/views/view_005_075deg.png",
|
||
"_turntable/cg_75340e8e/views/view_004_060deg.png",
|
||
"_turntable/cg_75340e8e/views/view_003_045deg.png",
|
||
"_turntable/cg_75340e8e/views/view_002_030deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_022_330deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_021_315deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_020_300deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_019_285deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_018_270deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_017_255deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_016_240deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_015_225deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_014_210deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_011_165deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_009_135deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_008_120deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_007_105deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_006_090deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_005_075deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_004_060deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_003_045deg.png",
|
||
"_turntable/cg_f85e7ae1/views/view_002_030deg.png",
|
||
"_turntable/up_c31bb439/views/view_023_345deg.png",
|
||
"_turntable/up_c31bb439/views/view_022_330deg.png",
|
||
"_turntable/up_c31bb439/views/view_021_315deg.png",
|
||
"_turntable/up_c31bb439/views/view_020_300deg.png",
|
||
"_turntable/up_c31bb439/views/view_019_285deg.png",
|
||
"_turntable/up_c31bb439/views/view_018_270deg.png",
|
||
"_turntable/up_c31bb439/views/view_017_255deg.png",
|
||
"_turntable/up_c31bb439/views/view_016_240deg.png",
|
||
"_turntable/up_c31bb439/views/view_015_225deg.png",
|
||
"_turntable/up_c31bb439/views/view_014_210deg.png",
|
||
"_turntable/up_c31bb439/views/view_013_195deg.png",
|
||
"_turntable/up_c31bb439/views/view_012_180deg.png",
|
||
"_turntable/up_c31bb439/views/view_011_165deg.png",
|
||
"_turntable/up_c31bb439/views/view_010_150deg.png",
|
||
"_turntable/up_c31bb439/views/view_009_135deg.png",
|
||
"_turntable/up_c31bb439/views/view_008_120deg.png",
|
||
"_turntable/up_c31bb439/views/view_007_105deg.png",
|
||
"_turntable/up_c31bb439/views/view_006_090deg.png",
|
||
"_turntable/up_c31bb439/views/view_005_075deg.png",
|
||
"_turntable/up_c31bb439/views/view_004_060deg.png",
|
||
"_turntable/up_c31bb439/views/view_003_045deg.png",
|
||
"_turntable/up_c31bb439/views/view_002_030deg.png",
|
||
"_turntable/up_c31bb439/views/view_001_015deg.png",
|
||
"_turntable/up_c31bb439/views/view_000_000deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_023_345deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_022_330deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_021_315deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_020_300deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_019_285deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_018_270deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_017_255deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_016_240deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_015_225deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_014_210deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_013_195deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_012_180deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_011_165deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_010_150deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_009_135deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_008_120deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_007_105deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_006_090deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_005_075deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_004_060deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_003_045deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_002_030deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_001_015deg.png",
|
||
"_turntable/cg_4671b6c4/views/view_000_000deg.png",
|
||
"_turntable/cg_74544975/views/view_017_255deg.png",
|
||
"_turntable/cg_74544975/views/view_016_240deg.png",
|
||
"_turntable/cg_74544975/views/view_015_225deg.png",
|
||
"_turntable/cg_74544975/views/view_014_210deg.png",
|
||
"_turntable/cg_74544975/views/view_013_195deg.png",
|
||
"_turntable/cg_74544975/views/view_012_180deg.png",
|
||
"_turntable/cg_74544975/views/view_011_165deg.png",
|
||
"_turntable/cg_74544975/views/view_010_150deg.png",
|
||
"_turntable/cg_74544975/views/view_009_135deg.png",
|
||
"_turntable/cg_74544975/views/view_008_120deg.png",
|
||
"_turntable/cg_74544975/views/view_007_105deg.png",
|
||
"_turntable/cg_74544975/views/view_006_090deg.png",
|
||
"_turntable/cg_74544975/views/view_005_075deg.png",
|
||
"_turntable/cg_74544975/views/view_003_045deg.png",
|
||
"_turntable/cg_74544975/views/view_002_030deg.png",
|
||
"_turntable/cg_73e141fa/views/view_023_345deg.png",
|
||
"_turntable/cg_73e141fa/views/view_022_330deg.png",
|
||
"_turntable/cg_73e141fa/views/view_021_315deg.png",
|
||
"_turntable/cg_73e141fa/views/view_020_300deg.png",
|
||
"_turntable/cg_73e141fa/views/view_019_285deg.png",
|
||
"_turntable/cg_73e141fa/views/view_018_270deg.png",
|
||
"_turntable/cg_73e141fa/views/view_017_255deg.png",
|
||
"_turntable/cg_73e141fa/views/view_016_240deg.png",
|
||
"_turntable/cg_73e141fa/views/view_015_225deg.png",
|
||
"_turntable/cg_73e141fa/views/view_014_210deg.png",
|
||
"_turntable/cg_73e141fa/views/view_013_195deg.png",
|
||
"_turntable/cg_73e141fa/views/view_012_180deg.png",
|
||
"_turntable/cg_73e141fa/views/view_011_165deg.png",
|
||
"_turntable/cg_73e141fa/views/view_010_150deg.png",
|
||
"_turntable/cg_73e141fa/views/view_009_135deg.png",
|
||
"_turntable/cg_73e141fa/views/view_008_120deg.png",
|
||
"_turntable/cg_73e141fa/views/view_007_105deg.png",
|
||
"_turntable/cg_73e141fa/views/view_006_090deg.png",
|
||
"_turntable/cg_73e141fa/views/view_005_075deg.png",
|
||
"_turntable/cg_73e141fa/views/view_004_060deg.png",
|
||
"_turntable/cg_73e141fa/views/view_003_045deg.png",
|
||
"_turntable/cg_73e141fa/views/view_002_030deg.png",
|
||
"_turntable/cg_73e141fa/views/view_001_015deg.png",
|
||
"_turntable/cg_73e141fa/views/view_000_000deg.png",
|
||
"_turntable/img_51.png/views/view_023_345deg.png",
|
||
"_turntable/img_51.png/views/view_022_330deg.png",
|
||
"_turntable/img_51.png/views/view_021_315deg.png",
|
||
"_turntable/img_51.png/views/view_020_300deg.png",
|
||
"_turntable/img_51.png/views/view_019_285deg.png",
|
||
"_turntable/img_51.png/views/view_018_270deg.png",
|
||
"_turntable/img_51.png/views/view_017_255deg.png",
|
||
"_turntable/img_51.png/views/view_016_240deg.png",
|
||
"_turntable/img_51.png/views/view_015_225deg.png",
|
||
"_turntable/img_51.png/views/view_014_210deg.png",
|
||
"_turntable/img_51.png/views/view_013_195deg.png",
|
||
"_turntable/img_51.png/views/view_012_180deg.png",
|
||
"_turntable/img_51.png/views/view_011_165deg.png",
|
||
"_turntable/img_51.png/views/view_010_150deg.png",
|
||
"_turntable/img_51.png/views/view_009_135deg.png",
|
||
"_turntable/img_51.png/views/view_008_120deg.png",
|
||
"_turntable/img_51.png/views/view_007_105deg.png",
|
||
"_turntable/img_51.png/views/view_006_090deg.png",
|
||
"_turntable/img_51.png/views/view_005_075deg.png",
|
||
"_turntable/img_51.png/views/view_004_060deg.png",
|
||
"_turntable/img_51.png/views/view_003_045deg.png",
|
||
"_turntable/img_51.png/views/view_002_030deg.png",
|
||
"_turntable/img_51.png/views/view_001_015deg.png",
|
||
"_turntable/img_51.png/views/view_000_000deg.png",
|
||
"_turntable/up_455c085f/views/view_023_345deg.png",
|
||
"_turntable/up_455c085f/views/view_022_330deg.png",
|
||
"_turntable/up_455c085f/views/view_021_315deg.png",
|
||
"_turntable/up_455c085f/views/view_020_300deg.png",
|
||
"_turntable/up_455c085f/views/view_019_285deg.png",
|
||
"_turntable/up_455c085f/views/view_018_270deg.png",
|
||
"_turntable/up_455c085f/views/view_017_255deg.png",
|
||
"_turntable/up_455c085f/views/view_016_240deg.png",
|
||
"_turntable/up_455c085f/views/view_015_225deg.png",
|
||
"_turntable/up_455c085f/views/view_014_210deg.png",
|
||
"_turntable/up_455c085f/views/view_013_195deg.png",
|
||
"_turntable/up_455c085f/views/view_012_180deg.png",
|
||
"_turntable/up_455c085f/views/view_011_165deg.png",
|
||
"_turntable/up_455c085f/views/view_010_150deg.png",
|
||
"_turntable/up_455c085f/views/view_009_135deg.png",
|
||
"_turntable/up_455c085f/views/view_008_120deg.png",
|
||
"_turntable/up_455c085f/views/view_007_105deg.png",
|
||
"_turntable/up_455c085f/views/view_006_090deg.png",
|
||
"_turntable/up_455c085f/views/view_005_075deg.png",
|
||
"_turntable/up_455c085f/views/view_004_060deg.png",
|
||
"_turntable/up_455c085f/views/view_003_045deg.png",
|
||
"_turntable/up_455c085f/views/view_002_030deg.png",
|
||
"_turntable/up_455c085f/views/view_001_015deg.png",
|
||
"_turntable/up_455c085f/views/view_000_000deg.png",
|
||
"20260627_161604_image.nobg.png",
|
||
"20260627_161342_image.nobg.png",
|
||
"20260618_053458_image.png_face.png",
|
||
"20260627_161342_image.png",
|
||
"20260627_161217_pad_20260627_161055_image.png",
|
||
"20260627_161055_image.png",
|
||
"20260627_160901_mr_sc_image.png",
|
||
"20260627_160718_mr_sc_image.png",
|
||
"20260627_160551_mr_sc_image.png",
|
||
"20260627_160407_mr_sc_image.png",
|
||
"20260627_160228_mr_sc_image.png",
|
||
"20260627_160117_mr_sc_image.png",
|
||
"20260627_155655_sc_image.png",
|
||
"20260627_155122_20150913_211324.jpg",
|
||
"20260627_155049_20150913_211324.jpg",
|
||
"20260627_154946_20150913_211324.jpg",
|
||
"20260627_154852_20150913_211324.jpg",
|
||
"20260620_031100_20150913_211324.jpg",
|
||
"20260627_154745_20150913_211324.jpg",
|
||
"20260627_154404_20150913_211324.jpg",
|
||
"20260627_153734_pad_20260620_031100_20150913_211324.jpg",
|
||
"20260625_050554_sc_3_20260618_173728_image.nobg.png",
|
||
"_turntable/cg_ad73335e/views/view_022_330deg.png",
|
||
"_turntable/cg_ad73335e/views/view_021_315deg.png",
|
||
"_turntable/cg_ad73335e/views/view_020_300deg.png",
|
||
"_turntable/cg_ad73335e/views/view_019_285deg.png",
|
||
"_turntable/cg_ad73335e/views/view_002_030deg.png",
|
||
"_turntable/cg_ad73335e/views/view_001_015deg.png",
|
||
"cg_ac6274f0_face.png",
|
||
"20260618_180939_image.png_face.png",
|
||
"img_51.png_face.png",
|
||
"20260627_150448_pad_20260627_150234_pad_20260627_150207_pad_20260627_145719_img_51.png",
|
||
"20260627_150351_pad_20260627_150234_pad_20260627_150207_pad_20260627_145719_img_51.png",
|
||
"20260627_150251_pad_20260627_150234_pad_20260627_150207_pad_20260627_145719_img_51.png",
|
||
"20260627_150234_pad_20260627_150207_pad_20260627_145719_img_51.png",
|
||
"20260627_150207_pad_20260627_145719_img_51.png",
|
||
"20260627_150013_img_51.png",
|
||
"20260627_145730_img_51.png",
|
||
"20260627_145719_img_51.png",
|
||
"20260627_145706_mr_img_51.png",
|
||
"20260627_145436_image.png",
|
||
"_turntable/cg_f9537c3f/views/view_023_345deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_001_015deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_000_000deg.png",
|
||
"_turntable/up_98c8443e/views/view_023_345deg.png",
|
||
"_turntable/up_98c8443e/views/view_000_000deg.png",
|
||
"up_7382d00e_face.png",
|
||
"up_455c085f_face.png",
|
||
"_turntable/20150913_211324.jpg/views/view_012_180deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_013_195deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_014_210deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_016_240deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_021_315deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_003_045deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_011_165deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_010_150deg.png",
|
||
"20260618_182810_image.png_face.png",
|
||
"20260619_124210_1_20260619_123958_image.nobg.png",
|
||
"20260620_011328_mr_1_20260619_124038_image.nobg.png",
|
||
"20260619_124258_2_20260619_124038_image.nobg.png",
|
||
"20260619_124210_1_20260619_123958_image.png",
|
||
"20260620_011328_mr_1_20260619_124038_image.png",
|
||
"20260620_011153_mr_1_20260619_124038_image.png",
|
||
"20260622_180912_image.png",
|
||
"20260620_011137_mr_1_20260619_124038_image.png",
|
||
"20260620_011120_mr_1_20260619_124038_image.png",
|
||
"20260620_010925_mr_1_20260619_124038_image.png",
|
||
"20260620_010911_mr_1_20260619_124038_image.png",
|
||
"20260622_181407_0_20260619_124038_image.png",
|
||
"20260619_184150_2_20260619_184116_image.png",
|
||
"20260619_124242_2_20260619_123958_image.png",
|
||
"20260620_010559_1_20260619_124038_image.png",
|
||
"20260624_020723_image.png",
|
||
"20260624_020455_image.png",
|
||
"20260624_015653_image.png",
|
||
"20260624_011605_image.png",
|
||
"20260624_010045_7_20260624_005912_image.png",
|
||
"_turntable/b1.png/views/view_018_270deg.png",
|
||
"_turntable/b1.png/views/view_017_255deg.png",
|
||
"20260618_182106_image.png_face.png",
|
||
"20260622_175657_4_20260622_175551_image.png",
|
||
"20260622_175712_5_20260622_175551_image.png",
|
||
"_turntable/cg_2afca8fc/views/view_001_015deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_016_240deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_013_195deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_009_135deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_008_120deg.png",
|
||
"up_ae825fce_face.png",
|
||
"_turntable/cg_4e2b6449/views/view_000_000deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_001_015deg.png",
|
||
"20260627_134506_pad_20260627_130837_image.png",
|
||
"20260627_134409_pad_20260627_130837_image.png",
|
||
"_turntable/cg_4e2b6449/views/view_003_045deg.png",
|
||
"_turntable/cg_4e2b6449/views/view_002_030deg.png",
|
||
"20260627_133359_pad_20260627_132332_image.nobg.png",
|
||
"20260627_133236_pad_20260627_132332_image.nobg.png",
|
||
"20260627_133149_pad_20260627_132332_image.nobg.png",
|
||
"20260627_133128_pad_20260627_132332_image.nobg.png",
|
||
"20260627_133024_pad_20260627_132332_image.nobg.png",
|
||
"20260627_132913_pad_20260627_132332_image.nobg.png",
|
||
"20260627_132744_pad_20260627_132332_image.nobg.png",
|
||
"20260627_132456_pad_20260627_132332_image.nobg.png",
|
||
"20260627_132402_pad_20260627_132332_image.nobg.png",
|
||
"20260627_132402_pad_20260627_132332_image.png",
|
||
"20260627_132332_image.png",
|
||
"_turntable/cg_65f71c4d/views/view_001_015deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_000_000deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_023_345deg.png",
|
||
"20260627_132208_pad_20260626_224632_sc_image.png",
|
||
"20260627_132151_sc_image.png",
|
||
"20260627_132035_pad_20260626_224632_sc_image.png",
|
||
"_turntable/cg_870f3f93/views/view_023_345deg.png",
|
||
"_turntable/cg_870f3f93/views/view_018_270deg.png",
|
||
"_turntable/cg_870f3f93/views/view_016_240deg.png",
|
||
"_turntable/cg_870f3f93/views/view_015_225deg.png",
|
||
"_turntable/cg_870f3f93/views/view_014_210deg.png",
|
||
"_turntable/cg_870f3f93/views/view_013_195deg.png",
|
||
"_turntable/cg_870f3f93/views/view_012_180deg.png",
|
||
"_turntable/cg_870f3f93/views/view_011_165deg.png",
|
||
"_turntable/cg_870f3f93/views/view_010_150deg.png",
|
||
"_turntable/cg_870f3f93/views/view_009_135deg.png",
|
||
"_turntable/cg_870f3f93/views/view_008_120deg.png",
|
||
"_turntable/cg_870f3f93/views/view_005_075deg.png",
|
||
"_turntable/cg_870f3f93/views/view_004_060deg.png",
|
||
"_turntable/cg_870f3f93/views/view_003_045deg.png",
|
||
"_turntable/cg_870f3f93/views/view_001_015deg.png",
|
||
"_turntable/cg_870f3f93/views/view_000_000deg.png",
|
||
"20260627_131628_pad_20260622_053758_2_20260621_115215_image.png",
|
||
"20260627_131605_pad_20260622_053758_2_20260621_115215_image.png",
|
||
"20260627_131420_2_20260621_115215_image.png",
|
||
"20260627_131120_pad_20260627_130837_image.png",
|
||
"20260627_131008_pad_20260627_130837_image.png",
|
||
"20260627_130925_pad_20260627_130837_image.png",
|
||
"20260627_130837_image.png",
|
||
"20260627_130827_image.png",
|
||
"_turntable/20260618_133729_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_003_045deg.png",
|
||
"_turntable/cg_3942c16b/views/view_007_105deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_007_105deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_009_135deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_008_120deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_023_345deg.png",
|
||
"20260625_014722_sc_mr_mr_image.nobg.png",
|
||
"_turntable/up_a1dddf6f/views/view_022_330deg.png",
|
||
"20260625_014604_sc_mr_mr_image.nobg.png",
|
||
"_turntable/up_a1dddf6f/views/view_021_315deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_020_300deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_019_285deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_018_270deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_015_225deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_016_240deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_014_210deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_013_195deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_011_165deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_010_150deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_004_060deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_003_045deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_002_030deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_001_015deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_000_000deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_022_330deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_023_345deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_001_015deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_000_000deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_001_015deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_000_000deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_023_345deg.png",
|
||
"_turntable/img_80.png/views/view_023_345deg.png",
|
||
"_turntable/img_80.png/views/view_001_015deg.png",
|
||
"_turntable/img_80.png/views/view_000_000deg.png",
|
||
"_turntable/cg_58c41add/views/view_017_255deg.png",
|
||
"_turntable/img_31.png/views/view_023_345deg.png",
|
||
"_turntable/img_31.png/views/view_016_240deg.png",
|
||
"_turntable/img_31.png/views/view_015_225deg.png",
|
||
"_turntable/img_31.png/views/view_014_210deg.png",
|
||
"_turntable/img_31.png/views/view_013_195deg.png",
|
||
"_turntable/img_31.png/views/view_012_180deg.png",
|
||
"_turntable/img_31.png/views/view_011_165deg.png",
|
||
"_turntable/img_31.png/views/view_010_150deg.png",
|
||
"_turntable/img_31.png/views/view_009_135deg.png",
|
||
"_turntable/img_31.png/views/view_008_120deg.png",
|
||
"_turntable/img_31.png/views/view_002_030deg.png",
|
||
"_turntable/img_31.png/views/view_001_015deg.png",
|
||
"_turntable/img_31.png/views/view_000_000deg.png",
|
||
"_turntable/img_87.png/views/view_014_210deg.png",
|
||
"_turntable/img_87.png/views/view_013_195deg.png",
|
||
"_turntable/img_87.png/views/view_012_180deg.png",
|
||
"_turntable/img_87.png/views/view_010_150deg.png",
|
||
"_turntable/img_87.png/views/view_009_135deg.png",
|
||
"_turntable/img_87.png/views/view_001_015deg.png",
|
||
"_turntable/img_87.png/views/view_000_000deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_002_030deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_001_015deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_000_000deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_023_345deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_022_330deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_018_270deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_017_255deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_016_240deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_015_225deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_014_210deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_013_195deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_012_180deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_011_165deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_010_150deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_009_135deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_008_120deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_007_105deg.png",
|
||
"20260627_124455_image.png",
|
||
"20260627_124123_12_20260627_123731_image.png",
|
||
"20260627_124106_11_20260627_123731_image.png",
|
||
"20260627_124049_image.png",
|
||
"20260627_124033_10_20260627_123731_image.png",
|
||
"20260627_124018_image.png",
|
||
"20260627_124001_9_20260627_123731_image.png",
|
||
"20260627_123945_8_20260627_123731_image.png",
|
||
"20260627_123935_image.png",
|
||
"20260627_123928_7_20260627_123731_image.png",
|
||
"20260627_123911_6_20260627_123731_image.png",
|
||
"20260627_123855_5_20260627_123731_image.png",
|
||
"20260627_123838_4_20260627_123731_image.png",
|
||
"20260627_123822_3_20260627_123731_image.png",
|
||
"20260627_123806_2_20260627_123731_image.png",
|
||
"20260627_123753_1_20260627_123731_image.png",
|
||
"20260627_123742_0_20260627_123731_image.png",
|
||
"20260627_123731_image.png",
|
||
"20260627_123721_12_20260627_123259_image.png",
|
||
"20260627_123710_11_20260627_123259_image.png",
|
||
"20260627_123659_10_20260627_123259_image.png",
|
||
"20260627_123648_9_20260627_123259_image.png",
|
||
"20260627_123636_8_20260627_123259_image.png",
|
||
"20260627_123619_7_20260627_123259_image.png",
|
||
"20260627_123603_6_20260627_123259_image.png",
|
||
"20260627_123546_5_20260627_123259_image.png",
|
||
"20260627_123530_4_20260627_123259_image.png",
|
||
"20260627_123519_3_20260627_123259_image.png",
|
||
"20260627_123507_2_20260627_123259_image.png",
|
||
"20260627_123456_1_20260627_123259_image.png",
|
||
"20260627_123445_0_20260627_123259_image.png",
|
||
"20260627_123259_image.png",
|
||
"_turntable/img_87.png/views/view_011_165deg.png",
|
||
"_turntable/img_87.png/views/view_008_120deg.png",
|
||
"_turntable/img_87.png/views/view_007_105deg.png",
|
||
"_turntable/img_87.png/views/view_006_090deg.png",
|
||
"_turntable/img_87.png/views/view_005_075deg.png",
|
||
"_turntable/img_87.png/views/view_004_060deg.png",
|
||
"_turntable/img_87.png/views/view_003_045deg.png",
|
||
"_turntable/img_87.png/views/view_002_030deg.png",
|
||
"_turntable/img_31.png/views/view_022_330deg.png",
|
||
"_turntable/img_31.png/views/view_021_315deg.png",
|
||
"_turntable/img_31.png/views/view_020_300deg.png",
|
||
"_turntable/img_31.png/views/view_019_285deg.png",
|
||
"_turntable/img_31.png/views/view_018_270deg.png",
|
||
"_turntable/img_31.png/views/view_017_255deg.png",
|
||
"_turntable/img_31.png/views/view_007_105deg.png",
|
||
"_turntable/img_31.png/views/view_006_090deg.png",
|
||
"_turntable/img_31.png/views/view_005_075deg.png",
|
||
"_turntable/img_31.png/views/view_004_060deg.png",
|
||
"_turntable/img_31.png/views/view_003_045deg.png",
|
||
"_turntable/cg_58c41add/views/view_023_345deg.png",
|
||
"_turntable/cg_58c41add/views/view_022_330deg.png",
|
||
"_turntable/cg_58c41add/views/view_021_315deg.png",
|
||
"_turntable/cg_58c41add/views/view_020_300deg.png",
|
||
"_turntable/cg_58c41add/views/view_019_285deg.png",
|
||
"_turntable/cg_58c41add/views/view_018_270deg.png",
|
||
"_turntable/cg_58c41add/views/view_016_240deg.png",
|
||
"_turntable/cg_58c41add/views/view_015_225deg.png",
|
||
"_turntable/cg_58c41add/views/view_014_210deg.png",
|
||
"_turntable/cg_58c41add/views/view_013_195deg.png",
|
||
"_turntable/cg_58c41add/views/view_012_180deg.png",
|
||
"_turntable/cg_58c41add/views/view_011_165deg.png",
|
||
"_turntable/cg_58c41add/views/view_010_150deg.png",
|
||
"_turntable/cg_58c41add/views/view_009_135deg.png",
|
||
"_turntable/cg_58c41add/views/view_008_120deg.png",
|
||
"_turntable/cg_58c41add/views/view_007_105deg.png",
|
||
"_turntable/cg_58c41add/views/view_006_090deg.png",
|
||
"_turntable/cg_58c41add/views/view_005_075deg.png",
|
||
"_turntable/cg_58c41add/views/view_004_060deg.png",
|
||
"_turntable/cg_58c41add/views/view_003_045deg.png",
|
||
"_turntable/cg_58c41add/views/view_002_030deg.png",
|
||
"_turntable/cg_58c41add/views/view_001_015deg.png",
|
||
"_turntable/cg_58c41add/views/view_000_000deg.png",
|
||
"_turntable/img_80.png/views/view_022_330deg.png",
|
||
"_turntable/img_80.png/views/view_021_315deg.png",
|
||
"_turntable/img_80.png/views/view_020_300deg.png",
|
||
"_turntable/img_80.png/views/view_019_285deg.png",
|
||
"_turntable/img_80.png/views/view_018_270deg.png",
|
||
"_turntable/img_80.png/views/view_017_255deg.png",
|
||
"_turntable/img_80.png/views/view_016_240deg.png",
|
||
"_turntable/img_80.png/views/view_015_225deg.png",
|
||
"_turntable/img_80.png/views/view_014_210deg.png",
|
||
"_turntable/img_80.png/views/view_013_195deg.png",
|
||
"_turntable/img_80.png/views/view_012_180deg.png",
|
||
"_turntable/img_80.png/views/view_011_165deg.png",
|
||
"_turntable/img_80.png/views/view_010_150deg.png",
|
||
"_turntable/img_80.png/views/view_009_135deg.png",
|
||
"_turntable/img_80.png/views/view_008_120deg.png",
|
||
"_turntable/img_80.png/views/view_007_105deg.png",
|
||
"_turntable/img_80.png/views/view_006_090deg.png",
|
||
"_turntable/img_80.png/views/view_005_075deg.png",
|
||
"_turntable/img_80.png/views/view_004_060deg.png",
|
||
"_turntable/img_80.png/views/view_003_045deg.png",
|
||
"_turntable/img_80.png/views/view_002_030deg.png",
|
||
"_turntable/up_94204739/views/view_023_345deg.png",
|
||
"_turntable/up_94204739/views/view_022_330deg.png",
|
||
"_turntable/up_94204739/views/view_021_315deg.png",
|
||
"_turntable/up_94204739/views/view_020_300deg.png",
|
||
"_turntable/up_94204739/views/view_019_285deg.png",
|
||
"_turntable/up_94204739/views/view_018_270deg.png",
|
||
"_turntable/up_94204739/views/view_017_255deg.png",
|
||
"_turntable/up_94204739/views/view_016_240deg.png",
|
||
"_turntable/up_94204739/views/view_015_225deg.png",
|
||
"_turntable/up_94204739/views/view_014_210deg.png",
|
||
"_turntable/up_94204739/views/view_013_195deg.png",
|
||
"_turntable/up_94204739/views/view_012_180deg.png",
|
||
"_turntable/up_94204739/views/view_011_165deg.png",
|
||
"_turntable/up_94204739/views/view_010_150deg.png",
|
||
"_turntable/up_94204739/views/view_009_135deg.png",
|
||
"_turntable/up_94204739/views/view_008_120deg.png",
|
||
"_turntable/up_94204739/views/view_007_105deg.png",
|
||
"_turntable/up_94204739/views/view_006_090deg.png",
|
||
"_turntable/up_94204739/views/view_005_075deg.png",
|
||
"_turntable/up_94204739/views/view_004_060deg.png",
|
||
"_turntable/up_94204739/views/view_003_045deg.png",
|
||
"_turntable/up_94204739/views/view_002_030deg.png",
|
||
"_turntable/up_94204739/views/view_001_015deg.png",
|
||
"_turntable/up_94204739/views/view_000_000deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_022_330deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_021_315deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_020_300deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_019_285deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_018_270deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_017_255deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_016_240deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_015_225deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_014_210deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_013_195deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_012_180deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_011_165deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_010_150deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_009_135deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_008_120deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_007_105deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_006_090deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_005_075deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_004_060deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_003_045deg.png",
|
||
"_turntable/cg_fb1043d8/views/view_002_030deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_003_045deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_182106_image.png/views/view_000_000deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_017_255deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_012_180deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_006_090deg.png",
|
||
"_turntable/up_a1dddf6f/views/view_005_075deg.png",
|
||
"_turntable/up_52dede0c/views/view_023_345deg.png",
|
||
"_turntable/up_52dede0c/views/view_022_330deg.png",
|
||
"_turntable/up_52dede0c/views/view_021_315deg.png",
|
||
"_turntable/up_52dede0c/views/view_020_300deg.png",
|
||
"_turntable/up_52dede0c/views/view_019_285deg.png",
|
||
"_turntable/up_52dede0c/views/view_018_270deg.png",
|
||
"_turntable/up_52dede0c/views/view_017_255deg.png",
|
||
"_turntable/up_52dede0c/views/view_016_240deg.png",
|
||
"_turntable/up_52dede0c/views/view_015_225deg.png",
|
||
"_turntable/up_52dede0c/views/view_014_210deg.png",
|
||
"_turntable/up_52dede0c/views/view_013_195deg.png",
|
||
"_turntable/up_52dede0c/views/view_012_180deg.png",
|
||
"_turntable/up_52dede0c/views/view_011_165deg.png",
|
||
"_turntable/up_52dede0c/views/view_010_150deg.png",
|
||
"_turntable/up_52dede0c/views/view_009_135deg.png",
|
||
"_turntable/up_52dede0c/views/view_008_120deg.png",
|
||
"_turntable/up_52dede0c/views/view_007_105deg.png",
|
||
"_turntable/up_52dede0c/views/view_006_090deg.png",
|
||
"_turntable/up_52dede0c/views/view_005_075deg.png",
|
||
"_turntable/up_52dede0c/views/view_004_060deg.png",
|
||
"_turntable/up_52dede0c/views/view_003_045deg.png",
|
||
"_turntable/up_52dede0c/views/view_002_030deg.png",
|
||
"_turntable/up_52dede0c/views/view_001_015deg.png",
|
||
"_turntable/up_52dede0c/views/view_000_000deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_022_330deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_021_315deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_020_300deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_019_285deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_018_270deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_017_255deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_016_240deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_015_225deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_014_210deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_013_195deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_012_180deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_011_165deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_010_150deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_009_135deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_008_120deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_007_105deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_006_090deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_005_075deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_004_060deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_003_045deg.png",
|
||
"_turntable/up_f9a5bb33/views/view_002_030deg.png",
|
||
"_turntable/cg_3942c16b/views/view_023_345deg.png",
|
||
"_turntable/cg_3942c16b/views/view_022_330deg.png",
|
||
"_turntable/cg_3942c16b/views/view_021_315deg.png",
|
||
"_turntable/cg_3942c16b/views/view_020_300deg.png",
|
||
"_turntable/cg_3942c16b/views/view_019_285deg.png",
|
||
"_turntable/cg_3942c16b/views/view_018_270deg.png",
|
||
"_turntable/cg_3942c16b/views/view_017_255deg.png",
|
||
"_turntable/cg_3942c16b/views/view_016_240deg.png",
|
||
"_turntable/cg_3942c16b/views/view_015_225deg.png",
|
||
"_turntable/cg_3942c16b/views/view_014_210deg.png",
|
||
"_turntable/cg_3942c16b/views/view_013_195deg.png",
|
||
"_turntable/cg_3942c16b/views/view_012_180deg.png",
|
||
"_turntable/cg_3942c16b/views/view_011_165deg.png",
|
||
"_turntable/cg_3942c16b/views/view_010_150deg.png",
|
||
"_turntable/cg_3942c16b/views/view_009_135deg.png",
|
||
"_turntable/cg_3942c16b/views/view_008_120deg.png",
|
||
"_turntable/cg_3942c16b/views/view_006_090deg.png",
|
||
"_turntable/cg_3942c16b/views/view_005_075deg.png",
|
||
"_turntable/cg_3942c16b/views/view_004_060deg.png",
|
||
"_turntable/cg_3942c16b/views/view_003_045deg.png",
|
||
"_turntable/cg_3942c16b/views/view_002_030deg.png",
|
||
"_turntable/cg_3942c16b/views/view_001_015deg.png",
|
||
"_turntable/cg_3942c16b/views/view_000_000deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_022_330deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_003_045deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_053458_image.png/views/view_000_000deg.png",
|
||
"_turntable/cg_7ec17537/views/view_023_345deg.png",
|
||
"_turntable/cg_7ec17537/views/view_022_330deg.png",
|
||
"_turntable/cg_7ec17537/views/view_021_315deg.png",
|
||
"_turntable/cg_7ec17537/views/view_020_300deg.png",
|
||
"_turntable/cg_7ec17537/views/view_019_285deg.png",
|
||
"_turntable/cg_7ec17537/views/view_018_270deg.png",
|
||
"_turntable/cg_7ec17537/views/view_017_255deg.png",
|
||
"_turntable/cg_7ec17537/views/view_016_240deg.png",
|
||
"_turntable/cg_7ec17537/views/view_015_225deg.png",
|
||
"_turntable/cg_7ec17537/views/view_014_210deg.png",
|
||
"_turntable/cg_7ec17537/views/view_013_195deg.png",
|
||
"_turntable/cg_7ec17537/views/view_012_180deg.png",
|
||
"_turntable/cg_7ec17537/views/view_011_165deg.png",
|
||
"_turntable/cg_7ec17537/views/view_010_150deg.png",
|
||
"_turntable/cg_7ec17537/views/view_009_135deg.png",
|
||
"_turntable/cg_7ec17537/views/view_008_120deg.png",
|
||
"_turntable/cg_7ec17537/views/view_007_105deg.png",
|
||
"_turntable/cg_7ec17537/views/view_006_090deg.png",
|
||
"_turntable/cg_7ec17537/views/view_005_075deg.png",
|
||
"_turntable/cg_7ec17537/views/view_004_060deg.png",
|
||
"_turntable/cg_7ec17537/views/view_003_045deg.png",
|
||
"_turntable/cg_7ec17537/views/view_002_030deg.png",
|
||
"_turntable/cg_7ec17537/views/view_001_015deg.png",
|
||
"_turntable/cg_7ec17537/views/view_000_000deg.png",
|
||
"_turntable/up_154b502c/views/view_023_345deg.png",
|
||
"_turntable/up_154b502c/views/view_022_330deg.png",
|
||
"_turntable/up_154b502c/views/view_021_315deg.png",
|
||
"_turntable/up_154b502c/views/view_020_300deg.png",
|
||
"_turntable/up_154b502c/views/view_019_285deg.png",
|
||
"_turntable/up_154b502c/views/view_018_270deg.png",
|
||
"_turntable/up_154b502c/views/view_017_255deg.png",
|
||
"_turntable/up_154b502c/views/view_016_240deg.png",
|
||
"_turntable/up_154b502c/views/view_015_225deg.png",
|
||
"_turntable/up_154b502c/views/view_014_210deg.png",
|
||
"_turntable/up_154b502c/views/view_013_195deg.png",
|
||
"_turntable/up_154b502c/views/view_012_180deg.png",
|
||
"_turntable/up_154b502c/views/view_011_165deg.png",
|
||
"_turntable/up_154b502c/views/view_010_150deg.png",
|
||
"_turntable/up_154b502c/views/view_009_135deg.png",
|
||
"_turntable/up_154b502c/views/view_008_120deg.png",
|
||
"_turntable/up_154b502c/views/view_007_105deg.png",
|
||
"_turntable/up_154b502c/views/view_006_090deg.png",
|
||
"_turntable/up_154b502c/views/view_005_075deg.png",
|
||
"_turntable/up_154b502c/views/view_004_060deg.png",
|
||
"_turntable/up_154b502c/views/view_003_045deg.png",
|
||
"_turntable/up_154b502c/views/view_002_030deg.png",
|
||
"_turntable/up_154b502c/views/view_001_015deg.png",
|
||
"_turntable/up_154b502c/views/view_000_000deg.png",
|
||
"_turntable/cg_36a33bae/views/view_023_345deg.png",
|
||
"_turntable/cg_36a33bae/views/view_022_330deg.png",
|
||
"_turntable/cg_36a33bae/views/view_021_315deg.png",
|
||
"_turntable/cg_36a33bae/views/view_020_300deg.png",
|
||
"_turntable/cg_36a33bae/views/view_019_285deg.png",
|
||
"_turntable/cg_36a33bae/views/view_018_270deg.png",
|
||
"_turntable/cg_36a33bae/views/view_017_255deg.png",
|
||
"_turntable/cg_36a33bae/views/view_016_240deg.png",
|
||
"_turntable/cg_36a33bae/views/view_015_225deg.png",
|
||
"_turntable/cg_36a33bae/views/view_014_210deg.png",
|
||
"_turntable/cg_36a33bae/views/view_013_195deg.png",
|
||
"_turntable/cg_36a33bae/views/view_012_180deg.png",
|
||
"_turntable/cg_36a33bae/views/view_011_165deg.png",
|
||
"_turntable/cg_36a33bae/views/view_010_150deg.png",
|
||
"_turntable/cg_36a33bae/views/view_009_135deg.png",
|
||
"_turntable/cg_36a33bae/views/view_008_120deg.png",
|
||
"_turntable/cg_36a33bae/views/view_007_105deg.png",
|
||
"_turntable/cg_36a33bae/views/view_006_090deg.png",
|
||
"_turntable/cg_36a33bae/views/view_005_075deg.png",
|
||
"_turntable/cg_36a33bae/views/view_004_060deg.png",
|
||
"_turntable/cg_36a33bae/views/view_003_045deg.png",
|
||
"_turntable/cg_36a33bae/views/view_002_030deg.png",
|
||
"_turntable/cg_36a33bae/views/view_001_015deg.png",
|
||
"_turntable/cg_36a33bae/views/view_000_000deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_022_330deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_133729_image.png/views/view_000_000deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_022_330deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_003_045deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_181715_image.png/views/view_000_000deg.png",
|
||
"_turntable/up_a1104f04/views/view_022_330deg.png",
|
||
"_turntable/up_a1104f04/views/view_021_315deg.png",
|
||
"_turntable/up_a1104f04/views/view_020_300deg.png",
|
||
"_turntable/up_a1104f04/views/view_019_285deg.png",
|
||
"_turntable/up_a1104f04/views/view_018_270deg.png",
|
||
"_turntable/up_a1104f04/views/view_017_255deg.png",
|
||
"_turntable/up_a1104f04/views/view_016_240deg.png",
|
||
"_turntable/up_a1104f04/views/view_015_225deg.png",
|
||
"_turntable/up_a1104f04/views/view_014_210deg.png",
|
||
"_turntable/up_a1104f04/views/view_013_195deg.png",
|
||
"_turntable/up_a1104f04/views/view_012_180deg.png",
|
||
"_turntable/up_a1104f04/views/view_011_165deg.png",
|
||
"_turntable/up_a1104f04/views/view_010_150deg.png",
|
||
"_turntable/up_a1104f04/views/view_009_135deg.png",
|
||
"_turntable/up_a1104f04/views/view_008_120deg.png",
|
||
"_turntable/up_a1104f04/views/view_007_105deg.png",
|
||
"_turntable/up_a1104f04/views/view_006_090deg.png",
|
||
"_turntable/up_a1104f04/views/view_005_075deg.png",
|
||
"_turntable/up_a1104f04/views/view_004_060deg.png",
|
||
"_turntable/up_a1104f04/views/view_003_045deg.png",
|
||
"_turntable/up_a1104f04/views/view_002_030deg.png",
|
||
"_turntable/up_a1104f04/views/view_001_015deg.png",
|
||
"_turntable/up_a1104f04/views/view_000_000deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_023_345deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_022_330deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_021_315deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_020_300deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_019_285deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_018_270deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_017_255deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_016_240deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_015_225deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_014_210deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_013_195deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_012_180deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_011_165deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_010_150deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_009_135deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_008_120deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_007_105deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_006_090deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_005_075deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_004_060deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_003_045deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_002_030deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_001_015deg.png",
|
||
"_turntable/cg_8c8d8ec9/views/view_000_000deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_023_345deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_022_330deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_021_315deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_020_300deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_019_285deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_018_270deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_017_255deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_016_240deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_015_225deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_014_210deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_013_195deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_012_180deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_011_165deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_010_150deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_009_135deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_008_120deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_007_105deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_006_090deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_005_075deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_004_060deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_003_045deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_002_030deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_001_015deg.png",
|
||
"_turntable/cg_9231ec1c/views/view_000_000deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_023_345deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_022_330deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_020_300deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_019_285deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_018_270deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_017_255deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_015_225deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_009_135deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_008_120deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_007_105deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_006_090deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_005_075deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_004_060deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_002_030deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_001_015deg.png",
|
||
"_turntable/20150913_211324.jpg/views/view_000_000deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_022_330deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_003_045deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_182810_image.png/views/view_000_000deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_023_345deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_022_330deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_021_315deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_020_300deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_019_285deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_018_270deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_017_255deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_016_240deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_015_225deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_014_210deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_013_195deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_012_180deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_011_165deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_010_150deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_009_135deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_008_120deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_007_105deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_006_090deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_005_075deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_004_060deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_003_045deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_002_030deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_001_015deg.png",
|
||
"_turntable/up_8cf9e62e/views/view_000_000deg.png",
|
||
"_turntable/cg_870f3f93/views/view_022_330deg.png",
|
||
"_turntable/cg_870f3f93/views/view_021_315deg.png",
|
||
"_turntable/cg_870f3f93/views/view_020_300deg.png",
|
||
"_turntable/cg_870f3f93/views/view_019_285deg.png",
|
||
"_turntable/cg_870f3f93/views/view_017_255deg.png",
|
||
"_turntable/cg_870f3f93/views/view_007_105deg.png",
|
||
"_turntable/cg_870f3f93/views/view_006_090deg.png",
|
||
"_turntable/cg_870f3f93/views/view_002_030deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_023_345deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_022_330deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_021_315deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_020_300deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_019_285deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_018_270deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_017_255deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_016_240deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_015_225deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_014_210deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_013_195deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_012_180deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_011_165deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_010_150deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_009_135deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_008_120deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_007_105deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_006_090deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_005_075deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_004_060deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_003_045deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_002_030deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_001_015deg.png",
|
||
"_turntable/cg_dc9c9bcc/views/view_000_000deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_022_330deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_003_045deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_134348_image.png/views/view_000_000deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_022_330deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_003_045deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_125748_image.png/views/view_000_000deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_023_345deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_022_330deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_021_315deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_020_300deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_019_285deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_018_270deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_017_255deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_016_240deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_015_225deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_014_210deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_013_195deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_012_180deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_011_165deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_010_150deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_009_135deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_008_120deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_007_105deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_006_090deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_005_075deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_004_060deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_003_045deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_002_030deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_001_015deg.png",
|
||
"_turntable/cg_ac6274f0/views/view_000_000deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_022_330deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_003_045deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_181848_image.png/views/view_000_000deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_022_330deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_021_315deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_020_300deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_019_285deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_018_270deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_017_255deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_016_240deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_015_225deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_014_210deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_013_195deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_012_180deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_011_165deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_010_150deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_009_135deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_008_120deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_007_105deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_006_090deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_005_075deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_004_060deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_003_045deg.png",
|
||
"_turntable/cg_65f71c4d/views/view_002_030deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_021_315deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_020_300deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_019_285deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_006_090deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_005_075deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_004_060deg.png",
|
||
"_turntable/cg_ed2e43d1/views/view_003_045deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_022_330deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_003_045deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_125217_image.png/views/view_000_000deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_023_345deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_022_330deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_021_315deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_020_300deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_019_285deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_018_270deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_017_255deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_016_240deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_015_225deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_014_210deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_013_195deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_012_180deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_011_165deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_010_150deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_009_135deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_008_120deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_007_105deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_006_090deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_005_075deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_004_060deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_003_045deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_002_030deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_001_015deg.png",
|
||
"_turntable/up_9f6b53b9/views/view_000_000deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_022_330deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_021_315deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_020_300deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_019_285deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_018_270deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_017_255deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_016_240deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_015_225deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_014_210deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_013_195deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_012_180deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_011_165deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_010_150deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_009_135deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_008_120deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_007_105deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_006_090deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_005_075deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_004_060deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_003_045deg.png",
|
||
"_turntable/cg_f9537c3f/views/view_002_030deg.png",
|
||
"_turntable/cg_58c0891c/views/view_023_345deg.png",
|
||
"_turntable/cg_58c0891c/views/view_022_330deg.png",
|
||
"_turntable/cg_58c0891c/views/view_021_315deg.png",
|
||
"_turntable/cg_58c0891c/views/view_020_300deg.png",
|
||
"_turntable/cg_58c0891c/views/view_019_285deg.png",
|
||
"_turntable/cg_58c0891c/views/view_018_270deg.png",
|
||
"_turntable/cg_58c0891c/views/view_017_255deg.png",
|
||
"_turntable/cg_58c0891c/views/view_016_240deg.png",
|
||
"_turntable/cg_58c0891c/views/view_015_225deg.png",
|
||
"_turntable/cg_58c0891c/views/view_014_210deg.png",
|
||
"_turntable/cg_58c0891c/views/view_013_195deg.png",
|
||
"_turntable/cg_58c0891c/views/view_012_180deg.png",
|
||
"_turntable/cg_58c0891c/views/view_011_165deg.png",
|
||
"_turntable/cg_58c0891c/views/view_010_150deg.png",
|
||
"_turntable/cg_58c0891c/views/view_009_135deg.png",
|
||
"_turntable/cg_58c0891c/views/view_008_120deg.png",
|
||
"_turntable/cg_58c0891c/views/view_007_105deg.png",
|
||
"_turntable/cg_58c0891c/views/view_006_090deg.png",
|
||
"_turntable/cg_58c0891c/views/view_005_075deg.png",
|
||
"_turntable/cg_58c0891c/views/view_004_060deg.png",
|
||
"_turntable/cg_58c0891c/views/view_003_045deg.png",
|
||
"_turntable/cg_58c0891c/views/view_002_030deg.png",
|
||
"_turntable/cg_58c0891c/views/view_001_015deg.png",
|
||
"_turntable/cg_58c0891c/views/view_000_000deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_023_345deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_021_315deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_020_300deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_019_285deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_018_270deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_017_255deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_016_240deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_015_225deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_014_210deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_013_195deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_012_180deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_011_165deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_010_150deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_009_135deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_008_120deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_007_105deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_006_090deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_005_075deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_004_060deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_003_045deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_001_015deg.png",
|
||
"_turntable/cg_5991f3f2/views/view_000_000deg.png",
|
||
"_turntable/out4.png/views/view_023_345deg.png",
|
||
"_turntable/out4.png/views/view_022_330deg.png",
|
||
"_turntable/out4.png/views/view_021_315deg.png",
|
||
"_turntable/out4.png/views/view_020_300deg.png",
|
||
"_turntable/out4.png/views/view_019_285deg.png",
|
||
"_turntable/out4.png/views/view_018_270deg.png",
|
||
"_turntable/out4.png/views/view_017_255deg.png",
|
||
"_turntable/out4.png/views/view_016_240deg.png",
|
||
"_turntable/out4.png/views/view_015_225deg.png",
|
||
"_turntable/out4.png/views/view_014_210deg.png",
|
||
"_turntable/out4.png/views/view_013_195deg.png",
|
||
"_turntable/out4.png/views/view_012_180deg.png",
|
||
"_turntable/out4.png/views/view_011_165deg.png",
|
||
"_turntable/out4.png/views/view_010_150deg.png",
|
||
"_turntable/out4.png/views/view_009_135deg.png",
|
||
"_turntable/cg_18040099/views/view_023_345deg.png",
|
||
"_turntable/cg_18040099/views/view_022_330deg.png",
|
||
"_turntable/cg_18040099/views/view_021_315deg.png",
|
||
"_turntable/cg_18040099/views/view_020_300deg.png",
|
||
"_turntable/cg_18040099/views/view_019_285deg.png",
|
||
"_turntable/cg_18040099/views/view_018_270deg.png",
|
||
"_turntable/cg_18040099/views/view_017_255deg.png",
|
||
"_turntable/cg_18040099/views/view_016_240deg.png",
|
||
"_turntable/cg_18040099/views/view_015_225deg.png",
|
||
"_turntable/cg_18040099/views/view_014_210deg.png",
|
||
"_turntable/cg_18040099/views/view_013_195deg.png",
|
||
"_turntable/cg_18040099/views/view_012_180deg.png",
|
||
"_turntable/cg_18040099/views/view_011_165deg.png",
|
||
"_turntable/cg_18040099/views/view_010_150deg.png",
|
||
"_turntable/cg_18040099/views/view_009_135deg.png",
|
||
"_turntable/cg_18040099/views/view_008_120deg.png",
|
||
"_turntable/cg_18040099/views/view_007_105deg.png",
|
||
"_turntable/cg_18040099/views/view_006_090deg.png",
|
||
"_turntable/cg_18040099/views/view_005_075deg.png",
|
||
"_turntable/cg_18040099/views/view_004_060deg.png",
|
||
"_turntable/cg_18040099/views/view_003_045deg.png",
|
||
"_turntable/cg_18040099/views/view_002_030deg.png",
|
||
"_turntable/cg_18040099/views/view_001_015deg.png",
|
||
"_turntable/cg_18040099/views/view_000_000deg.png",
|
||
"20260627_044054_pad_20260621_205908_0_20260621_205151_image.png",
|
||
"20260627_044022_pad_20260621_205908_0_20260621_205151_image.png",
|
||
"20260627_043800_dup_20260624_055327_image.nobg.nobg.png",
|
||
"20260624_125511_dup_20260624_055327_image.nobg.nobg.png",
|
||
"20260624_125511_dup_20260624_055327_image.nobg.nobg.nobg.png",
|
||
"20260627_043555_dup_20260624_055327_image.nobg.nobg.nobg.png",
|
||
"20260627_043555_dup_20260624_055327_image.nobg.nobg.png",
|
||
"20260627_043455_dup_20260624_055327_image.nobg.png",
|
||
"20260624_125511_dup_20260624_055327_image.nobg.png",
|
||
"20260627_043155_crop_20260627_042652_pad_20260627_042554_pad_20260627_042228_image.nobg.png",
|
||
"20260627_043055_pad_20260627_042228_image.nobg.png",
|
||
"20260627_042900_pad_20260627_042554_pad_20260627_042228_image.nobg.png",
|
||
"20260627_042835_crop_20260627_042652_pad_20260627_042554_pad_20260627_042228_image.nobg.png",
|
||
"20260627_042719_pad_20260627_042554_pad_20260627_042228_image.nobg.png",
|
||
"20260627_042652_pad_20260627_042554_pad_20260627_042228_image.nobg.png",
|
||
"20260627_042652_pad_20260627_042554_pad_20260627_042228_image.nobg.nobg.png",
|
||
"20260627_042620_pad_20260627_042228_image.nobg.png",
|
||
"20260627_042554_pad_20260627_042228_image.nobg.png",
|
||
"20260627_042554_pad_20260627_042228_image.nobg.nobg.png",
|
||
"20260627_042525_image.nobg.png",
|
||
"20260627_042228_image.nobg.png",
|
||
"20260627_042228_image.png",
|
||
"20260627_042256_image.png",
|
||
"20260627_042100_pose_image.png",
|
||
"20260627_042003_image.png",
|
||
"20260627_041915_image.png",
|
||
"20260627_041701_image.png",
|
||
"20260627_041236_image.nobg.png",
|
||
"20260627_041236_image.png",
|
||
"20260621_033259_image.nobg.png",
|
||
"20260627_040944_pad_20260627_030425_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_040926_pad_20260627_030425_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_040817_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_040738_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_040304_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_040022_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_035757_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_035559_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_035145_sc_dup_20260624_125526_dup_20260624_055327_image.nobg.nobg.nobg.png",
|
||
"20260627_035026_sc_dup_20260624_125526_dup_20260624_055327_image.nobg.nobg.nobg.png",
|
||
"20260627_034934_sc_dup_20260624_125526_dup_20260624_055327_image.nobg.nobg.png",
|
||
"20260627_034736_sc_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_034657_sc_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_034539_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_034516_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_034136_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_033930_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_033810_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_033515_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_033439_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_033320_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_033211_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_033128_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_033036_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_032921_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_031155_sc_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_031104_sc_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_030811_sc_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_030739_sc_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"cg_18040099_face.png",
|
||
"20260627_030425_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_030441_crop_20260627_030425_dup_20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260627_030425_dup_20260624_125526_dup_20260624_055327_image.nobg.nobg.png",
|
||
"20260627_030344_sc_image.png",
|
||
"20260627_030145_sc_dup_20260624_055327_image.nobg.png",
|
||
"20260627_030108_sc_dup_20260624_055327_image.nobg.png",
|
||
"20260627_030017_sc_dup_20260624_055327_image.nobg.png",
|
||
"20260627_025856_image.png",
|
||
"20260627_025639_sc_2_20260619_202924_image.png",
|
||
"20260627_025354_sc_2_20260619_202924_image.png",
|
||
"20260627_025021_sc_image.png",
|
||
"20260627_024950_sc_image.png",
|
||
"20260627_024719_sc_image.png",
|
||
"20260627_024645_image.png",
|
||
"20260627_024603_sc_image.png",
|
||
"20260627_024532_sc_sc_image.png",
|
||
"20260627_024445_image.png",
|
||
"20260627_024048_sc_image.nobg.png",
|
||
"20260627_023522_sc_image.nobg.png",
|
||
"20260627_023305_image.png",
|
||
"20260627_022851_image.nobg.png",
|
||
"20260627_022636_image.nobg.png",
|
||
"20260627_022513_image.nobg.png",
|
||
"20260627_022032_image.nobg.png",
|
||
"20260627_021300_sc_image.png",
|
||
"20260627_021145_image.nobg.png",
|
||
"20260627_020858_sc_image.png",
|
||
"20260627_020758_sc_image.png",
|
||
"20260627_020659_sc_image.png",
|
||
"20260627_020432_image.nobg.png",
|
||
"20260627_020352_image.png",
|
||
"20260627_020333_image.png",
|
||
"20260627_020210_image.png",
|
||
"20260627_015719_sc_image.png",
|
||
"20260627_014938_sc_image.png",
|
||
"20260627_014744_sc_image.png",
|
||
"20260627_014710_sc_image.png",
|
||
"20260627_013752_sc_image.png",
|
||
"20260627_013633_sc_image.png",
|
||
"20260627_013548_sc_image.png",
|
||
"cg_37d910ad_face.png",
|
||
"20260627_013006_sc_image.png",
|
||
"20260627_012859_sc_image.png",
|
||
"20260627_012814_image.png",
|
||
"_turntable/20260618_181600_image.png/views/view_011_330deg.png",
|
||
"_turntable/20260618_181600_image.png/views/view_010_300deg.png",
|
||
"_turntable/20260618_181600_image.png/views/view_009_270deg.png",
|
||
"_turntable/20260618_181600_image.png/views/view_008_240deg.png",
|
||
"_turntable/20260618_181600_image.png/views/view_007_210deg.png",
|
||
"_turntable/20260618_181600_image.png/views/view_006_180deg.png",
|
||
"_turntable/20260618_181600_image.png/views/view_005_150deg.png",
|
||
"_turntable/20260618_181600_image.png/views/view_004_120deg.png",
|
||
"_turntable/20260618_181600_image.png/views/view_003_090deg.png",
|
||
"_turntable/20260618_181600_image.png/views/view_002_060deg.png",
|
||
"_turntable/20260618_181600_image.png/views/view_001_030deg.png",
|
||
"_turntable/20260618_181600_image.png/views/view_000_000deg.png",
|
||
"20260626_234431_crop_20260626_231655_sc_image.nobg.png",
|
||
"20260626_231655_sc_image.nobg.png",
|
||
"20260618_181600_image.png_face.png",
|
||
"20260626_231655_sc_image.png",
|
||
"20260626_234334_dup_20260626_231655_sc_image.png",
|
||
"20260626_231349_sc_image.png",
|
||
"20260626_231127_image.png",
|
||
"_turntable/up_624995c3/views/view_011_330deg.png",
|
||
"_turntable/up_624995c3/views/view_010_300deg.png",
|
||
"_turntable/up_624995c3/views/view_009_270deg.png",
|
||
"_turntable/up_624995c3/views/view_008_240deg.png",
|
||
"_turntable/up_624995c3/views/view_007_210deg.png",
|
||
"_turntable/up_624995c3/views/view_006_180deg.png",
|
||
"_turntable/up_624995c3/views/view_005_150deg.png",
|
||
"_turntable/up_624995c3/views/view_004_120deg.png",
|
||
"_turntable/up_624995c3/views/view_003_090deg.png",
|
||
"_turntable/up_624995c3/views/view_002_060deg.png",
|
||
"_turntable/up_624995c3/views/view_001_030deg.png",
|
||
"_turntable/up_624995c3/views/view_000_000deg.png",
|
||
"up_624995c3_face.png",
|
||
"20260626_224632_sc_image.png",
|
||
"20260626_224037_sc_image.png",
|
||
"20260626_223814_sc_image.png",
|
||
"20260626_222911_sc_image.png",
|
||
"20260626_222706_sc_image.png",
|
||
"20260626_222509_sc_image.png",
|
||
"20260626_222302_sc_image.png",
|
||
"_turntable/up_624995c3/views/view_002_030deg.png",
|
||
"_turntable/up_624995c3/views/view_001_015deg.png",
|
||
"20260626_220958_image.png",
|
||
"_turntable/cg_37d910ad/views/view_011_330deg.png",
|
||
"_turntable/cg_37d910ad/views/view_010_300deg.png",
|
||
"_turntable/cg_37d910ad/views/view_009_270deg.png",
|
||
"_turntable/cg_37d910ad/views/view_008_240deg.png",
|
||
"_turntable/cg_37d910ad/views/view_007_210deg.png",
|
||
"_turntable/cg_37d910ad/views/view_006_180deg.png",
|
||
"_turntable/cg_37d910ad/views/view_005_150deg.png",
|
||
"_turntable/cg_37d910ad/views/view_004_120deg.png",
|
||
"_turntable/cg_37d910ad/views/view_003_090deg.png",
|
||
"_turntable/cg_37d910ad/views/view_002_060deg.png",
|
||
"_turntable/cg_37d910ad/views/view_001_030deg.png",
|
||
"_turntable/cg_37d910ad/views/view_000_000deg.png",
|
||
"_turntable/out4.png/views/view_007_105deg.png",
|
||
"_turntable/out4.png/views/view_006_090deg.png",
|
||
"_turntable/out4.png/views/view_005_075deg.png",
|
||
"_turntable/out4.png/views/view_004_060deg.png",
|
||
"_turntable/out4.png/views/view_003_045deg.png",
|
||
"_turntable/out4.png/views/view_002_030deg.png",
|
||
"_turntable/out4.png/views/view_001_015deg.png",
|
||
"_turntable/out4.png/views/view_000_000deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_023_345deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_022_330deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_021_315deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_020_300deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_019_285deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_018_270deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_017_255deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_016_240deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_015_225deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_014_210deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_013_195deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_012_180deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_011_165deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_010_150deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_009_135deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_008_120deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_007_105deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_006_090deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_005_075deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_004_060deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_003_045deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_002_030deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_001_015deg.png",
|
||
"_turntable/cg_d032c1ef/views/view_000_000deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_022_330deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_003_045deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_174516_image.png/views/view_000_000deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_022_330deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_003_045deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_054230_image.png/views/view_000_000deg.png",
|
||
"_turntable/cg_f1e85987/views/view_023_345deg.png",
|
||
"_turntable/cg_f1e85987/views/view_022_330deg.png",
|
||
"_turntable/cg_f1e85987/views/view_021_315deg.png",
|
||
"_turntable/cg_f1e85987/views/view_020_300deg.png",
|
||
"_turntable/cg_f1e85987/views/view_019_285deg.png",
|
||
"_turntable/cg_f1e85987/views/view_018_270deg.png",
|
||
"_turntable/cg_f1e85987/views/view_017_255deg.png",
|
||
"_turntable/cg_f1e85987/views/view_016_240deg.png",
|
||
"_turntable/cg_f1e85987/views/view_015_225deg.png",
|
||
"_turntable/cg_f1e85987/views/view_014_210deg.png",
|
||
"_turntable/cg_f1e85987/views/view_013_195deg.png",
|
||
"_turntable/cg_f1e85987/views/view_012_180deg.png",
|
||
"_turntable/cg_f1e85987/views/view_011_165deg.png",
|
||
"_turntable/cg_f1e85987/views/view_010_150deg.png",
|
||
"_turntable/cg_f1e85987/views/view_009_135deg.png",
|
||
"_turntable/cg_f1e85987/views/view_008_120deg.png",
|
||
"_turntable/cg_f1e85987/views/view_007_105deg.png",
|
||
"_turntable/cg_f1e85987/views/view_006_090deg.png",
|
||
"_turntable/cg_f1e85987/views/view_005_075deg.png",
|
||
"_turntable/cg_f1e85987/views/view_004_060deg.png",
|
||
"_turntable/cg_f1e85987/views/view_003_045deg.png",
|
||
"_turntable/cg_f1e85987/views/view_002_030deg.png",
|
||
"_turntable/cg_f1e85987/views/view_001_015deg.png",
|
||
"_turntable/cg_f1e85987/views/view_000_000deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_023_345deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_022_330deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_021_315deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_020_300deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_019_285deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_018_270deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_017_255deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_016_240deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_015_225deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_014_210deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_013_195deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_012_180deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_011_165deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_010_150deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_009_135deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_008_120deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_007_105deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_006_090deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_005_075deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_004_060deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_003_045deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_002_030deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_001_015deg.png",
|
||
"_turntable/up_76ef7e4f/views/view_000_000deg.png",
|
||
"_turntable/up_a4d16da7/turntable.mp4",
|
||
"_turntable/up_a4d16da7/views/view_023_345deg.png",
|
||
"_turntable/up_a4d16da7/views/view_022_330deg.png",
|
||
"_turntable/up_a4d16da7/views/view_021_315deg.png",
|
||
"_turntable/up_a4d16da7/views/view_020_300deg.png",
|
||
"_turntable/up_a4d16da7/views/view_019_285deg.png",
|
||
"_turntable/up_a4d16da7/views/view_018_270deg.png",
|
||
"_turntable/up_a4d16da7/views/view_017_255deg.png",
|
||
"_turntable/up_a4d16da7/views/view_016_240deg.png",
|
||
"_turntable/up_a4d16da7/views/view_015_225deg.png",
|
||
"_turntable/up_a4d16da7/views/view_014_210deg.png",
|
||
"_turntable/up_a4d16da7/views/view_013_195deg.png",
|
||
"_turntable/up_a4d16da7/views/view_012_180deg.png",
|
||
"_turntable/up_a4d16da7/views/view_011_165deg.png",
|
||
"_turntable/up_a4d16da7/views/view_010_150deg.png",
|
||
"_turntable/up_a4d16da7/views/view_009_135deg.png",
|
||
"_turntable/up_a4d16da7/views/view_008_120deg.png",
|
||
"_turntable/up_a4d16da7/views/view_007_105deg.png",
|
||
"_turntable/up_a4d16da7/views/view_006_090deg.png",
|
||
"_turntable/up_a4d16da7/views/view_005_075deg.png",
|
||
"_turntable/up_a4d16da7/views/view_004_060deg.png",
|
||
"_turntable/up_a4d16da7/views/view_003_045deg.png",
|
||
"_turntable/up_a4d16da7/views/view_002_030deg.png",
|
||
"_turntable/up_a4d16da7/views/view_001_015deg.png",
|
||
"_turntable/up_a4d16da7/views/view_000_000deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_023_345deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_022_330deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_021_315deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_020_300deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_019_285deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_018_270deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_017_255deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_016_240deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_015_225deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_014_210deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_013_195deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_012_180deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_011_165deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_010_150deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_009_135deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_008_120deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_007_105deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_006_090deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_005_075deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_004_060deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_003_045deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_002_030deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_001_015deg.png",
|
||
"_turntable/20260618_180939_image.png/views/view_000_000deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_023_345deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_022_330deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_021_315deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_020_300deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_019_285deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_018_270deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_017_255deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_015_225deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_014_210deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_012_180deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_011_165deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_010_150deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_007_105deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_006_090deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_005_075deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_004_060deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_003_045deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_002_030deg.png",
|
||
"_turntable/cg_2afca8fc/views/view_000_000deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_023_345deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_021_315deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_020_300deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_019_285deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_018_270deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_017_255deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_016_240deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_015_225deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_014_210deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_013_195deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_012_180deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_011_165deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_010_150deg.png",
|
||
"up_b7db33a9_face.png",
|
||
"20260626_162432_6_20260626_161654_image.png",
|
||
"20260626_162410_image.png",
|
||
"20260626_161941_image.png",
|
||
"20260626_161844_6_20260626_161654_image.png",
|
||
"20260626_161727_0_20260626_161654_image.png",
|
||
"_turntable/cg_7c7b22c9/views/view_009_135deg.png",
|
||
"20260626_161654_image.png",
|
||
"_turntable/cg_7c7b22c9/views/view_008_120deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_007_105deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_006_090deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_005_075deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_004_060deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_003_045deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_002_030deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_001_015deg.png",
|
||
"_turntable/cg_7c7b22c9/views/view_000_000deg.png",
|
||
"cg_e79533a4_face.png",
|
||
"cg_73e141fa_face.png",
|
||
"20260626_154945_image.png",
|
||
"20260626_154934_image.png",
|
||
"20260626_154922_image.png",
|
||
"20260626_154745_image.png",
|
||
"20260626_154728_mr_image.png",
|
||
"20260626_154605_image.png",
|
||
"20260626_154535_mr_image.png",
|
||
"20260626_154301_mr_image.png",
|
||
"20260626_153213_image.png",
|
||
"20260626_153157_image.png",
|
||
"20260626_153133_image.png",
|
||
"20260618_015041_20260618_004313_20260616_021938_20160903_200935.jpg",
|
||
"20260618_015041_20260618_004313_20260616_021938_20160903_200935.nobg.nobg.png",
|
||
"20260626_124145_20160903_200935.jpg",
|
||
"cg_4671b6c4_face.png",
|
||
"20260626_152247_crop_20260626_152107_20160903_200935.nobg.nobg.nobg.nobg.png",
|
||
"20260626_152107_20160903_200935.nobg.png",
|
||
"20260626_151945_dup_20260626_151844_20160903_200935.nobg.png",
|
||
"20260626_151844_20160903_200935.nobg.png",
|
||
"20260626_145831_9_20260626_145421_image.png",
|
||
"20260626_145818_8_20260626_145421_image.png",
|
||
"20260626_145803_7_20260626_145421_image.png",
|
||
"_turntable/up_5b23ff92/views/view_016_240deg.png",
|
||
"20260626_145717_6_20260626_145421_image.png",
|
||
"_turntable/up_5b23ff92/views/view_015_225deg.png",
|
||
"20260626_145636_5_20260626_145421_image.png",
|
||
"20260626_145620_4_20260626_145421_image.png",
|
||
"20260626_145604_3_20260626_145421_image.png",
|
||
"20260626_145547_2_20260626_145421_image.png",
|
||
"20260626_145531_1_20260626_145421_image.png",
|
||
"20260626_145514_0_20260626_145421_image.png",
|
||
"20260626_145421_image.png",
|
||
"20260626_145037_image.png",
|
||
"20260626_144719_9_20260626_144519_image.png",
|
||
"20260626_144656_7_20260626_144519_image.png",
|
||
"20260626_144645_6_20260626_144519_image.png",
|
||
"20260626_144611_3_20260626_144519_image.png",
|
||
"20260626_144600_2_20260626_144519_image.png",
|
||
"20260626_144549_1_20260626_144519_image.png",
|
||
"20260626_144538_0_20260626_144519_image.png",
|
||
"20260626_144519_image.png",
|
||
"_turntable/up_5b23ff92/views/view_014_210deg.png",
|
||
"20260626_144226_image.png",
|
||
"up_819bf34a_face.png",
|
||
"20260624_001219_image.nobg.png",
|
||
"20260626_143306_image.png",
|
||
"20260626_143200_image.png",
|
||
"20260626_143016_image.png",
|
||
"20260626_141714_7_20260626_141438_image.png",
|
||
"20260626_141657_6_20260626_141438_image.png",
|
||
"20260626_141649_image.png",
|
||
"20260626_141630_image.png",
|
||
"20260626_141608_3_20260626_141438_image.png",
|
||
"20260626_141553_2_20260626_141438_image.png",
|
||
"20260626_141534_1_20260626_141438_image.png",
|
||
"20260626_141518_0_20260626_141438_image.png",
|
||
"_turntable/up_5b23ff92/views/view_013_195deg.png",
|
||
"20260626_141438_image.png",
|
||
"_turntable/up_5b23ff92/views/view_012_180deg.png",
|
||
"_turntable/up_5b23ff92/views/view_011_165deg.png",
|
||
"_turntable/up_5b23ff92/views/view_010_150deg.png",
|
||
"_turntable/up_5b23ff92/views/view_009_135deg.png",
|
||
"_turntable/up_5b23ff92/views/view_008_120deg.png",
|
||
"_turntable/up_5b23ff92/views/view_007_105deg.png",
|
||
"_turntable/up_5b23ff92/views/view_006_090deg.png",
|
||
"_turntable/up_5b23ff92/views/view_005_075deg.png",
|
||
"_turntable/up_5b23ff92/views/view_004_060deg.png",
|
||
"_turntable/up_5b23ff92/views/view_003_045deg.png",
|
||
"_turntable/up_5b23ff92/views/view_002_030deg.png",
|
||
"_turntable/up_5b23ff92/views/view_001_015deg.png",
|
||
"_turntable/up_5b23ff92/views/view_000_000deg.png",
|
||
"_turntable/cg_ad73335e/views/view_023_345deg.png",
|
||
"_turntable/cg_ad73335e/views/view_018_270deg.png",
|
||
"_turntable/cg_ad73335e/views/view_017_255deg.png",
|
||
"_turntable/cg_ad73335e/views/view_016_240deg.png",
|
||
"_turntable/cg_ad73335e/views/view_015_225deg.png",
|
||
"_turntable/cg_ad73335e/views/view_014_210deg.png",
|
||
"_turntable/cg_ad73335e/views/view_013_195deg.png",
|
||
"_turntable/cg_ad73335e/views/view_012_180deg.png",
|
||
"_turntable/cg_ad73335e/views/view_011_165deg.png",
|
||
"_turntable/cg_ad73335e/views/view_010_150deg.png",
|
||
"_turntable/cg_ad73335e/views/view_009_135deg.png",
|
||
"_turntable/cg_ad73335e/views/view_008_120deg.png",
|
||
"_turntable/cg_ad73335e/views/view_007_105deg.png",
|
||
"_turntable/cg_ad73335e/views/view_006_090deg.png",
|
||
"_turntable/cg_ad73335e/views/view_005_075deg.png",
|
||
"_turntable/cg_ad73335e/views/view_004_060deg.png",
|
||
"_turntable/cg_ad73335e/views/view_003_045deg.png",
|
||
"_turntable/cg_ad73335e/views/view_000_000deg.png",
|
||
"_turntable/cg_61b863a8/views/view_023_345deg.png",
|
||
"_turntable/cg_61b863a8/views/view_022_330deg.png",
|
||
"_turntable/cg_61b863a8/views/view_021_315deg.png",
|
||
"_turntable/cg_61b863a8/views/view_020_300deg.png",
|
||
"_turntable/cg_61b863a8/views/view_019_285deg.png",
|
||
"_turntable/cg_61b863a8/views/view_018_270deg.png",
|
||
"_turntable/cg_61b863a8/views/view_017_255deg.png",
|
||
"_turntable/cg_61b863a8/views/view_016_240deg.png",
|
||
"_turntable/cg_61b863a8/views/view_015_225deg.png",
|
||
"_turntable/cg_61b863a8/views/view_014_210deg.png",
|
||
"_turntable/cg_61b863a8/views/view_013_195deg.png",
|
||
"_turntable/cg_61b863a8/views/view_012_180deg.png",
|
||
"_turntable/cg_61b863a8/views/view_011_165deg.png",
|
||
"_turntable/cg_61b863a8/views/view_010_150deg.png",
|
||
"_turntable/cg_61b863a8/views/view_009_135deg.png",
|
||
"_turntable/cg_61b863a8/views/view_008_120deg.png",
|
||
"_turntable/cg_61b863a8/views/view_007_105deg.png",
|
||
"_turntable/cg_61b863a8/views/view_006_090deg.png",
|
||
"_turntable/cg_61b863a8/views/view_005_075deg.png",
|
||
"_turntable/cg_61b863a8/views/view_004_060deg.png",
|
||
"_turntable/cg_61b863a8/views/view_003_045deg.png",
|
||
"_turntable/cg_61b863a8/views/view_002_030deg.png",
|
||
"_turntable/cg_61b863a8/views/view_001_015deg.png",
|
||
"_turntable/cg_61b863a8/views/view_000_000deg.png",
|
||
"_turntable/cg_f0049873/views/view_023_345deg.png",
|
||
"_turntable/cg_f0049873/views/view_022_330deg.png",
|
||
"_turntable/cg_f0049873/views/view_021_315deg.png",
|
||
"_turntable/cg_f0049873/views/view_020_300deg.png",
|
||
"_turntable/cg_f0049873/views/view_019_285deg.png",
|
||
"_turntable/cg_f0049873/views/view_018_270deg.png",
|
||
"_turntable/cg_f0049873/views/view_017_255deg.png",
|
||
"_turntable/cg_f0049873/views/view_016_240deg.png",
|
||
"_turntable/cg_f0049873/views/view_015_225deg.png",
|
||
"_turntable/cg_f0049873/views/view_014_210deg.png",
|
||
"_turntable/cg_f0049873/views/view_013_195deg.png",
|
||
"_turntable/cg_f0049873/views/view_012_180deg.png",
|
||
"_turntable/cg_f0049873/views/view_011_165deg.png",
|
||
"_turntable/cg_f0049873/views/view_010_150deg.png",
|
||
"_turntable/cg_f0049873/views/view_009_135deg.png",
|
||
"_turntable/cg_f0049873/views/view_008_120deg.png",
|
||
"_turntable/cg_f0049873/views/view_007_105deg.png",
|
||
"_turntable/cg_f0049873/views/view_006_090deg.png",
|
||
"_turntable/cg_f0049873/views/view_005_075deg.png",
|
||
"_turntable/cg_f0049873/views/view_004_060deg.png",
|
||
"_turntable/cg_f0049873/views/view_003_045deg.png",
|
||
"_turntable/cg_f0049873/views/view_002_030deg.png",
|
||
"_turntable/cg_f0049873/views/view_001_015deg.png",
|
||
"_turntable/cg_f0049873/views/view_000_000deg.png",
|
||
"_turntable/cg_08203e88/views/view_023_345deg.png",
|
||
"_turntable/cg_08203e88/views/view_022_330deg.png",
|
||
"_turntable/cg_08203e88/views/view_021_315deg.png",
|
||
"_turntable/cg_08203e88/views/view_020_300deg.png",
|
||
"_turntable/cg_08203e88/views/view_019_285deg.png",
|
||
"_turntable/cg_08203e88/views/view_018_270deg.png",
|
||
"_turntable/up_98c8443e/views/view_022_330deg.png",
|
||
"_turntable/up_98c8443e/views/view_021_315deg.png",
|
||
"_turntable/up_98c8443e/views/view_020_300deg.png",
|
||
"_turntable/up_98c8443e/views/view_019_285deg.png",
|
||
"_turntable/up_98c8443e/views/view_018_270deg.png",
|
||
"_turntable/up_98c8443e/views/view_017_255deg.png",
|
||
"_turntable/up_98c8443e/views/view_016_240deg.png",
|
||
"_turntable/up_98c8443e/views/view_015_225deg.png",
|
||
"_turntable/up_98c8443e/views/view_014_210deg.png",
|
||
"_turntable/up_98c8443e/views/view_013_195deg.png",
|
||
"_turntable/up_98c8443e/views/view_012_180deg.png",
|
||
"_turntable/up_98c8443e/views/view_011_165deg.png",
|
||
"_turntable/up_98c8443e/views/view_010_150deg.png",
|
||
"_turntable/up_98c8443e/views/view_009_135deg.png",
|
||
"_turntable/up_98c8443e/views/view_008_120deg.png",
|
||
"_turntable/up_98c8443e/views/view_007_105deg.png",
|
||
"_turntable/up_98c8443e/views/view_006_090deg.png",
|
||
"_turntable/up_98c8443e/views/view_005_075deg.png",
|
||
"_turntable/up_98c8443e/views/view_004_060deg.png",
|
||
"_turntable/up_98c8443e/views/view_003_045deg.png",
|
||
"_turntable/up_98c8443e/views/view_002_030deg.png",
|
||
"_turntable/up_98c8443e/views/view_001_015deg.png",
|
||
"up_98c8443e_face.png",
|
||
"up_810b587c_face.png",
|
||
"cg_7c7b22c9_face.png",
|
||
"20160903_200935.jpg_face.png",
|
||
"_turntable/cg_08203e88/views/view_017_255deg.png",
|
||
"_turntable/cg_08203e88/views/view_016_240deg.png",
|
||
"_turntable/cg_08203e88/views/view_015_225deg.png",
|
||
"_turntable/cg_08203e88/views/view_014_210deg.png",
|
||
"_turntable/cg_08203e88/views/view_013_195deg.png",
|
||
"_turntable/cg_08203e88/views/view_012_180deg.png",
|
||
"_turntable/cg_08203e88/views/view_011_165deg.png",
|
||
"_turntable/cg_08203e88/views/view_010_150deg.png",
|
||
"_turntable/cg_08203e88/views/view_009_135deg.png",
|
||
"_turntable/cg_08203e88/views/view_008_120deg.png",
|
||
"_turntable/cg_08203e88/views/view_007_105deg.png",
|
||
"_turntable/cg_08203e88/views/view_006_090deg.png",
|
||
"_turntable/cg_08203e88/views/view_005_075deg.png",
|
||
"_turntable/cg_08203e88/views/view_004_060deg.png",
|
||
"_turntable/cg_08203e88/views/view_003_045deg.png",
|
||
"_turntable/cg_08203e88/views/view_002_030deg.png",
|
||
"_turntable/cg_08203e88/views/view_001_015deg.png",
|
||
"_turntable/cg_08203e88/views/view_000_000deg.png",
|
||
"_turntable/up_3a05a2de/views/view_023_345deg.png",
|
||
"_turntable/up_3a05a2de/views/view_022_330deg.png",
|
||
"_turntable/up_3a05a2de/views/view_021_315deg.png",
|
||
"_turntable/up_3a05a2de/views/view_020_300deg.png",
|
||
"_turntable/up_3a05a2de/views/view_019_285deg.png",
|
||
"_turntable/up_3a05a2de/views/view_018_270deg.png",
|
||
"_turntable/up_3a05a2de/views/view_017_255deg.png",
|
||
"_turntable/up_3a05a2de/views/view_016_240deg.png",
|
||
"_turntable/up_3a05a2de/views/view_015_225deg.png",
|
||
"_turntable/up_3a05a2de/views/view_014_210deg.png",
|
||
"_turntable/up_3a05a2de/views/view_013_195deg.png",
|
||
"_turntable/up_3a05a2de/views/view_012_180deg.png",
|
||
"_turntable/up_3a05a2de/views/view_011_165deg.png",
|
||
"_turntable/up_3a05a2de/views/view_010_150deg.png",
|
||
"_turntable/up_3a05a2de/views/view_009_135deg.png",
|
||
"_turntable/up_3a05a2de/views/view_008_120deg.png",
|
||
"_turntable/up_3a05a2de/views/view_007_105deg.png",
|
||
"_turntable/up_3a05a2de/views/view_006_090deg.png",
|
||
"_turntable/up_3a05a2de/views/view_005_075deg.png",
|
||
"_turntable/up_3a05a2de/views/view_004_060deg.png",
|
||
"_turntable/up_3a05a2de/views/view_003_045deg.png",
|
||
"_turntable/up_3a05a2de/views/view_002_030deg.png",
|
||
"_turntable/up_3a05a2de/views/view_001_015deg.png",
|
||
"_turntable/up_3a05a2de/views/view_000_000deg.png",
|
||
"_turntable/up_b64786e7/views/view_023_345deg.png",
|
||
"_turntable/up_b64786e7/views/view_022_330deg.png",
|
||
"_turntable/up_b64786e7/views/view_021_315deg.png",
|
||
"_turntable/up_b64786e7/views/view_020_300deg.png",
|
||
"_turntable/up_b64786e7/views/view_019_285deg.png",
|
||
"_turntable/up_b64786e7/views/view_018_270deg.png",
|
||
"_turntable/up_b64786e7/views/view_017_255deg.png",
|
||
"_turntable/up_b64786e7/views/view_016_240deg.png",
|
||
"_turntable/up_b64786e7/views/view_015_225deg.png",
|
||
"_turntable/up_b64786e7/views/view_014_210deg.png",
|
||
"_turntable/up_b64786e7/views/view_013_195deg.png",
|
||
"_turntable/up_b64786e7/views/view_012_180deg.png",
|
||
"_turntable/up_b64786e7/views/view_011_165deg.png",
|
||
"_turntable/up_b64786e7/views/view_010_150deg.png",
|
||
"_turntable/up_b64786e7/views/view_009_135deg.png",
|
||
"_turntable/up_b64786e7/views/view_008_120deg.png",
|
||
"_turntable/up_b64786e7/views/view_007_105deg.png",
|
||
"_turntable/up_b64786e7/views/view_006_090deg.png",
|
||
"_turntable/up_b64786e7/views/view_005_075deg.png",
|
||
"_turntable/up_b64786e7/views/view_004_060deg.png",
|
||
"_turntable/up_b64786e7/views/view_003_045deg.png",
|
||
"_turntable/up_b64786e7/views/view_002_030deg.png",
|
||
"_turntable/up_b64786e7/views/view_001_015deg.png",
|
||
"_turntable/up_b64786e7/views/view_000_000deg.png",
|
||
"_turntable/up_ab858b0a/views/view_021_315deg.png",
|
||
"_turntable/up_ab858b0a/views/view_020_300deg.png",
|
||
"_turntable/up_ab858b0a/views/view_019_285deg.png",
|
||
"_turntable/up_ab858b0a/views/view_018_270deg.png",
|
||
"_turntable/up_ab858b0a/views/view_017_255deg.png",
|
||
"_turntable/up_ab858b0a/views/view_016_240deg.png",
|
||
"_turntable/up_ab858b0a/views/view_015_225deg.png",
|
||
"_turntable/up_ab858b0a/views/view_014_210deg.png",
|
||
"_turntable/up_ab858b0a/views/view_013_195deg.png",
|
||
"_turntable/up_ab858b0a/views/view_012_180deg.png",
|
||
"_turntable/up_ab858b0a/views/view_011_165deg.png",
|
||
"_turntable/up_ab858b0a/views/view_010_150deg.png",
|
||
"_turntable/up_ab858b0a/views/view_009_135deg.png",
|
||
"_turntable/up_ab858b0a/views/view_008_120deg.png",
|
||
"_turntable/up_ab858b0a/views/view_007_105deg.png",
|
||
"_turntable/up_ab858b0a/views/view_006_090deg.png",
|
||
"_turntable/up_ab858b0a/views/view_005_075deg.png",
|
||
"_turntable/up_ab858b0a/views/view_004_060deg.png",
|
||
"_turntable/up_ab858b0a/views/view_003_045deg.png",
|
||
"_turntable/up_ab858b0a/views/view_002_030deg.png",
|
||
"_turntable/up_ab858b0a/views/view_001_015deg.png",
|
||
"_turntable/up_ab858b0a/views/view_000_000deg.png",
|
||
"_turntable/up_f475c600/views/view_023_345deg.png",
|
||
"_turntable/up_f475c600/views/view_022_330deg.png",
|
||
"_turntable/up_f475c600/views/view_021_315deg.png",
|
||
"_turntable/up_f475c600/views/view_020_300deg.png",
|
||
"_turntable/up_f475c600/views/view_019_285deg.png",
|
||
"_turntable/up_f475c600/views/view_018_270deg.png",
|
||
"_turntable/up_f475c600/views/view_017_255deg.png",
|
||
"_turntable/up_f475c600/views/view_016_240deg.png",
|
||
"_turntable/up_f475c600/views/view_015_225deg.png",
|
||
"_turntable/up_f475c600/views/view_014_210deg.png",
|
||
"_turntable/up_f475c600/views/view_013_195deg.png",
|
||
"_turntable/up_f475c600/views/view_012_180deg.png",
|
||
"_turntable/up_f475c600/views/view_011_165deg.png",
|
||
"_turntable/up_f475c600/views/view_010_150deg.png",
|
||
"_turntable/up_f475c600/views/view_009_135deg.png",
|
||
"_turntable/up_f475c600/views/view_008_120deg.png",
|
||
"_turntable/up_f475c600/views/view_007_105deg.png",
|
||
"_turntable/up_f475c600/views/view_006_090deg.png",
|
||
"_turntable/up_f475c600/views/view_005_075deg.png",
|
||
"_turntable/up_f475c600/views/view_004_060deg.png",
|
||
"_turntable/up_f475c600/views/view_003_045deg.png",
|
||
"_turntable/up_f475c600/views/view_002_030deg.png",
|
||
"_turntable/up_f475c600/views/view_001_015deg.png",
|
||
"_turntable/up_f475c600/views/view_000_000deg.png",
|
||
"_turntable/up_810b587c/turntable.mp4",
|
||
"_turntable/up_810b587c/views/view_023_345deg.png",
|
||
"_turntable/up_810b587c/views/view_022_330deg.png",
|
||
"_turntable/up_810b587c/views/view_021_315deg.png",
|
||
"_turntable/up_810b587c/views/view_020_300deg.png",
|
||
"_turntable/up_810b587c/views/view_019_285deg.png",
|
||
"_turntable/up_810b587c/views/view_018_270deg.png",
|
||
"_turntable/up_810b587c/views/view_017_255deg.png",
|
||
"_turntable/up_810b587c/views/view_016_240deg.png",
|
||
"_turntable/up_810b587c/views/view_015_225deg.png",
|
||
"_turntable/up_810b587c/views/view_014_210deg.png",
|
||
"_turntable/up_810b587c/views/view_013_195deg.png",
|
||
"_turntable/up_810b587c/views/view_012_180deg.png",
|
||
"_turntable/up_810b587c/views/view_011_165deg.png",
|
||
"_turntable/up_810b587c/views/view_010_150deg.png",
|
||
"_turntable/up_810b587c/views/view_009_135deg.png",
|
||
"_turntable/up_810b587c/views/view_008_120deg.png",
|
||
"_turntable/up_810b587c/views/view_007_105deg.png",
|
||
"_turntable/up_810b587c/views/view_006_090deg.png",
|
||
"_turntable/up_810b587c/views/view_005_075deg.png",
|
||
"_turntable/up_810b587c/views/view_004_060deg.png",
|
||
"_turntable/up_810b587c/views/view_003_045deg.png",
|
||
"_turntable/up_810b587c/views/view_002_030deg.png",
|
||
"_turntable/up_810b587c/views/view_001_015deg.png",
|
||
"_turntable/up_810b587c/views/view_000_000deg.png",
|
||
"_turntable/img_77.png/views/view_023_345deg.png",
|
||
"_turntable/img_77.png/views/view_022_330deg.png",
|
||
"_turntable/img_77.png/views/view_021_315deg.png",
|
||
"_turntable/img_77.png/views/view_020_300deg.png",
|
||
"_turntable/img_77.png/views/view_019_285deg.png",
|
||
"_turntable/img_77.png/views/view_018_270deg.png",
|
||
"_turntable/img_77.png/views/view_017_255deg.png",
|
||
"_turntable/img_77.png/views/view_016_240deg.png",
|
||
"_turntable/img_77.png/views/view_015_225deg.png",
|
||
"_turntable/img_77.png/views/view_014_210deg.png",
|
||
"_turntable/img_77.png/views/view_013_195deg.png",
|
||
"_turntable/img_77.png/views/view_012_180deg.png",
|
||
"_turntable/img_77.png/views/view_011_165deg.png",
|
||
"_turntable/img_77.png/views/view_010_150deg.png",
|
||
"_turntable/img_77.png/views/view_009_135deg.png",
|
||
"_turntable/img_77.png/views/view_008_120deg.png",
|
||
"_turntable/img_77.png/views/view_007_105deg.png",
|
||
"_turntable/img_77.png/views/view_006_090deg.png",
|
||
"_turntable/img_77.png/views/view_005_075deg.png",
|
||
"_turntable/img_77.png/views/view_004_060deg.png",
|
||
"_turntable/img_77.png/views/view_003_045deg.png",
|
||
"_turntable/img_77.png/views/view_002_030deg.png",
|
||
"_turntable/img_77.png/views/view_001_015deg.png",
|
||
"_turntable/img_77.png/views/view_000_000deg.png",
|
||
"_turntable/cg_e79533a4/views/view_023_345deg.png",
|
||
"_turntable/cg_e79533a4/views/view_022_330deg.png",
|
||
"_turntable/cg_e79533a4/views/view_021_315deg.png",
|
||
"_turntable/cg_e79533a4/views/view_020_300deg.png",
|
||
"_turntable/cg_e79533a4/views/view_019_285deg.png",
|
||
"_turntable/cg_e79533a4/views/view_018_270deg.png",
|
||
"_turntable/cg_e79533a4/views/view_017_255deg.png",
|
||
"_turntable/cg_e79533a4/views/view_016_240deg.png",
|
||
"_turntable/cg_e79533a4/views/view_015_225deg.png",
|
||
"_turntable/cg_e79533a4/views/view_014_210deg.png",
|
||
"_turntable/cg_e79533a4/views/view_013_195deg.png",
|
||
"_turntable/cg_e79533a4/views/view_012_180deg.png",
|
||
"_turntable/cg_e79533a4/views/view_011_165deg.png",
|
||
"_turntable/cg_e79533a4/views/view_010_150deg.png",
|
||
"_turntable/cg_e79533a4/views/view_009_135deg.png",
|
||
"_turntable/cg_e79533a4/views/view_008_120deg.png",
|
||
"_turntable/cg_e79533a4/views/view_007_105deg.png",
|
||
"_turntable/cg_e79533a4/views/view_006_090deg.png",
|
||
"_turntable/cg_e79533a4/views/view_005_075deg.png",
|
||
"_turntable/cg_e79533a4/views/view_004_060deg.png",
|
||
"_turntable/cg_e79533a4/views/view_003_045deg.png",
|
||
"_turntable/cg_e79533a4/views/view_002_030deg.png",
|
||
"_turntable/cg_e79533a4/views/view_001_015deg.png",
|
||
"_turntable/cg_e79533a4/views/view_000_000deg.png",
|
||
"_turntable/b1.png/views/view_023_345deg.png",
|
||
"_turntable/b1.png/views/view_022_330deg.png",
|
||
"_turntable/b1.png/views/view_021_315deg.png",
|
||
"_turntable/b1.png/views/view_020_300deg.png",
|
||
"_turntable/b1.png/views/view_019_285deg.png",
|
||
"_turntable/up_e25ad102/turntable.mp4",
|
||
"_turntable/up_e25ad102/views/view_011_330deg.png",
|
||
"_turntable/up_e25ad102/views/view_010_300deg.png",
|
||
"_turntable/up_e25ad102/views/view_009_270deg.png",
|
||
"_turntable/b1.png/views/view_016_240deg.png",
|
||
"_turntable/up_e25ad102/views/view_008_240deg.png",
|
||
"_turntable/up_e25ad102/views/view_007_210deg.png",
|
||
"_turntable/up_e25ad102/views/view_006_180deg.png",
|
||
"_turntable/up_e25ad102/views/view_005_150deg.png",
|
||
"_turntable/up_e25ad102/views/view_004_120deg.png",
|
||
"_turntable/up_e25ad102/views/view_003_090deg.png",
|
||
"_turntable/up_e25ad102/views/view_002_060deg.png",
|
||
"_turntable/up_e25ad102/views/view_001_030deg.png",
|
||
"_turntable/up_e25ad102/views/view_000_000deg.png",
|
||
"_turntable/b1.png/views/view_015_225deg.png",
|
||
"_turntable/b1.png/views/view_014_210deg.png",
|
||
"_turntable/b1.png/views/view_013_195deg.png",
|
||
"_turntable/b1.png/views/view_012_180deg.png",
|
||
"_turntable/b1.png/views/view_011_165deg.png",
|
||
"_turntable/b1.png/views/view_010_150deg.png",
|
||
"_turntable/b1.png/views/view_009_135deg.png",
|
||
"_turntable/b1.png/views/view_008_120deg.png",
|
||
"_turntable/b1.png/views/view_007_105deg.png",
|
||
"_turntable/b1.png/views/view_006_090deg.png",
|
||
"_turntable/b1.png/views/view_005_075deg.png",
|
||
"_turntable/b1.png/views/view_004_060deg.png",
|
||
"_turntable/b1.png/views/view_003_045deg.png",
|
||
"_turntable/b1.png/views/view_002_030deg.png",
|
||
"_turntable/b1.png/views/view_001_015deg.png",
|
||
"_turntable/b1.png/views/view_000_000deg.png",
|
||
"img_14.png_face.png",
|
||
"20260626_053719_pad_20260619_224737_img_14.nobg.png",
|
||
"20260626_053719_pad_20260619_224737_img_14.nobg.nobg.png",
|
||
"20260626_053754_pad_20260619_224737_img_14.png",
|
||
"20260626_053719_pad_20260619_224737_img_14.png",
|
||
"20260618_053802_image.png_face.png",
|
||
"20260626_043734_7_20260618_053802_image.nobg.nobg.png",
|
||
"20260626_043915_crop_20260626_043734_7_20260618_053802_image.nobg.nobg.png",
|
||
"20260626_043734_7_20260618_053802_image.nobg.png",
|
||
"20260626_043529_pad_20260618_053932_7_20260618_053802_image.nobg.png",
|
||
"20260618_053932_7_20260618_053802_image.nobg.png",
|
||
"20260626_041550_sc_pad_20260626_034830_jb.nobg.png",
|
||
"20260626_041539_sc_pad_20260626_034830_jb.nobg.png",
|
||
"20260626_041515_sc_pad_20260626_034830_jb.nobg.png",
|
||
"20260626_041456_sc_pad_20260626_034830_jb.nobg.png",
|
||
"20260626_041410_sc_pad_20260626_034830_jb.nobg.png",
|
||
"20260626_041256_sc_pad_20260626_034830_jb.nobg.png",
|
||
"20260626_040655_sc_pad_20260626_034830_jb.nobg.png",
|
||
"20260626_040550_sc_pad_20260626_034830_jb.nobg.png",
|
||
"20260626_035131_pose_pad_20260626_034830_jb.nobg.png",
|
||
"20260626_035101_pad_20260626_034830_jb.nobg.png",
|
||
"20260626_034830_jb.nobg.png",
|
||
"20260626_034830_jb.png",
|
||
"20260626_034708_jb.png",
|
||
"20260626_034601_jb.png",
|
||
"20260626_034455_jb.png",
|
||
"20260626_031810_1_20260618_050846_img_4.nobg.png",
|
||
"20260618_050923_2_20260618_050846_img_4.nobg.png",
|
||
"20260626_030908_1_20260618_050846_img_4.nobg.png",
|
||
"20260626_031821_1_20260618_050846_img_4.png",
|
||
"20260626_031810_1_20260618_050846_img_4.png",
|
||
"20260626_031759_jb1.png",
|
||
"20260626_031748_jb1.png",
|
||
"20260626_031736_jb1.png",
|
||
"20260626_031725_jb.png",
|
||
"20260626_031658_jb.png",
|
||
"20260626_030929_jb.png",
|
||
"20260626_030918_jb1.png",
|
||
"20260626_030908_1_20260618_050846_img_4.png",
|
||
"20260626_025708_2_20260625_205722_image.png",
|
||
"20260626_025338_pad_20260625_205913_8_20260625_205722_image.png",
|
||
"20260626_023528_pad_20260625_205913_8_20260625_205722_image.png",
|
||
"20260626_021942_pad_20260625_205913_8_20260625_205722_image.png",
|
||
"20260626_021533_pad_20260625_205828_4_20260625_205722_image.png",
|
||
"20260626_014422_pad_20260626_005644_dup_20260626_005504_Pasted image (9).nobg.png",
|
||
"20260626_005644_dup_20260626_005504_Pasted image (9).nobg.png",
|
||
"20260626_005504_Pasted image (9).nobg.png",
|
||
"20260626_005504_Pasted image (9).png",
|
||
"20260626_005644_dup_20260626_005504_Pasted image (9).png",
|
||
"20260626_005322_dup_20260626_003125_dup_Pasted image (9).png",
|
||
"20260626_003125_dup_Pasted image (9).png",
|
||
"cg_ad73335e_face.png",
|
||
"20260625_232346_sc_image.png",
|
||
"20260625_232242_sc_image.png",
|
||
"20260625_232204_sc_image.png",
|
||
"20260625_231601_sc_image.png",
|
||
"20260625_225929_sc_image.png",
|
||
"20260625_223304_p12.png",
|
||
"20260625_213330_pad_20260625_193323_image.png",
|
||
"20260625_210437_image.png",
|
||
"20260625_210432_image.png",
|
||
"20260625_210424_image.png",
|
||
"20260625_205925_9_20260625_205722_image.png",
|
||
"20260625_205913_8_20260625_205722_image.png",
|
||
"20260625_205902_7_20260625_205722_image.png",
|
||
"20260625_205851_6_20260625_205722_image.png",
|
||
"20260625_205839_5_20260625_205722_image.png",
|
||
"20260625_205828_4_20260625_205722_image.png",
|
||
"20260625_205817_3_20260625_205722_image.png",
|
||
"20260625_205806_2_20260625_205722_image.png",
|
||
"20260625_205756_1_20260625_205722_image.png",
|
||
"20260625_205745_0_20260625_205722_image.png",
|
||
"20260625_205722_image.png",
|
||
"20260625_194224_image.png",
|
||
"20260625_193852_pose_image.png",
|
||
"20260625_193744_sc_image.png",
|
||
"20260625_193526_image.png",
|
||
"up_fe1de29e_face.png",
|
||
"20260625_193345_image.png",
|
||
"20260625_193334_image.png",
|
||
"20260625_193323_image.png",
|
||
"20260625_193258_mr_image.png",
|
||
"20260625_193157_image.png",
|
||
"20260625_192755_image.png",
|
||
"20260625_192722_9_20260625_192510_image.png",
|
||
"20260625_192711_8_20260625_192510_image.png",
|
||
"20260625_192659_7_20260625_192510_image.png",
|
||
"20260625_192646_6_20260625_192510_image.png",
|
||
"20260625_192557_3_20260625_192510_image.png",
|
||
"20260625_192549_image.png",
|
||
"20260625_192543_2_20260625_192510_image.png",
|
||
"20260625_192532_1_20260625_192510_image.png",
|
||
"20260625_192521_0_20260625_192510_image.png",
|
||
"20260625_192510_image.png",
|
||
"20260625_191944_8_20260625_191641_image.png",
|
||
"20260625_191932_7_20260625_191641_image.png",
|
||
"20260625_191921_6_20260625_191641_image.png",
|
||
"20260625_191910_5_20260625_191641_image.png",
|
||
"20260625_191905_Screenshot_From_2026-06-25_19-15-26.png",
|
||
"20260625_191848_3_20260625_191641_image.png",
|
||
"20260625_191837_2_20260625_191641_image.png",
|
||
"20260625_191827_1_20260625_191641_image.png",
|
||
"20260625_191816_0_20260625_191641_image.png",
|
||
"20260625_191641_image.png",
|
||
"20260625_051219_sc_3_20260618_173728_image.png",
|
||
"20260625_050959_sc_3_20260618_173728_image.png",
|
||
"20260625_050729_sc_3_20260618_173728_image.png",
|
||
"20260625_050554_sc_3_20260618_173728_image.png",
|
||
"20260625_050458_sc_3_20260618_173728_image.png",
|
||
"20260625_050259_sc_3_20260618_173728_image.png",
|
||
"20260625_050157_sc_3_20260618_173728_image.png",
|
||
"20260625_045904_sc_3_20260618_173728_image.png",
|
||
"20260618_173728_image.png_face.png",
|
||
"20260625_045029_pose_3_20260618_173728_image.png",
|
||
"20260625_044738_pose_pose_pose_3_20260618_173728_image.png",
|
||
"20260625_044655_pose_pose_3_20260618_173728_image.png",
|
||
"20260625_044615_pose_3_20260618_173728_image.png",
|
||
"20260625_042243_5_20260621_205151_image.png",
|
||
"20260625_042116_image.png",
|
||
"20260625_041840_image.png",
|
||
"20260625_041714_image.png",
|
||
"20260625_041647_7_20260621_205151_image.png",
|
||
"20260625_041514_7_20260621_205151_image.png",
|
||
"20260625_040236_pose_sc_image.png",
|
||
"20260625_032521_dup_20260625_032449_sc_image.png",
|
||
"20260625_032449_sc_image.nobg.png",
|
||
"up_f9a5bb33_face.png",
|
||
"20260625_032449_sc_image.png",
|
||
"20260625_034504_dup_20260625_032449_sc_image.png",
|
||
"20260625_032341_sc_image.png",
|
||
"20260625_032146_sc_image.png",
|
||
"20260625_032107_image.png",
|
||
"20260625_031345_dup_20260625_031229_sc_image.png",
|
||
"20260625_031229_sc_image.png",
|
||
"20260625_030956_sc_image.png",
|
||
"20260625_030904_sc_image.png",
|
||
"20260625_030526_sc_image.png",
|
||
"20260625_030438_image.png",
|
||
"20260625_025228_dup_20260625_024438_dup_20260624_165109_0_20260622_091731_image.nobg.png",
|
||
"20260625_025156_dup_20260624_165109_0_20260622_091731_image.nobg.png",
|
||
"20260625_025115_dup_20260624_165109_0_20260622_091731_image.nobg.png",
|
||
"20260625_024438_dup_20260624_165109_0_20260622_091731_image.nobg.png",
|
||
"20260625_025335_dup_20260625_024438_dup_20260624_165109_0_20260622_091731_image.nobg.png",
|
||
"20260625_023536_dup_20260624_165109_0_20260622_091731_image.nobg.png",
|
||
"20260624_165109_0_20260622_091731_image.nobg.png",
|
||
"20260625_023536_dup_20260624_165109_0_20260622_091731_image.png",
|
||
"20260624_165109_0_20260622_091731_image.png",
|
||
"up_ab858b0a_face.png",
|
||
"20260625_022332_dup_20260625_022250_sc_image.png",
|
||
"20260625_022250_sc_image.png",
|
||
"20260625_022250_sc_image.nobg.png",
|
||
"20260625_022109_sc_image.png",
|
||
"20260625_022007_sc_image.png",
|
||
"20260625_021910_image.png",
|
||
"20260625_021702_sc_image.png",
|
||
"20260625_021615_sc_image.png",
|
||
"20260625_020212_dup_20260625_015711_sc_image.png",
|
||
"20260625_015711_sc_image.png",
|
||
"20260625_015711_sc_image.nobg.png",
|
||
"up_e25ad102_face.png",
|
||
"20260625_015900_sc_image.png",
|
||
"20260625_015413_sc_image.png",
|
||
"20260625_015213_image.png",
|
||
"up_a1dddf6f_face.png",
|
||
"20260625_014914_sc_mr_mr_image.nobg.png",
|
||
"20260625_014212_sc_0_20260619_124038_image.png",
|
||
"20260625_014026_sc_0_20260619_124038_image.png",
|
||
"20260625_013902_sc_0_20260619_124038_image.png",
|
||
"20260625_013708_sc_0_20260619_124038_image.png",
|
||
"20260625_013515_sc_0_20260619_124038_image.png",
|
||
"20260625_012538_7_20260622_101056_image.png",
|
||
"20260625_012527_7_20260622_101056_image.png",
|
||
"20260625_012516_7_20260622_101056_image.png",
|
||
"20260625_012442_7_20260622_101056_image.nobg.png",
|
||
"20260625_012420_7_20260622_101056_image.png",
|
||
"20260625_012357_2_20260622_101056_image.png",
|
||
"20260625_012346_2_20260622_101056_image.png",
|
||
"20260625_012335_2_20260622_101056_image.png",
|
||
"20260625_011955_fs_badly_1039s-1109s_0s-17s_mr_image.nobg.png_prev50.mp4",
|
||
"20260625_011802_sc_mr_image.nobg.png",
|
||
"20260625_005004_sc_mr_image.nobg.png",
|
||
"20260625_004141_sc_sc_mr_image.nobg.png",
|
||
"20260625_003214_sc_image.nobg.png",
|
||
"20260625_001534_sc_mr_image.nobg.png",
|
||
"20260625_001215_sc_mr_image.nobg.png",
|
||
"20260625_001047_sc_mr_image.nobg.png",
|
||
"20260625_000950_sc_mr_image.nobg.png",
|
||
"20260625_000851_sc_mr_image.nobg.png",
|
||
"20260625_000441_sc_mr_image.nobg.png",
|
||
"20260625_000430_mr_mr_image.nobg.png",
|
||
"20260625_000257_sc_mr_image.nobg.png",
|
||
"20260624_235623_sc_mr_image.nobg.png",
|
||
"20260624_235607_mr_sc_mr_image.nobg.png",
|
||
"20260624_234643_sc_mr_image.nobg.png",
|
||
"20260624_234358_sc_image.nobg.png",
|
||
"20260624_234309_mr_mr_image.nobg.png",
|
||
"20260624_233935_sc_mr_image.nobg.png",
|
||
"20260624_233903_mr_mr_image.nobg.png",
|
||
"20260624_233746_sc_mr_image.nobg.png",
|
||
"20260624_233254_sc_image.nobg.png",
|
||
"cg_6f321af3_face.png",
|
||
"20260624_224315_2_20260622_101056_image.png",
|
||
"20260624_224039_mr_image.nobg.png",
|
||
"20260624_224028_mr_image.nobg.png",
|
||
"20260624_224017_mr_image.nobg.png",
|
||
"20260624_224005_mr_image.nobg.png",
|
||
"20260624_223954_mr_image.nobg.png",
|
||
"20260624_223944_mr_image.nobg.png",
|
||
"20260624_223926_mr_image.nobg.png",
|
||
"20260624_220323_mr_image.nobg.png",
|
||
"20260624_205941_image.png",
|
||
"20260624_205924_image.png",
|
||
"20260624_205906_image.png",
|
||
"20260624_205638_image.png",
|
||
"20260624_205626_image.png",
|
||
"20260624_205615_image.png",
|
||
"20260624_203915_image.png",
|
||
"20260624_203904_image.png",
|
||
"20260624_203853_image.png",
|
||
"20260624_203510_image.png",
|
||
"up_52dede0c_face.png",
|
||
"20260624_202851_image.png",
|
||
"20260624_202831_image.png",
|
||
"20260624_202804_image.png",
|
||
"20260624_200205_0_20260619_124038_image.png",
|
||
"20260624_200155_image.png",
|
||
"20260624_195836_mr_mr_image.png",
|
||
"20260624_195729_mr_0_20260619_124038_image.png",
|
||
"20260624_195615_mr_cg_6f321af3_face.png",
|
||
"20260624_195448_mr_image.png",
|
||
"20260624_195054_image.png",
|
||
"20260624_194520_7_20260622_101056_image.png",
|
||
"20260624_194509_7_20260622_101056_image.png",
|
||
"20260624_194453_7_20260622_101056_image.png",
|
||
"20260624_194438_7_20260622_101056_image.png",
|
||
"20260624_194426_7_20260622_101056_image.png",
|
||
"20260624_194409_7_20260622_101056_image.png",
|
||
"20260624_194353_7_20260622_101056_image.png",
|
||
"20260624_194339_7_20260622_101056_image.png",
|
||
"20260624_194322_7_20260622_101056_image.png",
|
||
"20260624_194305_7_20260622_101056_image.png",
|
||
"20260624_194247_7_20260622_101056_image.png",
|
||
"20260624_194231_7_20260622_101056_image.png",
|
||
"20260624_193539_7_20260622_101056_image.png",
|
||
"20260624_193522_7_20260622_101056_image.png",
|
||
"20260624_193506_7_20260622_101056_image.png",
|
||
"20260624_193449_7_20260622_101056_image.png",
|
||
"20260624_193415_7_20260622_101056_image.png",
|
||
"20260624_193359_7_20260622_101056_image.png",
|
||
"20260624_193342_7_20260622_101056_image.png",
|
||
"20260624_193326_7_20260622_101056_image.png",
|
||
"20260624_193310_7_20260622_101056_image.png",
|
||
"20260624_193252_7_20260622_101056_image.png",
|
||
"20260622_101448_7_20260622_101056_image.png",
|
||
"20260624_193236_7_20260622_101056_image.png",
|
||
"20260624_193219_7_20260622_101056_image.png",
|
||
"20260624_193202_7_20260622_101056_image.png",
|
||
"20260624_193147_7_20260622_101056_image.png",
|
||
"20260624_193130_7_20260622_101056_image.png",
|
||
"20260624_193113_7_20260622_101056_image.png",
|
||
"20260624_193056_7_20260622_101056_image.png",
|
||
"20260624_193039_7_20260622_101056_image.png",
|
||
"20260622_101204_2_20260622_101056_image.png",
|
||
"20260624_192733_mr_image.nobg.png",
|
||
"20260624_192717_mr_image.nobg.png",
|
||
"20260624_192706_mr_image.nobg.png",
|
||
"20260624_192650_mr_image.nobg.png",
|
||
"20260624_192634_mr_image.nobg.png",
|
||
"20260624_192616_mr_image.nobg.png",
|
||
"20260624_192559_mr_image.nobg.png",
|
||
"20260624_192542_mr_image.nobg.png",
|
||
"20260624_192524_mr_image.nobg.png",
|
||
"20260624_192507_mr_image.nobg.png",
|
||
"20260624_192451_mr_image.nobg.png",
|
||
"20260624_192434_mr_image.nobg.png",
|
||
"20260624_192418_mr_image.nobg.png",
|
||
"20260624_192402_mr_image.nobg.png",
|
||
"20260624_192345_mr_image.nobg.png",
|
||
"20260624_192256_mr_image.nobg.png",
|
||
"20260624_192239_mr_image.nobg.png",
|
||
"20260624_192135_mr_image.nobg.png",
|
||
"20260624_192030_mr_image.nobg.png",
|
||
"20260624_191944_mr_image.nobg.png",
|
||
"20260624_191905_mr_image.nobg.png",
|
||
"20260624_191751_mr_image.nobg.png",
|
||
"20260624_191625_mr_image.nobg.png",
|
||
"20260624_191535_mr_image.nobg.png",
|
||
"20260624_191436_mr_image.nobg.png",
|
||
"20260624_191419_mr_image.nobg.png",
|
||
"20260624_191402_mr_image.nobg.png",
|
||
"20260624_191345_mr_image.nobg.png",
|
||
"20260624_191312_mr_image.nobg.png",
|
||
"20260624_191238_mr_image.nobg.png",
|
||
"20260624_191148_mr_image.nobg.png",
|
||
"20260624_191131_mr_image.nobg.png",
|
||
"20260624_191115_mr_image.nobg.png",
|
||
"20260624_191058_mr_image.nobg.png",
|
||
"20260624_191041_mr_image.nobg.png",
|
||
"20260624_190921_mr_image.nobg.png",
|
||
"20260624_190812_mr_image.nobg.png",
|
||
"20260624_190652_mr_image.nobg.png",
|
||
"20260624_190559_mr_image.png",
|
||
"20260624_190401_mr_image.nobg.png",
|
||
"20260624_190350_mr_image.nobg.png",
|
||
"20260624_190339_mr_image.nobg.png",
|
||
"20260624_190328_mr_image.nobg.png",
|
||
"20260624_190316_mr_image.nobg.png",
|
||
"20260624_190254_mr_image.nobg.png",
|
||
"20260624_190231_mr_image.nobg.png",
|
||
"20260624_190220_mr_image.nobg.png",
|
||
"20260624_190209_mr_image.nobg.png",
|
||
"20260624_190158_mr_image.nobg.png",
|
||
"20260624_190146_mr_image.nobg.png",
|
||
"20260624_190135_mr_image.nobg.png",
|
||
"20260624_190124_mr_image.nobg.png",
|
||
"20260624_190113_mr_image.nobg.png",
|
||
"20260624_190101_mr_image.nobg.png",
|
||
"20260624_190050_mr_image.nobg.png",
|
||
"20260624_190039_mr_image.nobg.png",
|
||
"20260624_190027_mr_image.nobg.png",
|
||
"20260624_190016_mr_image.nobg.png",
|
||
"20260624_190005_mr_image.nobg.png",
|
||
"20260624_185954_mr_image.nobg.png",
|
||
"20260624_185827_mr_image.nobg.png",
|
||
"20260624_183919_mr_image.png",
|
||
"20260624_183855_mr_image.png",
|
||
"20260624_183830_mr_image.png",
|
||
"20260624_183806_mr_image.png",
|
||
"20260624_183741_mr_image.png",
|
||
"20260624_183717_mr_image.png",
|
||
"20260624_183652_mr_image.png",
|
||
"20260624_183628_mr_image.png",
|
||
"20260624_183500_image.png",
|
||
"20260624_183211_Screenshot_From_2026-06-24_18-23-06.png",
|
||
"20260624_183148_9_20260624_182958_image.png",
|
||
"20260624_183137_8_20260624_182958_image.png",
|
||
"20260624_183126_7_20260624_182958_image.png",
|
||
"20260624_183104_5_20260624_182958_image.png",
|
||
"20260624_183053_4_20260624_182958_image.png",
|
||
"20260624_183042_3_20260624_182958_image.png",
|
||
"20260624_183031_2_20260624_182958_image.png",
|
||
"20260624_183021_1_20260624_182958_image.png",
|
||
"20260624_183010_0_20260624_182958_image.png",
|
||
"20260624_182958_image.png",
|
||
"20260624_174900_image.png",
|
||
"20260624_174943_dup_20260624_173115_image.png",
|
||
"20260624_175711_dup_20260624_174943_dup_20260624_173115_image.png",
|
||
"20260624_173115_image.png",
|
||
"20260624_174757_image.png",
|
||
"20260624_174442_image.png",
|
||
"20260624_172923_image.nobg.png",
|
||
"20260624_170638_image.nobg.png",
|
||
"20260624_170210_image.nobg.png",
|
||
"20260624_165804_image.nobg.png",
|
||
"20260624_165804_image.png",
|
||
"20260624_165624_image.png",
|
||
"20260624_165429_image.png",
|
||
"20260624_165311_image.png",
|
||
"20260624_164917_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_164534_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_164523_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_164512_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_164501_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_164449_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_164438_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_164427_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_164404_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_164353_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_162601_mr_0_20260622_093058_image.nobg.png",
|
||
"20260624_164240_crop_20260624_162342_1_20260622_093058_image.png",
|
||
"20260624_164218_crop_20260624_162601_mr_0_20260622_093058_image.nobg.png",
|
||
"20260624_164059_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_164116_dup_20260624_163951_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_163634_dup_20260624_163352_dup_20260624_162601_mr_0_20260622_093058_image.png",
|
||
"20260624_162510_image.png",
|
||
"20260624_162408_image.png",
|
||
"20260624_162342_1_20260622_093058_image.png",
|
||
"20260624_160808_fs_badly_1039s-1109s_0_20260622_091731_image.png_prev50.mp4",
|
||
"20260624_160423_1_20260619_040135_image.png",
|
||
"20260624_160412_1_20260619_040135_image.png",
|
||
"20260624_160400_1_20260619_040135_image.png",
|
||
"20260624_160349_1_20260619_040135_image.png",
|
||
"20260624_160338_1_20260619_040135_image.png",
|
||
"20260624_160327_1_20260619_040135_image.png",
|
||
"20260624_160305_1_20260619_040135_image.png",
|
||
"20260624_160254_1_20260619_040135_image.png",
|
||
"20260624_160242_1_20260619_040135_image.png",
|
||
"20260624_160231_1_20260619_040135_image.png",
|
||
"20260624_160220_1_20260619_040135_image.png",
|
||
"20260624_160209_1_20260619_040135_image.png",
|
||
"20260624_160157_1_20260619_040135_image.png",
|
||
"20260624_160146_1_20260619_040135_image.png",
|
||
"20260624_160135_1_20260619_040135_image.png",
|
||
"20260624_160124_1_20260619_040135_image.png",
|
||
"20260624_160113_1_20260619_040135_image.png",
|
||
"20260624_160102_1_20260619_040135_image.png",
|
||
"20260624_154852_img_72.png",
|
||
"20260624_154840_img_72.png",
|
||
"20260624_154821_img_72.png",
|
||
"20260624_154802_img_72.png",
|
||
"20260624_154746_img_72.png",
|
||
"20260624_154729_img_72.png",
|
||
"20260624_154712_img_72.png",
|
||
"20260624_154639_img_72.png",
|
||
"20260624_154548_img_72.png",
|
||
"20260624_154536_img_72.png",
|
||
"20260624_154525_img_72.png",
|
||
"20260624_154514_img_72.png",
|
||
"20260624_154503_img_72.png",
|
||
"20260624_154453_img_72.png",
|
||
"20260624_154442_img_72.png",
|
||
"20260624_154318_img_72.png",
|
||
"20260624_154236_img_72.png",
|
||
"20260624_154125_img_72.png",
|
||
"20260624_153940_img_72.png",
|
||
"20260624_153831_img_72.png",
|
||
"20260624_153614_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_153417_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_153344_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_153102_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_153051_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_153040_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_153029_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_153018_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_153006_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152955_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152944_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152922_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152911_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152900_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152838_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152826_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152815_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152804_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152753_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152742_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152731_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152720_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152710_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152659_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152334_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152317_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152244_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152228_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152212_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152155_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152138_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152123_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152112_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152100_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152049_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152038_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_152027_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_143523_Screenshot_From_2026-06-17_07-41-27.nobg.png",
|
||
"20260624_151746_dup_20260624_143523_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_143523_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_151501_dup_20260624_143523_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_151439_dup_20260624_143523_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_151250_Screenshot_From_2026-06-17_07-41-27.nobg.png",
|
||
"20260624_151239_Screenshot_From_2026-06-17_07-41-27.nobg.png",
|
||
"20260624_151228_Screenshot_From_2026-06-17_07-41-27.nobg.png",
|
||
"20260624_151216_Screenshot_From_2026-06-17_07-41-27.nobg.png",
|
||
"20260624_151205_Screenshot_From_2026-06-17_07-41-27.nobg.png",
|
||
"20260624_151143_Screenshot_From_2026-06-17_07-41-27.nobg.png",
|
||
"20260624_151131_Screenshot_From_2026-06-17_07-41-27.nobg.png",
|
||
"20260624_151053_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_143727_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_143714_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_143700_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_143647_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_143635_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_151613_dup_20260624_143523_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_144952_dup_20260624_143523_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_143024_mr_image.png",
|
||
"20260624_142959_mr_image.png",
|
||
"20260624_142923_mr_image.png",
|
||
"20260624_142856_mr_image.png",
|
||
"20260624_142832_mr_image.png",
|
||
"20260624_142622_img_19_2.png",
|
||
"20260624_142611_image.png",
|
||
"20260624_142600_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_142509_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_142457_img_19_2.png",
|
||
"20260624_142445_image.png",
|
||
"20260624_141325_img_19_2.png",
|
||
"20260624_140813_Screenshot_From_2026-06-17_07-43-19.png",
|
||
"20260624_140807_Screenshot_From_2026-06-17_07-41-27.png",
|
||
"20260624_140605_image.png",
|
||
"20260624_140532_image.png",
|
||
"20260624_140221_image.png",
|
||
"20260624_140129_image.png",
|
||
"20260624_135959_image.png",
|
||
"20260624_135912_image.png",
|
||
"20260619_194418_test123.nobg.png",
|
||
"20260624_134855_dup_20260624_055327_image.nobg.png",
|
||
"20260624_134836_dup_20260624_055327_image.nobg.png",
|
||
"20260624_125705_dup_20260624_055327_image.png",
|
||
"20260624_125526_dup_20260624_055327_image.nobg.png",
|
||
"20260624_123646_image.png",
|
||
"20260624_123608_image.png",
|
||
"20260624_121517_image.png",
|
||
"20260624_121506_image.png",
|
||
"20260624_121454_image.png",
|
||
"20260624_121439_image.png",
|
||
"20260624_121422_image.png",
|
||
"20260624_121353_image.png",
|
||
"20260624_121336_image.png",
|
||
"20260624_121320_image.png",
|
||
"20260624_121247_image.png",
|
||
"20260624_121231_image.png",
|
||
"20260624_121214_image.png",
|
||
"20260624_121140_image.png",
|
||
"20260624_121124_image.png",
|
||
"20260624_121003_mr_image.png",
|
||
"20260624_120932_mr_image.png",
|
||
"20260624_120916_mr_image.png",
|
||
"20260624_120859_mr_image.png",
|
||
"20260624_120842_mr_image.png",
|
||
"20260624_120825_mr_image.png",
|
||
"20260624_120809_mr_image.png",
|
||
"20260624_120520_mr_image.png",
|
||
"20260624_120503_mr_image.png",
|
||
"20260624_120446_mr_image.png",
|
||
"20260624_120430_mr_image.png",
|
||
"20260624_120414_mr_image.png",
|
||
"20260624_120135_mr_image.png",
|
||
"20260624_120119_mr_image.png",
|
||
"20260624_114651_mr_image.png",
|
||
"20260624_114639_mr_image.png",
|
||
"20260624_114607_mr_image.png",
|
||
"up_8cf9e62e_face.png",
|
||
"20260624_114501_mr_image.png",
|
||
"20260624_114430_mr_image.png",
|
||
"20260624_114300_mr_image.png",
|
||
"20260624_114039_mr_image.png",
|
||
"20260624_114009_mr_image.png",
|
||
"20260624_113931_mr_image.png",
|
||
"20260624_113819_image.png",
|
||
"20260624_113748_image.png",
|
||
"20260624_113717_image.png",
|
||
"20260624_113644_image.png",
|
||
"20260624_113240_image.png",
|
||
"20260624_113229_image.png",
|
||
"20260624_113217_image.png",
|
||
"20260624_113127_image.png",
|
||
"20260624_113039_image.png",
|
||
"20260624_112942_image.png",
|
||
"20260624_112852_image.png",
|
||
"20260624_110950_image.png",
|
||
"20260624_110613_image.png",
|
||
"20260624_110551_image.png",
|
||
"20260624_110443_image.png",
|
||
"20260624_110310_image.png",
|
||
"20260624_110253_image.png",
|
||
"20260624_110237_image.png",
|
||
"20260624_110158_mr_image.png",
|
||
"20260624_110028_image.png",
|
||
"20260624_110008_image.png",
|
||
"20260624_105859_5_20260624_105736_image.png",
|
||
"20260624_105813_2_20260624_105736_image.png",
|
||
"20260624_105757_1_20260624_105736_image.png",
|
||
"20260624_105736_image.png",
|
||
"20260624_103951_8_20260624_103246_image.nobg.png",
|
||
"20260624_103908_7_20260624_103246_image.nobg.png",
|
||
"20260624_104609_mr_1_20260624_103353_image.png",
|
||
"20260624_104059_image.png",
|
||
"20260624_104023_image.png",
|
||
"20260624_103940_7_20260624_103353_image.png",
|
||
"20260624_103919_5_20260624_103449_image.png",
|
||
"20260624_103908_7_20260624_103246_image.png",
|
||
"20260624_103846_6_20260624_103310_Screenshot_From_2026-06-24_10-30-32.png",
|
||
"20260624_103814_5_20260624_103353_image.png",
|
||
"20260624_103803_5_20260624_103310_Screenshot_From_2026-06-24_10-30-32.png",
|
||
"20260624_103742_5_20260624_103246_image.png",
|
||
"20260624_103642_4_20260624_103246_image.png",
|
||
"20260624_103609_1_20260624_103449_image.png",
|
||
"20260624_103601_3_20260624_103246_image.png",
|
||
"20260624_103547_2_20260624_103353_image.png",
|
||
"20260624_103536_2_20260624_103310_Screenshot_From_2026-06-24_10-30-32.png",
|
||
"20260624_103515_2_20260624_103246_image.png",
|
||
"20260624_103504_1_20260624_103353_image.png",
|
||
"20260624_103454_1_20260624_103310_Screenshot_From_2026-06-24_10-30-32.png",
|
||
"20260624_103449_image.png",
|
||
"20260624_103443_1_20260624_103246_image.png",
|
||
"20260624_103422_0_20260624_103310_Screenshot_From_2026-06-24_10-30-32.png",
|
||
"20260624_103411_0_20260624_103246_image.png",
|
||
"20260624_103353_image.png",
|
||
"20260624_103310_Screenshot_From_2026-06-24_10-30-32.png",
|
||
"20260624_103246_image.png",
|
||
"20260624_075439_0_20260622_101128_image.png",
|
||
"20260624_075405_0_20260622_101128_image.png",
|
||
"20260624_075343_0_20260622_101128_image.png",
|
||
"20260624_075331_0_20260622_101128_image.png",
|
||
"20260624_075258_0_20260622_101128_image.png",
|
||
"20260624_075244_0_20260622_101128_image.png",
|
||
"20260624_074849_0_20260622_101128_image.png",
|
||
"20260624_074835_0_20260622_101128_image.png",
|
||
"20260624_074818_0_20260622_101128_image.png",
|
||
"20260624_074801_0_20260622_101128_image.png",
|
||
"20260624_074659_5_20260622_101128_image.png",
|
||
"20260624_074623_5_20260622_101128_image.png",
|
||
"20260624_074536_2_20260622_101128_image.png",
|
||
"20260624_074423_0_20260622_101128_image.png",
|
||
"20260624_074303_2_20260622_101128_image.png",
|
||
"20260624_074154_0_20260622_101128_image.png",
|
||
"20260624_073959_2_20260622_101128_image.nobg.png",
|
||
"20260624_073907_2_20260622_101128_image.nobg.png",
|
||
"20260624_073757_2_20260622_101128_image.png",
|
||
"20260624_073656_2_20260622_101128_image.png",
|
||
"20260624_073634_2_20260622_101128_image.nobg.png",
|
||
"20260624_073429_image.nobg.png",
|
||
"20260624_073305_image.nobg.png",
|
||
"20260624_073222_7_20260622_101056_image.png",
|
||
"20260624_073100_7_20260622_101056_image.png",
|
||
"20260624_072848_7_20260622_101056_image.png",
|
||
"20260624_072658_7_20260622_101056_image.png",
|
||
"20260624_072406_image.nobg.png",
|
||
"20260624_072050_image.nobg.png",
|
||
"20260624_070918_image.nobg.png",
|
||
"20260624_070038_image.nobg.png",
|
||
"20260624_062056_image.nobg.png",
|
||
"20260624_055327_image.png",
|
||
"20260624_055134_image.png",
|
||
"20260624_055049_image.png",
|
||
"20260624_054816_image.png",
|
||
"20260624_054549_image.png",
|
||
"20260624_054134_image.png",
|
||
"20260624_054006_image.png",
|
||
"20260624_053727_mr_image.png",
|
||
"20260624_053637_mr_image.png",
|
||
"20260624_053541_mr_image.png",
|
||
"20260624_053439_mr_image.png",
|
||
"20260624_053147_mr_image.png",
|
||
"20260624_052826_others.jpeg",
|
||
"20260624_052755_mr_image.png",
|
||
"20260624_051049_others.jpeg",
|
||
"20260624_051003_others.jpeg",
|
||
"20260624_050750_others.jpeg",
|
||
"20260624_050145_mr_image.png",
|
||
"20260624_050134_mr_image.png",
|
||
"20260624_050124_mr_image.png",
|
||
"20260624_045145_mr_image.nobg.png",
|
||
"20260624_045240_mr_image.png",
|
||
"20260624_045145_mr_image.png",
|
||
"20260624_042747_mr_image.png",
|
||
"20260624_044343_mr_image.png",
|
||
"20260624_044054_mr_image.nobg.png",
|
||
"20260624_043937_mr_image.png",
|
||
"20260624_043843_mr_image.png",
|
||
"20260624_043811_mr_image.png",
|
||
"20260624_043304_mr_image.png",
|
||
"20260624_043253_mr_image.png",
|
||
"20260624_042912_mr_image.png",
|
||
"20260624_042437_mr_test123.jpeg",
|
||
"20260624_042249_image.png",
|
||
"20260624_042157_image.png",
|
||
"20260624_041946_others.jpeg",
|
||
"20260624_041847_0_20260623_164608_Screenshot_From_2026-06-23_16-43-24.png",
|
||
"20260624_041552_0_20260623_164608_Screenshot_From_2026-06-23_16-43-24.png",
|
||
"20260624_041453_mr_Screenshot_From_2026-06-23_16-43-13.png",
|
||
"20260624_041230_Screenshot_From_2026-06-23_16-43-13.png",
|
||
"20260624_041219_Screenshot_From_2026-06-23_16-43-13.png",
|
||
"20260624_041006_Screenshot_From_2026-06-23_16-43-13.png",
|
||
"20260624_040539_Screenshot_From_2026-06-23_16-43-13.png",
|
||
"20260624_040502_Screenshot_From_2026-06-23_16-43-13.png",
|
||
"20260624_035837_Screenshot_From_2026-06-23_16-38-43.png",
|
||
"20260624_035826_Screenshot_From_2026-06-23_16-38-43.png",
|
||
"20260624_035804_Screenshot_From_2026-06-23_16-38-43.png",
|
||
"20260624_035743_Screenshot_From_2026-06-23_16-38-43.png",
|
||
"20260624_035620_Screenshot_From_2026-06-23_16-43-13.png",
|
||
"20260624_035609_Screenshot_From_2026-06-23_16-43-13.png",
|
||
"20260624_032316_fs_SlaapKamer_7_20260619_202903_image.png_prev50.mp4",
|
||
"20260624_031522_0_20260622_173855_image.nobg.png",
|
||
"20260624_031522_0_20260622_173855_image.png",
|
||
"20260624_025922_0_20260622_173855_image.png",
|
||
"20260624_025623_0_20260622_173855_image.png",
|
||
"20260624_024332_0_20260622_173855_image.png",
|
||
"20260624_024321_0_20260622_173855_image.png",
|
||
"20260624_024310_0_20260622_173855_image.png",
|
||
"20260624_024225_0_20260622_173855_image.png",
|
||
"20260624_024054_0_20260622_173855_image.png",
|
||
"20260624_024043_0_20260622_173855_image.png",
|
||
"20260624_024032_0_20260622_173855_image.png",
|
||
"20260624_024020_0_20260622_173855_image.png",
|
||
"20260624_024009_0_20260622_173855_image.png",
|
||
"20260624_023859_0_20260622_173855_image.png",
|
||
"20260624_023847_0_20260622_173855_image.png",
|
||
"20260624_023813_0_20260622_173855_image.png",
|
||
"20260624_023614_0_20260622_173855_image.png",
|
||
"20260624_023502_image.png",
|
||
"20260624_023451_image.png",
|
||
"20260624_023428_image.png",
|
||
"20260624_023417_image.png",
|
||
"20260624_023355_image.png",
|
||
"20260624_023332_image.png",
|
||
"20260624_023310_image.png",
|
||
"20260624_023259_image.png",
|
||
"20260624_023247_image.png",
|
||
"20260624_023236_image.png",
|
||
"20260624_023225_image.png",
|
||
"20260624_023214_image.png",
|
||
"20260624_023203_image.png",
|
||
"20260624_023127_image.png",
|
||
"20260624_023048_image.png",
|
||
"20260624_022956_image.png",
|
||
"20260624_022943_image.png",
|
||
"20260624_022930_image.png",
|
||
"20260624_022917_image.png",
|
||
"20260624_022904_image.png",
|
||
"20260624_022744_image.png",
|
||
"20260624_022731_image.png",
|
||
"20260624_022717_image.png",
|
||
"20260624_022652_image.png",
|
||
"20260624_022639_image.png",
|
||
"20260624_022533_image.png",
|
||
"20260624_022415_image.png",
|
||
"20260624_022401_image.png",
|
||
"20260624_022349_image.png",
|
||
"20260624_022029_image.png",
|
||
"20260624_022017_image.png",
|
||
"20260624_021938_image.png",
|
||
"20260624_021926_image.png",
|
||
"20260624_021913_image.png",
|
||
"20260624_021900_image.png",
|
||
"20260624_021718_image.png",
|
||
"20260624_021705_image.png",
|
||
"20260624_021640_image.png",
|
||
"20260624_021614_image.png",
|
||
"20260624_021535_image.png",
|
||
"20260624_021523_image.png",
|
||
"20260624_020935_image.png",
|
||
"20260624_020924_image.png",
|
||
"20260624_020913_image.png",
|
||
"20260624_020902_image.png",
|
||
"20260624_020817_image.png",
|
||
"20260624_020806_image.png",
|
||
"20260624_020755_image.png",
|
||
"20260624_020734_image.png",
|
||
"20260624_020712_image.png",
|
||
"20260624_020701_image.png",
|
||
"20260624_020616_image.png",
|
||
"20260624_020413_image.png",
|
||
"20260624_020224_image.png",
|
||
"20260624_020008_image.png",
|
||
"20260624_014136_image.png",
|
||
"20260624_014107_image.png",
|
||
"20260624_011624_image.png",
|
||
"20260624_011613_image.png",
|
||
"20260624_010711_0_20260624_005912_image.png",
|
||
"20260624_010318_image.png",
|
||
"20260624_010056_8_20260624_005912_image.png",
|
||
"20260624_010033_6_20260624_005912_image.png",
|
||
"20260624_010022_5_20260624_005912_image.png",
|
||
"20260624_010011_4_20260624_005912_image.png",
|
||
"20260624_010000_3_20260624_005912_image.png",
|
||
"20260624_005946_2_20260624_005912_image.png",
|
||
"20260624_005935_1_20260624_005912_image.png",
|
||
"20260624_005925_0_20260624_005912_image.png",
|
||
"20260624_005912_image.png",
|
||
"20260624_001219_image.png",
|
||
"20260623_165512_2_20260623_164608_Screenshot_From_2026-06-23_16-43-24.png",
|
||
"20260623_165429_Screenshot_From_2026-06-23_16-43-24.png",
|
||
"20260623_164857_2_20260623_164608_Screenshot_From_2026-06-23_16-43-24.png",
|
||
"20260623_164712_Screenshot_From_2026-06-23_16-38-43.png",
|
||
"20260623_164707_Screenshot_From_2026-06-23_16-39-09.png",
|
||
"20260623_164702_Screenshot_From_2026-06-23_16-40-05.png",
|
||
"20260623_164657_Screenshot_From_2026-06-23_16-41-48.png",
|
||
"20260623_164653_Screenshot_From_2026-06-23_16-42-52.png",
|
||
"20260623_164648_Screenshot_From_2026-06-23_16-43-13.png",
|
||
"20260623_164608_Screenshot_From_2026-06-23_16-43-24.png",
|
||
"20260623_003039_0_20260622_173855_image.png",
|
||
"20260623_001759_0_20260622_173855_image.png",
|
||
"20260623_001510_0_20260622_173855_image.png",
|
||
"20260623_001411_0_20260622_173855_image.png",
|
||
"20260623_001233_0_20260622_173855_image.png",
|
||
"20260623_001041_0_20260622_173855_image.png",
|
||
"20260622_235333_fs_badly_949s-1038s_1_20260622_193118_image.png_prev50.mp4",
|
||
"20260622_235229_fs_friend_260s-273s_image.png.mp4",
|
||
"20260622_234344_image.png",
|
||
"20260622_234205_image.png",
|
||
"20260622_233849_image.png",
|
||
"20260622_233522_image.png",
|
||
"20260622_233511_image.png",
|
||
"20260622_233500_image.png",
|
||
"20260622_233439_image.png",
|
||
"20260622_232330_fs_badly_1039s-1109s_image.png_prev50.mp4",
|
||
"20260622_231728_fs_mp4_sd_973s-1077s_image.png_prev50.mp4",
|
||
"20260622_231602_image.png",
|
||
"20260622_231551_image.png",
|
||
"20260622_231539_image.png",
|
||
"20260622_231528_image.png",
|
||
"20260622_231454_image.png",
|
||
"20260622_231444_image.png",
|
||
"20260622_231432_image.png",
|
||
"20260622_231422_image.png",
|
||
"20260622_231034_image.png",
|
||
"20260622_231017_image.png",
|
||
"20260622_231000_image.png",
|
||
"20260622_230944_image.png",
|
||
"20260622_230927_image.png",
|
||
"20260622_230910_image.png",
|
||
"20260622_230841_image.png",
|
||
"20260622_230756_image.png",
|
||
"20260622_230600_image.png",
|
||
"20260622_230538_image.png",
|
||
"20260622_230431_image.png",
|
||
"20260622_230421_image.png",
|
||
"20260622_230059_image.png",
|
||
"20260622_225422_image.png",
|
||
"20260622_225349_image.png",
|
||
"20260622_225321_image.png",
|
||
"20260622_225310_image.png",
|
||
"20260622_224723_image.png",
|
||
"20260622_224711_image.png",
|
||
"20260622_224700_image.png",
|
||
"20260622_224649_image.png",
|
||
"20260622_224638_image.png",
|
||
"20260622_224627_image.png",
|
||
"20260622_224616_image.png",
|
||
"20260622_224605_image.png",
|
||
"20260622_224554_image.png",
|
||
"20260622_224543_image.png",
|
||
"20260622_224532_image.png",
|
||
"20260622_224521_image.png",
|
||
"20260622_224457_image.png",
|
||
"20260622_224434_image.png",
|
||
"20260622_224402_image.png",
|
||
"20260622_224001_image.png",
|
||
"20260622_223642_image.png",
|
||
"20260622_223622_2_20260622_193118_image.png",
|
||
"20260622_223315_image.png",
|
||
"20260622_222138_image.png",
|
||
"20260622_222119_0_20260619_124038_image.png",
|
||
"20260622_221407_0_20260619_124038_image.png",
|
||
"20260622_221350_0_20260619_124038_image.png",
|
||
"20260622_221333_0_20260619_124038_image.png",
|
||
"20260622_221317_0_20260619_124038_image.png",
|
||
"20260622_221300_0_20260619_124038_image.png",
|
||
"20260622_221244_0_20260619_124038_image.png",
|
||
"20260622_221228_0_20260619_124038_image.png",
|
||
"20260622_215853_0_20260619_124038_image.png",
|
||
"20260622_215842_0_20260619_124038_image.png",
|
||
"20260622_215831_0_20260619_124038_image.png",
|
||
"20260622_215820_0_20260619_124038_image.png",
|
||
"20260622_215703_dup_20260622_181910_0_20260619_124038_image.png",
|
||
"20260622_205918_0_20260619_124038_image.png",
|
||
"20260622_205853_0_20260619_124038_image.png",
|
||
"20260622_205722_0_20260619_124038_image.png",
|
||
"20260622_205705_0_20260619_124038_image.png",
|
||
"20260622_205648_0_20260619_124038_image.png",
|
||
"20260622_205522_0_20260619_124038_image.png",
|
||
"20260622_193821_Pasted_image__2_.png",
|
||
"20260622_193813_Pasted_image.png",
|
||
"20260622_193601_7_20260622_193118_image.png",
|
||
"20260622_193544_6_20260622_193118_image.png",
|
||
"20260622_193527_5_20260622_193118_image.png",
|
||
"20260622_193428_2_20260622_193118_image.png",
|
||
"20260622_193411_1_20260622_193118_image.png",
|
||
"20260622_193354_0_20260622_193118_image.png",
|
||
"20260622_193118_image.png",
|
||
"20260622_212012_dup_20260622_181910_0_20260619_124038_image.png",
|
||
"20260622_181910_0_20260619_124038_image.png",
|
||
"20260622_181734_0_20260619_124038_image.png",
|
||
"20260622_181720_0_20260619_124038_image.png",
|
||
"20260622_181634_0_20260619_124038_image.png",
|
||
"20260622_181543_0_20260619_124038_image.png",
|
||
"20260622_181201_0_20260619_124038_image.png",
|
||
"20260622_175746_7_20260622_175551_image.png",
|
||
"20260622_175729_6_20260622_175551_image.png",
|
||
"20260622_175645_3_20260622_175551_image.png",
|
||
"20260622_175633_2_20260622_175551_image.png",
|
||
"20260622_175622_1_20260622_175551_image.png",
|
||
"20260622_175609_0_20260622_175551_image.png",
|
||
"20260622_175551_image.png",
|
||
"20260622_174030_6_20260622_173855_image.png",
|
||
"20260622_174013_5_20260622_173855_image.png",
|
||
"20260622_173956_4_20260622_173855_image.png",
|
||
"20260622_173940_3_20260622_173855_image.png",
|
||
"20260622_173928_2_20260622_173855_image.png",
|
||
"20260622_173917_1_20260622_173855_image.png",
|
||
"20260622_173906_0_20260622_173855_image.png",
|
||
"20260622_173855_image.png",
|
||
"20260622_163152_7_20260622_162948_Screenshot_From_2026-06-22_15-55-43.png",
|
||
"20260622_163139_6_20260622_162948_Screenshot_From_2026-06-22_15-55-43.png",
|
||
"20260622_163126_5_20260622_162948_Screenshot_From_2026-06-22_15-55-43.png",
|
||
"20260622_163111_4_20260622_162948_Screenshot_From_2026-06-22_15-55-43.png",
|
||
"20260622_163055_3_20260622_162948_Screenshot_From_2026-06-22_15-55-43.png",
|
||
"20260622_163036_2_20260622_162948_Screenshot_From_2026-06-22_15-55-43.png",
|
||
"20260622_163017_1_20260622_162948_Screenshot_From_2026-06-22_15-55-43.png",
|
||
"20260622_163004_0_20260622_162948_Screenshot_From_2026-06-22_15-55-43.png",
|
||
"20260622_162948_Screenshot_From_2026-06-22_15-55-43.png",
|
||
"20260622_162939_7_20260622_162727_image.png",
|
||
"20260622_162925_6_20260622_162727_image.png",
|
||
"20260622_162910_5_20260622_162727_image.png",
|
||
"20260622_162833_3_20260622_162727_image.png",
|
||
"20260622_162816_2_20260622_162727_image.png",
|
||
"20260622_162759_1_20260622_162727_image.png",
|
||
"20260622_162744_0_20260622_162727_image.png",
|
||
"20260622_162727_image.png",
|
||
"20260622_162501_6_20260622_162338_image.png",
|
||
"20260622_162449_5_20260622_162338_image.png",
|
||
"20260622_162438_4_20260622_162338_image.png",
|
||
"20260622_162413_2_20260622_162338_image.png",
|
||
"20260622_162402_1_20260622_162338_image.png",
|
||
"20260622_162351_0_20260622_162338_image.png",
|
||
"20260622_162338_image.png",
|
||
"20260622_101342_5_20260622_101056_image.png",
|
||
"20260622_104936_image.nobg.png",
|
||
"20260622_134207_dup_20260622_104936_image.nobg.png",
|
||
"20260622_152438_image.png",
|
||
"20260622_152419_image.png",
|
||
"20260622_152336_image.png",
|
||
"20260622_152017_image.png",
|
||
"20260622_151959_image.png",
|
||
"20260622_151946_image.png",
|
||
"20260622_151320_2_20260622_101128_image.nobg.png",
|
||
"20260622_151308_2_20260622_101128_image.nobg.png",
|
||
"20260622_151235_2_20260622_101128_image.nobg.png",
|
||
"20260622_151224_2_20260622_101128_image.nobg.png",
|
||
"20260622_151213_2_20260622_101128_image.nobg.png",
|
||
"20260622_150929_2_20260622_101128_image.png",
|
||
"20260622_150907_2_20260622_101128_image.png",
|
||
"20260622_150855_2_20260622_101128_image.png",
|
||
"20260622_150831_2_20260622_101128_image.png",
|
||
"20260622_150820_2_20260622_101128_image.png",
|
||
"20260622_150637_2_20260622_101128_image.png",
|
||
"20260622_150603_2_20260622_101128_image.png",
|
||
"20260622_150546_2_20260622_101128_image.png",
|
||
"20260622_150529_2_20260622_101128_image.png",
|
||
"20260622_150513_2_20260622_101128_image.png",
|
||
"20260622_150500_2_20260622_101128_image.png",
|
||
"20260622_150443_2_20260622_101128_image.png",
|
||
"20260622_150426_2_20260622_101128_image.png",
|
||
"20260622_150409_2_20260622_101128_image.png",
|
||
"20260622_145046_2_20260622_101128_image.png",
|
||
"20260622_145026_2_20260622_101128_image.png",
|
||
"20260622_145007_2_20260622_101128_image.png",
|
||
"20260622_144949_2_20260622_101128_image.png",
|
||
"20260622_144931_2_20260622_101128_image.png",
|
||
"20260622_144857_2_20260622_101128_image.png",
|
||
"20260622_144659_1_20260622_101128_image.png",
|
||
"20260622_144641_1_20260622_101128_image.png",
|
||
"20260622_144623_1_20260622_101128_image.png",
|
||
"20260622_144605_1_20260622_101128_image.png",
|
||
"20260622_144529_1_20260622_101128_image.png",
|
||
"20260622_144512_1_20260622_101128_image.png",
|
||
"20260622_144436_1_20260622_101128_image.png",
|
||
"20260622_144217_2_20260622_101128_image.png",
|
||
"20260622_144202_2_20260622_101128_image.png",
|
||
"20260622_144145_2_20260622_101128_image.png",
|
||
"20260622_144128_2_20260622_101128_image.png",
|
||
"20260622_144025_2_20260622_101128_image.png",
|
||
"20260622_143657_1_20260622_101128_image.png",
|
||
"20260622_143634_1_20260622_101128_image.png",
|
||
"20260622_143623_1_20260622_101128_image.png",
|
||
"20260622_143612_1_20260622_101128_image.png",
|
||
"20260622_143601_1_20260622_101128_image.png",
|
||
"20260622_143549_1_20260622_101128_image.png",
|
||
"20260622_143538_1_20260622_101128_image.png",
|
||
"20260622_143439_1_20260622_101128_image.png",
|
||
"20260622_143422_1_20260622_101128_image.png",
|
||
"20260622_142649_2_20260622_101128_image.png",
|
||
"20260622_142457_1_20260622_101128_image.png",
|
||
"20260622_142320_2_20260622_101128_image.nobg.png",
|
||
"20260622_141903_2_20260622_101128_image.nobg.png",
|
||
"20260622_141751_2_20260622_101128_image.nobg.png",
|
||
"20260622_141626_1_20260622_101128_image.png",
|
||
"20260622_104622_image.nobg.png",
|
||
"20260622_130905_fs_SectionOriginal_image.png.mp4",
|
||
"20260622_130633_2_20260621_115215_image.png",
|
||
"20260622_130622_2_20260621_115215_image.png",
|
||
"20260622_130611_2_20260621_115215_image.png",
|
||
"20260622_130600_2_20260621_115215_image.png",
|
||
"20260622_130548_2_20260621_115215_image.png",
|
||
"20260622_130537_2_20260621_115215_image.png",
|
||
"20260622_130526_2_20260621_115215_image.png",
|
||
"20260622_130514_2_20260621_115215_image.png",
|
||
"20260622_130503_2_20260621_115215_image.png",
|
||
"20260622_130452_2_20260621_115215_image.png",
|
||
"20260622_130440_2_20260621_115215_image.png",
|
||
"20260622_130429_2_20260621_115215_image.png",
|
||
"20260622_130418_2_20260621_115215_image.png",
|
||
"20260622_130406_2_20260621_115215_image.png",
|
||
"20260622_130355_2_20260621_115215_image.png",
|
||
"20260622_130344_2_20260621_115215_image.png",
|
||
"20260622_130333_2_20260621_115215_image.png",
|
||
"20260622_130321_2_20260621_115215_image.png",
|
||
"20260622_130310_2_20260621_115215_image.png",
|
||
"20260622_130259_2_20260621_115215_image.png",
|
||
"20260622_130248_2_20260621_115215_image.png",
|
||
"20260622_130237_2_20260621_115215_image.png",
|
||
"20260622_130225_2_20260621_115215_image.png",
|
||
"20260622_130214_2_20260621_115215_image.png",
|
||
"20260622_130204_2_20260621_115215_image.png",
|
||
"20260622_130153_2_20260621_115215_image.png",
|
||
"20260622_130101_2_20260621_115215_image.png",
|
||
"20260622_130049_2_20260621_115215_image.png",
|
||
"20260622_130038_2_20260621_115215_image.png",
|
||
"20260622_130015_2_20260621_115215_image.png",
|
||
"20260622_130004_2_20260621_115215_image.png",
|
||
"20260622_125930_2_20260621_115215_image.png",
|
||
"20260622_125908_2_20260621_115215_image.png",
|
||
"20260622_125104_fs_BathRoom_247s-265s_image.png.mp4",
|
||
"20260622_124127_fs_mp4_sd_973s-1077s_image.png.mp4",
|
||
"20260622_124014_image.png",
|
||
"20260622_123629_image.png",
|
||
"20260622_122709_fs_mp4_sd_973s-1077s_image.png.mp4",
|
||
"20260622_115557_image.png",
|
||
"20260622_114540_image.png",
|
||
"20260622_113509_image.png",
|
||
"20260622_113309_image.png",
|
||
"20260622_113025_image.png",
|
||
"20260622_112640_image.png",
|
||
"20260622_112144_image.png",
|
||
"20260622_111959_image.png",
|
||
"20260622_111906_image.png",
|
||
"20260622_110937_image.png",
|
||
"20260622_110838_image.png",
|
||
"20260622_110821_image.png",
|
||
"20260622_110713_image.png",
|
||
"20260622_110605_image.png",
|
||
"20260622_110150_image.png",
|
||
"20260622_110026_image.png",
|
||
"20260622_105953_image.png",
|
||
"20260622_105837_6_20260622_091731_image.png",
|
||
"20260622_105627_image.png",
|
||
"20260622_101056_image.nobg.png",
|
||
"20260622_104207_2_20260622_101128_image.nobg.png",
|
||
"20260622_104155_2_20260622_101128_image.nobg.png",
|
||
"20260622_104142_2_20260622_101128_image.nobg.png",
|
||
"20260622_104130_2_20260622_101128_image.nobg.png",
|
||
"20260622_104039_2_20260622_101128_image.png",
|
||
"20260622_101252_2_20260622_101128_image.nobg.png",
|
||
"20260622_103029_0_20260622_101128_image.png",
|
||
"20260622_102856_0_20260622_101128_image.png",
|
||
"20260622_101554_8_20260622_101128_image.png",
|
||
"20260622_101537_7_20260622_101128_image.png",
|
||
"20260622_101521_8_20260622_101056_image.png",
|
||
"20260622_101504_6_20260622_101128_image.png",
|
||
"20260622_101432_5_20260622_101128_image.png",
|
||
"20260622_101415_6_20260622_101056_image.png",
|
||
"20260622_101359_4_20260622_101128_image.png",
|
||
"20260622_101252_2_20260622_101128_image.png",
|
||
"20260622_101219_1_20260622_101128_image.png",
|
||
"20260622_101147_0_20260622_101128_image.png",
|
||
"20260622_101128_image.png",
|
||
"20260622_101056_image.png",
|
||
"20260622_100921_image.png",
|
||
"20260622_100902_image.png",
|
||
"20260622_100842_image.png",
|
||
"20260622_100823_image.png",
|
||
"20260622_100700_image.png",
|
||
"20260622_100423_image.png",
|
||
"20260622_100314_image.png",
|
||
"20260622_100036_image.png",
|
||
"20260622_095947_image.png",
|
||
"20260622_095929_image.png",
|
||
"20260622_095832_image.png",
|
||
"20260622_093228_7_20260622_093058_image.png",
|
||
"20260622_093216_6_20260622_093058_image.png",
|
||
"20260622_093205_5_20260622_093058_image.png",
|
||
"20260622_093154_4_20260622_093058_image.png",
|
||
"20260622_093142_3_20260622_093058_image.png",
|
||
"20260622_093131_2_20260622_093058_image.png",
|
||
"20260622_093120_1_20260622_093058_image.png",
|
||
"20260622_093058_image.png",
|
||
"20260622_091851_6_20260622_091731_image.png",
|
||
"20260622_091839_5_20260622_091731_image.png",
|
||
"20260622_091828_4_20260622_091731_image.png",
|
||
"20260622_091817_3_20260622_091731_image.png",
|
||
"20260622_091744_0_20260622_091731_image.png",
|
||
"20260622_091731_image.png",
|
||
"20260622_054019_fs_badly_2_20260621_115215_image.png.mp4",
|
||
"20260622_053758_2_20260621_115215_image.png",
|
||
"20260622_053746_2_20260621_115215_image.png",
|
||
"20260622_053735_2_20260621_115215_image.png",
|
||
"20260622_053724_2_20260621_115215_image.png",
|
||
"20260622_053641_2_20260621_115215_image.png",
|
||
"20260622_053630_2_20260621_115215_image.png",
|
||
"20260622_053423_0_20260621_205151_image.png",
|
||
"20260622_053412_0_20260621_205151_image.png",
|
||
"20260622_053350_0_20260621_205151_image.png",
|
||
"20260622_053246_image.png",
|
||
"20260622_053235_image.png",
|
||
"20260622_053225_image.png",
|
||
"20260622_053054_image.png",
|
||
"20260622_052430_image.png",
|
||
"20260622_052419_image.png",
|
||
"20260622_052407_image.png",
|
||
"20260622_052345_image.png",
|
||
"20260622_052333_image.png",
|
||
"20260622_052322_image.png",
|
||
"20260622_052300_image.png",
|
||
"20260622_052220_image.png",
|
||
"20260622_051734_image.png",
|
||
"20260622_051711_image.png",
|
||
"20260622_051655_image.png",
|
||
"20260622_051644_image.png",
|
||
"20260622_050048_image.png",
|
||
"20260622_045636_4_20260621_205151_image.png",
|
||
"20260622_045623_4_20260621_205151_image.png",
|
||
"20260622_045558_4_20260621_205151_image.png",
|
||
"20260622_045545_4_20260621_205151_image.png",
|
||
"20260622_045413_image.png",
|
||
"20260622_045401_image.png",
|
||
"20260622_045136_image.png",
|
||
"20260622_042822_image.png",
|
||
"20260622_042801_image.png",
|
||
"20260622_042750_image.png",
|
||
"20260622_041503_image.png",
|
||
"20260622_041451_image.png",
|
||
"20260622_041438_image.png",
|
||
"20260622_041319_image.png",
|
||
"20260622_041306_image.png",
|
||
"20260622_041253_image.png",
|
||
"20260622_040911_b1.png",
|
||
"20260622_040727_b1.png",
|
||
"20260622_040702_b1.png",
|
||
"20260622_040636_b1.png",
|
||
"20260622_040347_b1.png",
|
||
"20260622_035945_b1.png",
|
||
"20260622_034529_b1.png",
|
||
"20260622_033840_b1.png",
|
||
"20260622_033829_b1.png",
|
||
"20260622_033806_b1.png",
|
||
"20260622_033744_b1.png",
|
||
"20260622_033733_b1.png",
|
||
"20260622_033722_b1.png",
|
||
"20260622_033700_b1.png",
|
||
"20260622_033649_b1.png",
|
||
"20260622_033638_b1.png",
|
||
"20260622_033628_b1.png",
|
||
"20260622_033617_b1.png",
|
||
"20260622_033134_b1.png",
|
||
"20260622_033123_b1.png",
|
||
"20260622_033112_b1.png",
|
||
"20260622_033101_b1.png",
|
||
"20260622_033050_b1.png",
|
||
"20260622_033039_b1.png",
|
||
"20260622_033029_b1.png",
|
||
"20260622_033018_b1.png",
|
||
"20260622_033007_b1.png",
|
||
"20260622_032956_b1.png",
|
||
"20260622_032223_b1.png",
|
||
"20260622_032212_b1.png",
|
||
"20260622_032201_b1.png",
|
||
"20260622_032150_b1.png",
|
||
"20260622_032140_b1.png",
|
||
"20260622_032118_b1.png",
|
||
"20260622_031725_b1.png",
|
||
"20260622_031708_b1.png",
|
||
"20260622_030845_b1.png",
|
||
"20260622_030832_b1.png",
|
||
"20260622_030754_b1.png",
|
||
"20260622_030728_b1.png",
|
||
"20260622_030715_b1.png",
|
||
"20260622_025308_b1.png",
|
||
"20260622_025234_b1.png",
|
||
"20260622_025128_b1.png",
|
||
"20260622_025116_b1.png",
|
||
"20260622_025105_b1.png",
|
||
"20260622_025055_b1.png",
|
||
"20260622_025044_b1.png",
|
||
"20260622_025033_b1.png",
|
||
"20260622_024933_b1.png",
|
||
"20260622_024740_b1.png",
|
||
"20260622_024730_b1.png",
|
||
"20260622_024719_b1.png",
|
||
"20260622_024708_b1.png",
|
||
"20260622_024600_b1.png",
|
||
"20260622_024437_b1.png",
|
||
"20260622_024426_b1.png",
|
||
"20260622_024311_b1.png",
|
||
"20260622_024159_b1.png",
|
||
"20260622_023750_b1.png",
|
||
"20260618_010638_b1.png",
|
||
"20260622_020521_dup_20260622_020223_image.nobg.png",
|
||
"20260622_021658_dup_20260621_212650_image.nobg.png",
|
||
"20260622_020223_image.png",
|
||
"20260622_020521_dup_20260622_020223_image.png",
|
||
"20260622_013636_image.nobg.png",
|
||
"20260622_015031_image.png",
|
||
"20260622_013636_image.png",
|
||
"20260622_013625_image.png",
|
||
"20260622_013614_image.png",
|
||
"20260622_005857_image.png",
|
||
"20260622_004346_fs_vacation_722s-775s_image.png.mp4",
|
||
"20260622_003713_fs_vacation_722s-775s_image.png.mp4",
|
||
"20260622_002215_fs_badly_1039s-1109s_1_20260619_202924_image.png.mp4",
|
||
"20260622_001127_fs_badly_949s-1038s_image.png.mp4",
|
||
"20260621_235251_fs_BathRoom_368s-386s_image.png.mp4",
|
||
"20260621_235129_fs_BathRoom_368s-386s_image.png.mp4",
|
||
"20260621_233236_fs_BathRoom_368s-386s_image.png.mp4",
|
||
"20260621_233034_fs_BathRoom_247s-265s_image.png.mp4",
|
||
"20260621_232901_fs_friend_260s-273s_image.png.mp4",
|
||
"20260621_232825_fs_friend_260s-273s_0_20260621_205151_image.png.mp4",
|
||
"20260621_232715_fs_friend_260s-273s_image.png.mp4",
|
||
"20260621_231051_image.png",
|
||
"20260621_231005_image.png",
|
||
"20260618_011539_20260617_133129_img_77.png",
|
||
"20260620_152017_image.png",
|
||
"20260618_013452_out4.png",
|
||
"20260620_025959_out4.png",
|
||
"20260622_021658_dup_20260621_212650_image.png",
|
||
"20260621_212650_image.png",
|
||
"20260621_210130_0_20260621_205151_image.png",
|
||
"20260621_210118_0_20260621_205151_image.png",
|
||
"20260621_210107_0_20260621_205151_image.png",
|
||
"20260621_205908_0_20260621_205151_image.png",
|
||
"20260621_205648_image.png",
|
||
"20260621_205356_7_20260621_205151_image.png",
|
||
"20260621_205338_6_20260621_205151_image.png",
|
||
"20260621_205321_5_20260621_205151_image.png",
|
||
"20260621_205304_4_20260621_205151_image.png",
|
||
"20260621_205247_3_20260621_205151_image.png",
|
||
"20260621_205230_2_20260621_205151_image.png",
|
||
"20260621_205213_1_20260621_205151_image.png",
|
||
"20260621_205202_0_20260621_205151_image.png",
|
||
"20260621_205151_image.png",
|
||
"20260621_203218_8_20260621_202941_image.png",
|
||
"20260621_203201_7_20260621_202941_image.png",
|
||
"20260621_203143_6_20260621_202941_image.png",
|
||
"20260621_203126_5_20260621_202941_image.png",
|
||
"20260621_203108_4_20260621_202941_image.png",
|
||
"20260621_203050_3_20260621_202941_image.png",
|
||
"20260621_203028_2_20260621_202941_image.png",
|
||
"20260621_203009_1_20260621_202941_image.png",
|
||
"20260621_202956_0_20260621_202941_image.png",
|
||
"20260621_202941_image.png",
|
||
"20260621_202759_8_20260621_202521_image.png",
|
||
"20260621_202743_7_20260621_202521_image.png",
|
||
"20260621_202726_6_20260621_202521_image.png",
|
||
"20260621_202704_5_20260621_202521_image.png",
|
||
"20260621_202630_3_20260621_202521_image.png",
|
||
"20260621_202615_2_20260621_202521_image.png",
|
||
"20260621_202602_1_20260621_202521_image.png",
|
||
"20260621_202548_0_20260621_202521_image.png",
|
||
"20260621_202521_image.png",
|
||
"20260621_115348_7_20260621_115215_image.png",
|
||
"20260621_115337_6_20260621_115215_image.png",
|
||
"20260621_115325_5_20260621_115215_image.png",
|
||
"20260621_115314_4_20260621_115215_image.png",
|
||
"20260621_115303_3_20260621_115215_image.png",
|
||
"20260621_115249_2_20260621_115215_image.png",
|
||
"20260621_115238_1_20260621_115215_image.png",
|
||
"20260621_115227_0_20260621_115215_image.png",
|
||
"20260621_115215_image.png",
|
||
"20260621_112614_sc_image.png",
|
||
"20260621_055418_sc_image.png",
|
||
"20260621_054553_sc_image.png",
|
||
"20260621_055114_sc_image.png",
|
||
"20260621_054746_sc_image.png",
|
||
"20260621_054610_sc_image.png",
|
||
"20260621_054243_sc_sc_sc_image.png",
|
||
"20260621_054214_sc_sc_image.png",
|
||
"20260621_054137_sc_image.png",
|
||
"20260621_054053_sc_image.png",
|
||
"20260621_053822_sc_image.png",
|
||
"20260621_053520_sc_image.png",
|
||
"20260621_053447_sc_sc_image.png",
|
||
"20260621_053345_sc_image.png",
|
||
"20260621_053228_sc_image.png",
|
||
"20260621_053131_sc_image.png",
|
||
"20260621_052951_image.png",
|
||
"20260621_052940_image.png",
|
||
"20260621_052930_image.png",
|
||
"20260621_052908_image.png",
|
||
"20260621_052621_image.png",
|
||
"20260621_052610_image.png",
|
||
"20260621_052600_image.png",
|
||
"20260621_052549_image.png",
|
||
"20260621_052538_image.png",
|
||
"20260621_052527_image.png",
|
||
"20260621_051626_sc_image.png",
|
||
"20260621_032154_image.png",
|
||
"20260621_045740_sc_image.png",
|
||
"20260621_045015_image.png",
|
||
"20260621_044941_image.png",
|
||
"20260621_044930_image.png",
|
||
"20260621_044908_image.png",
|
||
"20260621_044508_image.png",
|
||
"20260621_044446_image.png",
|
||
"20260621_044435_image.png",
|
||
"20260621_044048_image.png",
|
||
"20260621_043651_sc_sc_image.png",
|
||
"20260621_032916_image.png",
|
||
"20260621_040928_sc_image.png",
|
||
"20260621_040421_0_20260621_034735_image.png",
|
||
"20260621_040406_0_20260621_034735_image.png",
|
||
"20260621_040349_0_20260621_034735_image.png",
|
||
"20260621_040332_0_20260621_034735_image.png",
|
||
"20260621_040315_0_20260621_034735_image.png",
|
||
"20260621_040159_0_20260621_034735_image.png",
|
||
"20260621_034842_5_20260621_034735_image.png",
|
||
"20260621_034747_0_20260621_034735_image.png",
|
||
"20260621_034735_image.png",
|
||
"20260621_033325_image.png",
|
||
"20260621_033259_image.png",
|
||
"20260621_033234_image.png",
|
||
"20260621_033221_image.png",
|
||
"20260621_033208_image.png",
|
||
"20260621_033155_image.png",
|
||
"20260621_032903_image.png",
|
||
"20260621_032850_image.png",
|
||
"20260621_032836_image.png",
|
||
"20260621_032810_image.png",
|
||
"20260621_032709_image.png",
|
||
"20260621_032601_image.png",
|
||
"20260621_032218_image.png",
|
||
"20260621_032206_image.png",
|
||
"20260621_032143_image.png",
|
||
"20260621_032131_image.png",
|
||
"20260621_032119_image.png",
|
||
"20260621_032108_image.png",
|
||
"20260621_032056_image.png",
|
||
"20260621_032045_image.png",
|
||
"20260621_032034_image.png",
|
||
"20260621_031031_image.png",
|
||
"20260620_162107_fs_friend_image.png.mp4",
|
||
"20260620_155749_fs_friend_260s-273s_6_20260620_150941_image.png.mp4",
|
||
"20260620_155149_fs_friend_200s-220s_1_20260620_151425_image.png.mp4",
|
||
"20260620_154959_8_20260620_154810_image.png",
|
||
"20260620_154948_7_20260620_154810_image.png",
|
||
"20260620_154936_6_20260620_154810_image.png",
|
||
"20260620_154925_5_20260620_154810_image.png",
|
||
"20260620_154913_4_20260620_154810_image.png",
|
||
"20260620_154901_3_20260620_154810_image.png",
|
||
"20260620_154849_2_20260620_154810_image.png",
|
||
"20260620_154839_1_20260620_154810_image.png",
|
||
"20260620_154828_0_20260620_154810_image.png",
|
||
"20260620_154810_image.png",
|
||
"20260620_152926_1_20260620_151425_image.png",
|
||
"20260620_152838_image.png",
|
||
"20260620_151954_6_20260620_151835_image.png",
|
||
"20260620_151908_2_20260620_151835_image.png",
|
||
"20260620_151858_1_20260620_151835_image.png",
|
||
"20260620_151847_0_20260620_151835_image.png",
|
||
"20260620_151835_image.png",
|
||
"20260620_151628_1_20260620_151555_image.png",
|
||
"20260620_151555_image.png",
|
||
"20260620_151543_6_20260620_151425_image.png",
|
||
"20260620_151532_5_20260620_151425_image.png",
|
||
"20260620_151521_4_20260620_151425_image.png",
|
||
"20260620_151458_2_20260620_151425_image.png",
|
||
"20260620_151447_1_20260620_151425_image.png",
|
||
"20260620_151436_0_20260620_151425_image.png",
|
||
"20260620_151425_image.png",
|
||
"20260620_151323_6_20260620_150941_image.png",
|
||
"20260620_151249_3_20260620_150941_image.png",
|
||
"20260620_151223_1_20260620_150941_image.png",
|
||
"20260620_151209_0_20260620_150941_image.png",
|
||
"20260620_150941_image.png",
|
||
"20260620_053812_image.png",
|
||
"20260620_053801_image.png",
|
||
"20260620_053749_image.png",
|
||
"20260620_053738_image.png",
|
||
"20260620_053727_image.png",
|
||
"20260620_053716_image.png",
|
||
"20260620_053705_image.png",
|
||
"20260620_053641_image.png",
|
||
"20260620_053630_image.png",
|
||
"20260620_045040_fs_BathRoom_image.png.mp4",
|
||
"20260620_044811_fs_stair_2_20260619_202924_image.png.mp4",
|
||
"20260618_015102_20260616_022543_img_50.png",
|
||
"20260620_034637_fs_BathRoom_image.png.mp4",
|
||
"20260620_032550_20160903_200935.jpg",
|
||
"20260620_031338_20150913_211324.jpg",
|
||
"20260620_031211_20150913_211324.jpg",
|
||
"20260620_030933_20150913_211324.jpg",
|
||
"20260620_030839_20150913_211324.jpg",
|
||
"20260620_030644_4_20260618_125217_image.png",
|
||
"20260620_030633_4_20260618_125217_image.png",
|
||
"20260620_025855_b1.png",
|
||
"20260620_021352_2_20260618_172500_image.png",
|
||
"20260620_021330_2_20260618_172500_image.png",
|
||
"20260620_021319_2_20260618_172500_image.png",
|
||
"20260620_021308_2_20260618_172500_image.png",
|
||
"20260620_021258_2_20260618_172500_image.png",
|
||
"20260620_021247_2_20260618_172500_image.png",
|
||
"20260620_021226_2_20260618_172500_image.png",
|
||
"20260620_021215_2_20260618_172500_image.png",
|
||
"20260620_020431_2_20260618_172500_image.png",
|
||
"20260620_020408_2_20260618_172500_image.png",
|
||
"20260620_020357_2_20260618_172500_image.png",
|
||
"20260620_020346_2_20260618_172500_image.png",
|
||
"20260620_020335_2_20260618_172500_image.png",
|
||
"20260620_020324_2_20260618_172500_image.png",
|
||
"20260620_020313_2_20260618_172500_image.png",
|
||
"20260620_020302_2_20260618_172500_image.png",
|
||
"20260620_020251_2_20260618_172500_image.png",
|
||
"20260620_014953_image.png",
|
||
"20260620_014646_image.png",
|
||
"20260620_014349_image.png",
|
||
"20260620_014239_img_77.png",
|
||
"20260620_003901_2_20260619_175210_image.png",
|
||
"20260620_003844_2_20260619_175210_image.png",
|
||
"20260620_003828_2_20260619_175210_image.png",
|
||
"20260620_003708_2_20260619_175210_image.png",
|
||
"20260620_002715_img_38.png",
|
||
"20260620_002558_img_38.png",
|
||
"20260620_001342_1_20260619_202924_image.png",
|
||
"20260620_001328_1_20260619_202924_image.png",
|
||
"20260620_001230_1_20260619_202924_image.png",
|
||
"20260620_001215_1_20260619_202924_image.png",
|
||
"20260620_001202_1_20260619_202924_image.png",
|
||
"20260620_001145_1_20260619_202924_image.png",
|
||
"20260620_001129_1_20260619_202924_image.png",
|
||
"20260620_001100_1_20260619_202924_image.png",
|
||
"20260620_001048_1_20260619_202924_image.png",
|
||
"20260620_001032_1_20260619_202924_image.png",
|
||
"20260620_001003_1_20260619_202924_image.png",
|
||
"20260620_000453_1_20260619_202924_image.png",
|
||
"20260620_000437_1_20260619_202924_image.png",
|
||
"20260620_000404_1_20260619_202924_image.png",
|
||
"20260620_000353_1_20260619_202924_image.png",
|
||
"20260620_000330_1_20260619_202924_image.png",
|
||
"20260620_000319_1_20260619_202924_image.png",
|
||
"20260620_000308_1_20260619_202924_image.png",
|
||
"20260620_000257_1_20260619_202924_image.png",
|
||
"20260619_235919_1_20260619_202924_image.png",
|
||
"20260619_235909_1_20260619_202924_image.png",
|
||
"20260619_235858_1_20260619_202924_image.png",
|
||
"20260619_235847_1_20260619_202924_image.png",
|
||
"20260619_235614_1_20260619_202924_image.png",
|
||
"20260619_235557_1_20260619_202924_image.png",
|
||
"20260619_235541_1_20260619_202924_image.png",
|
||
"20260619_235514_1_20260619_202924_image.png",
|
||
"20260619_202956_1_20260619_202924_image.png",
|
||
"20260619_233813_img_38.png",
|
||
"20260619_225553_img_42.png",
|
||
"20260619_225523_img_84.png",
|
||
"20260619_225509_img_60.png",
|
||
"20260619_225409_img_59.png",
|
||
"20260619_225314_img_4.png",
|
||
"20260619_225244_img_86.png",
|
||
"20260619_225229_imgxxx.png",
|
||
"20260619_225215_img_15.png",
|
||
"20260619_225146_img_83.png",
|
||
"20260619_224934_img_38.png",
|
||
"20260619_224919_img_51.png",
|
||
"20260619_224851_img_36.png",
|
||
"20260619_224737_img_14.png",
|
||
"20260619_224557_img_81.png",
|
||
"20260619_224542_img_35.png",
|
||
"20260619_224133_img_87.png",
|
||
"20260619_224007_img_31.png",
|
||
"20260619_223856_img_37.png",
|
||
"20260619_223812_img_57.png",
|
||
"20260619_223757_img_82.png",
|
||
"20260619_223725_20160903_200935.jpg",
|
||
"20260619_223712_img_50.png",
|
||
"20260619_223643_img_48.png",
|
||
"20260619_223529_img_52.png",
|
||
"20260619_223512_20150913_211324.jpg",
|
||
"20260619_223441_img_77.png",
|
||
"20260619_223422_img_18.png",
|
||
"20260619_223330_img_73.png",
|
||
"20260619_223309_img_72.png",
|
||
"20260619_223249_img_80.png",
|
||
"20260619_223218_img_58.png",
|
||
"20260619_223101_img_41.png",
|
||
"20260619_223051_img_44.png",
|
||
"20260619_223040_img_78.png",
|
||
"20260619_205500_others.jpeg",
|
||
"20260619_205758_others.jpeg",
|
||
"20260619_205511_others.jpeg",
|
||
"20260619_205310_others.jpeg",
|
||
"20260618_004407_20260616_002456_test123.jpeg",
|
||
"20260619_204052_others.jpeg",
|
||
"20260619_204041_others.jpeg",
|
||
"20260619_204019_others.jpeg",
|
||
"20260619_204008_others.jpeg",
|
||
"20260619_203945_others.jpeg",
|
||
"20260619_203934_others.jpeg",
|
||
"20260619_203923_others.jpeg",
|
||
"20260619_203912_others.jpeg",
|
||
"20260619_203901_others.jpeg",
|
||
"20260619_203851_others.jpeg",
|
||
"20260619_203840_others.jpeg",
|
||
"20260619_203255_8_20260619_202924_image.png",
|
||
"20260619_203240_7_20260619_202924_image.png",
|
||
"20260619_203207_6_20260619_202924_image.png",
|
||
"20260619_203152_7_20260619_202903_image.png",
|
||
"20260619_203135_5_20260619_202924_image.png",
|
||
"20260619_203119_6_20260619_202903_image.png",
|
||
"20260619_203103_4_20260619_202924_image.png",
|
||
"20260619_203051_5_20260619_202903_image.png",
|
||
"20260619_203040_3_20260619_202924_image.png",
|
||
"20260619_203029_4_20260619_202903_image.png",
|
||
"20260619_203017_2_20260619_202924_image.png",
|
||
"20260619_203009_3_20260619_202903_image.png",
|
||
"20260619_202946_2_20260619_202903_image.png",
|
||
"20260619_202935_0_20260619_202924_image.png",
|
||
"20260619_202925_1_20260619_202903_image.png",
|
||
"20260619_202924_image.png",
|
||
"20260619_202914_0_20260619_202903_image.png",
|
||
"20260619_202903_image.png",
|
||
"20260619_200022_others.jpeg",
|
||
"20260619_195113_others.jpeg",
|
||
"20260619_195102_others.jpeg",
|
||
"20260619_195040_others.jpeg",
|
||
"20260619_195030_others.jpeg",
|
||
"20260619_194512_test123.jpeg",
|
||
"20260619_194429_test123.jpeg",
|
||
"20260619_194418_test123.jpeg",
|
||
"20260619_190224_7_20260619_190056_image.png",
|
||
"20260619_190213_6_20260619_190056_image.png",
|
||
"20260619_190201_5_20260619_190056_image.png",
|
||
"20260619_190150_4_20260619_190056_image.png",
|
||
"20260619_190139_3_20260619_190056_image.png",
|
||
"20260619_190128_2_20260619_190056_image.png",
|
||
"20260619_190117_1_20260619_190056_image.png",
|
||
"20260619_190106_0_20260619_190056_image.png",
|
||
"20260619_190056_image.png",
|
||
"20260619_184513_8_20260619_184329_image.png",
|
||
"20260619_184501_7_20260619_184329_image.png",
|
||
"20260619_184449_6_20260619_184329_image.png",
|
||
"20260619_184438_5_20260619_184329_image.png",
|
||
"20260619_184426_4_20260619_184329_image.png",
|
||
"20260619_184414_3_20260619_184329_image.png",
|
||
"20260619_184403_2_20260619_184329_image.png",
|
||
"20260619_184351_1_20260619_184329_image.png",
|
||
"20260619_184341_0_20260619_184329_image.png",
|
||
"20260619_184329_image.png",
|
||
"20260619_184236_6_20260619_184116_image.png",
|
||
"20260619_184139_1_20260619_184116_image.png",
|
||
"20260619_184128_0_20260619_184116_image.png",
|
||
"20260619_184116_image.png",
|
||
"20260619_181536_8_20260619_181308_img.png",
|
||
"20260619_181522_7_20260619_181308_img.png",
|
||
"20260619_181504_6_20260619_181308_img.png",
|
||
"20260619_181448_5_20260619_181308_img.png",
|
||
"20260619_181431_4_20260619_181308_img.png",
|
||
"20260619_181414_3_20260619_181308_img.png",
|
||
"20260619_181357_2_20260619_181308_img.png",
|
||
"20260619_181340_1_20260619_181308_img.png",
|
||
"20260619_181324_0_20260619_181308_img.png",
|
||
"20260619_181308_img.png",
|
||
"20260619_175426_7_20260619_175210_image.png",
|
||
"20260619_175409_6_20260619_175210_image.png",
|
||
"20260619_175335_4_20260619_175210_image.png",
|
||
"20260619_175317_3_20260619_175210_image.png",
|
||
"20260619_175300_2_20260619_175210_image.png",
|
||
"20260619_175243_1_20260619_175210_image.png",
|
||
"20260619_175227_0_20260619_175210_image.png",
|
||
"20260619_175210_image.png",
|
||
"20260616_002456_test123.jpeg",
|
||
"20260619_130342_others.jpeg",
|
||
"20260619_130358_others.jpeg",
|
||
"20260619_130415_others.jpeg",
|
||
"20260619_130606_others.jpeg",
|
||
"20260615_155354_others.jpeg",
|
||
"20260619_130726_others.jpeg",
|
||
"20260619_130742_others.jpeg",
|
||
"20260618_015052_20260618_004407_20260616_002456_test123.jpeg",
|
||
"20260619_172850_8_20260619_172708_image.png",
|
||
"20260619_172839_7_20260619_172708_image.png",
|
||
"20260619_172827_6_20260619_172708_image.png",
|
||
"20260619_172816_5_20260619_172708_image.png",
|
||
"20260619_172804_4_20260619_172708_image.png",
|
||
"20260619_172753_3_20260619_172708_image.png",
|
||
"20260619_172741_2_20260619_172708_image.png",
|
||
"20260619_172730_1_20260619_172708_image.png",
|
||
"20260619_172719_0_20260619_172708_image.png",
|
||
"20260619_172708_image.png",
|
||
"20260619_133311_8_20260619_133054_image.png",
|
||
"20260619_133300_7_20260619_133054_image.png",
|
||
"20260619_133248_6_20260619_133054_image.png",
|
||
"20260619_133237_5_20260619_133054_image.png",
|
||
"20260619_133226_4_20260619_133054_image.png",
|
||
"20260619_133214_3_20260619_133054_image.png",
|
||
"20260619_133203_8_20260619_132948_image.png",
|
||
"20260619_133152_2_20260619_133054_image.png",
|
||
"20260619_133141_7_20260619_132948_image.png",
|
||
"20260619_133128_1_20260619_133054_image.png",
|
||
"20260619_133117_6_20260619_132948_image.png",
|
||
"20260619_133107_0_20260619_133054_image.png",
|
||
"20260619_133056_5_20260619_132948_image.png",
|
||
"20260619_133054_image.png",
|
||
"20260619_133044_4_20260619_132948_image.png",
|
||
"20260619_133032_3_20260619_132948_image.png",
|
||
"20260619_133021_2_20260619_132948_image.png",
|
||
"20260619_133010_1_20260619_132948_image.png",
|
||
"20260619_132959_0_20260619_132948_image.png",
|
||
"20260619_132948_image.png",
|
||
"20260619_130712_others.jpeg",
|
||
"20260619_130549_others.jpeg",
|
||
"20260619_130043_mr_1_20260619_124038_image.png",
|
||
"20260619_125654_mr_mr_7_20260619_124038_image.png",
|
||
"20260619_125445_mr_7_20260619_124038_image.png",
|
||
"20260619_125434_mr_mr_1_20260619_040135_image.png",
|
||
"20260619_124456_6_20260619_124038_image.png",
|
||
"20260619_124435_5_20260619_124038_image.png",
|
||
"20260619_124351_4_20260619_123958_image.png",
|
||
"20260619_124333_3_20260619_124038_image.png",
|
||
"20260619_124226_1_20260619_124038_image.png",
|
||
"20260619_124154_0_20260619_124038_image.png",
|
||
"20260619_124139_0_20260619_123958_image.png",
|
||
"20260619_124038_image.png",
|
||
"20260619_123958_image.png",
|
||
"20260619_052514_mr_1_20260618_052526_image.png",
|
||
"20260619_052351_mr_mr_mr_mr_1_20260619_040135_image.png",
|
||
"20260619_052235_mr_1_20260619_040135_image.png",
|
||
"20260619_052030_mr_mr_mr_1_20260619_040135_image.png",
|
||
"20260619_051905_mr_mr_1_20260619_040135_image.png",
|
||
"20260619_051326_mr_1_20260619_040135_image.png",
|
||
"20260619_051245_1_20260619_040135_image.png",
|
||
"20260619_051226_mr_1_20260619_040135_image.png",
|
||
"20260619_050945_image.png",
|
||
"20260619_050207_1_20260619_040135_image.png",
|
||
"20260619_050114_1_20260619_040135_image.png",
|
||
"20260619_045946_1_20260619_040135_image.png",
|
||
"20260619_043856_1_20260619_040135_image.png",
|
||
"20260619_043845_1_20260619_040135_image.png",
|
||
"20260619_043740_1_20260619_040135_image.png",
|
||
"20260619_041924_8_20260619_041612_image.png",
|
||
"20260619_041913_1_20260619_040135_image.png",
|
||
"20260619_041902_7_20260619_041612_image.png",
|
||
"20260619_041850_1_20260619_040135_image.png",
|
||
"20260619_041839_6_20260619_041612_image.png",
|
||
"20260619_041829_1_20260619_040135_image.png",
|
||
"20260619_041818_5_20260619_041612_image.png",
|
||
"20260619_041806_1_20260619_040135_image.png",
|
||
"20260619_041756_4_20260619_041612_image.png",
|
||
"20260619_041744_1_20260619_040135_image.png",
|
||
"20260619_041734_3_20260619_041612_image.png",
|
||
"20260619_041722_1_20260619_040135_image.png",
|
||
"20260619_041711_2_20260619_041612_image.png",
|
||
"20260619_041701_1_20260619_040135_image.png",
|
||
"20260619_041639_1_20260619_040135_image.png",
|
||
"20260619_041628_0_20260619_041612_image.png",
|
||
"20260619_041618_1_20260619_040135_image.png",
|
||
"20260619_041612_image.png",
|
||
"20260619_041555_1_20260619_040135_image.png",
|
||
"20260619_041544_1_20260619_040135_image.png",
|
||
"20260619_041533_1_20260619_040135_image.png",
|
||
"20260619_041511_1_20260619_040135_image.png",
|
||
"20260619_041500_1_20260619_040135_image.png",
|
||
"20260619_041449_1_20260619_040135_image.png",
|
||
"20260619_041427_1_20260619_040135_image.png",
|
||
"20260619_041417_1_20260619_040135_image.png",
|
||
"20260619_041406_1_20260619_040135_image.png",
|
||
"20260619_041355_1_20260619_040135_image.png",
|
||
"20260619_041334_1_20260619_040135_image.png",
|
||
"20260619_041323_1_20260619_040135_image.png",
|
||
"20260619_041041_1_20260619_040135_image.png",
|
||
"20260619_041030_1_20260619_040135_image.png",
|
||
"20260619_041020_1_20260619_040135_image.png",
|
||
"20260619_040147_0_20260619_040135_image.png",
|
||
"20260619_040209_2_20260619_040135_image.png",
|
||
"20260619_040158_1_20260619_040135_image.png",
|
||
"20260619_040319_8_20260619_040135_image.png",
|
||
"20260619_040307_7_20260619_040135_image.png",
|
||
"20260619_040255_6_20260619_040135_image.png",
|
||
"20260619_040244_5_20260619_040135_image.png",
|
||
"20260619_040233_4_20260619_040135_image.png",
|
||
"20260619_040221_3_20260619_040135_image.png",
|
||
"20260619_040135_image.png",
|
||
"20260619_035006_2_20260618_053458_image.png",
|
||
"20260619_034656_2_20260618_053458_image.png",
|
||
"20260619_034055_2_20260618_053458_image.png",
|
||
"20260619_034044_2_20260618_053458_image.png",
|
||
"20260619_034033_2_20260618_053458_image.png",
|
||
"20260619_034011_2_20260618_053458_image.png",
|
||
"20260619_034001_2_20260618_053458_image.png",
|
||
"20260619_033950_2_20260618_053458_image.png",
|
||
"20260619_033939_2_20260618_053458_image.png",
|
||
"20260619_033804_2_20260618_053458_image.png",
|
||
"20260619_033754_2_20260618_053458_image.png",
|
||
"20260619_033743_2_20260618_053458_image.png",
|
||
"20260619_033732_2_20260618_053458_image.png",
|
||
"20260619_033721_2_20260618_053458_image.png",
|
||
"20260619_032726_2_20260618_053458_image.png",
|
||
"20260619_032715_2_20260618_053458_image.png",
|
||
"20260619_032704_2_20260618_053458_image.png",
|
||
"20260619_032257_0_20260618_181600_image.png",
|
||
"20260619_031916_image.png",
|
||
"20260619_024726_jb1.png",
|
||
"20260619_024715_jb1.png",
|
||
"20260619_024704_jb1.png",
|
||
"20260619_024327_jb1.png",
|
||
"20260618_010649_20260615_150340_test.png",
|
||
"20260618_011622_jb1.png",
|
||
"20260619_024118_0_20260618_181600_image.png",
|
||
"20260619_024056_0_20260618_181600_image.png",
|
||
"20260619_023250_image.png",
|
||
"20260619_022156_3_20260618_181600_image.png",
|
||
"20260619_022028_2_20260618_174613_image.png",
|
||
"20260619_021829_2_20260618_053458_image.png",
|
||
"20260619_021736_2_20260618_053458_image.png",
|
||
"20260619_020115_2_20260618_053458_image.png",
|
||
"20260619_015517_2_20260618_053458_image.png",
|
||
"20260619_015140_2_20260618_053458_image.png",
|
||
"20260619_014803_2_20260618_053458_image.png",
|
||
"20260619_012837_2_20260618_053458_image.png",
|
||
"20260619_004149_3_20260618_174613_image.png",
|
||
"20260619_003516_0_20260618_174613_image.png",
|
||
"20260619_002329_2_20260618_053458_image.png",
|
||
"20260619_001659_4_20260618_173728_image.png",
|
||
"20260618_234918_3_20260618_173728_image.png",
|
||
"20260618_231151_3_20260618_173728_image.png",
|
||
"20260618_231042_1_20260618_173728_image.png",
|
||
"20260618_230950_3_20260618_173728_image.png",
|
||
"20260618_225918_7_20260618_173728_image.png",
|
||
"20260618_225236_3_20260618_173728_image.png",
|
||
"20260618_223557_2_20260618_223501_image.png",
|
||
"20260618_223541_1_20260618_223501_image.png",
|
||
"20260618_223525_0_20260618_223501_image.png",
|
||
"20260618_223501_image.png",
|
||
"20260618_220918_2_20260618_053458_image.png",
|
||
"20260618_220907_2_20260618_053458_image.png",
|
||
"20260618_220856_2_20260618_053458_image.png",
|
||
"20260618_182944_7_20260618_182810_image.png",
|
||
"20260618_182932_6_20260618_182810_image.png",
|
||
"20260618_182920_5_20260618_182810_image.png",
|
||
"20260618_182909_4_20260618_182810_image.png",
|
||
"20260618_182857_3_20260618_182810_image.png",
|
||
"20260618_182842_2_20260618_182810_image.png",
|
||
"20260618_182832_1_20260618_182810_image.png",
|
||
"20260618_182821_0_20260618_182810_image.png",
|
||
"20260618_182810_image.png",
|
||
"20260618_182304_8_20260618_182106_image.png",
|
||
"20260618_182247_7_20260618_182106_image.png",
|
||
"20260618_182230_6_20260618_182106_image.png",
|
||
"20260618_182216_5_20260618_182106_image.png",
|
||
"20260618_182204_4_20260618_182106_image.png",
|
||
"20260618_182153_3_20260618_182106_image.png",
|
||
"20260618_182141_2_20260618_182106_image.png",
|
||
"20260618_182130_1_20260618_182106_image.png",
|
||
"20260618_182117_0_20260618_182106_image.png",
|
||
"20260618_182106_image.png",
|
||
"20260618_182104_8_20260618_181848_image.png",
|
||
"20260618_182052_7_20260618_181848_image.png",
|
||
"20260618_182041_6_20260618_181848_image.png",
|
||
"20260618_182029_5_20260618_181848_image.png",
|
||
"20260618_182018_4_20260618_181848_image.png",
|
||
"20260618_182007_3_20260618_181848_image.png",
|
||
"20260618_181944_2_20260618_181848_image.png",
|
||
"20260618_181934_7_20260618_181715_image.png",
|
||
"20260618_181922_1_20260618_181848_image.png",
|
||
"20260618_181912_6_20260618_181715_image.png",
|
||
"20260618_181901_0_20260618_181848_image.png",
|
||
"20260618_181851_5_20260618_181715_image.png",
|
||
"20260618_181848_image.png",
|
||
"20260618_181839_4_20260618_181715_image.png",
|
||
"20260618_181827_3_20260618_181715_image.png",
|
||
"20260618_181815_2_20260618_181715_image.png",
|
||
"20260618_181804_8_20260618_181600_image.png",
|
||
"20260618_181752_1_20260618_181715_image.png",
|
||
"20260618_181742_7_20260618_181600_image.png",
|
||
"20260618_181730_0_20260618_181715_image.png",
|
||
"20260618_181719_6_20260618_181600_image.png",
|
||
"20260618_181715_image.png",
|
||
"20260618_181708_5_20260618_181600_image.png",
|
||
"20260618_181656_4_20260618_181600_image.png",
|
||
"20260618_181645_3_20260618_181600_image.png",
|
||
"20260618_181633_2_20260618_181600_image.png",
|
||
"20260618_181622_1_20260618_181600_image.png",
|
||
"20260618_181611_0_20260618_181600_image.png",
|
||
"20260618_181600_image.png",
|
||
"20260618_181109_7_20260618_180939_image.png",
|
||
"20260618_181057_6_20260618_180939_image.png",
|
||
"20260618_181045_5_20260618_180939_image.png",
|
||
"20260618_181034_4_20260618_180939_image.png",
|
||
"20260618_181023_3_20260618_180939_image.png",
|
||
"20260618_181011_2_20260618_180939_image.png",
|
||
"20260618_181000_1_20260618_180939_image.png",
|
||
"20260618_180950_0_20260618_180939_image.png",
|
||
"20260618_180939_image.png",
|
||
"20260618_174926_7_20260618_174613_image.png",
|
||
"20260618_174909_6_20260618_174613_image.png",
|
||
"20260618_174852_5_20260618_174613_image.png",
|
||
"20260618_174835_4_20260618_174613_image.png",
|
||
"20260618_174803_3_20260618_174613_image.png",
|
||
"20260618_174747_7_20260618_174516_image.png",
|
||
"20260618_174730_2_20260618_174613_image.png",
|
||
"20260618_174714_6_20260618_174516_image.png",
|
||
"20260618_174657_1_20260618_174613_image.png",
|
||
"20260618_174641_5_20260618_174516_image.png",
|
||
"20260618_174625_0_20260618_174613_image.png",
|
||
"20260618_174615_4_20260618_174516_image.png",
|
||
"20260618_174613_image.png",
|
||
"20260618_174603_3_20260618_174516_image.png",
|
||
"20260618_174540_1_20260618_174516_image.png",
|
||
"20260618_174529_0_20260618_174516_image.png",
|
||
"20260618_174516_image.png",
|
||
"20260618_173955_7_20260618_173728_image.png",
|
||
"20260618_173941_6_20260618_173728_image.png",
|
||
"20260618_173923_5_20260618_173728_image.png",
|
||
"20260618_173906_4_20260618_173728_image.png",
|
||
"20260618_173848_3_20260618_173728_image.png",
|
||
"20260618_173828_2_20260618_173728_image.png",
|
||
"20260618_173808_1_20260618_173728_image.png",
|
||
"20260618_173747_0_20260618_173728_image.png",
|
||
"20260618_173728_image.png",
|
||
"20260618_172641_8_20260618_172500_image.png",
|
||
"20260618_172630_7_20260618_172500_image.png",
|
||
"20260618_172618_6_20260618_172500_image.png",
|
||
"20260618_172607_5_20260618_172500_image.png",
|
||
"20260618_172555_4_20260618_172500_image.png",
|
||
"20260618_172544_3_20260618_172500_image.png",
|
||
"20260618_172532_2_20260618_172500_image.png",
|
||
"20260618_172521_1_20260618_172500_image.png",
|
||
"20260618_172510_0_20260618_172500_image.png",
|
||
"20260618_172500_image.png",
|
||
"20260618_172015_8_20260618_171448_image.png",
|
||
"20260618_171956_6_20260618_171628_image.png",
|
||
"20260618_171939_7_20260618_171448_image.png",
|
||
"20260618_171923_5_20260618_171628_image.png",
|
||
"20260618_171906_6_20260618_171448_image.png",
|
||
"20260618_171851_4_20260618_171628_image.png",
|
||
"20260618_171834_5_20260618_171448_image.png",
|
||
"20260618_171819_3_20260618_171628_image.png",
|
||
"20260618_171802_4_20260618_171448_image.png",
|
||
"20260618_171746_2_20260618_171628_image.png",
|
||
"20260618_171732_3_20260618_171448_image.png",
|
||
"20260618_171717_1_20260618_171628_image.png",
|
||
"20260618_171706_2_20260618_171448_image.png",
|
||
"20260618_171655_0_20260618_171628_image.png",
|
||
"20260618_171639_1_20260618_171448_image.png",
|
||
"20260618_171628_image.png",
|
||
"20260618_171624_0_20260618_171448_image.png",
|
||
"20260618_171448_image.png",
|
||
"20260618_134543_8_20260618_134348_image.png",
|
||
"20260618_134532_7_20260618_134348_image.png",
|
||
"20260618_134520_6_20260618_134348_image.png",
|
||
"20260618_134508_5_20260618_134348_image.png",
|
||
"20260618_134454_4_20260618_134348_image.png",
|
||
"20260618_134440_3_20260618_134348_image.png",
|
||
"20260618_134424_2_20260618_134348_image.png",
|
||
"20260618_134409_1_20260618_134348_image.png",
|
||
"20260618_134359_0_20260618_134348_image.png",
|
||
"20260618_134348_image.png",
|
||
"20260618_133922_7_20260618_133729_image.png",
|
||
"20260618_133910_6_20260618_133729_image.png",
|
||
"20260618_133858_5_20260618_133729_image.png",
|
||
"20260618_133847_4_20260618_133729_image.png",
|
||
"20260618_133835_3_20260618_133729_image.png",
|
||
"20260618_133822_2_20260618_133729_image.png",
|
||
"20260618_133811_1_20260618_133729_image.png",
|
||
"20260618_133800_0_20260618_133729_image.png",
|
||
"20260618_133729_image.png",
|
||
"20260618_133023_10_20260618_132718_image.png",
|
||
"20260618_133011_9_20260618_132718_image.png",
|
||
"20260618_132956_8_20260618_132718_image.png",
|
||
"20260618_132941_7_20260618_132718_image.png",
|
||
"20260618_132923_6_20260618_132718_image.png",
|
||
"20260618_132906_5_20260618_132718_image.png",
|
||
"20260618_132848_4_20260618_132718_image.png",
|
||
"20260618_132831_3_20260618_132718_image.png",
|
||
"20260618_132817_2_20260618_132718_image.png",
|
||
"20260618_132806_1_20260618_132718_image.png",
|
||
"20260618_132755_0_20260618_132718_image.png",
|
||
"20260618_132718_image.png",
|
||
"20260618_132450_8_20260618_132215_image.png",
|
||
"20260618_132432_7_20260618_132215_image.png",
|
||
"20260618_132414_6_20260618_132215_image.png",
|
||
"20260618_132357_5_20260618_132215_image.png",
|
||
"20260618_132340_4_20260618_132215_image.png",
|
||
"20260618_132323_3_20260618_132215_image.png",
|
||
"20260618_132305_2_20260618_132215_image.png",
|
||
"20260618_132248_1_20260618_132215_image.png",
|
||
"20260618_132232_0_20260618_132215_image.png",
|
||
"20260618_132215_image.png",
|
||
"20260618_132152_8_20260618_131909_image.png",
|
||
"20260618_132134_7_20260618_131909_image.png",
|
||
"20260618_132114_6_20260618_131909_image.png",
|
||
"20260618_132054_5_20260618_131909_image.png",
|
||
"20260618_132035_4_20260618_131909_image.png",
|
||
"20260618_132017_3_20260618_131909_image.png",
|
||
"20260618_131959_2_20260618_131909_image.png",
|
||
"20260618_131943_1_20260618_131909_image.png",
|
||
"20260618_131926_0_20260618_131909_image.png",
|
||
"20260618_131909_image.png",
|
||
"20260618_131811_7_20260618_131624_image.png",
|
||
"20260618_131759_6_20260618_131624_image.png",
|
||
"20260618_131747_5_20260618_131624_image.png",
|
||
"20260618_131736_4_20260618_131624_image.png",
|
||
"20260618_131725_3_20260618_131624_image.png",
|
||
"20260618_131713_2_20260618_131624_image.png",
|
||
"20260618_131702_1_20260618_131624_image.png",
|
||
"20260618_131651_0_20260618_131624_image.png",
|
||
"20260618_131624_image.png",
|
||
"20260618_125928_8_20260618_125748_image.png",
|
||
"20260618_125917_7_20260618_125748_image.png",
|
||
"20260618_125905_6_20260618_125748_image.png",
|
||
"20260618_125854_5_20260618_125748_image.png",
|
||
"20260618_125842_4_20260618_125748_image.png",
|
||
"20260618_125831_3_20260618_125748_image.png",
|
||
"20260618_125819_2_20260618_125748_image.png",
|
||
"20260618_125809_1_20260618_125748_image.png",
|
||
"20260618_125758_0_20260618_125748_image.png",
|
||
"20260618_125748_image.png",
|
||
"20260618_125358_5_20260618_125217_image.png",
|
||
"20260618_125340_4_20260618_125217_image.png",
|
||
"20260618_125323_3_20260618_125217_image.png",
|
||
"20260618_125307_2_20260618_125217_image.png",
|
||
"20260618_125250_1_20260618_125217_image.png",
|
||
"20260618_125233_0_20260618_125217_image.png",
|
||
"20260618_125217_image.png",
|
||
"20260618_124424_5_20260618_124203_image.png",
|
||
"20260618_124412_4_20260618_124203_image.png",
|
||
"20260618_124355_3_20260618_124203_image.png",
|
||
"20260618_124334_2_20260618_124203_image.png",
|
||
"20260618_124316_1_20260618_124203_image.png",
|
||
"20260618_124256_0_20260618_124203_image.png",
|
||
"20260618_124203_image.png",
|
||
"20260618_123244_6_20260618_122931_image.png",
|
||
"20260618_123227_5_20260618_122931_image.png",
|
||
"20260618_123210_4_20260618_122931_image.png",
|
||
"20260618_123153_3_20260618_122931_image.png",
|
||
"20260618_123132_2_20260618_122931_image.png",
|
||
"20260618_123104_0_20260618_122931_image.png",
|
||
"20260618_122931_image.png",
|
||
"20260618_054411_8_20260618_054230_image.png",
|
||
"20260618_054400_7_20260618_054230_image.png",
|
||
"20260618_054348_6_20260618_054230_image.png",
|
||
"20260618_054337_5_20260618_054230_image.png",
|
||
"20260618_054325_4_20260618_054230_image.png",
|
||
"20260618_054314_3_20260618_054230_image.png",
|
||
"20260618_054303_2_20260618_054230_image.png",
|
||
"20260618_054252_1_20260618_054230_image.png",
|
||
"20260618_054241_0_20260618_054230_image.png",
|
||
"20260618_054230_image.png",
|
||
"20260618_053932_7_20260618_053802_image.png",
|
||
"20260618_053920_6_20260618_053802_image.png",
|
||
"20260618_053909_5_20260618_053802_image.png",
|
||
"20260618_053857_4_20260618_053802_image.png",
|
||
"20260618_053846_3_20260618_053802_image.png",
|
||
"20260618_053834_2_20260618_053802_image.png",
|
||
"20260618_053823_1_20260618_053802_image.png",
|
||
"20260618_053812_0_20260618_053802_image.png",
|
||
"20260618_053802_image.png",
|
||
"20260618_053627_7_20260618_053458_image.png",
|
||
"20260618_053615_6_20260618_053458_image.png",
|
||
"20260618_053604_5_20260618_053458_image.png",
|
||
"20260618_053553_4_20260618_053458_image.png",
|
||
"20260618_053530_2_20260618_053458_image.png",
|
||
"20260618_053519_1_20260618_053458_image.png",
|
||
"20260618_053508_0_20260618_053458_image.png",
|
||
"20260618_053458_image.png",
|
||
"20260618_052708_8_20260618_052526_image.png",
|
||
"20260618_052657_7_20260618_052526_image.png",
|
||
"20260618_052645_6_20260618_052526_image.png",
|
||
"20260618_052634_5_20260618_052526_image.png",
|
||
"20260618_052622_4_20260618_052526_image.png",
|
||
"20260618_052611_3_20260618_052526_image.png",
|
||
"20260618_052559_2_20260618_052526_image.png",
|
||
"20260618_052548_1_20260618_052526_image.png",
|
||
"20260618_052537_0_20260618_052526_image.png",
|
||
"20260618_052526_image.png",
|
||
"20260618_051029_7_20260618_050846_img_4.png",
|
||
"20260618_051006_5_20260618_050846_img_4.png",
|
||
"20260618_050923_2_20260618_050846_img_4.png",
|
||
"20260618_050913_1_20260618_050846_img_4.png",
|
||
"20260618_050902_0_20260618_050846_img_4.png",
|
||
"20260618_015217_20260618_010710_p12-2.png",
|
||
"20260618_015207_20260618_010732_p10.png",
|
||
"20260618_015156_20260618_010700_Pasted image (4).png",
|
||
"20260618_015145_20260618_010627_jb3.png",
|
||
"20260618_015135_20260618_011424_20260618_004356_bb_01.png",
|
||
"20260618_015113_20260618_004952_jb.png",
|
||
"20260618_015030_20260618_004858_p12.png",
|
||
"20260618_015019_20260618_010638_b1.png",
|
||
"20260618_015008_20260618_011445_20260618_004450_20260617_074413_img_73.png",
|
||
"20260618_013512_Pasted image (9).png",
|
||
"20260618_013502_kk563t.png",
|
||
"20260618_011612_20260616_003803_img_14.png",
|
||
"20260618_011517_20260618_002014_20260616_020035_img_36.png",
|
||
"20260618_011456_20260618_004439_20260617_005512_img_60.png",
|
||
"20260618_011445_20260618_004450_20260617_074413_img_73.png",
|
||
"20260618_011434_20260618_004847_20260615_150812_img_19_2.png",
|
||
"20260618_011424_20260618_004356_bb_01.png",
|
||
"20260618_010732_p10.png",
|
||
"20260618_010710_p12-2.png",
|
||
"20260618_010700_Pasted image (4).png",
|
||
"20260618_010627_jb3.png",
|
||
"20260618_004952_jb.png",
|
||
"20260618_004941_out7.png",
|
||
"20260618_004930_20260617_134041_img_84.png",
|
||
"20260618_004919_kbk99v.png",
|
||
"20260618_004909_p1.png",
|
||
"20260618_004858_p12.png",
|
||
"20260618_004847_20260615_150812_img_19_2.png",
|
||
"20260618_004512_20260617_215245_img_87.png",
|
||
"20260618_004501_20260617_134041_img_84.png",
|
||
"20260618_004450_20260617_074413_img_73.png",
|
||
"20260618_004439_20260617_005512_img_60.png",
|
||
"20260618_004428_20260616_015949_img_37.png",
|
||
"20260618_004417_20260616_004250_img_18.png",
|
||
"20260618_004356_bb_01.png",
|
||
"20260618_004345_p1.png",
|
||
"20260618_004334_Pasted image (3).png",
|
||
"20260618_004324_jb.png",
|
||
"20260618_004313_20260616_021938_20160903_200935.jpg",
|
||
"20260618_004003_jb.png",
|
||
"20260618_003952_20260616_021938_20160903_200935.jpg",
|
||
"20260616_021938_20160903_200935.jpg",
|
||
"20260618_002606_20260616_021938_20160903_200935.jpg",
|
||
"20260618_002036_20260616_015949_img_37.png",
|
||
"20260618_002025_20260616_020020_img_35.png",
|
||
"20260618_002014_20260616_020035_img_36.png",
|
||
"20260617_215245_img_87.png",
|
||
"20260617_134615_img_86.png",
|
||
"20260617_134229_img_83.png",
|
||
"20260617_134041_img_84.png",
|
||
"20260617_133917_img_82.png",
|
||
"20260617_133832_img_81.png",
|
||
"20260617_133519_img_80.png",
|
||
"20260617_133411_img_79.png",
|
||
"20260617_133154_img_78.png",
|
||
"20260617_133129_img_77.png",
|
||
"20260617_074413_img_73.png",
|
||
"20260617_074223_img_72.png",
|
||
"20260617_005512_img_60.png",
|
||
"20260617_005200_img_59.png",
|
||
"20260617_004814_img_57.png",
|
||
"20260616_023306_img_52.png",
|
||
"20260616_023209_img_51.png",
|
||
"20260616_022543_img_50.png",
|
||
"20260616_022349_img_48.png",
|
||
"20260616_021003_img_41.png",
|
||
"20260616_020059_img_38.png",
|
||
"20260616_020035_img_36.png",
|
||
"20260616_020020_img_35.png",
|
||
"20260616_015949_img_37.png",
|
||
"20260616_015341_img_32.png",
|
||
"20260616_014757_img_31.png",
|
||
"20260616_004250_img_18.png",
|
||
"20260616_003916_img_15.png",
|
||
"20260616_003803_img_14.png",
|
||
"20260615_152252_imgxxx.png",
|
||
"20260615_151829_img_92.png",
|
||
"20260615_151614_img_93.png",
|
||
"20260615_150812_img_19_2.png",
|
||
"20260615_150340_test.png",
|
||
"20260615_145017_img_93.png",
|
||
"b1.png",
|
||
"jb3.png",
|
||
"jb.png",
|
||
"jb1.png",
|
||
"img.png",
|
||
"out7.png",
|
||
"out5.png",
|
||
"out4.png",
|
||
"out3.png",
|
||
"out2.png",
|
||
"out.png",
|
||
"bb_01.png",
|
||
"tp236b.png",
|
||
"pass-1.png",
|
||
"t159zr-1.png",
|
||
"kbk99v.png",
|
||
"kk563t.png",
|
||
"20260626_003137_dup_20260626_003125_dup_Pasted image (9).png",
|
||
"Pasted image (9).png",
|
||
"Pasted image (4).png",
|
||
"Pasted image (3).png",
|
||
"Pasted image.png",
|
||
"pa01.png",
|
||
"pa0.png",
|
||
"p13.png",
|
||
"p12-2.png",
|
||
"p12.png",
|
||
"p10.png",
|
||
"p1.png",
|
||
"img_2.png",
|
||
"img_3.png"
|
||
];
|
||
// --- HYDRATION_END ---
|
||
|
||
// Supported extensions
|
||
const EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg', '.mp4', '.mov', '.webm'];
|
||
const VIDEO_EXTENSIONS = new Set(['.mp4', '.mov', '.webm', '.avi', '.mkv']);
|
||
function isVideo(name) { return VIDEO_EXTENSIONS.has((name.split('.').pop() ? '.'+name.split('.').pop().toLowerCase() : '')); }
|
||
// Videos can't render as an <img> thumbnail over file://, so the backend
|
||
// writes a sibling "<stem>.jpg" poster. Map a video URL → its poster URL
|
||
// (preserving the ?t= cache-buster). Non-video URLs pass through unchanged.
|
||
function posterFor(url) {
|
||
if (!url) return url;
|
||
const q = url.indexOf('?');
|
||
const path = q === -1 ? url : url.slice(0, q);
|
||
const qs = q === -1 ? '' : url.slice(q);
|
||
if (!/\.(mp4|mov|webm|avi|mkv)$/i.test(path)) return url;
|
||
return path.replace(/\.[^.]+$/, '.jpg') + qs;
|
||
}
|
||
|
||
// Auto-refresh interval in seconds (default: 120 = 2 minutes)
|
||
let REFRESH_INTERVAL = 120;
|
||
|
||
// ============================================
|
||
|
||
let autoRefreshTimer = null;
|
||
let knownFiles = new Set();
|
||
let currentFiles = [];
|
||
let currentGroups = [];
|
||
const groupData = new Map(); // gid → { urls[], names[] }
|
||
const fileGroups = {}; // filename → gid (for jumpToImage)
|
||
const cycleTimers = new Map(); // gid → intervalId
|
||
const cycleIdx = new Map(); // gid → current index
|
||
let _lastSelectedGid = localStorage.getItem('lastSelectedGid') || null;
|
||
let _prevGid = null; // for "Back" button in studio
|
||
let imageNames = {}; // filename → human name (from /names)
|
||
let clipDescriptions = {}; // filename → description
|
||
let customGroups = {}; // filename → groupId (from /groups)
|
||
let groupNames = {}; // group_id → group display name
|
||
let fileSortOrders = {}; // filename → sort_order (integer or null)
|
||
let filePoses = {}; // filename → pose name
|
||
let filePrompts = {}; // filename → generation prompt
|
||
let fileHidden = {}; // filename → boolean (hidden from cycling)
|
||
let fileArchived = {}; // filename → boolean
|
||
let _archiveViewActive = false; // when true, show archived items only
|
||
let fileHasBg = {}; // filename → boolean (has background, default true)
|
||
let fileSourceRefs = {}; // filename → array of source filenames
|
||
let fileHasClothing = {}; // filename → boolean|null (null = unknown)
|
||
let fileContentType = {}; // filename → 'image'|'video'
|
||
let fileFaceswapSrcVideo = {}; // filename → source video name
|
||
let privacyMode = localStorage.getItem('privacyMode') === 'true';
|
||
|
||
// ---- faceswap state ----
|
||
let _fsModelFilename = null; // model image selected for faceswap
|
||
let _fsSelectedVideo = null; // chosen template video name
|
||
let _fsJobId = null;
|
||
let _fsJobPollTimer = null;
|
||
let availableVideos = []; // populated from /videos
|
||
let _fsActiveTab = 'swap';
|
||
let _fsSelectedPoses = new Set();
|
||
let _sbPoseFilter = ''; // live pose-name filter query (Generate tab)
|
||
let _sbSelectedAngles = new Set(); // selected camera angle names
|
||
let _sbRefIndices = []; // ordered filmstrip indices selected as multi-ref (#1, #2, #3)
|
||
let _sbRefMode = 'combine'; // with 2–3 refs: 'combine' → 1 multi-ref output; 'each' → one edit per ref (cross product)
|
||
let _sbWireframeRef = ''; // wireframe video name for pose guide
|
||
let _sbWireframeTime = 0.5; // normalized frame time 0–1
|
||
let _sbPoseEdits = {}; // poseName → edited text
|
||
let _sbPadTop = 0; // canvas padding (px) applied before generation
|
||
let _sbPadRight = 0;
|
||
let _sbPadBottom = 0;
|
||
let _sbPadLeft = 0;
|
||
let _sbPadOutpaint = false;
|
||
let _sbPadUniform = false;
|
||
|
||
const CAMERA_ANGLES = [
|
||
// Absolute camera positions
|
||
{ name: 'Front', icon: '⊙', prompt: 'Head-on straight-on full-body portrait, frontal camera, realistic, transparent background', relative: false },
|
||
{ name: '¾ Left', icon: '↙', prompt: 'Three-quarter left, camera 45° to the left, full-body portrait, realistic, transparent background', relative: false },
|
||
{ name: '¾ Right', icon: '↗', prompt: 'Three-quarter right, camera 45° to the right, full-body portrait, realistic, transparent background', relative: false },
|
||
{ name: 'Side L', icon: '◁', prompt: 'Side profile, camera 90° to the left, full-body portrait, realistic, transparent background', relative: false },
|
||
{ name: 'Side R', icon: '▷', prompt: 'Side profile, camera 90° to the right, full-body portrait, realistic, transparent background', relative: false },
|
||
{ name: 'Back', icon: '⊕', prompt: 'Rear view, camera directly behind subject, full-body portrait, realistic, transparent background', relative: false },
|
||
{ name: 'High', icon: '△', prompt: "Bird's-eye view, camera above looking down, full-body, realistic, transparent background", relative: false },
|
||
{ name: 'Low', icon: '▽', prompt: "Worm's-eye view, camera below looking up, full-body portrait, realistic, transparent background", relative: false },
|
||
// Relative rotations — applied on top of current view
|
||
{ name: '↺ 45°', icon: '↺', prompt: 'Rotate 45° to the left from current view, full-body portrait, realistic, transparent background', relative: true },
|
||
{ name: '↻ 45°', icon: '↻', prompt: 'Rotate 45° to the right from current view, full-body portrait, realistic, transparent background', relative: true },
|
||
{ name: '↺ 90°', icon: '⟲', prompt: 'Rotate 90° to the left from current view, side profile, full-body portrait, realistic, transparent background', relative: true },
|
||
{ name: '↻ 90°', icon: '⟳', prompt: 'Rotate 90° to the right from current view, side profile, full-body portrait, realistic, transparent background', relative: true },
|
||
];
|
||
let _sbGenJobId = null;
|
||
let _sbGenJobPollTimer= null;
|
||
let _followLatestGid = null; // set to gid after generation to auto-jump to newest image
|
||
let _followLatestFilename = null;
|
||
let _slideshowActive = false;
|
||
let _slideshowTimer = null;
|
||
let _slideshowInterval= 4000;
|
||
let _fsTrimVideo = null;
|
||
|
||
function escHtml(s) {
|
||
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||
}
|
||
|
||
function getBaseName(name) {
|
||
return name.replace(/^(\d{8}_\d{6}_)+/, '');
|
||
}
|
||
|
||
function groupFiles(fileObjects) {
|
||
const map = new Map(); // gid → files[]
|
||
for (const f of fileObjects) {
|
||
const custom = customGroups[f.cleanName];
|
||
let gid;
|
||
if (custom) {
|
||
gid = custom.startsWith('solo:') ? f.cleanName : custom;
|
||
} else {
|
||
gid = getBaseName(f.cleanName);
|
||
}
|
||
if (!map.has(gid)) map.set(gid, []);
|
||
map.get(gid).push(f);
|
||
fileGroups[f.cleanName] = gid;
|
||
}
|
||
// Sort each group: by explicit sort_order first, then by filename length
|
||
for (const files of map.values()) {
|
||
files.sort((a, b) => {
|
||
const oa = fileSortOrders[a.cleanName] ?? 9999;
|
||
const ob = fileSortOrders[b.cleanName] ?? 9999;
|
||
if (oa !== ob) return oa - ob;
|
||
return a.cleanName.length - b.cleanName.length;
|
||
});
|
||
}
|
||
return [...map.entries()].map(([gid, files]) => ({ gid, files }));
|
||
}
|
||
|
||
function groupDisplayName(group) {
|
||
// 1. Explicit group_name set via UI
|
||
if (groupNames[group.gid]) return groupNames[group.gid];
|
||
// 2. Human name tagged for any file in the group
|
||
for (const f of group.files) {
|
||
if (imageNames[f.cleanName]) return imageNames[f.cleanName];
|
||
}
|
||
return group.gid;
|
||
}
|
||
|
||
function clearCycleTimers() {
|
||
cycleTimers.forEach(id => clearInterval(id));
|
||
cycleTimers.clear();
|
||
cycleIdx.clear();
|
||
}
|
||
|
||
function startGroupCycle(base) {
|
||
cycleIdx.set(base, 0); // index into visibleIdx array
|
||
const id = setInterval(() => {
|
||
// Don't update gallery cards while studio is open — saves GPU and avoids distraction
|
||
if (document.getElementById('studio').classList.contains('open')) return;
|
||
const data = groupData.get(base);
|
||
if (!data || !data.visibleIdx || data.visibleIdx.length < 2) { clearInterval(id); cycleTimers.delete(base); return; }
|
||
const card = [...document.querySelectorAll('.image-card')].find(c => c.dataset.group === base);
|
||
if (!card) { clearInterval(id); cycleTimers.delete(base); return; }
|
||
const nextSlot = (cycleIdx.get(base) + 1) % data.visibleIdx.length;
|
||
cycleIdx.set(base, nextSlot);
|
||
const realIdx = data.visibleIdx[nextSlot];
|
||
const img = card.querySelector('img');
|
||
if (img) img.src = data.urls[realIdx];
|
||
card.querySelectorAll('.dot').forEach((d, i) => d.classList.toggle('active', i === nextSlot));
|
||
}, 1800);
|
||
cycleTimers.set(base, id);
|
||
}
|
||
|
||
// --- studio view (replaces lightbox) ---
|
||
let lbUrls = [], lbNames = [], lbIdx = 0;
|
||
let _activeSidebarTab = localStorage.getItem('studioSidebarTab') || 'info';
|
||
// Maps fname → nobg sidecar URL; viewer shows sidecar while this entry exists
|
||
const _nobgSidecars = new Map();
|
||
|
||
function openLightbox(gid, startIdx) { // kept for compat
|
||
openStudio(gid, startIdx);
|
||
}
|
||
function closeLightbox() { closeStudio(); }
|
||
|
||
function openStudio(gid, startIdx) {
|
||
const data = groupData.get(gid);
|
||
if (!data) return;
|
||
if (lbCurrentGid !== gid) { // clear refs when switching groups
|
||
_prevGid = lbCurrentGid;
|
||
_sbRefIndices = [];
|
||
_sceneRefs = [null, null, null];
|
||
_scenePersonInit = false;
|
||
_sceneFrameBytes = null;
|
||
stopOrbitFast();
|
||
}
|
||
lbCurrentGid = gid;
|
||
_lastSelectedGid = gid;
|
||
localStorage.setItem('lastSelectedGid', gid);
|
||
lbUrls = data.urls;
|
||
lbNames = data.names;
|
||
lbIdx = startIdx !== undefined ? startIdx : (cycleIdx.get(gid) || 0);
|
||
updateStudio();
|
||
document.getElementById('studio').classList.add('open');
|
||
// Hide main page scrollbar — nothing in studio scrolls the page
|
||
document.body.style.overflow = 'hidden';
|
||
// Restore sidebar state
|
||
const sidebar = document.getElementById('studioSidebar');
|
||
const btn = document.getElementById('sidebarInnerToggle');
|
||
const wasCollapsed = localStorage.getItem('studioSidebarCollapsed') === '1';
|
||
sidebar.classList.toggle('collapsed', wasCollapsed);
|
||
if (btn) btn.textContent = wasCollapsed ? '⊞' : '⊟';
|
||
// Render current tab
|
||
switchSidebarTab(_activeSidebarTab, true);
|
||
}
|
||
|
||
/** Jump to a specific filename, switching groups if needed */
|
||
function jumpToImage(filename) {
|
||
const gid = fileGroups[filename];
|
||
if (!gid) { showToast('Image not found', 'error'); return; }
|
||
const data = groupData.get(gid);
|
||
if (!data) {
|
||
// If the group isn't in groupData (stale or archived), try to find it
|
||
// but usually groupData is kept in sync.
|
||
showToast('Group not loaded', 'error');
|
||
return;
|
||
}
|
||
const idx = data.names.indexOf(filename);
|
||
if (idx === -1) { showToast('Image not found in group', 'error'); return; }
|
||
openStudio(gid, idx);
|
||
}
|
||
|
||
function jumpToPrevGroup() {
|
||
if (_prevGid) openStudio(_prevGid);
|
||
}
|
||
|
||
function formatPadValue(v) {
|
||
if (v === undefined || v === null || v === '') return '0.00%';
|
||
const s = v.toString().trim().toLowerCase();
|
||
let val = parseFloat(s);
|
||
if (isNaN(val)) return '0.00%';
|
||
|
||
// If the user explicitly typed px
|
||
if (s.endsWith('px')) {
|
||
return val.toString(); // integer pixels
|
||
}
|
||
// If the user explicitly typed %
|
||
if (s.endsWith('%')) {
|
||
return val.toFixed(2) + '%';
|
||
}
|
||
// If contains a decimal, or if it's <= 10 (or < 1.0), it's percentage
|
||
if (s.includes('.') || val <= 10) {
|
||
// If it's a fraction like 0.1, convert to 10%
|
||
if (val > 0 && val < 1.0) {
|
||
return (val * 100).toFixed(2) + '%';
|
||
}
|
||
return val.toFixed(2) + '%';
|
||
}
|
||
// Otherwise, it's integer > 10, treat as pixels
|
||
return val.toString();
|
||
}
|
||
|
||
function syncPadFields(el) {
|
||
const id = el.id; // padLeft, padTop, etc. or sbPadLeft, sbPadTop
|
||
const val = el.value;
|
||
const prefix = id.startsWith('sbPad') ? 'sbPad' : 'pad';
|
||
const isSb = id.startsWith('sbPad');
|
||
|
||
// Check if Uniform mode is active for this context
|
||
const uniformEl = document.getElementById(isSb ? 'sbPadUniform' : 'padUniform');
|
||
const isUniform = uniformEl ? uniformEl.checked : false;
|
||
|
||
if (isUniform) {
|
||
// Set all four sides to the same value
|
||
['Top', 'Right', 'Bottom', 'Left'].forEach(side => {
|
||
const targetId = prefix + side;
|
||
const targetEl = document.getElementById(targetId);
|
||
if (targetEl && targetEl !== el) {
|
||
targetEl.value = val;
|
||
}
|
||
});
|
||
} else {
|
||
// Standard opposite-side sync if other side is 0
|
||
let pairId = null;
|
||
if (id.endsWith('Left')) pairId = id.replace('Left', 'Right');
|
||
else if (id.endsWith('Right')) pairId = id.replace('Right', 'Left');
|
||
else if (id.endsWith('Top')) pairId = id.replace('Top', 'Bottom');
|
||
else if (id.endsWith('Bottom')) pairId = id.replace('Bottom', 'Top');
|
||
|
||
if (pairId) {
|
||
const pairEl = document.getElementById(pairId);
|
||
if (pairEl && (pairEl.value === '0.00' || pairEl.value === '0' || pairEl.value === '0.00%' || pairEl.value === '')) {
|
||
pairEl.value = val;
|
||
}
|
||
}
|
||
}
|
||
if (isSb) {
|
||
updateSbGenBtn();
|
||
}
|
||
updatePadPreview();
|
||
}
|
||
|
||
function togglePadUniform(cb) {
|
||
const isSb = cb.id.startsWith('sbPad');
|
||
const prefix = isSb ? 'sbPad' : 'pad';
|
||
if (isSb) _sbPadUniform = cb.checked;
|
||
if (cb.checked) {
|
||
// Find a non-zero value, or default to Top, and propagate
|
||
const sides = ['Top', 'Right', 'Bottom', 'Left'];
|
||
let val = '0.00%';
|
||
for (let side of sides) {
|
||
const el = document.getElementById(prefix + side);
|
||
if (el && el.value && el.value !== '0' && el.value !== '0.00' && el.value !== '0.00%') {
|
||
val = el.value;
|
||
break;
|
||
}
|
||
}
|
||
sides.forEach(side => {
|
||
const el = document.getElementById(prefix + side);
|
||
if (el) el.value = val;
|
||
});
|
||
updatePadPreview();
|
||
}
|
||
}
|
||
|
||
function applyPadPreset(presetVal, isSb) {
|
||
const prefix = isSb ? 'sbPad' : 'pad';
|
||
const sides = ['Top', 'Right', 'Bottom', 'Left'];
|
||
|
||
if (presetVal === '0') {
|
||
// Reset all
|
||
sides.forEach(side => {
|
||
const el = document.getElementById(prefix + side);
|
||
if (el) el.value = '0.00%';
|
||
});
|
||
const uniformEl = document.getElementById(isSb ? 'sbPadUniform' : 'padUniform');
|
||
if (uniformEl) uniformEl.checked = false;
|
||
if (isSb) _sbPadUniform = false;
|
||
} else {
|
||
// If it ends with %
|
||
let formatted = presetVal;
|
||
if (presetVal.endsWith('%')) {
|
||
formatted = parseFloat(presetVal).toFixed(2) + '%';
|
||
} else if (presetVal.endsWith('px')) {
|
||
formatted = parseFloat(presetVal).toString();
|
||
}
|
||
|
||
sides.forEach(side => {
|
||
const el = document.getElementById(prefix + side);
|
||
if (el) el.value = formatted;
|
||
});
|
||
|
||
const uniformEl = document.getElementById(isSb ? 'sbPadUniform' : 'padUniform');
|
||
if (uniformEl) uniformEl.checked = true;
|
||
if (isSb) _sbPadUniform = true;
|
||
}
|
||
if (isSb) {
|
||
updateSbGenBtn();
|
||
}
|
||
updatePadPreview();
|
||
}
|
||
|
||
function getImageDisplayRect(img) {
|
||
if (!img || !img.naturalWidth) return null;
|
||
|
||
// If the image is zoomed, getBoundingClientRect() returns the SCALED dimensions.
|
||
// We need to account for this to find the actual visible image area.
|
||
const isZoomed = img.classList.contains('zoomed');
|
||
const scale = isZoomed ? 2.2 : 1.0;
|
||
|
||
const rect = img.getBoundingClientRect();
|
||
const container = img.parentElement;
|
||
const crect = container.getBoundingClientRect();
|
||
|
||
// The natural aspect ratio
|
||
const iw = img.naturalWidth;
|
||
const ih = img.naturalHeight;
|
||
const aspect = iw / ih;
|
||
|
||
// The container dimensions for the image (before scaling)
|
||
const cw = rect.width / scale;
|
||
const ch = rect.height / scale;
|
||
const caspect = cw / ch;
|
||
|
||
let dw, dh, dl, dt;
|
||
if (aspect > caspect) {
|
||
dw = cw;
|
||
dh = cw / aspect;
|
||
dl = 0;
|
||
dt = (ch - dh) / 2;
|
||
} else {
|
||
dh = ch;
|
||
dw = ch * aspect;
|
||
dt = 0;
|
||
dl = (cw - dw) / 2;
|
||
}
|
||
|
||
// Adjust for scale: the visual center stays the same,
|
||
// but the width/height and offsets from the top-left change.
|
||
if (isZoomed) {
|
||
const centerRelX = cw / 2;
|
||
const centerRelY = ch / 2;
|
||
|
||
// dw, dh are unscaled visual dimensions of the content.
|
||
// dl, dt are unscaled offsets within the img element's box.
|
||
|
||
// We want the scaled versions.
|
||
dw *= scale;
|
||
dh *= scale;
|
||
|
||
// The top-left of the content relative to the center of the img element:
|
||
const contentRelCenterX = dl - centerRelX;
|
||
const contentRelCenterY = dt - centerRelY;
|
||
|
||
// The top-left of the content relative to the center of the img element (scaled):
|
||
const contentRelCenterXScaled = contentRelCenterX * scale;
|
||
const contentRelCenterYScaled = contentRelCenterY * scale;
|
||
|
||
// The new offsets relative to the top-left of the scaled img element:
|
||
// Actually, rect.left/top are already for the scaled element's bounding box.
|
||
// We just need to find where the content starts within that bounding box.
|
||
const finalDl = contentRelCenterXScaled + (rect.width / 2);
|
||
const finalDt = contentRelCenterYScaled + (rect.height / 2);
|
||
|
||
return {
|
||
left: rect.left - crect.left + finalDl,
|
||
top: rect.top - crect.top + finalDt,
|
||
width: dw,
|
||
height: dh
|
||
};
|
||
}
|
||
|
||
const finalLeft = rect.left - crect.left + dl;
|
||
const finalTop = rect.top - crect.top + dt;
|
||
|
||
return { left: finalLeft, top: finalTop, width: dw, height: dh };
|
||
}
|
||
|
||
function updatePadPreview() {
|
||
const viewer = document.getElementById('studioViewer');
|
||
if (viewer) {
|
||
// Clean up existing preview overlays
|
||
viewer.querySelectorAll('.pad-preview-overlay').forEach(el => el.remove());
|
||
}
|
||
const img = document.getElementById('lbImg');
|
||
if (!viewer || !img || img.style.display === 'none') return;
|
||
|
||
const rect = getImageDisplayRect(img);
|
||
if (!rect) return;
|
||
|
||
const isManual = !!document.getElementById('padBar');
|
||
const getV = (side) => {
|
||
const el = document.getElementById(isManual ? 'pad' + side : 'sbPad' + side);
|
||
return el ? el.value : '0';
|
||
};
|
||
|
||
const fill = document.getElementById('padFill')?.value || 'transparent';
|
||
|
||
const sides = ['Left', 'Right', 'Top', 'Bottom'];
|
||
sides.forEach(side => {
|
||
const vStr = getV(side);
|
||
if (!vStr || vStr === '0' || vStr === '0.00' || vStr === '0.00%') return;
|
||
|
||
const val = parseFloat(vStr);
|
||
if (isNaN(val) || val <= 0) return;
|
||
|
||
const isPercent = vStr.includes('%');
|
||
const overlay = document.createElement('div');
|
||
overlay.className = 'pad-preview-overlay';
|
||
|
||
// Style based on selected fill mode to make preview professional & intuitive
|
||
if (isManual) {
|
||
if (fill === 'black') {
|
||
overlay.style.background = 'rgba(0, 0, 0, 0.7)';
|
||
overlay.style.borderColor = '#444';
|
||
} else if (fill === 'white') {
|
||
overlay.style.background = 'rgba(255, 255, 255, 0.7)';
|
||
overlay.style.borderColor = '#ccc';
|
||
} else {
|
||
overlay.style.background = 'rgba(245, 158, 11, 0.4)';
|
||
overlay.style.borderColor = '#f59e0b';
|
||
}
|
||
} else {
|
||
// Sidebar is always transparent background before outpaint
|
||
overlay.style.background = 'rgba(245, 158, 11, 0.4)';
|
||
overlay.style.borderColor = '#f59e0b';
|
||
}
|
||
|
||
if (side === 'Left' || side === 'Right') {
|
||
const w = isPercent ? (rect.width * val / 100) : val;
|
||
overlay.style.width = w + 'px';
|
||
overlay.style.top = rect.top + 'px';
|
||
overlay.style.height = rect.height + 'px';
|
||
if (side === 'Left') overlay.style.left = (rect.left - w) + 'px';
|
||
else overlay.style.left = (rect.left + rect.width) + 'px';
|
||
} else {
|
||
const h = isPercent ? (rect.height * val / 100) : val;
|
||
overlay.style.height = h + 'px';
|
||
overlay.style.left = rect.left + 'px';
|
||
overlay.style.width = rect.width + 'px';
|
||
if (side === 'Top') overlay.style.top = (rect.top - h) + 'px';
|
||
else overlay.style.top = (rect.top + rect.height) + 'px';
|
||
}
|
||
viewer.appendChild(overlay);
|
||
});
|
||
}
|
||
|
||
function closeStudio() {
|
||
document.getElementById('studio').classList.remove('open');
|
||
document.body.style.overflow = '';
|
||
stopSlideshow();
|
||
stopOrbitFast();
|
||
// Release every streaming/decoding video so closing the studio frees memory
|
||
// and stops background network streams (template clips + scenery preview).
|
||
_tplStopAll();
|
||
['sceneVideoEl', 'lbVideo', 'cltVideoPreview'].forEach(id => {
|
||
const v = document.getElementById(id);
|
||
if (v) { v.pause(); v.removeAttribute('src'); v.load(); }
|
||
});
|
||
}
|
||
|
||
function toggleSlideshow() {
|
||
if (_slideshowActive) stopSlideshow();
|
||
else startSlideshow();
|
||
}
|
||
|
||
function startSlideshow() {
|
||
if (_slideshowTimer) clearInterval(_slideshowTimer);
|
||
_slideshowActive = true;
|
||
const btn = document.getElementById('slideshowBtn');
|
||
if (btn) { btn.textContent = '⏸ Pause'; btn.style.color = '#60a5fa'; }
|
||
_slideshowTimer = setInterval(() => {
|
||
if (lbUrls.length > 1) { lbIdx = (lbIdx + 1) % lbUrls.length; updateStudio(); }
|
||
}, _slideshowInterval);
|
||
}
|
||
|
||
function stopSlideshow() {
|
||
_slideshowActive = false;
|
||
clearInterval(_slideshowTimer);
|
||
_slideshowTimer = null;
|
||
const btn = document.getElementById('slideshowBtn');
|
||
if (btn) { btn.textContent = '▶ Slideshow'; btn.style.color = ''; }
|
||
}
|
||
|
||
function updateSlideshowInterval() {
|
||
const sel = document.getElementById('slideshowInterval');
|
||
if (sel) _slideshowInterval = parseInt(sel.value, 10);
|
||
if (_slideshowActive) startSlideshow(); // restart with new interval
|
||
}
|
||
|
||
let _orbitFastActive = false;
|
||
let _orbitFastTimer = null;
|
||
function toggleOrbitFast() {
|
||
if (_orbitFastActive) stopOrbitFast();
|
||
else startOrbitFast();
|
||
}
|
||
function startOrbitFast() {
|
||
if (_slideshowActive) stopSlideshow();
|
||
_orbitFastActive = true;
|
||
const btn = document.getElementById('lbOrbitFastBtn');
|
||
if (btn) { btn.textContent = '⏸ Orbit'; btn.style.color = '#f59e0b'; }
|
||
if (_orbitFastTimer) clearInterval(_orbitFastTimer);
|
||
_orbitFastTimer = setInterval(() => {
|
||
if (lbUrls.length > 1) {
|
||
lbIdx = (lbIdx + 1) % lbUrls.length;
|
||
updateStudio();
|
||
}
|
||
}, 80); // ~12fps
|
||
}
|
||
function stopOrbitFast() {
|
||
_orbitFastActive = false;
|
||
if (_orbitFastTimer) clearInterval(_orbitFastTimer);
|
||
_orbitFastTimer = null;
|
||
const btn = document.getElementById('lbOrbitFastBtn');
|
||
if (btn) { btn.textContent = '▶ Orbit'; btn.style.color = ''; }
|
||
}
|
||
|
||
function lbNav(dir) {
|
||
lbIdx = (lbIdx + dir + lbUrls.length) % lbUrls.length;
|
||
updateStudio();
|
||
}
|
||
|
||
function lbNavTo(idx) {
|
||
lbIdx = idx;
|
||
updateStudio();
|
||
}
|
||
|
||
// Reference badge number for filmstrip thumb i (0 = none).
|
||
// Generate tab → position in _sbRefIndices; Scenery tab → slot index in _sceneRefs.
|
||
function _filmstripRefBadge(i) {
|
||
if (_activeSidebarTab === 'scenery') {
|
||
const name = lbNames[i];
|
||
const slot = _sceneRefs.findIndex(r => r && r.kind === 'file' && r.name === name);
|
||
return slot >= 0 ? slot + 1 : 0;
|
||
}
|
||
const pos = _sbRefIndices.indexOf(i);
|
||
return pos >= 0 ? pos + 1 : 0;
|
||
}
|
||
|
||
function sbFilmstripClick(event, i) {
|
||
if (event.shiftKey) {
|
||
event.preventDefault();
|
||
// Scenery tab uses positional slots (fills right→left); Generate uses _sbRefIndices.
|
||
if (_activeSidebarTab === 'scenery') {
|
||
sceneAssignRef(lbNames[i]); // re-renders panel + filmstrip
|
||
return;
|
||
}
|
||
const pos = _sbRefIndices.indexOf(i);
|
||
if (pos >= 0) {
|
||
_sbRefIndices.splice(pos, 1);
|
||
} else if (_sbRefIndices.length < 3) {
|
||
_sbRefIndices.push(i);
|
||
} else {
|
||
showToast('Max 3 references — Shift+Click a selected one to remove it', 'info');
|
||
}
|
||
updateStudio();
|
||
if (_activeSidebarTab === 'generate') renderSidebarGenerate();
|
||
} else {
|
||
lbNavTo(i);
|
||
}
|
||
}
|
||
|
||
function updateStudio() {
|
||
const fname = lbNames[lbIdx];
|
||
if (document.getElementById('poseCanvas')) _removePoseOverlay(); // skeleton is per-image
|
||
document.getElementById('poseResults')?.remove();
|
||
|
||
const lbImgEl = document.getElementById('lbImg');
|
||
const lbVideoEl = document.getElementById('lbVideo');
|
||
|
||
lbImgEl.onload = () => {
|
||
const dimEl = document.getElementById('lbImgDims');
|
||
if (dimEl) dimEl.textContent = `${lbImgEl.naturalWidth} x ${lbImgEl.naturalHeight}`;
|
||
|
||
// Force layout recalculation and overlay sync when a new image loads
|
||
requestAnimationFrame(() => {
|
||
updatePadPreview();
|
||
const cb = document.getElementById('sbCheckerboard');
|
||
if (cb && cb.checked) toggleCheckerboard(true);
|
||
});
|
||
};
|
||
|
||
_fsModelFilename = fname; // keep faceswap/scenery/segment in sync
|
||
const isVid = isVideo(fname) || fileContentType[fname] === 'video';
|
||
|
||
// Show image dimensions
|
||
const dimEl = document.getElementById('lbImgDims');
|
||
if (dimEl) {
|
||
if (!isVid && lbImgEl.naturalWidth) {
|
||
dimEl.textContent = `${lbImgEl.naturalWidth} x ${lbImgEl.naturalHeight}`;
|
||
} else {
|
||
dimEl.textContent = '';
|
||
}
|
||
}
|
||
|
||
if (isVid) {
|
||
lbImgEl.style.display = 'none';
|
||
lbVideoEl.style.display = '';
|
||
lbVideoEl.poster = posterFor(lbUrls[lbIdx]);
|
||
lbVideoEl.src = lbUrls[lbIdx];
|
||
|
||
// Explicitly clean up pad previews and hide checkerboard when navigating to a video
|
||
updatePadPreview();
|
||
toggleCheckerboard(false);
|
||
} else {
|
||
lbVideoEl.style.display = 'none';
|
||
lbVideoEl.src = '';
|
||
lbImgEl.style.display = '';
|
||
lbImgEl.classList.remove('zoomed');
|
||
lbImgEl.src = lbUrls[lbIdx];
|
||
// If it's already in cache, onload might not fire or already fired
|
||
if (lbImgEl.complete && lbImgEl.naturalWidth) {
|
||
lbImgEl.onload();
|
||
}
|
||
}
|
||
|
||
const backBtn = document.getElementById('lbBackBtn');
|
||
if (backBtn) backBtn.style.display = _prevGid ? '' : 'none';
|
||
|
||
const groupNameInput = document.getElementById('lbGroupNameInput');
|
||
groupNameInput.value = groupNames[lbCurrentGid] || '';
|
||
|
||
const nameInput = document.getElementById('lbNameInput');
|
||
if (nameInput) { nameInput.value = imageNames[fname] || ''; nameInput.placeholder = fname; }
|
||
|
||
const clipEl = document.getElementById('lbClipDesc');
|
||
if (clipEl) clipEl.textContent = clipDescriptions[fname] || '';
|
||
|
||
const genPromptWrap = document.getElementById('lbGenPromptWrap');
|
||
const genPromptEl = document.getElementById('lbGenPrompt');
|
||
const genPrompt = filePrompts[fname] || '';
|
||
if (genPromptWrap) genPromptWrap.style.display = genPrompt ? '' : 'none';
|
||
if (genPromptEl) genPromptEl.textContent = genPrompt;
|
||
|
||
const pose = filePoses[fname];
|
||
const poseTag = document.getElementById('lbPoseTag');
|
||
if (pose) { poseTag.textContent = '⛷ ' + pose; poseTag.style.display = ''; }
|
||
else { poseTag.style.display = 'none'; }
|
||
|
||
const hidden = !!fileHidden[fname];
|
||
const studio = document.getElementById('studio');
|
||
studio.classList.toggle('st-hidden', hidden);
|
||
const eyeBtn = document.getElementById('lbEyeBtn');
|
||
if (eyeBtn) { eyeBtn.textContent = hidden ? '🚫 Hidden' : '👁 Visibility'; eyeBtn.title = hidden ? 'Show in cycling' : 'Hide from cycling'; }
|
||
|
||
const noBgBtn = document.getElementById('lbNoBgBtn');
|
||
const undressBtn = document.getElementById('lbUndressBtn');
|
||
const faceswapBtn = document.getElementById('lbFaceswapBtn');
|
||
if (noBgBtn) noBgBtn.style.display = (!isVid && fileHasBg[fname] !== false) ? '' : 'none';
|
||
if (undressBtn) undressBtn.style.display = (!isVid && fileHasClothing[fname] === true) ? '' : 'none';
|
||
if (faceswapBtn)faceswapBtn.style.display = !isVid ? '' : 'none';
|
||
const cropBtn = document.getElementById('lbCropBtn');
|
||
if (cropBtn) cropBtn.style.display = (!isVid && fileHasBg[fname] === false) ? '' : 'none';
|
||
// Invert α is only meaningful on a transparent image (after background removal).
|
||
const invAlphaBtn = document.getElementById('lbInvertAlphaBtn');
|
||
if (invAlphaBtn) invAlphaBtn.style.display = (!isVid && fileHasBg[fname] === false) ? '' : 'none';
|
||
const poseBtn = document.getElementById('lbPoseBtn');
|
||
if (poseBtn) poseBtn.style.display = isVid ? 'none' : '';
|
||
const archiveBtn = document.getElementById('lbArchiveBtn');
|
||
if (archiveBtn) archiveBtn.textContent = fileArchived[fname] ? 'Restore' : 'Archive';
|
||
|
||
// Source refs
|
||
const refs = fileSourceRefs[fname];
|
||
const refsDiv = document.getElementById('lbSourceRefs');
|
||
if (refsDiv) {
|
||
if (refs && refs.length > 0) {
|
||
refsDiv.style.display = 'flex';
|
||
refsDiv.innerHTML = refs.map(r =>
|
||
`<img src="${IMAGE_FOLDER + r}" loading="lazy" title="${r}"
|
||
onclick="jumpToImage('${r.replace(/'/g,"\\'")}');">`
|
||
).join('');
|
||
} else {
|
||
refsDiv.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
// Download link
|
||
const dlBtn = document.getElementById('lbDownloadBtn');
|
||
if (dlBtn) { dlBtn.href = lbUrls[lbIdx]; dlBtn.setAttribute('download', fname); }
|
||
|
||
const multi = lbUrls.length > 1;
|
||
document.getElementById('lbCounter').textContent = multi ? `${lbIdx + 1} / ${lbUrls.length}` : '';
|
||
const prevBtn = document.getElementById('lbPrev');
|
||
const nextBtn = document.getElementById('lbNext');
|
||
if (prevBtn) prevBtn.style.visibility = multi ? '' : 'hidden';
|
||
if (nextBtn) nextBtn.style.visibility = multi ? '' : 'hidden';
|
||
const extractBtn = document.getElementById('lbExtract');
|
||
if (extractBtn) extractBtn.style.display = multi ? '' : 'none';
|
||
const moveUp = document.getElementById('lbMoveUp');
|
||
const moveDown = document.getElementById('lbMoveDown');
|
||
if (moveUp) moveUp.style.display = multi ? '' : 'none';
|
||
if (moveDown) moveDown.style.display = multi ? '' : 'none';
|
||
const prefBtn = document.getElementById('lbPreferredBtn');
|
||
if (prefBtn) prefBtn.style.display = multi && lbIdx > 0 ? '' : 'none';
|
||
|
||
// Film strip — always visible (override old display:none CSS)
|
||
const strip = document.getElementById('lbVariantStrip');
|
||
strip.style.display = 'flex';
|
||
const sceneryActive = _activeSidebarTab === 'scenery';
|
||
strip.innerHTML = lbUrls.map((url, i) => {
|
||
const n = lbNames[i];
|
||
const pose = filePoses[n] || '';
|
||
const act = i === lbIdx ? ' active' : '';
|
||
// Badge: Generate uses _sbRefIndices order; Scenery uses positional slots.
|
||
const badge = _filmstripRefBadge(i);
|
||
const refCls = badge > 0 ? ' ref-selected' : '';
|
||
const thumbSrc = isVideo(n) ? posterFor(url) : url;
|
||
// On the Scenery tab the thumbs are draggable onto the slots above.
|
||
const dragAttr = sceneryActive ? ' draggable="true" ondragstart="sceneFilmDragStart(event,' + i + ')"' : '';
|
||
return '<div class="lb-var-thumb' + act + refCls + '" onclick="sbFilmstripClick(event,' + i + ')"' + dragAttr + ' title="Click to view · Shift+Click to add as ref">'
|
||
+ '<img src="' + thumbSrc + '" loading="lazy" onerror="this.style.opacity=\'0.3\'">'
|
||
+ (isVideo(n) ? '<div class="lb-var-play">▶</div>' : '')
|
||
+ (pose ? '<div class="lb-var-pose">' + escHtml(pose) + '</div>' : '')
|
||
+ (badge > 0 ? '<div class="lb-var-ref-badge">#' + badge + '</div>' : '')
|
||
+ '</div>';
|
||
}).join('');
|
||
const active = strip.querySelector('.lb-var-thumb.active');
|
||
if (active) active.scrollIntoView({ block: 'nearest', inline: 'center' });
|
||
|
||
// Camera angle overlay bar
|
||
updateSbAngleBar();
|
||
|
||
// Face-book thumbnail — show on slot 0 when a face crop exists for this group
|
||
const faceBook = document.getElementById('lbFaceBook');
|
||
const faceThumb = document.getElementById('lbFaceThumb');
|
||
if (faceBook && faceThumb && lbCurrentGid && lbIdx === 0 && !isVid) {
|
||
loadFaceThumb(lbCurrentGid);
|
||
} else if (faceBook) {
|
||
faceBook.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
// Alias for old callsites
|
||
function updateLightbox() { updateStudio(); }
|
||
|
||
async function updateName() {
|
||
const fname = lbNames[lbIdx];
|
||
const newName = document.getElementById('lbNameInput').value;
|
||
if (newName === (imageNames[fname] || '')) return;
|
||
try {
|
||
const r = await fetch(`${API}/names/${fname}`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ name: newName })
|
||
});
|
||
if (r.ok) {
|
||
imageNames[fname] = newName;
|
||
showToast(`Name updated`);
|
||
}
|
||
} catch (e) {
|
||
showToast(`Failed to update name: ${e}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function updateGroupName() {
|
||
const gid = lbCurrentGid;
|
||
if (!gid) return;
|
||
const newName = document.getElementById('lbGroupNameInput').value.trim();
|
||
if (newName === (groupNames[gid] || '')) return;
|
||
try {
|
||
const r = await fetch(`${API}/group-names/${encodeURIComponent(gid)}`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ name: newName })
|
||
});
|
||
if (r.ok) {
|
||
if (newName) groupNames[gid] = newName;
|
||
else delete groupNames[gid];
|
||
showToast('Group name updated');
|
||
// Refresh gallery labels without full reload
|
||
document.querySelectorAll('.image-card').forEach(card => {
|
||
if (card.dataset.group === gid) {
|
||
const group = currentGroups.find(g => g.gid === gid);
|
||
if (group) card.querySelector('.image-name').textContent = groupDisplayName(group);
|
||
}
|
||
});
|
||
}
|
||
} catch (e) {
|
||
showToast(`Failed: ${e}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function lbMoveInGroup(dir) {
|
||
const gid = lbCurrentGid;
|
||
const data = groupData.get(gid);
|
||
if (!data || data.names.length < 2) return;
|
||
const newIdx = lbIdx + dir;
|
||
if (newIdx < 0 || newIdx >= data.names.length) return;
|
||
// Swap in local arrays
|
||
[data.names[lbIdx], data.names[newIdx]] = [data.names[newIdx], data.names[lbIdx]];
|
||
[data.urls[lbIdx], data.urls[newIdx]] = [data.urls[newIdx], data.urls[lbIdx]];
|
||
// Update sort orders locally
|
||
data.names.forEach((fn, i) => { fileSortOrders[fn] = i; });
|
||
// Persist to API
|
||
try {
|
||
await fetch(`${API}/groups/${encodeURIComponent(gid)}/order`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ filenames: data.names })
|
||
});
|
||
} catch (e) { /* non-fatal */ }
|
||
lbIdx = newIdx;
|
||
lbUrls = data.urls;
|
||
lbNames = data.names;
|
||
updateLightbox();
|
||
}
|
||
|
||
async function lbMoveToPosition(pos) {
|
||
const gid = lbCurrentGid;
|
||
const data = groupData.get(gid);
|
||
if (!data || data.names.length < 2) return;
|
||
|
||
// Get current index and target position
|
||
const currentIndex = lbIdx;
|
||
let targetIndex;
|
||
|
||
if (pos === 1) {
|
||
// Move to second position (index 1)
|
||
targetIndex = 1;
|
||
} else if (pos === -2) {
|
||
// Move to before last position (second to last)
|
||
targetIndex = data.names.length - 2;
|
||
} else {
|
||
return; // Invalid position
|
||
}
|
||
|
||
// Make sure target index is valid
|
||
if (targetIndex < 0 || targetIndex >= data.names.length) return;
|
||
|
||
// If already at target position, do nothing
|
||
if (currentIndex === targetIndex) return;
|
||
|
||
// Create a copy of the names array to work with
|
||
const newNames = [...data.names];
|
||
|
||
// Remove the item from its current position
|
||
const [movedItem] = newNames.splice(currentIndex, 1);
|
||
|
||
// Insert the item at the target position
|
||
newNames.splice(targetIndex, 0, movedItem);
|
||
|
||
// Update the group data with new order
|
||
data.names = newNames;
|
||
|
||
// Update sort orders locally
|
||
data.names.forEach((fn, i) => { fileSortOrders[fn] = i; });
|
||
|
||
// Persist to API
|
||
try {
|
||
await fetch(`${API}/groups/${encodeURIComponent(gid)}/order`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ filenames: data.names })
|
||
});
|
||
} catch (e) { /* non-fatal */ }
|
||
|
||
// Update the current index to reflect new position
|
||
lbIdx = targetIndex;
|
||
lbUrls = data.urls;
|
||
lbNames = data.names;
|
||
updateLightbox();
|
||
}
|
||
|
||
async function lbToggleHidden() {
|
||
const fname = lbNames[lbIdx];
|
||
const newHidden = !fileHidden[fname];
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/hidden`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ hidden: newHidden }),
|
||
});
|
||
if (!r.ok) { showToast('Failed to update visibility', 'error'); return; }
|
||
fileHidden[fname] = newHidden;
|
||
// Update visibleIdx for this group
|
||
const data = groupData.get(lbCurrentGid);
|
||
if (data) {
|
||
data.visibleIdx = data.names.map((n, i) => i).filter(i => !fileHidden[data.names[i]]);
|
||
// Restart or stop cycle accordingly
|
||
const timer = cycleTimers.get(lbCurrentGid);
|
||
if (timer) { clearInterval(timer); cycleTimers.delete(lbCurrentGid); }
|
||
if (data.visibleIdx.length > 1) startGroupCycle(lbCurrentGid);
|
||
// Update card count badge
|
||
const card = [...document.querySelectorAll('.image-card')].find(c => c.dataset.group === lbCurrentGid);
|
||
if (card) {
|
||
const vc = card.querySelector('.variant-count');
|
||
if (vc) {
|
||
const vn = data.visibleIdx.length;
|
||
const tot = data.names.length;
|
||
if (vn > 1) {
|
||
vc.innerHTML = `${vn}×${tot !== vn ? `<span style="opacity:.5;font-size:9px"> /${tot}</span>` : ''}`;
|
||
} else {
|
||
vc.remove();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
showToast(newHidden ? 'Hidden from cycling' : 'Visible in cycling');
|
||
updateLightbox();
|
||
} catch (e) {
|
||
showToast('API not reachable', 'error');
|
||
}
|
||
}
|
||
|
||
// Two-step inline confirmation: first click arms the button, second confirms.
|
||
let _deleteArmed = false, _deleteArmTimer = null;
|
||
function lbDeleteArm() {
|
||
const btn = document.getElementById('lbDeleteBtn');
|
||
if (!btn) return;
|
||
if (_deleteArmed) { lbDeleteConfirm(); return; }
|
||
_deleteArmed = true;
|
||
btn.textContent = 'Confirm delete';
|
||
btn.style.background = '#dc2626';
|
||
clearTimeout(_deleteArmTimer);
|
||
_deleteArmTimer = setTimeout(() => {
|
||
_deleteArmed = false;
|
||
if (btn) { btn.textContent = 'Delete'; btn.style.background = ''; }
|
||
}, 3500);
|
||
}
|
||
|
||
async function lbDeleteConfirm() {
|
||
_deleteArmed = false;
|
||
clearTimeout(_deleteArmTimer);
|
||
const btn = document.getElementById('lbDeleteBtn');
|
||
if (btn) { btn.textContent = 'Delete'; btn.style.background = ''; btn.disabled = true; }
|
||
const fname = lbNames[lbIdx];
|
||
try {
|
||
const r = await fetch(`${API}/images/${fname}`, { method: 'DELETE' });
|
||
if (r.ok) {
|
||
showToast(`Deleted`, 'success');
|
||
if (btn) btn.disabled = false;
|
||
// Remove from current group view
|
||
lbUrls.splice(lbIdx, 1);
|
||
lbNames.splice(lbIdx, 1);
|
||
if (lbNames.length === 0) {
|
||
closeStudio();
|
||
// Add a small delay to allow static regeneration to complete before refresh
|
||
setTimeout(refreshNow, 500);
|
||
} else {
|
||
lbIdx = Math.min(lbIdx, lbNames.length - 1);
|
||
updateStudio();
|
||
// Add a small delay to allow static regeneration to complete before refresh
|
||
setTimeout(refreshNow, 500);
|
||
}
|
||
} else {
|
||
showToast(`Failed to delete`, 'error');
|
||
if (btn) btn.disabled = false;
|
||
}
|
||
} catch (e) {
|
||
showToast(`Failed to delete: ${e}`, 'error');
|
||
if (btn) btn.disabled = false;
|
||
}
|
||
}
|
||
|
||
async function lbArchive() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname) return;
|
||
const btn = document.getElementById('lbArchiveBtn');
|
||
if (btn) btn.disabled = true;
|
||
try {
|
||
const endpoint = fileArchived[fname] ? 'unarchive' : 'archive';
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/${endpoint}`, { method: 'POST' });
|
||
if (r.ok) {
|
||
fileArchived[fname] = !fileArchived[fname];
|
||
const action = fileArchived[fname] ? 'Archived' : 'Restored';
|
||
showToast(`${action}: ${fname}`, 'success');
|
||
// Remove from current filmstrip view and navigate
|
||
lbUrls.splice(lbIdx, 1);
|
||
lbNames.splice(lbIdx, 1);
|
||
if (lbNames.length === 0) {
|
||
closeStudio();
|
||
// Add a small delay to allow static regeneration to complete before refresh
|
||
setTimeout(refreshNow, 500);
|
||
} else {
|
||
lbIdx = Math.min(lbIdx, lbNames.length - 1);
|
||
updateStudio();
|
||
// Add a small delay to allow static regeneration to complete before refresh
|
||
setTimeout(refreshNow, 500);
|
||
}
|
||
} else {
|
||
showToast('Archive failed', 'error');
|
||
}
|
||
} catch (e) { showToast('Archive error: ' + e, 'error'); }
|
||
if (btn) btn.disabled = false;
|
||
}
|
||
|
||
function toggleArchiveView() {
|
||
_archiveViewActive = !_archiveViewActive;
|
||
const btn = document.getElementById('archiveViewBtn');
|
||
if (btn) {
|
||
btn.textContent = _archiveViewActive ? '📦 Back to Gallery' : '📦 Archive';
|
||
btn.style.color = _archiveViewActive ? '#f59e0b' : '';
|
||
}
|
||
// Show/hide archive banner
|
||
let banner = document.getElementById('archiveBanner');
|
||
if (_archiveViewActive) {
|
||
if (!banner) {
|
||
banner = document.createElement('div');
|
||
banner.id = 'archiveBanner';
|
||
banner.style.cssText = 'background:#7c3aed22;border:1px solid #7c3aed;border-radius:6px;padding:8px 14px;font-size:12px;color:#a78bfa;margin:8px 0;text-align:center';
|
||
banner.textContent = '📦 Viewing archived items — these are hidden from the main gallery';
|
||
const gallery = document.getElementById('gallery');
|
||
if (gallery) gallery.parentNode.insertBefore(banner, gallery);
|
||
}
|
||
} else {
|
||
if (banner) banner.remove();
|
||
}
|
||
refreshNow();
|
||
}
|
||
|
||
async function lbAutoCrop() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname) return;
|
||
const btn = document.getElementById('lbCropBtn');
|
||
if (btn) { btn.disabled = true; btn.textContent = 'Cropping…'; }
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/autocrop`, { method: 'POST' });
|
||
if (r.ok) {
|
||
showToast('Cropped', 'success');
|
||
lbUrls[lbIdx] = IMAGE_FOLDER + fname + '?t=' + Date.now();
|
||
updateStudio();
|
||
} else {
|
||
showToast('Crop failed: ' + await r.text(), 'error');
|
||
}
|
||
} catch (e) { showToast('Crop failed: ' + e, 'error'); }
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Crop'; }
|
||
}
|
||
|
||
async function lbRotate(degrees) {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname || !/\.(png|jpg|jpeg|webp)$/i.test(fname)) { showToast('Select an image to rotate', 'info'); return; }
|
||
const btns = [document.getElementById('lbRotateLeftBtn'), document.getElementById('lbRotateRightBtn')];
|
||
btns.forEach(b => { if (b) b.disabled = true; });
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/rotate`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ degrees }),
|
||
});
|
||
if (r.ok) {
|
||
showToast('Rotated', 'success');
|
||
lbUrls[lbIdx] = IMAGE_FOLDER + fname + '?t=' + Date.now();
|
||
updateStudio();
|
||
} else {
|
||
showToast('Rotate failed: ' + await r.text(), 'error');
|
||
}
|
||
} catch (e) { showToast('Rotate failed: ' + e, 'error'); }
|
||
btns.forEach(b => { if (b) b.disabled = false; });
|
||
}
|
||
|
||
// --- body-pose skeleton preview (interactive) ---
|
||
const POSE_MIN_SCORE = 0.3;
|
||
const POSE_KP_COLORS = ['#ef4444','#f97316','#eab308','#22c55e','#06b6d4','#3b82f6','#a855f7'];
|
||
|
||
function _removePoseOverlay() {
|
||
document.getElementById('poseCanvas')?.remove();
|
||
document.getElementById('poseToolbar')?.remove();
|
||
window._poseState = null;
|
||
const btn = document.getElementById('lbPoseBtn');
|
||
if (btn) btn.classList.remove('primary');
|
||
}
|
||
|
||
async function lbTogglePose() {
|
||
if (document.getElementById('poseCanvas')) { _removePoseOverlay(); return; }
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname || !/\.(png|jpg|jpeg|webp)$/i.test(fname)) { showToast('Select an image', 'info'); return; }
|
||
const btn = document.getElementById('lbPoseBtn');
|
||
if (btn) { btn.disabled = true; btn.textContent = '…'; }
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/pose`, { method: 'POST' });
|
||
if (r.status === 501) {
|
||
showToast('Pose estimator not installed on the server (pip install rtmlib onnxruntime)', 'info', 6000);
|
||
} else if (r.ok) {
|
||
const d = await r.json();
|
||
if (!d.people || !d.people.length) showToast('No person detected', 'info');
|
||
else { _initPoseOverlay(d, fname); showToast(`Pose (${d.backend}) — drag joints to edit`, 'success'); }
|
||
} else {
|
||
showToast('Pose failed: ' + await r.text(), 'error');
|
||
}
|
||
} catch (e) { showToast('Pose failed: ' + e, 'error'); }
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Pose'; }
|
||
}
|
||
|
||
function _initPoseOverlay(data, fname) {
|
||
_removePoseOverlay();
|
||
const viewer = document.getElementById('studioViewer');
|
||
const img = document.getElementById('lbImg');
|
||
if (!viewer || !img) return;
|
||
const canvas = document.createElement('canvas');
|
||
canvas.id = 'poseCanvas';
|
||
canvas.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;cursor:grab;z-index:90;';
|
||
viewer.appendChild(canvas);
|
||
canvas.width = viewer.clientWidth;
|
||
canvas.height = viewer.clientHeight;
|
||
|
||
// object-fit:contain letterbox mapping image px ↔ canvas px.
|
||
const iW = data.width, iH = data.height;
|
||
const scale = Math.min(canvas.width / iW, canvas.height / iH);
|
||
const st = window._poseState = {
|
||
fname, canvas, ctx: canvas.getContext('2d'),
|
||
people: data.people.map(p => p.map(k => k.slice())), // deep copy (editable)
|
||
skeleton: data.skeleton, width: iW, height: iH,
|
||
scale, offX: (canvas.width - iW * scale) / 2, offY: (canvas.height - iH * scale) / 2,
|
||
drag: null,
|
||
};
|
||
|
||
// Floating toolbar (top-left so it doesn't fight the crop bar on the right).
|
||
const bar = document.createElement('div');
|
||
bar.id = 'poseToolbar';
|
||
bar.style.cssText = 'position:absolute;top:8px;left:8px;display:flex;gap:6px;z-index:102;';
|
||
bar.innerHTML =
|
||
'<button class="sb-btn" onclick="lbPoseReset()" title="Re-detect the original pose">Reset</button>'
|
||
+ '<button class="sb-btn primary" onclick="lbFindSimilarPose()" title="Find library images in this pose">Similar pose</button>'
|
||
+ '<div style="position:relative;display:inline-block">'
|
||
+ ' <button class="sb-btn" id="gestureMenuBtn" onclick="toggleGestureMenu(event)" title="Apply a gesture preset to the skeleton">Gesture ▾</button>'
|
||
+ ' <div id="gestureMenuPanel" style="display:none;position:absolute;top:100%;left:0;z-index:200;background:#18181b;border:1px solid #2a2a2a;border-radius:6px;padding:4px;box-shadow:0 6px 20px rgba(0,0,0,.7);min-width:150px;margin-top:4px"></div>'
|
||
+ '</div>'
|
||
+ '<button class="sb-btn" onclick="lbPoseFromVideo()" title="Pick a pose from a wireframe video frame">From video</button>'
|
||
+ '<button class="sb-btn" id="poseGenBtn" onclick="lbGenerateWithPose()" title="Generate the person in this pose (skeleton → Qwen image2 guide)">Generate ▸</button>';
|
||
viewer.appendChild(bar);
|
||
_buildGestureMenu();
|
||
|
||
canvas.addEventListener('mousedown', _posePointerDown);
|
||
canvas.addEventListener('mousemove', _posePointerMove);
|
||
window.addEventListener('mouseup', _posePointerUp);
|
||
_drawPose();
|
||
const btn = document.getElementById('lbPoseBtn');
|
||
if (btn) btn.classList.add('primary');
|
||
}
|
||
|
||
// --- Gesture presets: normalized [x,y,conf] per COCO-17 keypoint ---
|
||
// Defined in [0,1] image space for a portrait-oriented standing figure.
|
||
// Applied to the actual image by scaling to image pixel dimensions.
|
||
const GESTURE_PRESETS = [
|
||
{ name: 'T-pose', desc: 'Arms fully extended sideways',
|
||
kpts: [[.50,.07,1],[.47,.065,1],[.53,.065,1],[.43,.08,1],[.57,.08,1],
|
||
[.25,.22,1],[.75,.22,1],[.10,.22,1],[.90,.22,1],[-.02,.22,1],[1.02,.22,1],
|
||
[.40,.52,1],[.60,.52,1],[.40,.72,1],[.60,.72,1],[.40,.93,1],[.60,.93,1]] },
|
||
{ name: 'Wave right', desc: 'Right arm raised and waving',
|
||
kpts: [[.50,.07,1],[.47,.065,1],[.53,.065,1],[.43,.08,1],[.57,.08,1],
|
||
[.35,.22,1],[.65,.22,1],[.20,.38,1],[.78,.10,1],[.22,.52,1],[.70,.04,1],
|
||
[.40,.52,1],[.60,.52,1],[.40,.72,1],[.60,.72,1],[.40,.93,1],[.60,.93,1]] },
|
||
{ name: 'Arms raised', desc: 'Both arms raised above head',
|
||
kpts: [[.50,.07,1],[.47,.065,1],[.53,.065,1],[.43,.08,1],[.57,.08,1],
|
||
[.35,.22,1],[.65,.22,1],[.22,.08,1],[.78,.08,1],[.18,.02,1],[.82,.02,1],
|
||
[.40,.52,1],[.60,.52,1],[.40,.72,1],[.60,.72,1],[.40,.93,1],[.60,.93,1]] },
|
||
{ name: 'Hands on hips', desc: 'Hands resting on hips',
|
||
kpts: [[.50,.07,1],[.47,.065,1],[.53,.065,1],[.43,.08,1],[.57,.08,1],
|
||
[.35,.22,1],[.65,.22,1],[.28,.40,1],[.72,.40,1],[.35,.52,1],[.65,.52,1],
|
||
[.38,.52,1],[.62,.52,1],[.38,.72,1],[.62,.72,1],[.38,.93,1],[.62,.93,1]] },
|
||
{ name: 'Turn left 45°', desc: 'Body turned ~45° to the left',
|
||
kpts: [[.45,.07,1],[.44,.065,1],[.50,.065,1],[.41,.08,1],[.52,.08,1],
|
||
[.30,.22,1],[.58,.22,1],[.18,.38,1],[.65,.30,1],[.12,.52,1],[.70,.22,1],
|
||
[.36,.52,1],[.58,.52,1],[.36,.72,1],[.58,.72,1],[.36,.93,1],[.58,.93,1]] },
|
||
{ name: 'Turn right 45°', desc: 'Body turned ~45° to the right',
|
||
kpts: [[.55,.07,1],[.50,.065,1],[.56,.065,1],[.48,.08,1],[.59,.08,1],
|
||
[.42,.22,1],[.70,.22,1],[.35,.30,1],[.82,.38,1],[.30,.22,1],[.88,.52,1],
|
||
[.42,.52,1],[.64,.52,1],[.42,.72,1],[.64,.72,1],[.42,.93,1],[.64,.93,1]] },
|
||
];
|
||
|
||
function _buildGestureMenu() {
|
||
const panel = document.getElementById('gestureMenuPanel');
|
||
if (!panel) return;
|
||
panel.innerHTML = GESTURE_PRESETS.map((g, i) =>
|
||
`<div style="padding:5px 10px;cursor:pointer;font-size:11px;border-radius:4px;white-space:nowrap"
|
||
title="${escHtml(g.desc)}"
|
||
onmouseenter="this.style.background='#2a2a2a'" onmouseleave="this.style.background=''"
|
||
onclick="applyGesturePreset(${i})">${escHtml(g.name)}</div>`
|
||
).join('');
|
||
}
|
||
|
||
function toggleGestureMenu(e) {
|
||
e.stopPropagation();
|
||
const p = document.getElementById('gestureMenuPanel');
|
||
if (!p) return;
|
||
const open = p.style.display !== 'none';
|
||
p.style.display = open ? 'none' : 'block';
|
||
if (!open) {
|
||
const close = (ev) => { if (!p.contains(ev.target)) { p.style.display='none'; document.removeEventListener('click',close); } };
|
||
setTimeout(() => document.addEventListener('click', close), 0);
|
||
}
|
||
}
|
||
|
||
function applyGesturePreset(idx) {
|
||
const st = window._poseState;
|
||
if (!st || !st.people.length) { showToast('No pose overlay active — click Pose first', 'info'); return; }
|
||
const g = GESTURE_PRESETS[idx];
|
||
const W = st.width, H = st.height;
|
||
st.people[0] = g.kpts.map(([nx, ny, c]) => [nx * W, ny * H, c]);
|
||
st._gestureName = g.name; // remember for generation
|
||
_drawPose();
|
||
document.getElementById('gestureMenuPanel').style.display = 'none';
|
||
showToast(`Gesture: ${g.name} — click Generate ▸ to create`, 'success');
|
||
}
|
||
|
||
function _drawPose() {
|
||
const st = window._poseState; if (!st) return;
|
||
const { ctx, canvas, scale, offX, offY } = st;
|
||
const cx = x => offX + x * scale, cy = y => offY + y * scale;
|
||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
st.people.forEach((kpts, pi) => {
|
||
const color = POSE_KP_COLORS[pi % POSE_KP_COLORS.length];
|
||
ctx.lineWidth = 3; ctx.strokeStyle = color;
|
||
st.skeleton.forEach(([a, b]) => {
|
||
const pa = kpts[a], pb = kpts[b];
|
||
if (!pa || !pb || pa[2] < POSE_MIN_SCORE || pb[2] < POSE_MIN_SCORE) return;
|
||
ctx.beginPath(); ctx.moveTo(cx(pa[0]), cy(pa[1])); ctx.lineTo(cx(pb[0]), cy(pb[1])); ctx.stroke();
|
||
});
|
||
kpts.forEach((p, ki) => {
|
||
if (!p || p[2] < POSE_MIN_SCORE) return;
|
||
const hot = st.drag && st.drag.pi === pi && st.drag.ki === ki;
|
||
ctx.beginPath(); ctx.arc(cx(p[0]), cy(p[1]), hot ? 7 : 5, 0, Math.PI * 2);
|
||
ctx.fillStyle = '#fff'; ctx.fill();
|
||
ctx.lineWidth = 2; ctx.strokeStyle = color; ctx.stroke();
|
||
});
|
||
});
|
||
}
|
||
|
||
function _poseHitTest(mx, my) {
|
||
const st = window._poseState; if (!st) return null;
|
||
const cx = x => st.offX + x * st.scale, cy = y => st.offY + y * st.scale;
|
||
let best = null, bestD = 14 * 14; // ~14px grab radius
|
||
st.people.forEach((kpts, pi) => kpts.forEach((p, ki) => {
|
||
if (!p || p[2] < POSE_MIN_SCORE) return;
|
||
const dx = mx - cx(p[0]), dy = my - cy(p[1]), d = dx * dx + dy * dy;
|
||
if (d < bestD) { bestD = d; best = { pi, ki }; }
|
||
}));
|
||
return best;
|
||
}
|
||
|
||
function _poseEvtXY(e) {
|
||
const r = window._poseState.canvas.getBoundingClientRect();
|
||
return [e.clientX - r.left, e.clientY - r.top];
|
||
}
|
||
function _posePointerDown(e) {
|
||
const st = window._poseState; if (!st) return;
|
||
const [mx, my] = _poseEvtXY(e);
|
||
const hit = _poseHitTest(mx, my);
|
||
if (hit) { st.drag = hit; st.canvas.style.cursor = 'grabbing'; _drawPose(); e.preventDefault(); }
|
||
}
|
||
function _posePointerMove(e) {
|
||
const st = window._poseState; if (!st) return;
|
||
if (!st.drag) {
|
||
const [mx, my] = _poseEvtXY(e);
|
||
st.canvas.style.cursor = _poseHitTest(mx, my) ? 'grab' : 'default';
|
||
return;
|
||
}
|
||
const [mx, my] = _poseEvtXY(e);
|
||
const p = st.people[st.drag.pi][st.drag.ki];
|
||
p[0] = Math.max(0, Math.min(st.width, (mx - st.offX) / st.scale));
|
||
p[1] = Math.max(0, Math.min(st.height, (my - st.offY) / st.scale));
|
||
if (p[2] < POSE_MIN_SCORE) p[2] = 1.0; // dragging a hidden joint makes it visible
|
||
_drawPose();
|
||
}
|
||
function _posePointerUp() {
|
||
const st = window._poseState; if (!st || !st.drag) return;
|
||
st.drag = null; st.canvas.style.cursor = 'grab'; _drawPose();
|
||
}
|
||
|
||
async function lbPoseReset() {
|
||
const st = window._poseState; if (!st) return;
|
||
const fname = st.fname;
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/pose`, { method: 'POST' });
|
||
if (r.ok) { const d = await r.json(); if (d.people?.length) _initPoseOverlay(d, fname); }
|
||
} catch (e) { showToast('Reset failed: ' + e, 'error'); }
|
||
}
|
||
|
||
async function lbFindSimilarPose() {
|
||
const st = window._poseState; if (!st) return;
|
||
// Use the (possibly edited) primary skeleton.
|
||
const kpts = st.people[0];
|
||
const btn = event?.target;
|
||
if (btn) btn.disabled = true;
|
||
try {
|
||
const r = await fetch(`${API}/pose/similar`, {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ keypoints: kpts, width: st.width, height: st.height, limit: 12 }),
|
||
});
|
||
if (r.ok) {
|
||
const d = await r.json();
|
||
_renderPoseResults(d.similar || []);
|
||
} else if (r.status === 400) {
|
||
showToast('Pose too sparse — drag more joints into place', 'info');
|
||
} else {
|
||
showToast('Similar failed: ' + await r.text(), 'error');
|
||
}
|
||
} catch (e) { showToast('Similar failed: ' + e, 'error'); }
|
||
if (btn) btn.disabled = false;
|
||
}
|
||
|
||
async function lbGenerateWithPose() {
|
||
const st = window._poseState;
|
||
if (!st || !st.people.length) { showToast('No pose overlay active — click Pose first', 'info'); return; }
|
||
const fname = st.fname;
|
||
if (!fname) { showToast('No image active', 'info'); return; }
|
||
const btn = document.getElementById('poseGenBtn');
|
||
if (btn) { btn.disabled = true; btn.textContent = '…'; }
|
||
try {
|
||
const payload = {
|
||
model_filename: fname,
|
||
keypoints: st.people[0],
|
||
width: st.width,
|
||
height: st.height,
|
||
gesture_name: st._gestureName || null,
|
||
seed: -1,
|
||
};
|
||
const r = await fetch(`${API}/generate-with-pose`, {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(payload),
|
||
});
|
||
if (!r.ok) { showToast('Generate failed: ' + await r.text(), 'error'); return; }
|
||
const { job_id } = await r.json();
|
||
showToast('Pose generation started…', 'info');
|
||
if (btn) btn.textContent = 'Generating…';
|
||
const poll = () => setTimeout(async () => {
|
||
try {
|
||
const jr = await fetch(`${API}/batch/${job_id}`);
|
||
if (!jr.ok) { poll(); return; }
|
||
const job = await jr.json();
|
||
if (job.status === 'running') { poll(); return; }
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Generate ▸'; }
|
||
if (job.status === 'done') {
|
||
showToast('Pose generation done!', 'success');
|
||
_followLatestGid = lbCurrentGid;
|
||
refreshNow(true);
|
||
} else {
|
||
showToast('Pose generation failed: ' + (job.error || 'unknown'), 'error');
|
||
}
|
||
} catch (e) { poll(); }
|
||
}, 2500);
|
||
poll();
|
||
} catch (e) {
|
||
showToast('Generate error: ' + e, 'error');
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Generate ▸'; }
|
||
}
|
||
}
|
||
|
||
// ─── Pose from wireframe video ───────────────────────────────────────────
|
||
// Opens a modal that lists wireframe videos for the current group,
|
||
// lets the user scrub to a timestamp, then extracts keypoints from that
|
||
// frame and loads them into the draggable pose overlay.
|
||
async function lbPoseFromVideo() {
|
||
const st = window._poseState;
|
||
if (!st) { showToast('Open Pose first', 'info'); return; }
|
||
|
||
// Gather wireframe videos from the current group's images.
|
||
const videos = (lbNames || []).filter(n => /\.(mp4|webm|mov)$/i.test(n));
|
||
if (!videos.length) {
|
||
showToast('No videos in this group — generate a wireframe video first', 'info');
|
||
return;
|
||
}
|
||
|
||
// Build modal
|
||
const overlay = document.createElement('div');
|
||
overlay.id = 'poseVideoModal';
|
||
overlay.style.cssText = 'position:fixed;inset:0;z-index:900;background:rgba(0,0,0,.8);display:flex;align-items:center;justify-content:center;';
|
||
overlay.innerHTML = `
|
||
<div style="background:#18181b;border:1px solid #2a2a2a;border-radius:10px;padding:20px;width:480px;max-width:95vw;box-shadow:0 8px 40px rgba(0,0,0,.8)">
|
||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:14px">
|
||
<span style="font-size:14px;font-weight:600">Pick pose from wireframe video</span>
|
||
<button class="sb-btn" onclick="document.getElementById('poseVideoModal').remove()">✕</button>
|
||
</div>
|
||
<div style="margin-bottom:10px">
|
||
<label style="font-size:11px;color:#aaa;display:block;margin-bottom:4px">Video</label>
|
||
<select id="pvmVideoSelect" style="width:100%;background:#0f0f0f;color:#e0e0e0;border:1px solid #2a2a2a;border-radius:5px;padding:5px 8px;font-size:12px">
|
||
${videos.map(v => `<option value="${escHtml(v)}">${escHtml(v)}</option>`).join('')}
|
||
</select>
|
||
</div>
|
||
<div style="margin-bottom:10px">
|
||
<video id="pvmVideo" style="width:100%;border-radius:6px;background:#000;max-height:260px" preload="metadata"></video>
|
||
</div>
|
||
<div style="margin-bottom:14px">
|
||
<label style="font-size:11px;color:#aaa;display:block;margin-bottom:4px">Scrub to pose frame</label>
|
||
<input type="range" id="pvmScrubber" min="0" max="100" step="0.1" value="0"
|
||
style="width:100%;accent-color:#a78bfa">
|
||
<span id="pvmTimeLabel" style="font-size:11px;color:#888">0.0 s</span>
|
||
</div>
|
||
<div style="display:flex;gap:8px;justify-content:flex-end">
|
||
<button class="sb-btn" onclick="document.getElementById('poseVideoModal').remove()">Cancel</button>
|
||
<button class="sb-btn primary" id="pvmLoadBtn" onclick="pvmLoadPose()">Load pose</button>
|
||
</div>
|
||
</div>`;
|
||
document.body.appendChild(overlay);
|
||
|
||
const vidEl = document.getElementById('pvmVideo');
|
||
const select = document.getElementById('pvmVideoSelect');
|
||
const scrubber = document.getElementById('pvmScrubber');
|
||
const timeLabel = document.getElementById('pvmTimeLabel');
|
||
|
||
function pvmSetVideo(filename) {
|
||
vidEl.src = IMAGE_FOLDER + filename;
|
||
vidEl.load();
|
||
vidEl.addEventListener('loadedmetadata', () => {
|
||
const dur = vidEl.duration || 10;
|
||
scrubber.max = dur;
|
||
scrubber.step = Math.max(0.033, dur / 500);
|
||
scrubber.value = 0;
|
||
timeLabel.textContent = '0.0 s';
|
||
}, { once: true });
|
||
}
|
||
|
||
scrubber.addEventListener('input', () => {
|
||
const t = parseFloat(scrubber.value);
|
||
vidEl.currentTime = t;
|
||
timeLabel.textContent = t.toFixed(2) + ' s';
|
||
});
|
||
|
||
select.addEventListener('change', () => pvmSetVideo(select.value));
|
||
pvmSetVideo(videos[0]);
|
||
|
||
window.pvmLoadPose = async function() {
|
||
const video = select.value;
|
||
const time_s = parseFloat(scrubber.value);
|
||
const btn = document.getElementById('pvmLoadBtn');
|
||
btn.disabled = true;
|
||
btn.textContent = 'Extracting…';
|
||
try {
|
||
const r = await fetch(`${API}/pose/from-wireframe`, {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ video, time_s }),
|
||
});
|
||
if (!r.ok) { showToast('Pose extract failed: ' + await r.text(), 'error'); btn.disabled = false; btn.textContent = 'Load pose'; return; }
|
||
const d = await r.json();
|
||
// Rescale keypoints from video dimensions to current image dimensions
|
||
const scaleX = st.width / d.width;
|
||
const scaleY = st.height / d.height;
|
||
st.people[0] = d.keypoints.map(([x, y, c]) => [x * scaleX, y * scaleY, c]);
|
||
st._gestureName = null; // came from real video, not a named preset
|
||
_drawPose();
|
||
document.getElementById('poseVideoModal').remove();
|
||
showToast(`Pose loaded from ${video} @ ${time_s.toFixed(2)}s`, 'success');
|
||
} catch (e) {
|
||
showToast('Pose extract error: ' + e, 'error');
|
||
btn.disabled = false; btn.textContent = 'Load pose';
|
||
}
|
||
};
|
||
}
|
||
|
||
async function lbFindSimilarFaces() {
|
||
if (!lbCurrentGid) return;
|
||
try {
|
||
const r = await fetch(`${API}/faces/similar`, {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ group_id: lbCurrentGid, limit: 12 }),
|
||
});
|
||
if (r.status === 404) { _renderFaceResults(null); return; }
|
||
if (!r.ok) { showToast('Similar faces failed: ' + await r.text(), 'error'); return; }
|
||
const d = await r.json();
|
||
_renderFaceResults(d.similar || []);
|
||
} catch (e) { showToast('Similar faces failed: ' + e, 'error'); }
|
||
}
|
||
|
||
async function lbBuildFaceIndex() {
|
||
try {
|
||
const r = await fetch(`${API}/faces/index`, { method: 'POST' });
|
||
if (!r.ok) { showToast('Face index failed: ' + await r.text(), 'error'); return; }
|
||
showToast('Building face index…', 'info');
|
||
_pollFaceIndex();
|
||
} catch (e) { showToast('Face index failed: ' + e, 'error'); }
|
||
}
|
||
|
||
async function _pollFaceIndex() {
|
||
try {
|
||
const r = await fetch(`${API}/faces/index/status`);
|
||
if (r.ok) {
|
||
const s = await r.json();
|
||
if (s.running) {
|
||
showToast(`Face index: ${s.done}/${s.total}…`, 'info', 2500);
|
||
setTimeout(_pollFaceIndex, 2000);
|
||
} else {
|
||
showToast(`Face index ready (${s.indexed} embeddings)`, 'success');
|
||
}
|
||
}
|
||
} catch (e) {}
|
||
}
|
||
|
||
function _renderFaceResults(items) {
|
||
document.getElementById('faceResults')?.remove();
|
||
const viewer = document.getElementById('studioViewer');
|
||
if (!viewer) return;
|
||
const panel = document.createElement('div');
|
||
panel.id = 'faceResults';
|
||
panel.style.cssText = 'position:absolute;left:0;right:0;bottom:0;z-index:103;background:rgba(0,0,0,0.85);'
|
||
+ 'padding:8px;display:flex;gap:8px;overflow-x:auto;align-items:center;';
|
||
if (!items || !items.length) {
|
||
const msg = items === null
|
||
? 'No face embedding for this group — build the index first'
|
||
: 'No similar faces found';
|
||
panel.innerHTML = `<span style="color:#aaa;font-size:12px;flex:1">${msg}</span>`
|
||
+ '<button class="sb-btn" onclick="lbBuildFaceIndex();document.getElementById(\'faceResults\')?.remove()">Build index</button>'
|
||
+ '<button class="sb-btn" onclick="document.getElementById(\'faceResults\')?.remove()">Close</button>';
|
||
} else {
|
||
panel.innerHTML = '<span style="color:#aaa;font-size:11px;flex-shrink:0">Similar faces:</span>'
|
||
+ items.map(it => {
|
||
const u = IMAGE_FOLDER + it.filename + '?t=' + Date.now();
|
||
const gid = (it.group_id || '').replace(/'/g, "\\'");
|
||
const fn = it.filename.replace(/'/g, "\\'");
|
||
return `<div title="dist ${it.distance}" style="flex-shrink:0;cursor:pointer;text-align:center"
|
||
onclick="openFaceResult('${gid}','${fn}')">
|
||
<img src="${u}" loading="lazy" style="width:64px;height:64px;object-fit:cover;border-radius:5px;border:1px solid #444" onerror="this.style.opacity='0.3'">
|
||
<div style="font-size:9px;color:#777">${it.distance}</div></div>`;
|
||
}).join('')
|
||
+ '<button class="sb-btn" style="flex-shrink:0" onclick="document.getElementById(\'faceResults\')?.remove()">Close</button>';
|
||
}
|
||
viewer.appendChild(panel);
|
||
}
|
||
|
||
function openFaceResult(gid, fname) {
|
||
document.getElementById('faceResults')?.remove();
|
||
if (gid && groupData.has(gid)) {
|
||
openStudio(gid, 0);
|
||
} else {
|
||
const i = lbNames.indexOf(fname);
|
||
if (i >= 0) { lbIdx = i; updateStudio(); }
|
||
else showToast('Group not found in gallery: ' + fname, 'info', 5000);
|
||
}
|
||
}
|
||
|
||
function _renderPoseResults(items) {
|
||
document.getElementById('poseResults')?.remove();
|
||
const viewer = document.getElementById('studioViewer');
|
||
if (!viewer) return;
|
||
const panel = document.createElement('div');
|
||
panel.id = 'poseResults';
|
||
panel.style.cssText = 'position:absolute;left:0;right:0;bottom:0;z-index:103;background:rgba(0,0,0,0.85);'
|
||
+ 'padding:8px;display:flex;gap:8px;overflow-x:auto;align-items:center;';
|
||
if (!items.length) {
|
||
panel.innerHTML = '<span style="color:#aaa;font-size:12px;flex:1">No similar poses found — try building the pose index.</span>'
|
||
+ '<button class="sb-btn" onclick="lbBuildPoseIndex()">Build index</button>'
|
||
+ '<button class="sb-btn" onclick="document.getElementById(\'poseResults\')?.remove()">Close</button>';
|
||
} else {
|
||
panel.innerHTML = '<span style="color:#aaa;font-size:11px;flex-shrink:0">Similar poses:</span>'
|
||
+ items.map(it => {
|
||
const u = IMAGE_FOLDER + it.filename + '?t=' + Date.now();
|
||
return `<div title="dist ${it.distance}" style="flex-shrink:0;cursor:pointer;text-align:center"
|
||
onclick="openPoseResult('${(it.group_id||'').replace(/'/g,"\\'")}','${it.filename.replace(/'/g,"\\'")}')">
|
||
<img src="${u}" loading="lazy" style="width:64px;height:64px;object-fit:cover;border-radius:5px;border:1px solid #444" onerror="this.style.opacity='0.3'">
|
||
<div style="font-size:9px;color:#777">${it.distance}</div></div>`;
|
||
}).join('')
|
||
+ '<button class="sb-btn" style="flex-shrink:0" onclick="document.getElementById(\'poseResults\')?.remove()">Close</button>';
|
||
}
|
||
viewer.appendChild(panel);
|
||
}
|
||
|
||
function openPoseResult(gid, fname) {
|
||
_removePoseOverlay();
|
||
document.getElementById('poseResults')?.remove();
|
||
if (gid && groupData.has(gid)) {
|
||
const i = groupData.get(gid).names.indexOf(fname);
|
||
openStudio(gid, i >= 0 ? i : 0); // openStudio sets lbUrls/lbNames/lbIdx + updateStudio
|
||
} else {
|
||
const i = lbNames.indexOf(fname);
|
||
if (i >= 0) { lbIdx = i; updateStudio(); }
|
||
else showToast('Open its group from the gallery: ' + fname, 'info', 5000);
|
||
}
|
||
}
|
||
|
||
async function lbBuildPoseIndex() {
|
||
try {
|
||
const r = await fetch(`${API}/pose/index`, { method: 'POST' });
|
||
if (r.status === 501) { showToast('Pose estimator not installed', 'info'); return; }
|
||
if (!r.ok) { showToast('Index failed: ' + await r.text(), 'error'); return; }
|
||
showToast('Building pose index…', 'info');
|
||
_pollPoseIndex();
|
||
} catch (e) { showToast('Index failed: ' + e, 'error'); }
|
||
}
|
||
|
||
async function _pollPoseIndex() {
|
||
try {
|
||
const r = await fetch(`${API}/pose/index/status`);
|
||
if (r.ok) {
|
||
const s = await r.json();
|
||
if (s.running) {
|
||
showToast(`Pose index: ${s.done}/${s.total}…`, 'info', 2500);
|
||
setTimeout(_pollPoseIndex, 2000);
|
||
} else {
|
||
showToast(`Pose index ready (${s.indexed} images)`, 'success');
|
||
}
|
||
}
|
||
} catch (e) { /* stop polling */ }
|
||
}
|
||
|
||
function startManualCrop() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname || !/\.(png|jpg|jpeg|webp)$/i.test(fname)) { showToast('Select an image to crop', 'info'); return; }
|
||
const viewer = document.getElementById('studioViewer');
|
||
const img = document.getElementById('lbImg');
|
||
if (!img || img.style.display === 'none') { showToast('No image displayed', 'info'); return; }
|
||
if (document.getElementById('cropCanvas')) return; // already active
|
||
|
||
const canvas = document.createElement('canvas');
|
||
canvas.id = 'cropCanvas';
|
||
canvas.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;cursor:crosshair;z-index:100;';
|
||
viewer.appendChild(canvas);
|
||
|
||
const bar = document.createElement('div');
|
||
bar.id = 'cropBar';
|
||
// pointer-events:none on the container so the canvas underneath captures drags
|
||
// from the very top of the image. Individual interactive controls opt back in.
|
||
bar.style.cssText = 'position:absolute;top:0;left:0;right:0;display:flex;gap:8px;padding:8px;background:rgba(0,0,0,0.75);z-index:101;justify-content:flex-end;align-items:center;pointer-events:none;';
|
||
bar.innerHTML = '<span style="flex:1;font-size:11px;color:#aaa">Drag to select crop area</span>'
|
||
+ '<label style="display:flex;align-items:center;gap:4px;font-size:11px;color:#ccc;cursor:pointer;pointer-events:auto" title="Keep the original and crop a copy instead">'
|
||
+ '<input type="checkbox" id="cropAsCopy" checked style="cursor:pointer">Save as copy</label>'
|
||
+ '<button class="sb-btn" style="pointer-events:auto" onclick="cancelManualCrop()">Cancel</button>'
|
||
+ '<button class="sb-btn primary" style="pointer-events:auto" id="cropConfirmBtn" disabled onclick="confirmManualCrop()">Crop</button>';
|
||
viewer.appendChild(bar);
|
||
|
||
const st = window._cropState = { canvas, viewer, img, fname, x1: 0, y1: 0, x2: 0, y2: 0, dragging: false };
|
||
|
||
function resize() { canvas.width = viewer.clientWidth; canvas.height = viewer.clientHeight; draw(); }
|
||
function draw() {
|
||
const ctx = canvas.getContext('2d');
|
||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
const w = Math.abs(st.x2 - st.x1), h = Math.abs(st.y2 - st.y1);
|
||
if (w < 2 || h < 2) return;
|
||
const rx = Math.min(st.x1, st.x2), ry = Math.min(st.y1, st.y2);
|
||
ctx.fillStyle = 'rgba(0,0,0,0.5)';
|
||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||
ctx.clearRect(rx, ry, w, h);
|
||
ctx.strokeStyle = '#fff';
|
||
ctx.lineWidth = 1.5;
|
||
ctx.strokeRect(rx + 0.5, ry + 0.5, w - 1, h - 1);
|
||
}
|
||
st.draw = draw;
|
||
|
||
canvas.addEventListener('mousedown', e => {
|
||
const r = canvas.getBoundingClientRect();
|
||
st.x1 = st.x2 = e.clientX - r.left;
|
||
st.y1 = st.y2 = e.clientY - r.top;
|
||
st.dragging = true;
|
||
const cb = document.getElementById('cropConfirmBtn');
|
||
if (cb) cb.disabled = true;
|
||
});
|
||
canvas.addEventListener('mousemove', e => {
|
||
if (!st.dragging) return;
|
||
const r = canvas.getBoundingClientRect();
|
||
st.x2 = e.clientX - r.left;
|
||
st.y2 = e.clientY - r.top;
|
||
draw();
|
||
});
|
||
canvas.addEventListener('mouseup', () => {
|
||
st.dragging = false;
|
||
draw();
|
||
const w = Math.abs(st.x2 - st.x1), h = Math.abs(st.y2 - st.y1);
|
||
const cb = document.getElementById('cropConfirmBtn');
|
||
if (cb) cb.disabled = (w < 5 || h < 5);
|
||
});
|
||
|
||
resize();
|
||
}
|
||
|
||
function cancelManualCrop() {
|
||
document.getElementById('cropCanvas')?.remove();
|
||
document.getElementById('cropBar')?.remove();
|
||
window._cropState = null;
|
||
}
|
||
|
||
async function confirmManualCrop() {
|
||
const st = window._cropState;
|
||
if (!st) return;
|
||
const { canvas, img, fname } = st;
|
||
|
||
// Convert canvas rect → image pixel coords (account for object-fit: contain letterbox)
|
||
const cW = canvas.width, cH = canvas.height;
|
||
const iW = img.naturalWidth, iH = img.naturalHeight;
|
||
const scale = Math.min(cW / iW, cH / iH);
|
||
const dW = iW * scale, dH = iH * scale;
|
||
const offX = (cW - dW) / 2, offY = (cH - dH) / 2;
|
||
|
||
const toImg = (cx, cy) => ({
|
||
x: Math.round(Math.max(0, Math.min(iW, (cx - offX) / scale))),
|
||
y: Math.round(Math.max(0, Math.min(iH, (cy - offY) / scale))),
|
||
});
|
||
const p1 = toImg(Math.min(st.x1, st.x2), Math.min(st.y1, st.y2));
|
||
const p2 = toImg(Math.max(st.x1, st.x2), Math.max(st.y1, st.y2));
|
||
if (p2.x - p1.x < 2 || p2.y - p1.y < 2) { showToast('Crop area too small', 'info'); return; }
|
||
|
||
const asCopy = document.getElementById('cropAsCopy')?.checked ?? false;
|
||
const btn = document.getElementById('cropConfirmBtn');
|
||
if (btn) { btn.disabled = true; btn.textContent = 'Cropping…'; }
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/crop`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y, as_copy: asCopy }),
|
||
});
|
||
if (r.ok) {
|
||
const d = await r.json().catch(() => ({}));
|
||
cancelManualCrop();
|
||
if (asCopy && d.new_filename) {
|
||
showToast('Cropped to copy', 'success');
|
||
// Insert the cropped copy right after the original in the studio strip.
|
||
lbUrls.splice(lbIdx + 1, 0, IMAGE_FOLDER + d.new_filename + '?t=' + Date.now());
|
||
lbNames.splice(lbIdx + 1, 0, d.new_filename);
|
||
lbIdx = lbIdx + 1;
|
||
updateStudio();
|
||
refreshNow();
|
||
} else {
|
||
showToast('Cropped', 'success');
|
||
lbUrls[lbIdx] = IMAGE_FOLDER + fname + '?t=' + Date.now();
|
||
updateStudio();
|
||
}
|
||
} else {
|
||
showToast('Crop failed: ' + await r.text(), 'error');
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Crop'; }
|
||
}
|
||
} catch (e) {
|
||
showToast('Crop error: ' + e, 'error');
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Crop'; }
|
||
}
|
||
}
|
||
|
||
function startManualPad() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname || !/\.(png|jpg|jpeg|webp)$/i.test(fname)) { showToast('Select an image to pad', 'info'); return; }
|
||
const viewer = document.getElementById('studioViewer');
|
||
if (!viewer) return;
|
||
if (document.getElementById('padBar')) return; // already active
|
||
|
||
const bar = document.createElement('div');
|
||
bar.id = 'padBar';
|
||
bar.style.cssText = 'position:absolute;top:0;left:0;right:0;display:flex;gap:8px;padding:8px;background:rgba(0,0,0,0.85);z-index:101;justify-content:flex-end;align-items:center;flex-wrap:wrap;';
|
||
const numInput = (id, label) => {
|
||
const onInput = `syncPadFields(this)`;
|
||
const onBlur = `this.value = formatPadValue(this.value); updatePadPreview()`;
|
||
const onKeyDown = `if(event.key === 'Enter') { confirmManualPad(); event.preventDefault(); } if(event.key === 'Escape') { cancelManualPad(); event.preventDefault(); }`;
|
||
return `<label style="display:flex;align-items:center;gap:3px;font-size:11px;color:#ccc">${label}<input type="text" id="${id}" value="0.00%" oninput="${onInput}" onblur="${onBlur}" onkeydown="${onKeyDown}" style="width:52px;background:#111;border:1px solid #333;color:#ccc;border-radius:4px;padding:2px 4px;font-size:11px;text-align:right" placeholder="px or %"></label>`;
|
||
};
|
||
bar.innerHTML =
|
||
`<span style="font-size:11px;color:#aaa">Preset:</span>`
|
||
+ `<button class="sb-btn" style="padding:2px 6px;font-size:10px;margin-right:2px;" onclick="applyPadPreset('10%', false)">+10%</button>`
|
||
+ `<button class="sb-btn" style="padding:2px 6px;font-size:10px;margin-right:2px;" onclick="applyPadPreset('20%', false)">+20%</button>`
|
||
+ `<button class="sb-btn" style="padding:2px 6px;font-size:10px;margin-right:2px;" onclick="applyPadPreset('50%', false)">+50%</button>`
|
||
+ `<button class="sb-btn" style="padding:2px 6px;font-size:10px;margin-right:4px;" onclick="applyPadPreset('100px', false)">+100px</button>`
|
||
+ `<button class="sb-btn" style="padding:2px 6px;font-size:10px;margin-right:8px;background:#3b0764;color:#d8b4fe;border-color:#581c87" onclick="applyPadPreset('0', false)">Reset</button>`
|
||
+ `<span style="font-size:11px;color:#aaa;margin-right:4px;">Custom:</span>`
|
||
+ numInput('padTop','↑')
|
||
+ numInput('padRight','→')
|
||
+ numInput('padBottom','↓')
|
||
+ numInput('padLeft','←')
|
||
+ `<label style="display:flex;align-items:center;gap:3px;font-size:11px;color:#ccc;cursor:pointer" title="Keep all padding values equal">`
|
||
+ `<input type="checkbox" id="padUniform" style="cursor:pointer" onchange="togglePadUniform(this)">Uniform</label>`
|
||
+ `<select id="padFill" onchange="updatePadPreview()" style="background:#111;border:1px solid #333;color:#aaa;border-radius:4px;font-size:11px;padding:2px 5px">
|
||
<option value="transparent">Transparent</option>
|
||
<option value="black">Black</option>
|
||
<option value="white">White</option>
|
||
</select>`
|
||
+ `<label style="display:flex;align-items:center;gap:4px;font-size:11px;color:#ccc;cursor:pointer" title="Keep original and pad a copy">`
|
||
+ `<input type="checkbox" id="padAsCopy" checked style="cursor:pointer">Copy</label>`
|
||
+ `<label style="display:flex;align-items:center;gap:4px;font-size:11px;color:#ccc;cursor:pointer" title="Instruct the model to fill in the padded area">`
|
||
+ `<input type="checkbox" id="padOutpaint" style="cursor:pointer">Outpaint</label>`
|
||
+ `<button class="sb-btn" onclick="cancelManualPad()">Cancel</button>`
|
||
+ `<button class="sb-btn primary" id="padConfirmBtn" onclick="confirmManualPad()">Expand</button>`;
|
||
viewer.appendChild(bar);
|
||
updatePadPreview();
|
||
}
|
||
|
||
function cancelManualPad() {
|
||
document.getElementById('padBar')?.remove();
|
||
// Clean up preview overlays
|
||
document.querySelectorAll('.pad-preview-overlay').forEach(el => el.remove());
|
||
}
|
||
|
||
async function confirmManualPad() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname) return;
|
||
const top = document.getElementById('padTop')?.value || '0';
|
||
const right = document.getElementById('padRight')?.value || '0';
|
||
const bottom = document.getElementById('padBottom')?.value || '0';
|
||
const left = document.getElementById('padLeft')?.value || '0';
|
||
|
||
const isZero = (v) => !v || v === '0' || v === '0%' || v === '0px';
|
||
if (isZero(top) && isZero(right) && isZero(bottom) && isZero(left)) { showToast('Enter at least one padding value', 'info'); return; }
|
||
const fill = document.getElementById('padFill')?.value || 'transparent';
|
||
const asCopy = document.getElementById('padAsCopy')?.checked ?? true;
|
||
const outpaint = document.getElementById('padOutpaint')?.checked ?? false;
|
||
const btn = document.getElementById('padConfirmBtn');
|
||
if (btn) { btn.disabled = true; btn.textContent = outpaint ? 'Outpainting…' : 'Padding…'; }
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/pad`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ top, right, bottom, left, fill, as_copy: asCopy, outpaint }),
|
||
});
|
||
if (r.ok) {
|
||
const d = await r.json().catch(() => ({}));
|
||
cancelManualPad();
|
||
if (asCopy && d.new_filename) {
|
||
showToast(outpaint ? 'Outpainted (copy)' : 'Padded (copy)', 'success');
|
||
const newFn = d.new_filename;
|
||
// Clone metadata locally to avoid refresh blink
|
||
filePrompts[newFn] = filePrompts[fname];
|
||
filePoses[newFn] = filePoses[fname];
|
||
fileHasBg[newFn] = outpaint ? true : fileHasBg[fname];
|
||
fileHasClothing[newFn] = fileHasClothing[fname];
|
||
fileContentType[newFn] = 'image';
|
||
fileSourceRefs[newFn] = [fname];
|
||
|
||
lbUrls.splice(lbIdx + 1, 0, IMAGE_FOLDER + newFn + '?t=' + Date.now());
|
||
lbNames.splice(lbIdx + 1, 0, newFn);
|
||
lbIdx = lbIdx + 1;
|
||
|
||
const group = groupData.get(lbCurrentGid);
|
||
if (group) {
|
||
group.urls.splice(lbIdx, 0, lbUrls[lbIdx]);
|
||
group.names.splice(lbIdx, 0, lbNames[lbIdx]);
|
||
group.visibleIdx = group.names.map((_, i) => i).filter(i => !fileHidden[group.names[i]]);
|
||
}
|
||
|
||
updateStudio();
|
||
_followLatestFilename = newFn;
|
||
_followLatestGid = lbCurrentGid;
|
||
refreshNow(true);
|
||
} else {
|
||
showToast('Padded', 'success');
|
||
_bustStudioImage(fname);
|
||
}
|
||
} else {
|
||
showToast('Pad failed: ' + await r.text(), 'error');
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Expand'; }
|
||
}
|
||
} catch (e) {
|
||
showToast('Pad error: ' + e, 'error');
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Pad'; }
|
||
}
|
||
}
|
||
|
||
async function lbDuplicate() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname) return;
|
||
const btn = document.getElementById('lbDuplicateBtn');
|
||
if (btn) btn.disabled = true;
|
||
showToast('Duplicating…');
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/duplicate`, { method: 'POST' });
|
||
if (r.ok) {
|
||
const d = await r.json();
|
||
showToast('Duplicated', 'success');
|
||
const newFn = d.new_filename;
|
||
|
||
// Clone metadata locally to avoid refresh blink
|
||
filePrompts[newFn] = filePrompts[fname];
|
||
filePoses[newFn] = filePoses[fname];
|
||
fileHasBg[newFn] = fileHasBg[fname];
|
||
fileHasClothing[newFn] = fileHasClothing[fname];
|
||
fileContentType[newFn] = fileContentType[fname] || 'image';
|
||
fileSourceRefs[newFn] = [fname];
|
||
|
||
// Insert the duplicate right after the current image in the studio strip
|
||
lbUrls.splice(lbIdx + 1, 0, IMAGE_FOLDER + newFn + '?t=' + Date.now());
|
||
lbNames.splice(lbIdx + 1, 0, newFn);
|
||
lbIdx = lbIdx + 1;
|
||
|
||
const group = groupData.get(lbCurrentGid);
|
||
if (group) {
|
||
group.urls.splice(lbIdx, 0, lbUrls[lbIdx]);
|
||
group.names.splice(lbIdx, 0, lbNames[lbIdx]);
|
||
group.visibleIdx = group.names.map((_, i) => i).filter(i => !fileHidden[group.names[i]]);
|
||
}
|
||
|
||
updateStudio();
|
||
_followLatestGid = lbCurrentGid;
|
||
refreshNow(true);
|
||
} else {
|
||
showToast('Duplicate failed', 'error');
|
||
}
|
||
} catch (e) { showToast('Duplicate failed: ' + e, 'error'); }
|
||
if (btn) btn.disabled = false;
|
||
}
|
||
|
||
async function lbRemoveBg() {
|
||
const fname = lbNames[lbIdx];
|
||
const btn = document.getElementById('lbNoBgBtn');
|
||
if (btn) { btn.disabled = true; btn.textContent = 'Removing…'; }
|
||
showToast('Removing background…');
|
||
try {
|
||
const r = await fetch(`${API}/remove-background/${fname}`, { method: 'POST' });
|
||
if (r.ok) {
|
||
showToast('Background removed', 'success');
|
||
fileHasBg[fname] = false;
|
||
_bustStudioImage(fname); // cache-busts viewer + calls updateStudio()
|
||
refreshNow(true);
|
||
} else {
|
||
showToast(`Failed to remove background for ${fname}`, 'error');
|
||
}
|
||
} catch (e) {
|
||
showToast(`Failed to remove background: ${e}`, 'error');
|
||
} finally {
|
||
if (btn) { btn.disabled = false; btn.textContent = 'No BG'; }
|
||
}
|
||
}
|
||
|
||
async function lbInvertAlpha() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname || !/\.(png|webp)$/i.test(fname)) { showToast('Invert needs a transparent PNG', 'info'); return; }
|
||
const btn = document.getElementById('lbInvertAlphaBtn');
|
||
if (btn) btn.disabled = true;
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/invert-alpha`, { method: 'POST' });
|
||
if (r.ok) {
|
||
showToast('Transparency inverted', 'success');
|
||
fileHasBg[fname] = false;
|
||
_bustStudioImage(fname);
|
||
refreshNow(true);
|
||
} else {
|
||
showToast('Invert failed: ' + await r.text(), 'error');
|
||
}
|
||
} catch (e) { showToast('Invert failed: ' + e, 'error'); }
|
||
if (btn) btn.disabled = false;
|
||
}
|
||
|
||
async function lbMakeVideo() {
|
||
// Use Shift+Click selected refs if any, otherwise all images in the current group.
|
||
let filenames;
|
||
if (_sbRefIndices.length >= 2) {
|
||
filenames = _sbRefIndices.map(i => lbNames[i]).filter(n => n && !isVideo(n));
|
||
if (filenames.length < 2) { showToast('Need at least 2 non-video images selected (Shift+Click filmstrip)', 'info'); return; }
|
||
} else {
|
||
filenames = lbNames.filter(n => n && !isVideo(n));
|
||
if (filenames.length < 2) { showToast('Need at least 2 images in this group', 'info'); return; }
|
||
}
|
||
const btn = document.getElementById('lbMakeVideoBtn');
|
||
if (btn) { btn.disabled = true; btn.textContent = 'Creating…'; }
|
||
try {
|
||
const r = await fetch(`${API}/generate-video`, {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ filenames, fps: 8, loop: true }),
|
||
});
|
||
if (!r.ok) { showToast('Video failed: ' + await r.text(), 'error'); return; }
|
||
const d = await r.json();
|
||
showToast(`Video created: ${d.output}`, 'success');
|
||
_followLatestGid = lbCurrentGid;
|
||
refreshNow(true);
|
||
} catch (e) { showToast('Video error: ' + e, 'error'); }
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Make video'; }
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Orbit 3D preview
|
||
// ---------------------------------------------------------------------------
|
||
let _orbitPollTimer = null;
|
||
let _orbitEngine = 'depth'; // 'depth' or 'qwen'
|
||
let _orbitFrames = []; // frame URLs for flipper
|
||
let _orbitFlipperIdx = 0;
|
||
let _orbitFlipperTimer = null;
|
||
let _orbitFlipperFps = 1;
|
||
let _orbitFlipperRunning = false;
|
||
let _orbitCurrentMp4 = null; // last downloaded MP4 rel path
|
||
|
||
// ---- engine toggle ----
|
||
function orbitSetEngine(engine) {
|
||
_orbitEngine = engine;
|
||
document.getElementById('orbitEngineDepth').classList.toggle('active', engine === 'depth');
|
||
document.getElementById('orbitEngineQwen').classList.toggle('active', engine === 'qwen');
|
||
document.getElementById('orbitDepthControls').style.display = engine === 'depth' ? 'flex' : 'none';
|
||
document.getElementById('orbitQwenControls').classList.toggle('visible', engine === 'qwen');
|
||
if (engine === 'qwen') _refreshQwenCacheHint();
|
||
}
|
||
|
||
async function _refreshQwenCacheHint() {
|
||
const fname = lbNames[lbIdx];
|
||
const cacheEl = document.getElementById('orbitQwenCacheStatus');
|
||
const cacheBtn = document.getElementById('orbitLoadCacheBtn');
|
||
if (!fname) return;
|
||
cacheEl.style.display = 'none';
|
||
cacheBtn.style.display = 'none';
|
||
try {
|
||
const r = await fetch(`${IMAGE_FOLDER}_data/turntables.json?t=` + Date.now());
|
||
if (!r.ok) return;
|
||
const d = await r.json();
|
||
_turntablesData = d.turntables || [];
|
||
const found = _turntablesData.find(t =>
|
||
t.preferred_filename === fname || lbNames.includes(t.preferred_filename)
|
||
);
|
||
if (!found) return;
|
||
cacheEl.style.display = '';
|
||
if (found.completed) {
|
||
cacheEl.textContent = '✓ Pre-generated — instant load available';
|
||
cacheEl.style.color = '#4ade80';
|
||
cacheBtn.style.display = '';
|
||
} else {
|
||
const pct = Math.round(found.n_done / found.n_total * 100);
|
||
cacheEl.textContent = `Background: ${found.n_done}/${found.n_total} views (${pct}%)`;
|
||
cacheEl.style.color = '#f59e0b';
|
||
}
|
||
} catch(_) {}
|
||
}
|
||
|
||
// ---- open modal in settings state ----
|
||
function lbOrbit() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname || isVideo(fname)) { showToast('Select an image first', 'info'); return; }
|
||
// Always open in settings, never auto-play previous result
|
||
_orbitShowSettings();
|
||
document.getElementById('orbitModal').classList.add('open');
|
||
if (_orbitEngine === 'qwen') _refreshQwenCacheHint();
|
||
}
|
||
|
||
function _orbitShowSettings() {
|
||
_orbitFlipperStop();
|
||
document.getElementById('orbitSettings').style.display = '';
|
||
document.getElementById('orbitPlayer').style.display = 'none';
|
||
}
|
||
|
||
function orbitBackToSettings() {
|
||
_orbitShowSettings();
|
||
if (_orbitEngine === 'qwen') _refreshQwenCacheHint();
|
||
}
|
||
|
||
// ---- Generate button ----
|
||
async function orbitGenerate() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname) return;
|
||
|
||
// Switch to player state showing "rendering" status
|
||
document.getElementById('orbitSettings').style.display = 'none';
|
||
document.getElementById('orbitPlayer').style.display = '';
|
||
const status = document.getElementById('orbitStatus');
|
||
status.className = 'orbit-status';
|
||
document.getElementById('orbitFlipperWrap').classList.remove('visible');
|
||
document.getElementById('orbitVideo').style.display = 'none';
|
||
document.getElementById('orbitDlFramesBtn').style.display = 'none';
|
||
document.getElementById('orbitDlBtn').style.display = 'none';
|
||
document.getElementById('orbitNobgHint').style.display = 'none';
|
||
|
||
let body;
|
||
if (_orbitEngine === 'qwen') {
|
||
const n_views = parseInt(document.getElementById('orbitNviewsSelect').value, 10);
|
||
const steps = parseInt(document.getElementById('orbitStepsSelect').value, 10);
|
||
body = { filename: fname, engine: 'qwen', n_views, steps };
|
||
status.textContent = 'Generating Qwen turntable (~9 min for 24 views)…';
|
||
} else {
|
||
const parallax = parseInt(document.getElementById('orbitParallaxSlider').value, 10) / 100;
|
||
const mode = document.getElementById('orbitModeSelect').value;
|
||
body = { filename: fname, engine: 'depth', parallax, mode, n_frames: 36, fps: 24 };
|
||
status.textContent = 'Rendering orbit frames…';
|
||
}
|
||
|
||
let jobId;
|
||
try {
|
||
const r = await fetch(`${API}/orbit`, {
|
||
method: 'POST', headers: {'Content-Type':'application/json'},
|
||
body: JSON.stringify(body),
|
||
});
|
||
if (!r.ok) {
|
||
status.textContent = 'Error: ' + await r.text();
|
||
status.className = 'orbit-status err';
|
||
return;
|
||
}
|
||
const d = await r.json();
|
||
jobId = d.job_id;
|
||
if (d.cached) status.textContent = 'Loading cached turntable…';
|
||
} catch(e) {
|
||
status.textContent = 'Fetch error: ' + e;
|
||
status.className = 'orbit-status err';
|
||
return;
|
||
}
|
||
|
||
_orbitPoll(jobId);
|
||
}
|
||
|
||
// ---- Load cached instantly ----
|
||
async function orbitLoadCache() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname) return;
|
||
document.getElementById('orbitSettings').style.display = 'none';
|
||
document.getElementById('orbitPlayer').style.display = '';
|
||
const status = document.getElementById('orbitStatus');
|
||
status.textContent = 'Loading cached turntable…';
|
||
status.className = 'orbit-status';
|
||
document.getElementById('orbitFlipperWrap').classList.remove('visible');
|
||
document.getElementById('orbitNobgHint').style.display = 'none';
|
||
|
||
try {
|
||
const r = await fetch(`${IMAGE_FOLDER}_data/turntables.json?t=` + Date.now());
|
||
if (!r.ok) throw new Error('turntables.json not found');
|
||
const d = await r.json();
|
||
_turntablesData = d.turntables || [];
|
||
const found = _turntablesData.find(t =>
|
||
t.preferred_filename === fname || lbNames.includes(t.preferred_filename)
|
||
);
|
||
if (!found || !found.frames || !found.frames.length) {
|
||
status.textContent = 'No cached frames found';
|
||
status.className = 'orbit-status err';
|
||
return;
|
||
}
|
||
_orbitShowResult({ frames: found.frames, video_filename: found.video_path });
|
||
} catch(e) {
|
||
status.textContent = 'Error: ' + e;
|
||
status.className = 'orbit-status err';
|
||
}
|
||
}
|
||
|
||
// ---- Poll job until done ----
|
||
function _orbitPoll(jobId) {
|
||
if (_orbitPollTimer) clearInterval(_orbitPollTimer);
|
||
let dots = 0;
|
||
_orbitPollTimer = setInterval(async () => {
|
||
dots = (dots + 1) % 4;
|
||
try {
|
||
const jd = await (await fetch(`${API}/batch/${jobId}`)).json();
|
||
const status = document.getElementById('orbitStatus');
|
||
if (jd.status === 'running') {
|
||
const detail = jd.status_detail ? ` (${jd.status_detail})` : '';
|
||
status.textContent = 'Rendering' + '.'.repeat(dots + 1) + detail;
|
||
} else if (jd.status === 'done') {
|
||
clearInterval(_orbitPollTimer);
|
||
_orbitShowResult(jd);
|
||
} else {
|
||
clearInterval(_orbitPollTimer);
|
||
status.textContent = 'Failed: ' + (jd.error || jd.status);
|
||
status.className = 'orbit-status err';
|
||
}
|
||
} catch(_) {}
|
||
}, 1500);
|
||
}
|
||
|
||
// ---- Display result ----
|
||
function _orbitShowResult(jd) {
|
||
const status = document.getElementById('orbitStatus');
|
||
const cached = jd.cached ? ' (cached)' : '';
|
||
|
||
if (jd.frames && jd.frames.length > 0) {
|
||
// Frame flipper — shows transparency
|
||
_orbitStartFlipper(jd.frames);
|
||
status.textContent = `Done${cached} — ${jd.frames.length} frames`;
|
||
status.className = 'orbit-status ok';
|
||
document.getElementById('orbitDlFramesBtn').style.display = '';
|
||
if (jd.video_filename) {
|
||
_orbitCurrentMp4 = jd.video_filename;
|
||
document.getElementById('orbitDlBtn').style.display = '';
|
||
}
|
||
} else if (jd.video_filename) {
|
||
// Depth-card MP4 fallback
|
||
const mp4 = jd.video_filename;
|
||
const src = IMAGE_FOLDER + mp4;
|
||
const vid = document.getElementById('orbitVideo');
|
||
vid.src = src + '?t=' + Date.now();
|
||
vid.style.display = '';
|
||
vid.load(); vid.play().catch(()=>{});
|
||
status.textContent = `Done${cached}`;
|
||
status.className = 'orbit-status ok';
|
||
_orbitCurrentMp4 = mp4;
|
||
document.getElementById('orbitDlBtn').style.display = '';
|
||
if (jd.has_alpha === false)
|
||
document.getElementById('orbitNobgHint').style.display = '';
|
||
}
|
||
_pollTurntableBadge();
|
||
}
|
||
|
||
// ---- Frame flipper engine ----
|
||
function _orbitStartFlipper(frameRelPaths) {
|
||
_orbitFlipperStop();
|
||
_orbitFrames = frameRelPaths.map(p => IMAGE_FOLDER + p);
|
||
_orbitFlipperIdx = 0;
|
||
_orbitFlipperRunning = true;
|
||
|
||
const wrap = document.getElementById('orbitFlipperWrap');
|
||
wrap.classList.add('visible');
|
||
document.getElementById('orbitVideo').style.display = 'none';
|
||
document.getElementById('orbitFlipperPlayBtn').textContent = '⏸ Pause';
|
||
|
||
const img = document.getElementById('orbitFlipperImg');
|
||
if (_orbitFrames.length > 0) img.src = _orbitFrames[0];
|
||
|
||
_orbitFlipperTimer = setInterval(() => {
|
||
_orbitFlipperIdx = (_orbitFlipperIdx + 1) % _orbitFrames.length;
|
||
document.getElementById('orbitFlipperImg').src = _orbitFrames[_orbitFlipperIdx];
|
||
document.getElementById('orbitFrameCounter').textContent =
|
||
`${_orbitFlipperIdx + 1}/${_orbitFrames.length}`;
|
||
}, Math.round(1000 / _orbitFlipperFps));
|
||
}
|
||
|
||
function _orbitFlipperStop() {
|
||
if (_orbitFlipperTimer) { clearInterval(_orbitFlipperTimer); _orbitFlipperTimer = null; }
|
||
_orbitFlipperRunning = false;
|
||
}
|
||
|
||
function orbitFlipperToggle() {
|
||
if (_orbitFlipperRunning) {
|
||
_orbitFlipperStop();
|
||
document.getElementById('orbitFlipperPlayBtn').textContent = '▶ Play';
|
||
} else {
|
||
_orbitStartFlipper(_orbitFrames.map(u => u.startsWith(IMAGE_FOLDER) ? u.slice(IMAGE_FOLDER.length) : u));
|
||
document.getElementById('orbitFlipperPlayBtn').textContent = '⏸ Pause';
|
||
}
|
||
}
|
||
|
||
function _orbitSetFps(fps) {
|
||
_orbitFlipperFps = fps;
|
||
if (_orbitFlipperRunning && _orbitFrames.length > 0) {
|
||
// Restart with new interval
|
||
_orbitFlipperStop();
|
||
_orbitStartFlipper(_orbitFrames.map(u => u.startsWith(IMAGE_FOLDER) ? u.slice(IMAGE_FOLDER.length) : u));
|
||
}
|
||
}
|
||
|
||
// ---- Download helpers ----
|
||
function orbitDownload() {
|
||
if (!_orbitCurrentMp4) return;
|
||
const a = document.createElement('a');
|
||
a.href = IMAGE_FOLDER + _orbitCurrentMp4;
|
||
a.download = _orbitCurrentMp4.split('/').pop();
|
||
a.click();
|
||
}
|
||
|
||
function orbitDownloadFrames() {
|
||
// Download all frames as individual files
|
||
if (!_orbitFrames.length) return;
|
||
_orbitFrames.forEach((url, i) => {
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = `orbit_frame_${String(i).padStart(3,'0')}.png`;
|
||
// Stagger downloads slightly to avoid browser throttle
|
||
setTimeout(() => a.click(), i * 80);
|
||
});
|
||
}
|
||
|
||
function closeOrbitModal() {
|
||
if (_orbitPollTimer) { clearInterval(_orbitPollTimer); _orbitPollTimer = null; }
|
||
_orbitFlipperStop();
|
||
document.getElementById('orbitModal').classList.remove('open');
|
||
const v = document.getElementById('orbitVideo');
|
||
v.pause(); v.src = '';
|
||
}
|
||
|
||
// Close on backdrop click
|
||
document.getElementById('orbitModal').addEventListener('click', function(e) {
|
||
if (e.target === this) closeOrbitModal();
|
||
});
|
||
|
||
// --- Background turntable progress badge + sidebar orbit tab ----------
|
||
let _turntablesData = []; // cached from _data/turntables.json
|
||
|
||
async function _pollTurntableBadge() {
|
||
try {
|
||
const r = await fetch(`${API}/turntable/status`);
|
||
if (!r.ok) return;
|
||
const d = await r.json();
|
||
const badge = document.getElementById('orbitBadge');
|
||
const tbStatus = document.getElementById('turntableBgStatus');
|
||
const tbsDot = document.getElementById('tbsDot');
|
||
const tbsText = document.getElementById('tbsText');
|
||
const total = d.n_total || 0;
|
||
const done = d.n_complete || 0;
|
||
if (total === 0) { badge.style.display = 'none'; }
|
||
else if (done < total) { badge.textContent = `${done}/${total}`; badge.style.display = ''; }
|
||
else { badge.style.display = 'none'; }
|
||
|
||
if (document.getElementById('orbitModal').classList.contains('open') && tbStatus) {
|
||
tbStatus.style.display = 'flex';
|
||
if (d.is_generating) {
|
||
tbsDot.className = 'tbs-dot active';
|
||
tbsText.textContent = `Generating — ${done}/${total} actors done`;
|
||
} else if (done >= total && total > 0) {
|
||
tbsDot.className = 'tbs-dot done';
|
||
tbsText.textContent = `All ${total} actors pre-generated`;
|
||
} else if (d.is_paused) {
|
||
tbsDot.className = 'tbs-dot';
|
||
tbsText.textContent = 'Background generation paused';
|
||
} else {
|
||
tbsDot.className = 'tbs-dot';
|
||
tbsText.textContent = `Idle — ${done}/${total} actors have turntables (resumes after 45s idle)`;
|
||
}
|
||
}
|
||
|
||
// Refresh orbit tab badge on the tab button
|
||
const tabBtn = document.getElementById('sbTabOrbit');
|
||
if (tabBtn && done > 0) {
|
||
tabBtn.textContent = `Orbit${done < total ? ' (' + done + '/' + total + ')' : ' ✓'}`;
|
||
}
|
||
} catch(_) {}
|
||
}
|
||
setInterval(_pollTurntableBadge, 15000);
|
||
_pollTurntableBadge();
|
||
|
||
// ---- Orbit sidebar tab ----
|
||
let _orbitTabFlippers = {}; // group_id -> {timer, idx, frames, img}
|
||
|
||
async function renderSidebarOrbit() {
|
||
// Stop any existing full-player and clean up mini-flippers to avoid timer leaks
|
||
const oldViewer = document.getElementById('orbitFullViewer');
|
||
if (oldViewer && oldViewer._flipperTimer) {
|
||
clearTimeout(oldViewer._flipperTimer);
|
||
oldViewer._flipperTimer = null;
|
||
}
|
||
for (const gid in _orbitTabFlippers) {
|
||
if (_orbitTabFlippers[gid] && _orbitTabFlippers[gid].timer) {
|
||
clearInterval(_orbitTabFlippers[gid].timer);
|
||
}
|
||
}
|
||
_orbitTabFlippers = {};
|
||
|
||
const panel = document.getElementById('sbPanelOrbit');
|
||
panel.innerHTML = '<div style="font-size:11px;color:#555;padding:8px 0">Loading…</div>';
|
||
try {
|
||
const r = await fetch(`${IMAGE_FOLDER}_data/turntables.json?t=` + Date.now());
|
||
if (!r.ok) { panel.innerHTML = '<div style="font-size:11px;color:#555;padding:8px 0">No turntables yet.</div>'; return; }
|
||
const { turntables } = await r.json();
|
||
_turntablesData = turntables || [];
|
||
} catch(_) {
|
||
panel.innerHTML = '<div style="font-size:11px;color:#555;padding:8px 0">No turntables yet.</div>';
|
||
return;
|
||
}
|
||
|
||
if (!_turntablesData.length) {
|
||
panel.innerHTML = '<div style="font-size:11px;color:#555;padding:8px 0">No turntables generated yet.<br>Background generation starts when the app is idle for 45s.</div>';
|
||
return;
|
||
}
|
||
|
||
// Header with controls
|
||
let html = `<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">
|
||
<span style="font-size:11px;color:#666">${_turntablesData.length} groups</span>
|
||
<button class="sb-btn" style="font-size:10px" onclick="renderSidebarOrbit()">↻ Refresh</button>
|
||
</div>
|
||
<div id="orbitFullViewer" class="orbit-full-player"></div>
|
||
<div class="orbit-tab-grid" id="orbitTabGrid">`;
|
||
|
||
for (const t of _turntablesData) {
|
||
const label = t.group_name || t.group_id.slice(0, 8);
|
||
const thumb = t.frames.length > 0 ? IMAGE_FOLDER + t.frames[0] : '';
|
||
const compClass = t.completed ? ' complete' : '';
|
||
html += `<div class="orbit-tab-card${compClass}" onclick="_orbitTabOpen('${t.group_id}')" id="otc_${t.group_id}">
|
||
<div class="otc-img-wrap"><img id="otcImg_${t.group_id}" src="${thumb}" alt="${label}"></div>
|
||
<div class="otc-name" title="${label}">${label}</div>
|
||
<div class="otc-progress">${t.completed ? '✓' : `${t.n_done}/${t.n_total}`}</div>
|
||
</div>`;
|
||
}
|
||
html += '</div>';
|
||
panel.innerHTML = html;
|
||
|
||
// Start mini-flippers for completed turntables
|
||
for (const t of _turntablesData) {
|
||
if (t.completed && t.frames.length > 1) {
|
||
_orbitStartTabFlipper(t.group_id, t.frames);
|
||
}
|
||
}
|
||
}
|
||
|
||
function _orbitStartTabFlipper(groupId, frameRels) {
|
||
const imgEl = document.getElementById(`otcImg_${groupId}`);
|
||
if (!imgEl) return;
|
||
const urls = frameRels.map(f => IMAGE_FOLDER + f);
|
||
let idx = 0;
|
||
if (_orbitTabFlippers[groupId]) clearInterval(_orbitTabFlippers[groupId].timer);
|
||
const timer = setInterval(() => {
|
||
idx = (idx + 1) % urls.length;
|
||
const el = document.getElementById(`otcImg_${groupId}`);
|
||
if (el) el.src = urls[idx];
|
||
}, 1000); // 1fps in thumbnails
|
||
_orbitTabFlippers[groupId] = { timer, idx, urls };
|
||
}
|
||
|
||
function _orbitTabOpen(groupId) {
|
||
const t = _turntablesData.find(x => x.group_id === groupId);
|
||
if (!t || !t.frames.length) return;
|
||
|
||
const viewer = document.getElementById('orbitFullViewer');
|
||
if (!viewer) return;
|
||
|
||
// Stop any existing full-player
|
||
if (viewer._flipperTimer) { clearTimeout(viewer._flipperTimer); viewer._flipperTimer = null; }
|
||
|
||
const urls = t.frames.map(f => IMAGE_FOLDER + f);
|
||
let idx = 0;
|
||
const label = t.group_name || t.group_id.slice(0, 8);
|
||
|
||
viewer.className = 'orbit-full-player visible';
|
||
viewer.innerHTML = `
|
||
<div style="display:flex;justify-content:space-between;align-items:center;flex-shrink:0">
|
||
<span style="font-size:11px;color:#aaa;font-weight:600">${label}</span>
|
||
<button class="sb-btn" style="font-size:10px" onclick="
|
||
clearTimeout(this.closest('.orbit-full-player')._flipperTimer);
|
||
this.closest('.orbit-full-player').classList.remove('visible')">✕</button>
|
||
</div>
|
||
<div class="orbit-full-img-wrap">
|
||
<img id="orbitFullImg" src="${urls[0]}">
|
||
</div>
|
||
<div style="display:flex;gap:8px;align-items:center;flex-wrap:nowrap;flex-shrink:0">
|
||
<label style="font-size:11px;color:#666;display:flex;align-items:center;gap:6px;white-space:nowrap">
|
||
Speed
|
||
<input type="range" id="orbitFullFpsSlider" min="1" max="30" value="1" step="1"
|
||
style="width:60px;accent-color:#f59e0b"
|
||
oninput="_orbitFullSetFps(+this.value,this.nextElementSibling)">
|
||
<span style="min-width:30px">1fps</span>
|
||
</label>
|
||
<span id="orbitFullCounter" style="font-size:10px;color:#555;white-space:nowrap">1/${urls.length}</span>
|
||
</div>
|
||
<div style="margin-top:10px;display:flex;justify-content:flex-end">
|
||
<button class="sb-btn" style="font-size:10px;color:#ef4444" onclick="resetOrbit('${groupId}')">Reset / Remove Orbit</button>
|
||
</div>`;
|
||
|
||
let fps = 1;
|
||
function tick() {
|
||
idx = (idx + 1) % urls.length;
|
||
const el = document.getElementById('orbitFullImg');
|
||
if (el) el.src = urls[idx];
|
||
const ctr = document.getElementById('orbitFullCounter');
|
||
if (ctr) ctr.textContent = `${idx+1}/${urls.length}`;
|
||
viewer._flipperTimer = setTimeout(tick, Math.round(1000 / fps));
|
||
}
|
||
window._orbitFullSetFps = (v, label) => { fps = v; if(label) label.textContent = v+'fps'; };
|
||
viewer._flipperTimer = setTimeout(tick, Math.round(1000 / fps));
|
||
|
||
// Scroll into view
|
||
viewer.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||
}
|
||
|
||
async function resetOrbit(groupId) {
|
||
if (!confirm(`Are you sure you want to completely remove all orbit files and database records for group: ${groupId}?`)) return;
|
||
try {
|
||
const r = await fetch(`${API}/turntable/${encodeURIComponent(groupId)}`, { method: 'DELETE' });
|
||
if (r.ok) {
|
||
showToast('Orbit files and DB records removed successfully', 'success');
|
||
const viewer = document.getElementById('orbitFullViewer');
|
||
if (viewer) {
|
||
if (viewer._flipperTimer) {
|
||
clearTimeout(viewer._flipperTimer);
|
||
viewer._flipperTimer = null;
|
||
}
|
||
viewer.classList.remove('visible');
|
||
viewer.innerHTML = '';
|
||
}
|
||
setTimeout(refreshNow, 500);
|
||
} else {
|
||
const text = await r.text();
|
||
showToast(`Failed to reset orbit: ${text}`, 'error');
|
||
}
|
||
} catch (e) {
|
||
showToast(`Failed to reset orbit: ${e}`, 'error');
|
||
}
|
||
}
|
||
|
||
// Force the studio image (filmstrip + main viewer) to reload from disk.
|
||
function _bustStudioImage(fname) {
|
||
const t = Date.now();
|
||
const i = lbNames.indexOf(fname);
|
||
if (i >= 0) lbUrls[i] = IMAGE_FOLDER + fname + '?t=' + t;
|
||
const img = document.getElementById('lbImg');
|
||
if (img && lbNames[lbIdx] === fname) img.src = IMAGE_FOLDER + fname + '?t=' + t;
|
||
updateStudio();
|
||
}
|
||
|
||
function archiveGroup(el, event) {
|
||
if (event) event.stopPropagation();
|
||
const card = el.closest('.image-card');
|
||
card.classList.add('confirming-archive');
|
||
}
|
||
|
||
function cancelArchiveGroup(el, event) {
|
||
if (event) event.stopPropagation();
|
||
const card = el.closest('.image-card');
|
||
card.classList.remove('confirming-archive');
|
||
}
|
||
|
||
async function confirmArchiveGroup(el, event) {
|
||
if (event) event.stopPropagation();
|
||
const card = el.closest('.image-card');
|
||
const gid = card.dataset.group;
|
||
|
||
// Get all filenames in this group from groupData
|
||
const data = groupData.get(gid);
|
||
const filenames = data ? data.names : [];
|
||
|
||
try {
|
||
const r = await fetch(`${API}/groups/${gid}/archive`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ filenames })
|
||
});
|
||
if (r.ok) {
|
||
const d = await r.json();
|
||
showToast(`Group ${gid} archived (${d.count || 0} items)`, 'success');
|
||
console.log("[archive] Response:", d);
|
||
// Immediately hide the card from UI to feel responsive
|
||
card.style.opacity = '0.5';
|
||
card.style.pointerEvents = 'none';
|
||
setTimeout(refreshNow, 700);
|
||
} else {
|
||
showToast(`Failed to archive group ${gid}`, 'error');
|
||
card.classList.remove('confirming-archive');
|
||
}
|
||
} catch (e) {
|
||
showToast(`Failed to archive group: ${e}`, 'error');
|
||
card.classList.remove('confirming-archive');
|
||
}
|
||
}
|
||
|
||
async function removeBgGroup(el, event) {
|
||
if (event) event.stopPropagation();
|
||
const gid = el.closest('.image-card').dataset.group;
|
||
showToast(`Removing background for group ${gid}...`);
|
||
try {
|
||
const r = await fetch(`${API}/remove-background/group/${gid}`, { method: 'POST' });
|
||
if (r.ok) {
|
||
showToast(`Background removal started for group ${gid}`, 'info');
|
||
} else {
|
||
showToast(`Failed to start background removal for group ${gid}`, 'error');
|
||
}
|
||
} catch (e) {
|
||
showToast(`Failed to remove group background: ${e}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function handleUpload(eventOrFiles) {
|
||
const files = eventOrFiles.target ? eventOrFiles.target.files : eventOrFiles;
|
||
if (!files || files.length === 0) return;
|
||
|
||
showToast(`Uploading ${files.length} items...`);
|
||
|
||
for (const file of files) {
|
||
const formData = new FormData();
|
||
formData.append('image', file);
|
||
|
||
// If it's a pasted file, it might not have a useful name
|
||
const fileName = file.name || 'clipboard.png';
|
||
|
||
try {
|
||
const r = await fetch(`${API}/upload`, {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
if (r.ok) {
|
||
showToast(`Upload successful: ${fileName}`);
|
||
} else {
|
||
showToast(`Upload failed for ${fileName}`, 'error');
|
||
}
|
||
} catch (e) {
|
||
showToast(`Error uploading ${fileName}: ${e}`, 'error');
|
||
}
|
||
}
|
||
// The source image is registered by a background task slightly after /upload
|
||
// returns; one debounced refresh picks it up without piling up concurrent loads.
|
||
setTimeout(refreshNow, 800);
|
||
}
|
||
|
||
// --- clipboard paste support ---
|
||
window.addEventListener('paste', async (e) => {
|
||
const items = e.clipboardData.items;
|
||
const files = [];
|
||
for (const item of items) {
|
||
if (item.type.indexOf('image') !== -1) {
|
||
const blob = item.getAsFile();
|
||
files.push(blob);
|
||
}
|
||
}
|
||
if (files.length > 0) {
|
||
// If studio is open, offer to add to current group without triggering poses
|
||
if (lbCurrentGid) {
|
||
showPasteGroupDialog(files);
|
||
} else {
|
||
handleUpload(files);
|
||
}
|
||
}
|
||
});
|
||
|
||
function showToast(message, type = 'info', duration = 3000) {
|
||
const toast = document.getElementById('toast');
|
||
toast.textContent = message;
|
||
toast.className = `toast ${type} show`;
|
||
setTimeout(() => toast.classList.remove('show'), duration);
|
||
}
|
||
|
||
function formatTime(date) {
|
||
const now = new Date();
|
||
const diff = Math.floor((now - date) / 1000);
|
||
|
||
if (diff < 60) return 'Just now';
|
||
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
|
||
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
|
||
return date.toLocaleDateString();
|
||
}
|
||
|
||
function formatFileSize(bytes) {
|
||
if (bytes === 0) return '0 B';
|
||
const k = 1024;
|
||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
||
}
|
||
|
||
// Try to get file info using HEAD requests (works with some local servers)
|
||
async function getFileInfo(filename) {
|
||
try {
|
||
const response = await fetch(IMAGE_FOLDER + filename, { method: 'HEAD' });
|
||
const lastMod = response.headers.get('last-modified');
|
||
const size = response.headers.get('content-length');
|
||
return {
|
||
modified: lastMod ? new Date(lastMod) : new Date(),
|
||
size: size ? parseInt(size) : 0
|
||
};
|
||
} catch {
|
||
return { modified: new Date(), size: 0 };
|
||
}
|
||
}
|
||
|
||
async function scanFolder() {
|
||
// For local file:// protocol, we can't list directories.
|
||
// This works when served via HTTP or you can manually list files.
|
||
|
||
// METHOD 1: If you have a simple file server, it might return directory listing
|
||
try {
|
||
const response = await fetch(IMAGE_FOLDER);
|
||
const text = await response.text();
|
||
|
||
// Try to parse common directory listing formats
|
||
const parser = new DOMParser();
|
||
const doc = parser.parseFromString(text, 'text/html');
|
||
const links = Array.from(doc.querySelectorAll('a'))
|
||
.map(a => a.getAttribute('href'))
|
||
.filter(href => href && EXTENSIONS.some(ext => href.toLowerCase().endsWith(ext)))
|
||
.map(href => href.split('/').pop()); // Just the filename
|
||
|
||
if (links.length > 0) return links;
|
||
} catch (e) {
|
||
// Directory listing not available
|
||
}
|
||
|
||
// METHOD 2: Manual file list (fallback)
|
||
// If you can't use a server, list your files here or use the manual input below
|
||
return null;
|
||
}
|
||
|
||
function _hydrateImageMeta(imgs) {
|
||
imgs.forEach(img => {
|
||
if (img.name) imageNames[img.filename] = img.name;
|
||
if (img.clip_description)clipDescriptions[img.filename]= img.clip_description;
|
||
if (img.pose) filePoses[img.filename] = img.pose;
|
||
if (img.prompt) filePrompts[img.filename] = img.prompt;
|
||
if (img.group_name && img.group_id) groupNames[img.group_id] = img.group_name;
|
||
fileSortOrders[img.filename] = img.sort_order ?? null;
|
||
fileHidden[img.filename] = !!img.hidden;
|
||
fileArchived[img.filename] = !!img.archived;
|
||
fileHasBg[img.filename] = img.has_background !== false;
|
||
fileHasClothing[img.filename] = img.has_clothing ?? null;
|
||
fileContentType[img.filename] = img.content_type || 'image';
|
||
fileFaceswapSrcVideo[img.filename] = img.faceswap_source_video || null;
|
||
if (img.source_refs) {
|
||
try { fileSourceRefs[img.filename] = JSON.parse(img.source_refs); } catch(e) {}
|
||
}
|
||
if (img.group_id) customGroups[img.filename] = img.group_id;
|
||
});
|
||
}
|
||
|
||
async function loadImages(bypassStatic = false) {
|
||
if (loadImages._inFlight) return;
|
||
loadImages._inFlight = true;
|
||
const gallery = document.getElementById('gallery');
|
||
const statusDot = document.getElementById('statusDot');
|
||
statusDot.classList.add('updating');
|
||
|
||
let files = null;
|
||
let _staticOk = false;
|
||
|
||
// Primary: pre-generated static JSON (fast, no DB round-trip per request)
|
||
try {
|
||
if (bypassStatic) throw new Error("Bypass static requested");
|
||
const r = await fetch(`${API}/output/_data/images.json?t=${Date.now()}`, { signal: AbortSignal.timeout(4000) });
|
||
if (r.ok) {
|
||
const data = await r.json();
|
||
let imgs = data.images || [];
|
||
imgs = _archiveViewActive ? imgs.filter(i => i.archived) : imgs.filter(i => !i.archived);
|
||
files = imgs.map(img => img.filename);
|
||
_hydrateImageMeta(imgs);
|
||
_staticOk = true;
|
||
}
|
||
} catch (e) { /* fall through */ }
|
||
|
||
// Fallback: live API (handles first startup before static file is written)
|
||
if (!files) {
|
||
try {
|
||
const url = _archiveViewActive ? `${API}/images?archived=true` : `${API}/images`;
|
||
const r = await fetch(url, { signal: AbortSignal.timeout(12000) });
|
||
if (r.ok) {
|
||
const data = await r.json();
|
||
files = data.images.map(img => img.filename);
|
||
_hydrateImageMeta(data.images);
|
||
}
|
||
} catch (e) { /* API not reachable, fall through */ }
|
||
}
|
||
|
||
// Fallback: static list hydrated at page-load time
|
||
if (!files && typeof PRELOADED_IMAGES !== 'undefined' && PRELOADED_IMAGES.length > 0) {
|
||
files = PRELOADED_IMAGES;
|
||
}
|
||
|
||
if (!files) files = await scanFolder();
|
||
if (!files) files = await discoverImages();
|
||
|
||
const t = Date.now();
|
||
const fileObjects = files.map(name => ({
|
||
url: IMAGE_FOLDER + name + '?t=' + t,
|
||
cleanName: name.split('?')[0]
|
||
}));
|
||
|
||
const newFileNames = new Set(fileObjects.filter(f => !knownFiles.has(f.cleanName)).map(f => f.cleanName));
|
||
newFileNames.forEach(n => knownFiles.add(n));
|
||
currentFiles = fileObjects;
|
||
|
||
if (availablePoses && Object.keys(availablePoses).length === 0) {
|
||
loadPoses();
|
||
}
|
||
if (!availableVideos.length) {
|
||
loadTemplateVideos();
|
||
}
|
||
|
||
if (fileObjects.length === 0) {
|
||
// Only show "no images" if gallery is currently empty (avoid flash on slow API)
|
||
if (!gallery.querySelector('.image-card')) {
|
||
gallery.innerHTML = `
|
||
<div class="empty-state" style="grid-column: 1 / -1;">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
||
<circle cx="8.5" cy="8.5" r="1.5"/>
|
||
<path d="M21 15l-5-5L5 21"/>
|
||
</svg>
|
||
<h2>No images found</h2>
|
||
<p>Place images in the output folder and refresh.</p>
|
||
</div>`;
|
||
}
|
||
statusDot.classList.remove('updating');
|
||
loadImages._inFlight = false;
|
||
return;
|
||
}
|
||
|
||
// Refresh names/groups/group-names — skip if already hydrated from static JSON
|
||
if (!_staticOk) {
|
||
try {
|
||
const [nr, gr, gnr] = await Promise.all([
|
||
fetch(`${API}/names`, { signal: AbortSignal.timeout(2000) }),
|
||
fetch(`${API}/groups`, { signal: AbortSignal.timeout(2000) }),
|
||
fetch(`${API}/group-names`, { signal: AbortSignal.timeout(2000) }),
|
||
]);
|
||
if (nr.ok) imageNames = await nr.json();
|
||
if (gr.ok) customGroups = await gr.json();
|
||
if (gnr.ok) groupNames = await gnr.json();
|
||
} catch (e) { /* non-fatal */ }
|
||
}
|
||
|
||
clearCycleTimers();
|
||
groupData.clear();
|
||
currentGroups = groupFiles(fileObjects);
|
||
|
||
gallery.innerHTML = currentGroups.map((group, gIdx) => {
|
||
const { gid, files: gf } = group;
|
||
const urls = gf.map(f => f.url);
|
||
const names = gf.map(f => f.cleanName);
|
||
// Track which indices are visible (not hidden)
|
||
const visibleIdx = names.map((n, i) => i).filter(i => !fileHidden[names[i]]);
|
||
groupData.set(gid, { urls, names, visibleIdx });
|
||
|
||
// Auto-follow: if a generation just finished for this group, jump to the image
|
||
if (_followLatestFilename && names.includes(_followLatestFilename) && _isStudioOpen() && lbCurrentGid === gid) {
|
||
lbIdx = names.indexOf(_followLatestFilename);
|
||
_followLatestFilename = null;
|
||
_followLatestGid = null;
|
||
lbUrls = urls;
|
||
lbNames = names;
|
||
updateStudio();
|
||
} else if (_followLatestGid === gid && _isStudioOpen() && lbCurrentGid === gid && names.length > 0) {
|
||
_followLatestGid = null;
|
||
lbIdx = names.length - 1;
|
||
lbUrls = urls;
|
||
lbNames = names;
|
||
updateStudio();
|
||
}
|
||
|
||
const isNew = gf.some(f => newFileNames.has(f.cleanName));
|
||
const isRecent = gIdx < 3;
|
||
const count = gf.length;
|
||
const vCount = visibleIdx.length;
|
||
const label = groupDisplayName(group);
|
||
const safeGid = gid.replace(/"/g, '"');
|
||
const poses = [...new Set(gf.map(f => filePoses[f.cleanName]).filter(Boolean))];
|
||
const firstVisUrl = visibleIdx.length ? urls[visibleIdx[0]] : urls[0];
|
||
const firstVisName = visibleIdx.length ? names[visibleIdx[0]] : names[0];
|
||
const isVideoCard = isVideo(firstVisName) || fileContentType[firstVisName] === 'video';
|
||
const isFaceswap = !!fileFaceswapSrcVideo[firstVisName];
|
||
|
||
const mediaEl = isVideoCard
|
||
? `<video src="${firstVisUrl}" poster="${posterFor(firstVisUrl)}" autoplay muted loop playsinline preload="metadata"
|
||
onerror="this.style.opacity='0.2'" style="border-radius:0"></video>
|
||
<div class="video-play-overlay"><div class="video-play-icon">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="#111">
|
||
<polygon points="5,3 19,12 5,21"/>
|
||
</svg></div></div>`
|
||
: `<img src="${firstVisUrl}" alt="${label}" loading="${isRecent ? 'eager' : 'lazy'}" onerror="this.style.opacity='0.2'">`;
|
||
|
||
return `
|
||
<div class="image-card ${isNew ? 'new' : ''} ${_lastSelectedGid === gid ? 'highlight-prev' : ''}" data-group="${safeGid}" onclick="handleCardClick(this)">
|
||
<div class="archive-confirm-overlay" onclick="event.stopPropagation()">
|
||
<h3>Archive Group?</h3>
|
||
<div class="archive-confirm-buttons">
|
||
<button class="btn" style="font-size:11px;padding:4px 8px" onclick="cancelArchiveGroup(this, event)">Cancel</button>
|
||
<button class="btn danger" style="font-size:11px;padding:4px 8px" onclick="confirmArchiveGroup(this, event)">Archive</button>
|
||
</div>
|
||
</div>
|
||
<div class="image-actions">
|
||
${!isVideoCard ? `<div class="action-btn" title="Remove Background for Group" onclick="removeBgGroup(this, event)">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M12 2v4m0 12v4M2 12h4m12 0h4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/>
|
||
</svg>
|
||
</div>` : ''}
|
||
<div class="action-btn delete" title="Archive Entire Group" onclick="archiveGroup(this, event)">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M21 8H3V6h18v2zm-2 1h-14v12h14V9zm-10 3h2v6H9v-6zm4 0h2v6h-2v-6z"/>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
<div class="image-wrapper">
|
||
<div class="select-indicator"></div>
|
||
${vCount > 1 ? `<div class="variant-count">${vCount}×${count !== vCount ? `<span style="opacity:.5;font-size:9px"> /${count}</span>` : ''}</div>` : ''}
|
||
${mediaEl}
|
||
${vCount > 1 && !isVideoCard ? `<div class="variant-dots">${visibleIdx.map((_, i) => `<span class="dot${i === 0 ? ' active' : ''}"></span>`).join('')}</div>` : ''}
|
||
</div>
|
||
<div class="image-info">
|
||
<div class="image-name" title="${label}">${label}</div>
|
||
<div class="image-meta">
|
||
<span class="image-time">#${String(currentGroups.length - gIdx).padStart(3, '0')}</span>
|
||
${isVideoCard ? `<span class="badge video" style="font-size:9px">${isFaceswap ? 'Faceswap' : 'Video'}</span>` : ''}
|
||
${!isVideoCard && poses.length ? `<span class="badge" style="background:#78350f;font-size:9px">${escHtml(poses[0])}</span>` : ''}
|
||
${isRecent && !isVideoCard ? '<span class="badge recent">Latest</span>' : ''}
|
||
</div>
|
||
</div>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
currentGroups.forEach(g => {
|
||
const data = groupData.get(g.gid);
|
||
if (data && data.visibleIdx && data.visibleIdx.length > 1) startGroupCycle(g.gid);
|
||
});
|
||
|
||
updateCardSelection();
|
||
document.getElementById('count').textContent = `${currentGroups.length} group${currentGroups.length !== 1 ? 's' : ''} · ${fileObjects.length} images`;
|
||
if (newFileNames.size > 0) showToast(`${newFileNames.size} new image${newFileNames.size !== 1 ? 's' : ''} detected`, 'success');
|
||
|
||
// If studio is open, sync lbUrls/lbNames from freshly-loaded groupData
|
||
const studioEl = document.getElementById('studio');
|
||
if (studioEl?.classList.contains('open') && lbCurrentGid) {
|
||
const fresh = groupData.get(lbCurrentGid);
|
||
if (fresh) {
|
||
const curName = lbNames[lbIdx];
|
||
const newIdx = fresh.names.indexOf(curName);
|
||
// Snapshot ref filenames before overwriting lbNames
|
||
const oldRefNames = _sbRefIndices.map(i => lbNames[i]).filter(Boolean);
|
||
lbUrls = fresh.urls;
|
||
lbNames = fresh.names;
|
||
// Keep lbIdx stable when current image not yet in fresh data
|
||
// (e.g. duplicate just created — server JSON may lag by the debounce window).
|
||
if (newIdx >= 0) {
|
||
lbIdx = newIdx;
|
||
} else if (lbIdx >= fresh.names.length) {
|
||
lbIdx = fresh.names.length - 1;
|
||
}
|
||
// Re-map multi-ref selections to new positions by filename
|
||
_sbRefIndices = oldRefNames
|
||
.map(n => fresh.names.indexOf(n))
|
||
.filter(i => i >= 0);
|
||
updateStudio();
|
||
}
|
||
}
|
||
|
||
statusDot.classList.remove('updating');
|
||
loadImages._inFlight = false;
|
||
}
|
||
|
||
// Discover images by trying common patterns (fallback for file:// protocol)
|
||
async function discoverImages() {
|
||
const found = [];
|
||
|
||
// Try to find images by testing if they exist
|
||
// This is a best-effort approach for local file access
|
||
|
||
// If you know your naming pattern, add it here:
|
||
const patterns = [];
|
||
|
||
// Try to extract from page if there are any references
|
||
const images = document.querySelectorAll('img[src]');
|
||
images.forEach(img => {
|
||
const src = img.getAttribute('src');
|
||
if (src && EXTENSIONS.some(ext => src.toLowerCase().includes(ext))) {
|
||
found.push(src.split('/').pop().split('?')[0]);
|
||
}
|
||
});
|
||
|
||
// Remove duplicates
|
||
return [...new Set(found)];
|
||
}
|
||
|
||
function _isStudioOpen() {
|
||
return document.getElementById('studio')?.classList.contains('open') ?? false;
|
||
}
|
||
|
||
function setIntervalTime(seconds) {
|
||
REFRESH_INTERVAL = seconds;
|
||
document.getElementById('statusText').textContent = `Auto-refresh: ${seconds < 60 ? seconds + 's' : (seconds / 60) + 'm'}`;
|
||
|
||
clearInterval(autoRefreshTimer);
|
||
autoRefreshTimer = setInterval(_timedRefresh, REFRESH_INTERVAL * 1000);
|
||
|
||
showToast(`Refresh interval set to ${seconds < 60 ? seconds + ' seconds' : (seconds / 60) + ' minutes'}`, 'info');
|
||
}
|
||
|
||
// Called by the auto-refresh timer only — skips when studio is open to avoid
|
||
// disrupting the user's workflow. Job-completion code calls refreshNow() directly
|
||
// and is NOT suppressed (the studio sync path in loadImages handles that safely).
|
||
function _timedRefresh() {
|
||
if (!_isStudioOpen()) refreshNow();
|
||
}
|
||
|
||
// Debounced refresh — coalesces multiple rapid calls into one loadImages() run.
|
||
// Direct callers that need a response (pollJob completion, etc.) still use
|
||
// loadImages() directly; everything else should go through refreshNow().
|
||
let _refreshTimer = null;
|
||
let _refreshBypassStatic = false;
|
||
function refreshNow(bypass = false) {
|
||
if (bypass) _refreshBypassStatic = true;
|
||
clearTimeout(_refreshTimer);
|
||
_refreshTimer = setTimeout(() => {
|
||
loadImages(_refreshBypassStatic || !!_followLatestGid);
|
||
_refreshBypassStatic = false;
|
||
}, 80);
|
||
}
|
||
|
||
const API = 'http://127.0.0.1:8500';
|
||
|
||
// --- selection state ---
|
||
let selectionMode = false;
|
||
const selectedFiles = new Set();
|
||
const selectedVariants = new Map(); // gid -> chosen variant index
|
||
const selectedPoses = new Set();
|
||
let availablePoses = {};
|
||
|
||
async function loadPoses() {
|
||
try {
|
||
const r = await fetch(`${API}/poses`);
|
||
if (r.ok) {
|
||
availablePoses = await r.json();
|
||
renderPoseMenu();
|
||
}
|
||
} catch (e) { console.error("Failed to load poses", e); }
|
||
}
|
||
|
||
function renderPoseMenu() {
|
||
const menu = document.getElementById('poseMenu');
|
||
menu.innerHTML = Object.entries(availablePoses).map(([name, entry]) => {
|
||
const text = entry?.text ?? entry; // handle legacy string format
|
||
const beta = entry?.beta ?? false;
|
||
return `<div class="pose-item ${selectedPoses.has(name) ? 'active' : ''}"
|
||
onclick="togglePose('${name.replace(/'/g, "\\'")}')" title="${String(text).replace(/"/g, '"')}">
|
||
${name}${beta ? ' <span style="font-size:9px;background:#78350f;color:#fbbf24;padding:1px 4px;border-radius:3px;vertical-align:middle">β</span>' : ''}
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
|
||
function togglePose(name) {
|
||
if (selectedPoses.has(name)) selectedPoses.delete(name);
|
||
else selectedPoses.add(name);
|
||
renderPoseMenu();
|
||
updateBatchBar();
|
||
}
|
||
|
||
function togglePoseMenu() {
|
||
document.getElementById('poseMenu').classList.toggle('open');
|
||
document.getElementById('poseBtn').classList.toggle('active');
|
||
}
|
||
|
||
// --- variant picker ---
|
||
let _pickerGid = null;
|
||
|
||
function showVariantPicker(gid) {
|
||
const data = groupData.get(gid);
|
||
if (!data) return;
|
||
_pickerGid = gid;
|
||
const isSelected = selectedFiles.has(gid);
|
||
const chosen = selectedVariants.get(gid);
|
||
document.getElementById('variantPickerTitle').textContent =
|
||
`Pick reference image — ${data.urls.length} variants`;
|
||
document.getElementById('variantPickerGrid').innerHTML = data.urls.map((url, i) => {
|
||
const isHidden = fileHidden[data.names[i]];
|
||
const isPreferred = i === 0;
|
||
const prefClass = isPreferred ? ' preferred' : '';
|
||
const prefBtn = !isPreferred
|
||
? `<button class="set-preferred-btn" onclick="event.stopPropagation();setAsPreferred('${data.names[i].replace(/'/g,"\\'")}',this)" title="Set as preferred">★</button>`
|
||
: '';
|
||
return `<div class="variant-thumb${chosen === i ? ' selected' : ''}${isHidden ? ' hidden-img' : ''}${prefClass}"
|
||
onclick="selectVariant(${i})" title="${isHidden ? '🚫 Hidden — ' : ''}${isPreferred ? '★ Preferred — ' : ''}Variant ${i + 1}">
|
||
<img src="${isVideo(data.names[i]) ? posterFor(url) : url}" loading="lazy">
|
||
${prefBtn}
|
||
</div>`;
|
||
}).join('');
|
||
const deselectBtn = document.getElementById('variantPickerDeselect');
|
||
deselectBtn.style.display = isSelected ? '' : 'none';
|
||
document.getElementById('variantPicker').classList.add('open');
|
||
}
|
||
|
||
function selectVariant(idx) {
|
||
const gid = _pickerGid;
|
||
if (!gid) return;
|
||
selectedFiles.add(gid);
|
||
selectedVariants.set(gid, idx);
|
||
// Stop cycling and freeze on chosen variant
|
||
const timer = cycleTimers.get(gid);
|
||
if (timer) { clearInterval(timer); cycleTimers.delete(gid); }
|
||
const card = [...document.querySelectorAll('.image-card')].find(c => c.dataset.group === gid);
|
||
if (card) {
|
||
const data = groupData.get(gid);
|
||
if (data) {
|
||
const img = card.querySelector('img');
|
||
if (img) img.src = data.urls[idx];
|
||
card.querySelectorAll('.dot').forEach((d, i) => d.classList.toggle('active', i === idx));
|
||
}
|
||
}
|
||
closeVariantPicker();
|
||
updateCardSelection();
|
||
updateBatchBar();
|
||
}
|
||
|
||
function deselectGroupFromPicker() {
|
||
const gid = _pickerGid;
|
||
if (!gid) return;
|
||
selectedFiles.delete(gid);
|
||
selectedVariants.delete(gid);
|
||
_restartCycleIfNeeded(gid);
|
||
closeVariantPicker();
|
||
updateCardSelection();
|
||
updateBatchBar();
|
||
}
|
||
|
||
function closeVariantPicker() {
|
||
document.getElementById('variantPicker').classList.remove('open');
|
||
_pickerGid = null;
|
||
}
|
||
|
||
function toggleSelectMode() {
|
||
selectionMode = !selectionMode;
|
||
if (!selectionMode) {
|
||
selectedFiles.forEach(gid => _restartCycleIfNeeded(gid));
|
||
selectedFiles.clear();
|
||
selectedVariants.clear();
|
||
}
|
||
document.getElementById('selectBtn').classList.toggle('active', selectionMode);
|
||
const bar = document.getElementById('batchBar');
|
||
bar.classList.toggle('visible', selectionMode);
|
||
document.querySelector('.gallery').style.paddingBottom = selectionMode ? '90px' : '40px';
|
||
updateCardSelection();
|
||
updateBatchBar();
|
||
}
|
||
|
||
function handleCardClick(card) {
|
||
const gid = card.dataset.group;
|
||
if (selectionMode) {
|
||
const data = groupData.get(gid);
|
||
if (data && data.urls.length > 1) {
|
||
showVariantPicker(gid);
|
||
} else {
|
||
toggleFile(gid);
|
||
}
|
||
} else {
|
||
openLightbox(gid);
|
||
}
|
||
}
|
||
|
||
function toggleFile(base) {
|
||
if (selectedFiles.has(base)) {
|
||
selectedFiles.delete(base);
|
||
selectedVariants.delete(base);
|
||
_restartCycleIfNeeded(base);
|
||
} else {
|
||
selectedFiles.add(base);
|
||
}
|
||
updateCardSelection();
|
||
updateBatchBar();
|
||
}
|
||
|
||
function _restartCycleIfNeeded(gid) {
|
||
const data = groupData.get(gid);
|
||
if (data && data.visibleIdx && data.visibleIdx.length > 1 && !cycleTimers.has(gid)) {
|
||
startGroupCycle(gid);
|
||
}
|
||
}
|
||
|
||
function updateCardSelection() {
|
||
document.querySelectorAll('.image-card').forEach(card => {
|
||
const base = card.dataset.group;
|
||
card.classList.toggle('selectable', selectionMode);
|
||
card.classList.toggle('selected', selectionMode && selectedFiles.has(base));
|
||
});
|
||
}
|
||
|
||
function updateBatchBar() {
|
||
const n = selectedFiles.size;
|
||
document.getElementById('batchCount').textContent = `${n} group${n !== 1 ? 's' : ''} selected`;
|
||
document.getElementById('processBtn').textContent = n > 0 ? `Process (${n})` : 'Process';
|
||
const showMR = (n >= 2 && n <= 3);
|
||
document.getElementById('multiRefBtn').style.display = showMR ? '' : 'none';
|
||
const legend = document.getElementById('multiRefLegend');
|
||
if (legend) {
|
||
if (showMR) {
|
||
const names = [...selectedFiles].map((gid, i) => {
|
||
const data = groupData.get(gid);
|
||
const idx = selectedVariants.get(gid) ?? 0;
|
||
const fname = data?.names[idx] ?? gid;
|
||
return `ref${i+1}=${fname.split('/').pop().replace(/\?.*$/,'').slice(0,24)}`;
|
||
});
|
||
legend.textContent = names.join(' · ');
|
||
legend.style.display = '';
|
||
} else {
|
||
legend.style.display = 'none';
|
||
}
|
||
}
|
||
}
|
||
|
||
function selectAll() {
|
||
currentGroups.forEach(g => selectedFiles.add(g.gid));
|
||
updateCardSelection();
|
||
updateBatchBar();
|
||
}
|
||
|
||
function deselectAll() {
|
||
selectedFiles.forEach(gid => _restartCycleIfNeeded(gid));
|
||
selectedFiles.clear();
|
||
selectedVariants.clear();
|
||
updateCardSelection();
|
||
updateBatchBar();
|
||
}
|
||
|
||
// --- batch processing ---
|
||
let pollTimer = null;
|
||
|
||
async function processSelected() {
|
||
const prompt = document.getElementById('batchPromptInput').value.trim();
|
||
const poseEntries = [...selectedPoses].map(p => ({ name: p, text: availablePoses[p]?.text ?? availablePoses[p] }));
|
||
|
||
if (!prompt && poseEntries.length === 0) { showToast('Enter a prompt or select a pose', 'info'); return; }
|
||
|
||
// Build parallel prompt+pose arrays
|
||
const prompts = [];
|
||
const poses = [];
|
||
if (prompt) { prompts.push(prompt); poses.push(null); savePromptHistory(prompt); }
|
||
poseEntries.forEach(e => { prompts.push(e.text ?? e); poses.push(e.name); });
|
||
|
||
// Send the chosen (or newest) file from each selected group
|
||
const filenames = [...selectedFiles].flatMap(base => {
|
||
const data = groupData.get(base);
|
||
if (!data) return [];
|
||
const idx = selectedVariants.get(base) ?? 0;
|
||
return [data.names[idx]];
|
||
});
|
||
|
||
// Try to use a common group_id if all selected files belong to the same group
|
||
let group_id = null;
|
||
const groups = new Set();
|
||
[...selectedFiles].forEach(base => {
|
||
const custom = customGroups[groupData.get(base)?.names[0]];
|
||
if (custom) groups.add(custom);
|
||
else groups.add(base);
|
||
});
|
||
if (groups.size === 1) group_id = [...groups][0];
|
||
|
||
document.getElementById('batchProgress').textContent = 'Submitting…';
|
||
|
||
try {
|
||
const r = await fetch(`${API}/batch`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ filenames, prompt: prompts, poses, seed: -1, max_area: 0, group_id }),
|
||
});
|
||
if (!r.ok) { showToast('Batch failed to start', 'info'); document.getElementById('batchProgress').textContent = ''; return; }
|
||
const { job_id, total } = await r.json();
|
||
showToast(`Processing ${total} task${total !== 1 ? 's' : ''}…`, 'info');
|
||
pollJob(job_id, total);
|
||
} catch (e) {
|
||
showToast('API not reachable', 'info');
|
||
document.getElementById('batchProgress').textContent = '';
|
||
}
|
||
}
|
||
|
||
async function processMultiRef() {
|
||
const n = selectedFiles.size;
|
||
if (n < 2 || n > 3) { showToast('Select 2–3 images for multi-ref', 'info'); return; }
|
||
const prompt = document.getElementById('batchPromptInput').value.trim();
|
||
const poseEntries = [...selectedPoses].map(p => ({ name: p, text: availablePoses[p]?.text ?? availablePoses[p] }));
|
||
if (!prompt && poseEntries.length === 0) { showToast('Enter a prompt or select a pose', 'info'); return; }
|
||
|
||
// Resolve chosen variant for each selected group (first selected = primary/image1)
|
||
const filenames = [...selectedFiles].map(base => {
|
||
const data = groupData.get(base);
|
||
if (!data) return null;
|
||
const idx = selectedVariants.get(base) ?? 0;
|
||
return data.names[idx];
|
||
}).filter(Boolean);
|
||
|
||
const prompts = [];
|
||
const poses = [];
|
||
if (prompt) { prompts.push(prompt); poses.push(null); savePromptHistory(prompt); }
|
||
poseEntries.forEach(e => { prompts.push(e.text); poses.push(e.name); });
|
||
|
||
// Show which filename maps to which ref slot
|
||
const shortName = f => f.split('/').pop().replace(/\?.*$/, '');
|
||
let refLabel = `Ref1: ${shortName(filenames[0])} Ref2: ${shortName(filenames[1])}`;
|
||
if (filenames[2]) refLabel += ` Ref3: ${shortName(filenames[2])}`;
|
||
showToast(refLabel, 'info', 5000);
|
||
|
||
document.getElementById('batchProgress').textContent = 'Submitting…';
|
||
try {
|
||
const r = await fetch(`${API}/multi-ref`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ filenames, prompt: prompts, poses, seed: -1, max_area: 0 }),
|
||
});
|
||
if (!r.ok) { showToast('Multi-ref failed to start', 'info'); document.getElementById('batchProgress').textContent = ''; return; }
|
||
const { job_id, total } = await r.json();
|
||
showToast(`Multi-ref: ${total} task${total !== 1 ? 's' : ''}…`, 'info');
|
||
pollJob(job_id, total);
|
||
} catch (e) {
|
||
showToast('API not reachable', 'info');
|
||
document.getElementById('batchProgress').textContent = '';
|
||
}
|
||
}
|
||
|
||
function pollJob(job_id, total) {
|
||
clearTimeout(pollTimer);
|
||
pollTimer = setTimeout(async () => {
|
||
try {
|
||
const r = await fetch(`${API}/batch/${job_id}`);
|
||
if (!r.ok) return;
|
||
const job = await r.json();
|
||
const done = job.done + job.failed;
|
||
document.getElementById('batchProgress').textContent = `${done}/${total}`;
|
||
if (job.status === 'running') {
|
||
pollJob(job_id, total);
|
||
} else {
|
||
const msg = job.failed > 0
|
||
? `Done: ${job.done} ok, ${job.failed} failed`
|
||
: `Done: ${job.done} processed`;
|
||
document.getElementById('batchProgress').textContent = msg;
|
||
showToast(msg, 'success');
|
||
loadImages();
|
||
}
|
||
} catch (e) { /* ignore transient errors */ pollJob(job_id, total); }
|
||
}, 2000);
|
||
}
|
||
|
||
// --- prompt history (localStorage) ---
|
||
function loadPromptHistory() {
|
||
const hist = JSON.parse(localStorage.getItem('batchPromptHistory') || '[]');
|
||
const dl = document.getElementById('promptHistory');
|
||
dl.innerHTML = hist.map(p => `<option value="${p.replace(/"/g, '"')}"></option>`).join('');
|
||
}
|
||
|
||
function savePromptHistory(prompt) {
|
||
let hist = JSON.parse(localStorage.getItem('batchPromptHistory') || '[]');
|
||
hist = [prompt, ...hist.filter(p => p !== prompt)].slice(0, 20);
|
||
localStorage.setItem('batchPromptHistory', JSON.stringify(hist));
|
||
loadPromptHistory();
|
||
}
|
||
|
||
// --- tagging ---
|
||
let lbCurrentGid = null;
|
||
|
||
async function tagCurrentImage() {
|
||
const data = groupData.get(lbCurrentGid);
|
||
if (!data) return;
|
||
const filename = data.names[lbIdx];
|
||
const btn = document.querySelector('button[onclick="tagCurrentImage()"]');
|
||
if (btn) btn.textContent = '…';
|
||
try {
|
||
const r = await fetch(`${API}/tag`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ filename, threshold: 0.35, max_tags: 8 }),
|
||
});
|
||
if (r.ok) {
|
||
const { name } = await r.json();
|
||
imageNames[filename] = name;
|
||
document.getElementById('lbName').textContent = name;
|
||
showToast(`Tagged: ${name}`, 'success');
|
||
// Update the card label too
|
||
const card = [...document.querySelectorAll('.image-card')].find(c => c.dataset.group === lbCurrentGid);
|
||
if (card) card.querySelector('.image-name').textContent = name;
|
||
} else {
|
||
const err = await r.text();
|
||
showToast(`Tag failed: ${err}`, 'info');
|
||
}
|
||
} catch (e) { showToast('API not reachable', 'info'); }
|
||
if (btn) btn.textContent = 'Re-tag';
|
||
}
|
||
|
||
async function extractCurrentImage() {
|
||
const data = groupData.get(lbCurrentGid);
|
||
if (!data || data.names.length <= 1) { showToast('Already standalone', 'info'); return; }
|
||
const filename = data.names[lbIdx];
|
||
try {
|
||
const r = await fetch(`${API}/groups/extract`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ filename }),
|
||
});
|
||
if (r.ok) {
|
||
customGroups[filename] = `solo:${filename}`;
|
||
showToast('Extracted to own group', 'success');
|
||
closeLightbox();
|
||
await loadImages();
|
||
}
|
||
} catch (e) { showToast('API not reachable', 'info'); }
|
||
}
|
||
|
||
async function groupSelected() {
|
||
if (selectedFiles.size < 2) { showToast('Select 2+ groups to merge', 'info'); return; }
|
||
// Collect all filenames from selected groups
|
||
const filenames = [...selectedFiles].flatMap(gid => {
|
||
const data = groupData.get(gid);
|
||
return data ? data.names : [];
|
||
});
|
||
try {
|
||
const r = await fetch(`${API}/groups/merge`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ filenames }),
|
||
});
|
||
if (r.ok) {
|
||
const { group_id } = await r.json();
|
||
filenames.forEach(f => { customGroups[f] = group_id; });
|
||
showToast(`Merged into group ${group_id}`, 'success');
|
||
deselectAll();
|
||
await loadImages();
|
||
}
|
||
} catch (e) { showToast('API not reachable', 'info'); }
|
||
}
|
||
|
||
|
||
|
||
// Initialize
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const img = document.getElementById('lbImg');
|
||
if (img) {
|
||
const ro = new ResizeObserver(() => {
|
||
if (_isStudioOpen()) {
|
||
requestAnimationFrame(() => {
|
||
updatePadPreview();
|
||
const cb = document.getElementById('sbCheckerboard');
|
||
if (cb && cb.checked) toggleCheckerboard(true);
|
||
});
|
||
}
|
||
});
|
||
ro.observe(img);
|
||
}
|
||
|
||
window.addEventListener('resize', () => {
|
||
if (_isStudioOpen()) {
|
||
requestAnimationFrame(() => {
|
||
updatePadPreview();
|
||
const cb = document.getElementById('sbCheckerboard');
|
||
if (cb && cb.checked) toggleCheckerboard(true);
|
||
});
|
||
}
|
||
});
|
||
loadPromptHistory();
|
||
loadImages();
|
||
autoRefreshTimer = setInterval(_timedRefresh, REFRESH_INTERVAL * 1000);
|
||
|
||
// Privacy mode init — apply stored state on load
|
||
(function applyPrivacyInit() {
|
||
const btn = document.getElementById('privacyBtn');
|
||
if (privacyMode) { btn.textContent = '🔒'; document.title = PRIV_TITLE; }
|
||
})();
|
||
|
||
// Studio + variant picker + select-mode keyboard nav
|
||
document.addEventListener('keydown', e => {
|
||
const tag = document.activeElement && document.activeElement.tagName;
|
||
if ((e.key === 'p' || e.key === 'P') && tag !== 'INPUT' && tag !== 'TEXTAREA') {
|
||
togglePrivacyMode(); return;
|
||
}
|
||
if ((e.key === 'c' || e.key === 'C') && tag !== 'INPUT' && tag !== 'TEXTAREA') {
|
||
if (document.getElementById('studio').classList.contains('open')) {
|
||
const cb = document.getElementById('sbCheckerboard');
|
||
const viewer = document.getElementById('studioViewer');
|
||
const cur = viewer ? viewer.classList.contains('show-checker') : (cb ? cb.checked : false);
|
||
toggleCheckerboard(!cur);
|
||
return;
|
||
}
|
||
}
|
||
if ((e.key === 'a' || e.key === 'A') && tag !== 'INPUT' && tag !== 'TEXTAREA') {
|
||
if (document.getElementById('studio').classList.contains('open')) {
|
||
startManualPad(); return;
|
||
}
|
||
}
|
||
if ((e.key === 'g' || e.key === 'G') && tag !== 'INPUT' && tag !== 'TEXTAREA') {
|
||
if (_activeSidebarTab === 'generate') {
|
||
const btn = document.getElementById('sbGenBtn');
|
||
if (btn && !btn.disabled) btn.click();
|
||
e.preventDefault();
|
||
}
|
||
}
|
||
if (e.key === 'Escape') {
|
||
if (document.getElementById('variantPicker').classList.contains('open')) {
|
||
closeVariantPicker(); return;
|
||
}
|
||
if (document.getElementById('studio').classList.contains('open')) {
|
||
closeStudio(); return;
|
||
}
|
||
if (selectionMode) {
|
||
toggleSelectMode(); return;
|
||
}
|
||
}
|
||
if (!document.getElementById('studio').classList.contains('open')) return;
|
||
if (tag === 'INPUT' || tag === 'TEXTAREA') return;
|
||
// Throttle filmstrip navigation — ignore if last nav was < 80 ms ago.
|
||
if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
|
||
const now = Date.now();
|
||
if (now - (document._lbNavLast || 0) >= 80) {
|
||
document._lbNavLast = now;
|
||
lbNav(e.key === 'ArrowLeft' ? -1 : 1);
|
||
}
|
||
e.preventDefault();
|
||
}
|
||
if (e.key === 'Home') {
|
||
lbIdx = 0;
|
||
updateStudio();
|
||
e.preventDefault();
|
||
}
|
||
if (e.key === 'End') {
|
||
lbIdx = lbUrls.length - 1;
|
||
updateStudio();
|
||
e.preventDefault();
|
||
}
|
||
if (e.key === ' ') {
|
||
const vid = document.getElementById('lbVideo');
|
||
if (vid && vid.style.display !== 'none') {
|
||
vid.paused ? vid.play() : vid.pause();
|
||
e.preventDefault();
|
||
}
|
||
}
|
||
// Guard async actions against concurrent executions via a simple inflight flag.
|
||
if ((e.key === 'h' || e.key === 'H') && tag !== 'INPUT') {
|
||
if (!document._lbHideInFlight) { document._lbHideInFlight = true; lbToggleHidden().finally(() => { document._lbHideInFlight = false; }); }
|
||
}
|
||
if (e.key === 'F' || e.key === 'f') {
|
||
lbSetPreferred();
|
||
e.preventDefault();
|
||
}
|
||
if (e.key === 'Delete') {
|
||
lbArchive();
|
||
e.preventDefault();
|
||
}
|
||
// ArrowUp/Down move images within the group — debounce rapid keypresses.
|
||
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
||
clearTimeout(document._lbMoveTimer);
|
||
const d = e.key === 'ArrowUp' ? -1 : 1;
|
||
document._lbMoveTimer = setTimeout(() => lbMoveInGroup(d), 120);
|
||
e.preventDefault();
|
||
}
|
||
// Key 2 moves image to second position in group
|
||
if (e.key === '2') {
|
||
clearTimeout(document._lbMoveTimer);
|
||
document._lbMoveTimer = setTimeout(() => lbMoveToPosition(1), 120); // Position 1 = second place
|
||
e.preventDefault();
|
||
}
|
||
// Key 9 moves image to before the last position in group
|
||
if (e.key === '9') {
|
||
clearTimeout(document._lbMoveTimer);
|
||
document._lbMoveTimer = setTimeout(() => lbMoveToPosition(-2), 120); // Position -2 = second to last
|
||
e.preventDefault();
|
||
}
|
||
});
|
||
});
|
||
|
||
// Privacy overlay — disguise the page as a study-chat on any focus loss.
|
||
const PRIV_TITLE = 'AI Assist';
|
||
const REAL_TITLE = document.title; // "Studio Monitor"
|
||
|
||
function showPrivacyOverlay() {
|
||
const ov = document.getElementById('privacyOverlay');
|
||
ov.style.display = 'flex';
|
||
// Force a synchronous layout+style flush so the disguise is painted
|
||
// BEFORE the window manager grabs the minimize thumbnail — otherwise
|
||
// the first minimize leaks the real content. Text-only overlay = no
|
||
// async asset loads to wait on.
|
||
void ov.offsetHeight;
|
||
document.title = PRIV_TITLE;
|
||
}
|
||
|
||
// blur fires before the minimize snapshot in every browser we target, so
|
||
// this is the line of defense for the taskbar thumbnail.
|
||
window.addEventListener('blur', () => { if (privacyMode) showPrivacyOverlay(); });
|
||
window.addEventListener('pagehide', () => { if (privacyMode) showPrivacyOverlay(); });
|
||
|
||
document.addEventListener('visibilitychange', () => {
|
||
if (document.hidden) {
|
||
if (privacyMode) showPrivacyOverlay();
|
||
} else {
|
||
// Don't auto-hide — user must click overlay or press P to dismiss
|
||
loadImages();
|
||
}
|
||
});
|
||
|
||
function togglePrivacyMode() {
|
||
privacyMode = !privacyMode;
|
||
localStorage.setItem('privacyMode', privacyMode);
|
||
const btn = document.getElementById('privacyBtn');
|
||
btn.textContent = privacyMode ? '🔒' : '🔓';
|
||
if (privacyMode) {
|
||
document.title = PRIV_TITLE; // keep the taskbar label discreet even while active
|
||
} else {
|
||
document.title = REAL_TITLE;
|
||
document.getElementById('privacyOverlay').style.display = 'none';
|
||
}
|
||
}
|
||
|
||
function disablePrivacyOverlay() {
|
||
// Resume work, but privacy mode stays armed — keep the title discreet.
|
||
document.getElementById('privacyOverlay').style.display = 'none';
|
||
}
|
||
|
||
// Load the face-book thumbnail for a group. Face extraction runs
|
||
// asynchronously after "set preferred", so we ask the API whether the
|
||
// crop exists (works over file://, unlike probing with an <img> that
|
||
// logs a 404 during the race) and poll a few times while it's pending.
|
||
async function loadFaceThumb(gid, retries = 0) {
|
||
const faceBook = document.getElementById('lbFaceBook');
|
||
const faceThumb = document.getElementById('lbFaceThumb');
|
||
if (!faceBook || !faceThumb || !gid) return;
|
||
try {
|
||
const r = await fetch(`${API}/faces/${encodeURIComponent(gid)}`);
|
||
const d = await r.json();
|
||
// Ignore stale responses if the user navigated away.
|
||
if (lbCurrentGid !== gid || lbIdx !== 0) return;
|
||
if (d.exists) {
|
||
faceThumb.onerror = () => { faceBook.style.display = 'none'; };
|
||
faceThumb.src = IMAGE_FOLDER + d.filename + '?t=' + Date.now();
|
||
faceBook.style.display = '';
|
||
} else if (retries > 0) {
|
||
setTimeout(() => loadFaceThumb(gid, retries - 1), 1500);
|
||
} else {
|
||
faceBook.style.display = 'none';
|
||
}
|
||
} catch (e) {
|
||
faceBook.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
async function setAsPreferred(filename, btn) {
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(filename)}/set-preferred`, {method:'POST'});
|
||
if (!r.ok) { console.error('set-preferred failed', r.status); return; }
|
||
const data = await r.json();
|
||
// Update sort orders locally
|
||
const gid = data.group_id;
|
||
fileSortOrders[filename] = 0;
|
||
// Re-open picker to reflect new order
|
||
showVariantPicker(filename);
|
||
// Poll for the freshly-extracted face crop (async on the server).
|
||
loadFaceThumb(gid, 8);
|
||
} catch(e) { console.error(e); }
|
||
}
|
||
|
||
|
||
async function lbUndress() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname) return;
|
||
const btn = document.getElementById('lbUndressBtn');
|
||
btn.textContent = '...';
|
||
btn.disabled = true;
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/undress`, {method:'POST'});
|
||
if (!r.ok) { showToast('Undress failed', 'error'); return; }
|
||
showToast('Undress queued', 'success');
|
||
} catch(e) {
|
||
showToast('Undress error', 'error');
|
||
} finally {
|
||
btn.textContent = 'Undress';
|
||
btn.disabled = false;
|
||
}
|
||
}
|
||
|
||
// ---- sidebar & studio ----
|
||
|
||
function toggleSidebar() {
|
||
const sidebar = document.getElementById('studioSidebar');
|
||
const btn = document.getElementById('sidebarInnerToggle');
|
||
const collapsed = sidebar.classList.toggle('collapsed');
|
||
if (btn) btn.textContent = collapsed ? '⊞' : '⊟';
|
||
localStorage.setItem('studioSidebarCollapsed', collapsed ? '1' : '0');
|
||
}
|
||
|
||
function switchSidebarTab(tab, initial) {
|
||
_tplStopAll(); // release any hovered template video before re-rendering panels
|
||
_activeSidebarTab = tab;
|
||
localStorage.setItem('studioSidebarTab', tab);
|
||
document.querySelectorAll('#studioSidebar .sb-tab').forEach(t =>
|
||
t.classList.toggle('active', t.dataset.tab === tab)
|
||
);
|
||
const panelId = 'sbPanel' + tab.charAt(0).toUpperCase() + tab.slice(1);
|
||
document.querySelectorAll('#studioSidebar .sb-panel').forEach(p =>
|
||
p.classList.toggle('active', p.id === panelId)
|
||
);
|
||
if (tab === 'generate') renderSidebarGenerate();
|
||
if (tab === 'faceswap') renderSidebarFaceswap();
|
||
if (tab === 'scenery') renderSidebarScenery();
|
||
if (tab === 'segment') renderSidebarSegment();
|
||
if (tab === 'orbit') renderSidebarOrbit();
|
||
}
|
||
|
||
function toggleImageZoom() {
|
||
const img = document.getElementById('lbImg');
|
||
if (img) {
|
||
img.classList.toggle('zoomed');
|
||
// The ResizeObserver will handle the follow-up logic automatically.
|
||
}
|
||
}
|
||
|
||
// ---- sidebar panels ----
|
||
|
||
function renderSidebarGenerate() {
|
||
const panel = document.getElementById('sbPanelGenerate');
|
||
if (!panel) return;
|
||
// Preserve live input values before re-render
|
||
const prevPromptVal = document.getElementById('sbGenPromptInput')?.value ?? '';
|
||
_sbPadTop = document.getElementById('sbPadTop')?.value ?? _sbPadTop;
|
||
_sbPadRight = document.getElementById('sbPadRight')?.value ?? _sbPadRight;
|
||
_sbPadBottom = document.getElementById('sbPadBottom')?.value ?? _sbPadBottom;
|
||
_sbPadLeft = document.getElementById('sbPadLeft')?.value ?? _sbPadLeft;
|
||
_sbPadOutpaint = document.getElementById('sbPadOutpaint')?.checked ?? _sbPadOutpaint;
|
||
_sbPadUniform = document.getElementById('sbPadUniform')?.checked ?? _sbPadUniform;
|
||
_fsSelectedPoses.forEach(name => {
|
||
const el = document.getElementById('sbPoseEdit_' + CSS.escape(name));
|
||
if (el) _sbPoseEdits[name] = el.value;
|
||
});
|
||
|
||
const donePoses = new Set();
|
||
lbNames.forEach(n => { if (filePoses[n]) donePoses.add(filePoses[n]); });
|
||
|
||
// Camera angles section — absolute positions
|
||
const absAngles = CAMERA_ANGLES.filter(a => !a.relative);
|
||
const relAngles = CAMERA_ANGLES.filter(a => a.relative);
|
||
const ORBIT_NAMES = ['Front', '¾ Left', '¾ Right', 'Side L', 'Side R', 'Back'];
|
||
const allOrbitSel = ORBIT_NAMES.every(n => _sbSelectedAngles.has(n));
|
||
let html = `<div style="display:flex;align-items:center;gap:6px;margin-bottom:3px">
|
||
<span class="sb-label" style="margin:0;flex:1">Camera — absolute</span>
|
||
<button class="sb-btn${allOrbitSel?' primary':''}" style="padding:2px 8px;font-size:10px" onclick="sbToggleOrbitAll()" title="Select all 6 orbit angles (front · ¾ · side · back)">${allOrbitSel?'✓ Orbit all':'Orbit all'}</button>
|
||
</div><div class="sb-camera-grid" id="sbCameraGrid">`;
|
||
html += absAngles.map(a => {
|
||
const sel = _sbSelectedAngles.has(a.name) ? ' selected' : '';
|
||
const done = donePoses.has(a.name) ? ' done' : '';
|
||
const nSafe = a.name.replace(/'/g, "\\'");
|
||
return `<button class="sb-angle-btn${done}${sel}" onclick="toggleSbAngle('${nSafe}')" title="${escHtml(a.prompt)}">${a.icon}<br><span style="font-size:9px">${escHtml(a.name)}</span></button>`;
|
||
}).join('');
|
||
html += '</div>';
|
||
|
||
// Relative rotations
|
||
html += '<div class="sb-label" style="margin-top:6px">Camera — relative</div><div class="sb-camera-grid" id="sbRelAngleGrid">';
|
||
html += relAngles.map(a => {
|
||
const sel = _sbSelectedAngles.has(a.name) ? ' selected' : '';
|
||
const done = donePoses.has(a.name) ? ' done' : '';
|
||
const nSafe = a.name.replace(/'/g, "\\'");
|
||
return `<button class="sb-angle-btn${done}${sel}" onclick="toggleSbAngle('${nSafe}')" title="${escHtml(a.prompt)}">${a.icon}<br><span style="font-size:9px">${escHtml(a.name)}</span></button>`;
|
||
}).join('');
|
||
html += '</div>';
|
||
|
||
// Wireframe pose reference
|
||
const wfSel = _sbWireframeRef || '';
|
||
const wfT = _sbWireframeTime;
|
||
html += `<div class="sb-sep"></div>
|
||
<div class="sb-label">Wireframe pose guide <span style="font-size:10px;color:#555;font-weight:400">(optional)</span></div>
|
||
<select id="sbWireframeSelect" onchange="sbSelectWireframe(this.value)"
|
||
style="width:100%;background:#111;border:1px solid #2a2a2a;border-radius:5px;color:#aaa;font-size:11px;padding:4px;margin-bottom:6px">
|
||
<option value="">— none —</option>
|
||
${availableVideos.map(v => `<option value="${escHtml(v)}"${wfSel===v?' selected':''}>${escHtml(v.replace(/\.[^.]+$/,''))}</option>`).join('')}
|
||
</select>
|
||
${wfSel ? `<div style="display:flex;align-items:center;gap:6px;margin-bottom:6px">
|
||
<span style="font-size:11px;color:#777">Frame:</span>
|
||
<input type="range" id="sbWireframeTimeSlider" min="0" max="100" value="${Math.round(wfT*100)}"
|
||
oninput="sbUpdateWireframeTime(this.value/100)"
|
||
style="flex:1">
|
||
<span id="sbWireframeTimeLabel" style="font-size:11px;color:#777;min-width:28px">${Math.round(wfT*100)}%</span>
|
||
<img id="sbWireframeThumb" src="${API}/wireframe/frame/${encodeURIComponent(wfSel)}?t=${wfT}"
|
||
style="width:48px;height:48px;object-fit:contain;border-radius:4px;border:1px solid #333">
|
||
</div>` : ''}`;
|
||
|
||
// Custom prompt field - positioned above poses and under wireframe
|
||
html += `<div class="sb-label" style="display:flex;justify-content:space-between;align-items:center">
|
||
<span>Custom prompt</span>
|
||
<button class="sb-btn" id="sbRefineBtn" style="font-size:9px;padding:2px 6px;margin:0"
|
||
onclick="refineSbPrompt()" title="AI prompt refinement">✨ Refine</button>
|
||
</div>
|
||
<div class="sb-prompt-wrap" style="position:relative;margin-bottom:10px">
|
||
<textarea class="sb-input" id="sbGenPromptInput" rows="2" autocomplete="off"
|
||
placeholder="Prompt — overrides pose"
|
||
oninput="updateSbGenBtn();autoGrowPrompt(this);showPromptSuggest(this)"
|
||
onfocus="autoGrowPrompt(this);showPromptSuggest(this)"
|
||
onblur="setTimeout(()=>hidePromptSuggest(),150)"
|
||
onkeydown="if(event.key==='Escape')hidePromptSuggest()"
|
||
style="resize:none;max-height:320px;overflow-y:auto;line-height:1.4;font-family:inherit">${escHtml(prevPromptVal)}</textarea>
|
||
<div id="sbPromptSuggest" style="display:none;position:absolute;left:0;right:0;z-index:50;max-height:180px;overflow-y:auto;background:#18181b;border:1px solid #2a2a2a;border-radius:6px;margin-top:2px;box-shadow:0 6px 20px rgba(0,0,0,0.6)"></div>
|
||
</div>`;
|
||
|
||
html += '<div class="sb-sep"></div><div class="sb-label">Poses</div>';
|
||
const poseCount = availablePoses ? Object.keys(availablePoses).length : 0;
|
||
if (poseCount > 8) {
|
||
html += `<input type="text" id="sbPoseFilter" class="sb-input" placeholder="🔍 Filter poses…"
|
||
value="${escHtml(_sbPoseFilter)}" oninput="sbFilterPoses(this.value)"
|
||
style="margin-bottom:6px;font-size:11px;padding:5px 8px">`;
|
||
}
|
||
html += '<div class="sb-poses-grid" id="sbPosesGrid">';
|
||
if (!poseCount) {
|
||
html += '<div style="font-size:11px;color:#555;padding:6px 0">No poses loaded</div>';
|
||
} else {
|
||
html += Object.entries(availablePoses).map(([name, entry]) => {
|
||
let cls = 'sb-pose-btn';
|
||
if (donePoses.has(name)) cls += ' done';
|
||
if (_fsSelectedPoses.has(name)) cls += ' selected';
|
||
if (entry?.beta) cls += ' beta';
|
||
const nSafe = name.replace(/'/g, "\\'");
|
||
const tip = String(entry?.text ?? entry).replace(/"/g,'"');
|
||
return `<button class="${cls}" data-pose-name="${escHtml(name.toLowerCase())}" onclick="toggleSbPose('${nSafe}')" title="${tip}">${escHtml(name)}</button>`;
|
||
}).join('');
|
||
}
|
||
html += `</div>`;
|
||
|
||
// Fine-tune section: one editable textarea per selected pose.
|
||
// "Save" persists the edit to poses.md; "Delete" removes the pose entirely.
|
||
if (_fsSelectedPoses.size > 0) {
|
||
html += `<div class="sb-sep"></div><div class="sb-label">Fine-tune poses
|
||
<span style="font-size:9px;color:#555;font-weight:400">· Save persists to poses.md</span></div>`;
|
||
_fsSelectedPoses.forEach(name => {
|
||
const origText = String(availablePoses[name]?.text ?? availablePoses[name] ?? name);
|
||
const curText = (_sbPoseEdits[name] !== undefined) ? _sbPoseEdits[name] : origText;
|
||
const idSafe = escHtml(name).replace(/"/g, '"');
|
||
const isBeta = !!(availablePoses[name]?.beta);
|
||
html += `<div class="sb-pose-edit" data-pose="${escHtml(name)}">
|
||
<div style="display:flex;align-items:center;gap:6px;margin-bottom:3px">
|
||
<span style="font-size:11px;color:#888;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escHtml(name)}</span>
|
||
<label style="font-size:10px;color:#777;display:flex;align-items:center;gap:3px;cursor:pointer"><input type="checkbox" class="sb-pose-beta" ${isBeta?'checked':''}>beta</label>
|
||
<button class="sb-btn" style="padding:2px 8px;font-size:10px" onclick="savePoseToServer(this)">Save</button>
|
||
<button class="sb-btn danger" style="padding:2px 8px;font-size:10px" onclick="deletePoseFromServer(this)">Delete</button>
|
||
</div>
|
||
<textarea id="sbPoseEdit_${idSafe}" class="sb-pose-text"
|
||
style="width:100%;box-sizing:border-box;background:#111;border:1px solid #2a2a2a;border-radius:5px;color:#ccc;font-size:11px;padding:6px;resize:vertical;min-height:60px;margin-bottom:6px"
|
||
oninput="sbSavePoseEdit('${name.replace(/'/g,"\\'")}')">${escHtml(curText)}</textarea>
|
||
</div>`;
|
||
});
|
||
}
|
||
|
||
// Add a brand-new pose (persisted to poses.md).
|
||
html += `<div class="sb-sep"></div>
|
||
<details style="margin-bottom:6px">
|
||
<summary style="font-size:11px;color:#888;cursor:pointer">+ New pose</summary>
|
||
<input type="text" id="sbNewPoseName" placeholder="Pose name"
|
||
style="width:100%;box-sizing:border-box;background:#111;border:1px solid #2a2a2a;border-radius:5px;color:#ccc;font-size:11px;padding:6px;margin:6px 0 4px">
|
||
<textarea id="sbNewPoseText" placeholder="Prompt text"
|
||
style="width:100%;box-sizing:border-box;background:#111;border:1px solid #2a2a2a;border-radius:5px;color:#ccc;font-size:11px;padding:6px;resize:vertical;min-height:48px"></textarea>
|
||
<label style="font-size:10px;color:#777;display:flex;align-items:center;gap:3px;cursor:pointer;margin-top:4px"><input type="checkbox" id="sbNewPoseBeta">beta</label>
|
||
<button class="sb-btn primary" style="margin-top:4px;font-size:10px;padding:3px 10px" onclick="addNewPose()">Add pose</button>
|
||
</details>`;
|
||
|
||
// Sticky footer — always visible without scrolling: refs + custom prompt + Generate
|
||
html += `<div class="sb-gen-footer">`;
|
||
if (_sbRefIndices.length > 0) {
|
||
const refThumbs = _sbRefIndices.map((idx, pos) => {
|
||
const fn = lbNames[idx] || '';
|
||
const u = lbUrls[idx] || '';
|
||
const thumb = isVideo(fn) ? posterFor(u) : u;
|
||
return `<div class="sb-gen-ref" title="Click to remove · ${escHtml(fn)}" style="cursor:pointer" onclick="sbRemoveRef(${idx})"><img src="${thumb}" loading="lazy" onerror="this.style.opacity='0.3'"><div class="sb-gen-ref-badge">${pos+1}</div><div class="sb-gen-ref-x">×</div></div>`;
|
||
}).join('');
|
||
html += `<div class="sb-label" style="margin-bottom:4px">References <span style="color:#555;font-weight:400;font-size:9px">· Shift+Click filmstrip or click a thumb to remove</span></div>
|
||
<div class="sb-gen-refs">${refThumbs}</div>`;
|
||
// With 2–3 refs, let the user choose how the prompt is applied.
|
||
if (_sbRefIndices.length >= 2) {
|
||
const n = _sbRefIndices.length;
|
||
const modeBtn = (mode, label, tip) =>
|
||
`<button onclick="setSbRefMode('${mode}')" title="${tip}"
|
||
style="flex:1;padding:5px 6px;font-size:10px;border-radius:5px;cursor:pointer;
|
||
border:1px solid ${_sbRefMode===mode?'#2563eb':'#2a2a2a'};
|
||
background:${_sbRefMode===mode?'#0c1f44':'#111'};
|
||
color:${_sbRefMode===mode?'#7dabff':'#888'}">${label}</button>`;
|
||
html += `<div style="display:flex;gap:4px;margin:6px 0 4px">
|
||
${modeBtn('combine', 'Combine → 1', 'Blend all selected refs into ONE image (#1 = image1, #2 = image2, #3 = image3)')}
|
||
${modeBtn('each', `Each → ${n}`, 'Apply the prompt to each ref separately → one output per ref')}
|
||
</div>
|
||
<div style="font-size:9px;color:#555;margin-bottom:2px">${_sbRefMode==='combine'
|
||
? 'One combined image; reference slots as #1/#2/#3 in the prompt.'
|
||
: 'One edit per selected reference.'}</div>`;
|
||
}
|
||
}
|
||
html += `<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:8px">
|
||
<span style="font-size:11px;color:#666" title="Expand canvas before generation — gives the model space to outpaint">Pad:</span>
|
||
${['Top','Right','Bottom','Left'].map(side => {
|
||
const val = side==='Top'?_sbPadTop:side==='Right'?_sbPadRight:side==='Bottom'?_sbPadBottom:_sbPadLeft;
|
||
const onInput = `syncPadFields(this)`;
|
||
const onBlur = `this.value = formatPadValue(this.value); ${onInput}`;
|
||
return `<label style="display:flex;align-items:center;gap:2px;font-size:11px;color:#888">${side[0]}
|
||
<input type="text" id="sbPad${side}" value="${val}" placeholder="px or %"
|
||
style="width:48px;background:#111;border:1px solid #2a2a2a;color:#ccc;border-radius:4px;padding:2px 4px;font-size:11px;text-align:right"
|
||
oninput="${onInput}" onblur="${onBlur}"
|
||
onkeydown="if(event.key === 'Enter') { submitSbGenerate(); event.preventDefault(); }"></label>`;
|
||
}).join('')}
|
||
<label style="display:flex;align-items:center;gap:2px;font-size:11px;color:#888;cursor:pointer" title="Keep all padding values equal">
|
||
<input type="checkbox" id="sbPadUniform" ${_sbPadUniform?'checked':''} onchange="togglePadUniform(this)" style="cursor:pointer">Uniform</label>
|
||
<button class="sb-btn" style="padding:2px 5px;font-size:9px;margin:0" onclick="applyPadPreset('10%', true)">+10%</button>
|
||
<button class="sb-btn" style="padding:2px 5px;font-size:9px;margin:0" onclick="applyPadPreset('20%', true)">+20%</button>
|
||
<button class="sb-btn" style="padding:2px 5px;font-size:9px;margin:0" onclick="applyPadPreset('50%', true)">+50%</button>
|
||
<button class="sb-btn" style="padding:2px 5px;font-size:9px;margin:0;background:#3b0764;color:#d8b4fe;border-color:#581c87" onclick="applyPadPreset('0', true)">Reset</button>
|
||
<label style="display:flex;align-items:center;gap:3px;font-size:11px;color:#888;cursor:pointer" title="Instruct the model to fill in the padded area">
|
||
<input type="checkbox" id="sbPadOutpaint" ${(_sbPadOutpaint??false)?'checked':''} onchange="updateSbGenBtn()" style="cursor:pointer">Outpaint</label>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
||
<span id="sbGenJobStatus" style="flex:1;font-size:11px;color:#888;display:none;min-width:120px">
|
||
<span class="sb-spinner"></span><span id="sbGenJobText">Generating…</span>
|
||
</span>
|
||
<button class="sb-btn danger" id="sbCancelBtn" style="display:none" onclick="cancelSbGenerate()">Cancel</button>
|
||
<button class="sb-btn primary" id="sbGenBtn" onclick="submitSbGenerate()" disabled>Generate</button>
|
||
</div>
|
||
</div>`;
|
||
panel.innerHTML = html;
|
||
updateSbGenBtn();
|
||
const promptEl = document.getElementById('sbGenPromptInput');
|
||
if (promptEl) autoGrowPrompt(promptEl);
|
||
if (_sbPoseFilter) sbFilterPoses(_sbPoseFilter); // re-apply after rebuild
|
||
}
|
||
|
||
// Grow the custom-prompt textarea to fit its content (capped by max-height in CSS).
|
||
function autoGrowPrompt(el) {
|
||
if (!el) return;
|
||
el.style.height = 'auto';
|
||
el.style.height = Math.min(el.scrollHeight, 160) + 'px';
|
||
}
|
||
|
||
// History autocomplete for the custom-prompt textarea. <datalist> can't bind to a
|
||
// <textarea>, so this reuses the same `batchPromptHistory` store that feeds the
|
||
// #promptHistory datalist and renders a small suggestion dropdown.
|
||
// Prompt history dropdown — shared by the Generate and Scenery prompt textareas.
|
||
// Pass the suggestion-box id so each textarea drives its own dropdown.
|
||
function showPromptSuggest(el, boxId = 'sbPromptSuggest') {
|
||
const box = document.getElementById(boxId);
|
||
if (!box) return;
|
||
const hist = JSON.parse(localStorage.getItem('batchPromptHistory') || '[]');
|
||
const q = (el.value || '').trim().toLowerCase();
|
||
const matches = (q ? hist.filter(p => p.toLowerCase().includes(q) && p.toLowerCase() !== q) : hist).slice(0, 40);
|
||
if (!matches.length) { box.style.display = 'none'; return; }
|
||
box.style.top = (el.offsetTop + el.offsetHeight) + 'px';
|
||
const inputId = el.id;
|
||
box.innerHTML = matches.map(p =>
|
||
`<div class="prompt-suggest-item" onmousedown="event.preventDefault();pickPromptSuggest(this,'${inputId}','${boxId}')"
|
||
style="padding:6px 9px;font-size:11px;color:#ccc;cursor:pointer;border-bottom:1px solid #222;white-space:nowrap;overflow:hidden;text-overflow:ellipsis"
|
||
onmouseover="this.style.background='#1f2937'" onmouseout="this.style.background=''"
|
||
data-val="${escHtml(p)}" title="${escHtml(p)}">${escHtml(p)}</div>`).join('');
|
||
box.style.display = 'block';
|
||
}
|
||
|
||
function hidePromptSuggest(boxId = 'sbPromptSuggest') {
|
||
const box = document.getElementById(boxId);
|
||
if (box) box.style.display = 'none';
|
||
}
|
||
|
||
function pickPromptSuggest(item, inputId = 'sbGenPromptInput', boxId = 'sbPromptSuggest') {
|
||
const el = document.getElementById(inputId);
|
||
if (!el) return;
|
||
el.value = item.dataset.val;
|
||
hidePromptSuggest(boxId);
|
||
if (inputId === 'sbGenPromptInput') updateSbGenBtn();
|
||
autoGrowPrompt(el);
|
||
el.focus();
|
||
}
|
||
|
||
function sbSavePoseEdit(name) {
|
||
const el = document.getElementById('sbPoseEdit_' + CSS.escape(name));
|
||
if (el) _sbPoseEdits[name] = el.value;
|
||
}
|
||
|
||
// Refetch poses from the backend, then re-render both the Generate tab and batch menu.
|
||
async function _reloadPosesUI() {
|
||
try {
|
||
const r = await fetch(`${API}/poses`);
|
||
if (r.ok) availablePoses = await r.json();
|
||
} catch (e) { /* non-fatal */ }
|
||
renderSidebarGenerate();
|
||
if (document.getElementById('poseMenu')) renderPoseMenu();
|
||
}
|
||
|
||
async function savePoseToServer(btn) {
|
||
const box = btn.closest('.sb-pose-edit');
|
||
if (!box) return;
|
||
const name = box.dataset.pose;
|
||
const text = box.querySelector('.sb-pose-text')?.value ?? '';
|
||
const beta = box.querySelector('.sb-pose-beta')?.checked ?? false;
|
||
btn.disabled = true;
|
||
try {
|
||
const r = await fetch(`${API}/poses`, {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ name, text, beta }),
|
||
});
|
||
if (r.ok) {
|
||
delete _sbPoseEdits[name]; // edit is now the saved baseline
|
||
showToast('Pose saved', 'success');
|
||
await _reloadPosesUI();
|
||
} else {
|
||
showToast('Save failed: ' + await r.text(), 'error');
|
||
btn.disabled = false;
|
||
}
|
||
} catch (e) { showToast('Save error: ' + e, 'error'); btn.disabled = false; }
|
||
}
|
||
|
||
async function deletePoseFromServer(btn) {
|
||
const box = btn.closest('.sb-pose-edit');
|
||
if (!box) return;
|
||
const name = box.dataset.pose;
|
||
if (!confirm(`Delete pose "${name}"?`)) return;
|
||
btn.disabled = true;
|
||
try {
|
||
const r = await fetch(`${API}/poses/${encodeURIComponent(name)}`, { method: 'DELETE' });
|
||
if (r.ok) {
|
||
_fsSelectedPoses.delete(name);
|
||
selectedPoses.delete(name);
|
||
delete _sbPoseEdits[name];
|
||
showToast('Pose deleted', 'success');
|
||
await _reloadPosesUI();
|
||
} else {
|
||
showToast('Delete failed: ' + await r.text(), 'error');
|
||
btn.disabled = false;
|
||
}
|
||
} catch (e) { showToast('Delete error: ' + e, 'error'); btn.disabled = false; }
|
||
}
|
||
|
||
async function addNewPose() {
|
||
const name = (document.getElementById('sbNewPoseName')?.value || '').trim();
|
||
const text = (document.getElementById('sbNewPoseText')?.value || '').trim();
|
||
const beta = document.getElementById('sbNewPoseBeta')?.checked ?? false;
|
||
if (!name) { showToast('Pose name is required', 'info'); return; }
|
||
try {
|
||
const r = await fetch(`${API}/poses`, {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ name, text, beta }),
|
||
});
|
||
if (r.ok) {
|
||
showToast('Pose added', 'success');
|
||
await _reloadPosesUI();
|
||
} else {
|
||
showToast('Add failed: ' + await r.text(), 'error');
|
||
}
|
||
} catch (e) { showToast('Add error: ' + e, 'error'); }
|
||
}
|
||
|
||
function toggleSbPose(name) {
|
||
if (_fsSelectedPoses.has(name)) _fsSelectedPoses.delete(name);
|
||
else _fsSelectedPoses.add(name);
|
||
renderSidebarGenerate();
|
||
}
|
||
|
||
// Live client-side filter of the pose grid by name substring (no re-render).
|
||
function sbFilterPoses(q) {
|
||
_sbPoseFilter = q || '';
|
||
const needle = _sbPoseFilter.trim().toLowerCase();
|
||
document.querySelectorAll('#sbPosesGrid .sb-pose-btn').forEach(btn => {
|
||
const name = btn.dataset.poseName || '';
|
||
btn.style.display = (!needle || name.includes(needle)) ? '' : 'none';
|
||
});
|
||
}
|
||
|
||
function toggleSbAngle(name) {
|
||
if (_sbSelectedAngles.has(name)) _sbSelectedAngles.delete(name);
|
||
else _sbSelectedAngles.add(name);
|
||
renderSidebarGenerate();
|
||
}
|
||
|
||
function sbToggleOrbitAll() {
|
||
const ORBIT_NAMES = ['Front', '¾ Left', '¾ Right', 'Side L', 'Side R', 'Back'];
|
||
const allSel = ORBIT_NAMES.every(n => _sbSelectedAngles.has(n));
|
||
if (allSel) { ORBIT_NAMES.forEach(n => _sbSelectedAngles.delete(n)); }
|
||
else { ORBIT_NAMES.forEach(n => _sbSelectedAngles.add(n)); }
|
||
renderSidebarGenerate();
|
||
}
|
||
|
||
function setSbRefMode(mode) {
|
||
_sbRefMode = mode;
|
||
renderSidebarGenerate();
|
||
}
|
||
|
||
// Remove a reference by clicking its thumbnail above the custom prompt — no page
|
||
// refresh needed (previously refs could only be cleared via Shift+Click filmstrip).
|
||
function sbRemoveRef(idx) {
|
||
const pos = _sbRefIndices.indexOf(idx);
|
||
if (pos >= 0) _sbRefIndices.splice(pos, 1);
|
||
updateStudio();
|
||
if (_activeSidebarTab === 'generate') renderSidebarGenerate();
|
||
}
|
||
|
||
function sbSelectWireframe(val) {
|
||
_sbWireframeRef = val;
|
||
renderSidebarGenerate();
|
||
}
|
||
|
||
function sbUpdateWireframeTime(t) {
|
||
_sbWireframeTime = t;
|
||
const label = document.getElementById('sbWireframeTimeLabel');
|
||
if (label) label.textContent = Math.round(t * 100) + '%';
|
||
// Debounced thumb update
|
||
clearTimeout(sbUpdateWireframeTime._timer);
|
||
sbUpdateWireframeTime._timer = setTimeout(() => {
|
||
const thumb = document.getElementById('sbWireframeThumb');
|
||
if (thumb && _sbWireframeRef) {
|
||
thumb.src = `${API}/wireframe/frame/${encodeURIComponent(_sbWireframeRef)}?t=${t}&_=${Date.now()}`;
|
||
}
|
||
}, 400);
|
||
}
|
||
|
||
async function submitSingleAngle(prompt) {
|
||
if (!_fsModelFilename) return;
|
||
const bar = document.getElementById('studioAngleBar');
|
||
if (bar) bar.style.pointerEvents = 'none';
|
||
try {
|
||
const r = await fetch(`${API}/batch`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
filenames: [_fsModelFilename], prompt: [prompt],
|
||
poses: [null], seed: -1, max_area: 0, group_id: lbCurrentGid,
|
||
wireframe_ref: _sbWireframeRef || null,
|
||
wireframe_time: _sbWireframeTime,
|
||
}),
|
||
});
|
||
if (r.ok) {
|
||
const { job_id } = await r.json();
|
||
_followLatestGid = lbCurrentGid;
|
||
showToast('Generating angle…', 'info');
|
||
// Light polling — refresh when done
|
||
const poll = () => setTimeout(async () => {
|
||
try {
|
||
const s = await fetch(`${API}/batch/${job_id}`).then(r => r.json());
|
||
if (s.status === 'done') { refreshNow(true); }
|
||
else if (s.status !== 'error' && s.status !== 'cancelled') poll();
|
||
} catch(e) {}
|
||
}, 2000);
|
||
poll();
|
||
} else { showToast('Angle generation failed', 'error'); }
|
||
} catch(e) { showToast('API error: ' + e, 'error'); }
|
||
if (bar) bar.style.pointerEvents = '';
|
||
}
|
||
|
||
function updateSbAngleBar() {
|
||
const bar = document.getElementById('studioAngleBar');
|
||
if (!bar) return;
|
||
const isVid = lbNames[lbIdx] && (isVideo(lbNames[lbIdx]) || fileContentType[lbNames[lbIdx]] === 'video');
|
||
const hasCrop = !!document.getElementById('cropCanvas');
|
||
if (isVid || hasCrop || !lbNames[lbIdx]) {
|
||
bar.style.display = 'none';
|
||
return;
|
||
}
|
||
bar.style.display = 'flex';
|
||
// Show 6 most useful angles (skip High/Low for overlay — rare)
|
||
const overlay = CAMERA_ANGLES.slice(0, 6);
|
||
bar.innerHTML = overlay.map(a => {
|
||
const pSafe = a.prompt.replace(/'/g, "\\'");
|
||
return `<button onclick="submitSingleAngle('${pSafe}')" title="${escHtml(a.name)}">${a.icon}</button>`;
|
||
}).join('') + `
|
||
<button onclick="startManualPad()" title="Pad canvas (Expand image)" style="color:#f59e0b">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin:auto;display:block">
|
||
<path d="M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z"/>
|
||
<path d="M12 8v8m-4-4h8"/>
|
||
</svg>
|
||
</button>`;
|
||
}
|
||
|
||
function updateSbGenBtn() {
|
||
const btn = document.getElementById('sbGenBtn');
|
||
if (!btn) return;
|
||
const hasPrompt = (document.getElementById('sbGenPromptInput')?.value || '').trim().length > 0;
|
||
const getV = (id) => document.getElementById(id)?.value || '0';
|
||
const padValStr = getV('sbPadTop') + getV('sbPadRight') + getV('sbPadBottom') + getV('sbPadLeft');
|
||
const hasPadding = padValStr.replace(/[0%\s]/g, '').length > 0;
|
||
const outpaint = document.getElementById('sbPadOutpaint')?.checked;
|
||
const hasPaddingAction = hasPadding && outpaint;
|
||
const total = _fsSelectedPoses.size + _sbSelectedAngles.size + (hasPrompt ? 1 : 0) + (hasPaddingAction && !hasPrompt ? 1 : 0);
|
||
btn.disabled = total === 0;
|
||
btn.textContent = total > 1 ? `Generate (${total})` : 'Generate';
|
||
}
|
||
|
||
async function cancelSbGenerate() {
|
||
if (!_sbGenJobId) return;
|
||
const cancelBtn = document.getElementById('sbCancelBtn');
|
||
if (cancelBtn) cancelBtn.disabled = true;
|
||
try {
|
||
await fetch(`${API}/batch/${_sbGenJobId}`, { method: 'DELETE' });
|
||
showToast('Generation cancelled', 'info');
|
||
} catch (_) {}
|
||
clearTimeout(_sbGenJobPollTimer);
|
||
_sbGenJobId = null;
|
||
const btn = document.getElementById('sbGenBtn');
|
||
const statusEl = document.getElementById('sbGenJobStatus');
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Generate'; }
|
||
if (cancelBtn) { cancelBtn.disabled = false; cancelBtn.style.display = 'none'; }
|
||
if (statusEl) statusEl.style.display = 'none';
|
||
}
|
||
|
||
async function refineSbPrompt() {
|
||
const el = document.getElementById('sbGenPromptInput');
|
||
if (!el) return;
|
||
let prompt = el.value.trim();
|
||
if (!prompt) {
|
||
// If empty, use current image's prompt if available
|
||
if (lbIdx >= 0 && lbNames[lbIdx]) {
|
||
const fn = lbNames[lbIdx];
|
||
prompt = filePrompts[fn] || '';
|
||
}
|
||
}
|
||
if (!prompt) {
|
||
showToast('No prompt to refine. Enter one or select an image.', 'info');
|
||
return;
|
||
}
|
||
|
||
const btn = document.getElementById('sbRefineBtn');
|
||
const oldHtml = btn.innerHTML;
|
||
btn.disabled = true;
|
||
btn.innerHTML = '<span class="sb-spinner" style="width:10px;height:10px;border-width:2px;margin:0"></span> Refining…';
|
||
|
||
try {
|
||
const r = await fetch(`${API}/refine-prompt`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ prompt })
|
||
});
|
||
if (r.ok) {
|
||
const { refined } = await r.json();
|
||
// Append refined prompt to original
|
||
// User said: (original prompt+ gen_poses.py refined prompt)
|
||
el.value = (el.value.trim() + "\n\n" + refined).trim();
|
||
autoGrowPrompt(el);
|
||
updateSbGenBtn();
|
||
showToast('Prompt refined', 'success');
|
||
} else {
|
||
showToast('Refinement failed: ' + await r.text(), 'error');
|
||
}
|
||
} catch (e) {
|
||
showToast('Refinement error: ' + e, 'error');
|
||
} finally {
|
||
btn.disabled = false;
|
||
btn.innerHTML = oldHtml;
|
||
}
|
||
}
|
||
|
||
async function submitSbGenerate() {
|
||
if (!_fsModelFilename) return;
|
||
const promptVal = (document.getElementById('sbGenPromptInput')?.value || '').trim();
|
||
const hasPoses = _fsSelectedPoses.size > 0 || _sbSelectedAngles.size > 0;
|
||
const getV = (id) => document.getElementById(id)?.value || '0';
|
||
const padValStr = getV('sbPadTop') + getV('sbPadRight') + getV('sbPadBottom') + getV('sbPadLeft');
|
||
const hasPadding = padValStr.replace(/[0%\s]/g, '').length > 0;
|
||
const outpaint = document.getElementById('sbPadOutpaint')?.checked;
|
||
const hasPaddingAction = hasPadding && outpaint;
|
||
|
||
if (!hasPoses && !hasPaddingAction) {
|
||
// If no poses or padding, we'll run base_prompts
|
||
}
|
||
_sbPadTop = document.getElementById('sbPadTop')?.value || '0';
|
||
_sbPadRight = document.getElementById('sbPadRight')?.value || '0';
|
||
_sbPadBottom = document.getElementById('sbPadBottom')?.value || '0';
|
||
_sbPadLeft = document.getElementById('sbPadLeft')?.value || '0';
|
||
_sbPadOutpaint = document.getElementById('sbPadOutpaint')?.checked || false;
|
||
const prompts = [], poses = [];
|
||
_fsSelectedPoses.forEach(name => {
|
||
poses.push(name);
|
||
// Use edited text if available, else original
|
||
const edited = _sbPoseEdits[name];
|
||
const orig = availablePoses[name]?.text ?? availablePoses[name] ?? name;
|
||
prompts.push((edited !== undefined && edited.trim()) ? edited.trim() : String(orig));
|
||
});
|
||
// Camera angles — treated as prompts with no pose tag
|
||
_sbSelectedAngles.forEach(name => {
|
||
const ang = CAMERA_ANGLES.find(a => a.name === name);
|
||
// Tag the output with the angle name (as a pose) so the angle button
|
||
// shows a green ✓ once generated, mirroring pose tracking.
|
||
if (ang) { prompts.push(ang.prompt); poses.push(name); }
|
||
});
|
||
if (promptVal) { prompts.push(promptVal); poses.push(null); savePromptHistory(promptVal); }
|
||
else if (_sbPadOutpaint) {
|
||
const isZero = (v) => !v || v === '0' || v === '0%' || v === '0px';
|
||
if (!isZero(_sbPadTop) || !isZero(_sbPadRight) || !isZero(_sbPadBottom) || !isZero(_sbPadLeft)) {
|
||
// If outpaint is checked but no prompt is given, we still want to trigger a task
|
||
prompts.push(""); poses.push(null);
|
||
}
|
||
}
|
||
if (prompts.length === 0) return;
|
||
const btn = document.getElementById('sbGenBtn');
|
||
btn.disabled = true; btn.textContent = 'Submitting…';
|
||
const statusEl = document.getElementById('sbGenJobStatus');
|
||
const textEl = document.getElementById('sbGenJobText');
|
||
const cancelBtn = document.getElementById('sbCancelBtn');
|
||
statusEl.style.display = 'inline-flex';
|
||
if (cancelBtn) cancelBtn.style.display = '';
|
||
textEl.textContent = `Queuing ${prompts.length} task(s)…`;
|
||
// Build filenames array: use multi-ref selection if set, else just primary
|
||
const refFilenames = _sbRefIndices.length > 0
|
||
? _sbRefIndices.map(idx => lbNames[idx]).filter(Boolean)
|
||
: [_fsModelFilename];
|
||
// Routing depends on the References mode toggle:
|
||
// 'combine' (≥2 refs) → /multi-ref: filenames[0] is primary (image1),
|
||
// the rest are extra refs (image2/image3) → ONE output per prompt.
|
||
// 'each' or a single ref → /batch: cross-products filenames × prompts
|
||
// → one edit per ref. (This was the only path before, which is why
|
||
// selecting 3 refs always produced 3 images.)
|
||
const useMultiRef = refFilenames.length >= 2 && _sbRefMode === 'combine';
|
||
const endpoint = useMultiRef ? '/multi-ref' : '/batch';
|
||
const isZero = (v) => !v || v === '0' || v === '0%' || v === '0px';
|
||
const padPayload = (!isZero(_sbPadTop) || !isZero(_sbPadRight) || !isZero(_sbPadBottom) || !isZero(_sbPadLeft))
|
||
? { pad_top: _sbPadTop, pad_right: _sbPadRight, pad_bottom: _sbPadBottom, pad_left: _sbPadLeft, pad_outpaint: _sbPadOutpaint }
|
||
: {};
|
||
const payload = useMultiRef
|
||
? { filenames: refFilenames, prompt: prompts, poses, seed: -1, max_area: 0, ...padPayload }
|
||
: { filenames: refFilenames, prompt: prompts, poses, seed: -1, max_area: 0,
|
||
group_id: lbCurrentGid,
|
||
wireframe_ref: _sbWireframeRef || null, wireframe_time: _sbWireframeTime,
|
||
...padPayload };
|
||
if (useMultiRef) {
|
||
const shortName = f => f.split('/').pop().replace(/\?.*$/, '');
|
||
let refLabel = `Combining → image1: ${shortName(refFilenames[0])}, image2: ${shortName(refFilenames[1])}`;
|
||
if (refFilenames[2]) refLabel += `, image3: ${shortName(refFilenames[2])}`;
|
||
showToast(refLabel, 'info', 5000);
|
||
}
|
||
try {
|
||
const r = await fetch(`${API}${endpoint}`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(payload),
|
||
});
|
||
if (!r.ok) {
|
||
textEl.textContent = `Error: ${await r.text()}`;
|
||
btn.disabled = false; btn.textContent = 'Retry';
|
||
if (cancelBtn) cancelBtn.style.display = 'none';
|
||
return;
|
||
}
|
||
const { job_id, total } = await r.json();
|
||
_sbGenJobId = job_id;
|
||
showToast(`Generating ${total} image${total !== 1 ? 's' : ''}…`, 'info');
|
||
textEl.textContent = `Generating… 0/${total}`;
|
||
let prevDone = 0;
|
||
const pollGen = () => {
|
||
clearTimeout(_sbGenJobPollTimer);
|
||
_sbGenJobPollTimer = setTimeout(async () => {
|
||
try {
|
||
const jr = await fetch(`${API}/batch/${job_id}`);
|
||
if (!jr.ok) { pollGen(); return; }
|
||
const job = await jr.json();
|
||
const finished = job.done + job.failed;
|
||
textEl.textContent = `Generating… ${finished}/${total}`;
|
||
// Refresh UI as each task completes; auto-follow to newest image
|
||
if (job.done > prevDone) {
|
||
prevDone = job.done;
|
||
_followLatestGid = lbCurrentGid;
|
||
refreshNow(true);
|
||
}
|
||
if (job.status === 'running') { pollGen(); return; }
|
||
if (job.status === 'cancelled') {
|
||
statusEl.style.display = 'none';
|
||
if (cancelBtn) cancelBtn.style.display = 'none';
|
||
btn.disabled = false; btn.textContent = 'Generate';
|
||
_sbGenJobId = null;
|
||
return;
|
||
}
|
||
if (job.status === 'done') {
|
||
showToast('Generation complete!', 'success');
|
||
statusEl.style.display = 'none';
|
||
if (cancelBtn) cancelBtn.style.display = 'none';
|
||
btn.disabled = false; btn.textContent = 'Generate';
|
||
_fsSelectedPoses.clear();
|
||
_sbSelectedAngles.clear();
|
||
_sbPoseEdits = {};
|
||
_sbGenJobId = null;
|
||
_followLatestGid = lbCurrentGid;
|
||
refreshNow(true);
|
||
renderSidebarGenerate();
|
||
} else {
|
||
textEl.textContent = `Error: ${job.error || 'unknown'}`;
|
||
btn.disabled = false; btn.textContent = 'Retry';
|
||
if (cancelBtn) cancelBtn.style.display = 'none';
|
||
_sbGenJobId = null;
|
||
}
|
||
} catch (e) { pollGen(); }
|
||
}, 2000);
|
||
};
|
||
pollGen();
|
||
} catch (e) {
|
||
textEl.textContent = `API error: ${e}`;
|
||
btn.disabled = false; btn.textContent = 'Retry';
|
||
if (cancelBtn) cancelBtn.style.display = 'none';
|
||
_sbGenJobId = null;
|
||
}
|
||
}
|
||
|
||
// ---- template video cards (faceswap / scenery) ----
|
||
// The wireframe library is large (dozens of files, >1 GB total) and lives on a
|
||
// network mount. Rendering every card as an autoplaying <video> spun up that many
|
||
// simultaneous decoders + range-streams, which OOM-crashed the renderer. Instead
|
||
// each card shows a static poster frame (a small server-rendered PNG) and only
|
||
// mounts a single real <video> while hovered — at most one decoder alive at a time.
|
||
|
||
// HTML for one template card. `extraHTML` lets faceswap add its trim button.
|
||
function _tplCardHTML(name, selected, onclickExpr, extraHTML = '') {
|
||
const stem = name.replace(/\.[^.]+$/, '');
|
||
const sel = selected ? ' selected' : '';
|
||
const nSafe = name.replace(/'/g, "\\'");
|
||
return `<div class="sb-template-card${sel}" onclick="${onclickExpr}"
|
||
onmouseenter="_tplPlay(this,'${nSafe}')" onmouseleave="_tplStop(this)">
|
||
<img src="${API}/wireframe/frame/${encodeURIComponent(name)}?t=0" loading="lazy" alt="">
|
||
<div class="sb-template-label">${escHtml(stem)}</div>${extraHTML}
|
||
</div>`;
|
||
}
|
||
|
||
// Mount + play a single <video> over the poster while hovered.
|
||
function _tplPlay(card, name) {
|
||
if (card._vid) return;
|
||
const v = document.createElement('video');
|
||
v.src = `${API}/wireframe/${encodeURIComponent(name)}`;
|
||
v.muted = true; v.loop = true; v.playsInline = true; v.preload = 'auto';
|
||
card.appendChild(v);
|
||
card._vid = v;
|
||
v.play().catch(() => {});
|
||
}
|
||
|
||
// Tear the video down completely so the decoder + network stream are released.
|
||
function _tplStop(card) {
|
||
const v = card && card._vid;
|
||
if (!v) return;
|
||
v.pause();
|
||
v.removeAttribute('src');
|
||
v.load(); // aborts the in-flight stream and frees the decoder
|
||
v.remove();
|
||
card._vid = null;
|
||
}
|
||
|
||
// Defensive: stop every active template video in the sidebar (called on tab switch
|
||
// so a hovered clip can't keep streaming under a freshly-rendered panel).
|
||
function _tplStopAll() {
|
||
document.querySelectorAll('#studioSidebar .sb-template-card').forEach(_tplStop);
|
||
}
|
||
|
||
// ---- faceswap sidebar tab ----
|
||
|
||
async function loadTemplateVideos() {
|
||
try {
|
||
const r = await fetch(`${API}/output/_data/videos.json`, { signal: AbortSignal.timeout(3000) });
|
||
if (r.ok) { availableVideos = (await r.json()).videos || []; return; }
|
||
} catch (_) {}
|
||
try {
|
||
const r = await fetch(`${API}/videos`);
|
||
if (!r.ok) return;
|
||
availableVideos = (await r.json()).videos || [];
|
||
} catch (_) { availableVideos = []; }
|
||
}
|
||
|
||
async function lbFaceswap() {
|
||
const data = groupData.get(lbCurrentGid);
|
||
if (!data) return;
|
||
_fsModelFilename = data.names[lbIdx];
|
||
_fsSelectedVideo = null;
|
||
if (!availableVideos.length) await loadTemplateVideos();
|
||
switchSidebarTab('faceswap');
|
||
// Expand sidebar if collapsed
|
||
document.getElementById('studioSidebar')?.classList.remove('collapsed');
|
||
document.getElementById('sidebarInnerToggle').textContent = '⊟';
|
||
}
|
||
|
||
function renderSidebarFaceswap() {
|
||
const panel = document.getElementById('sbPanelFaceswap');
|
||
if (!panel) return;
|
||
|
||
if (_fsTrimVideo) {
|
||
// Clip timeline view
|
||
const vSafe = _fsTrimVideo.replace(/'/g, "\\'");
|
||
panel.innerHTML = `
|
||
<div style="font-size:11px;color:#aaa;margin-bottom:6px;word-break:break-all">✂ ${escHtml(_fsTrimVideo)}</div>
|
||
<div class="clt-wrapper">
|
||
<video class="clt-video-preview" id="cltVideoPreview" muted preload="metadata"
|
||
src="${API}/wireframe/${encodeURIComponent(_fsTrimVideo)}"></video>
|
||
<div class="clt-track" id="cltTrack">
|
||
<div class="clt-bg"></div>
|
||
<div class="clt-range" id="cltRange"></div>
|
||
<div class="clt-handle" id="cltHandleStart"></div>
|
||
<div class="clt-handle end" id="cltHandleEnd"></div>
|
||
<div class="clt-playhead" id="cltPlayhead"></div>
|
||
</div>
|
||
<div class="clt-labels">
|
||
<span id="cltStartLabel">0:00</span>
|
||
<span id="cltEndLabel">0:00</span>
|
||
</div>
|
||
<input type="text" class="sb-input" id="cltOutputName"
|
||
placeholder="Output name (auto)" style="margin:6px 0">
|
||
<div id="cltStatus" style="font-size:11px;color:#aaa;min-height:16px"></div>
|
||
<div style="display:flex;gap:6px;margin-top:8px">
|
||
<button class="sb-btn" onclick="closeTrimPanel()">← Back</button>
|
||
<button class="sb-btn" onclick="cltPreviewRange()">▶ Preview</button>
|
||
<button class="sb-btn primary" onclick="submitTrimFromTimeline()">✂ Create clip</button>
|
||
</div>
|
||
</div>`;
|
||
requestAnimationFrame(() => initClipTimeline());
|
||
return;
|
||
}
|
||
|
||
// Template grid view
|
||
let html = '<div class="sb-label">Template videos</div>';
|
||
if (!availableVideos.length) {
|
||
html += '<div style="font-size:11px;color:#555;padding:6px 0">No wireframe videos found</div>';
|
||
} else {
|
||
html += '<div class="sb-template-grid">';
|
||
availableVideos.forEach(v => {
|
||
const vSafe = v.replace(/'/g, "\\'");
|
||
const trimBtn = `<button class="sb-template-trim-btn" title="Trim video"
|
||
onclick="event.stopPropagation();openTrimPanel('${vSafe}')">✂</button>`;
|
||
html += _tplCardHTML(v, _fsSelectedVideo === v, `sbSelectTemplate('${vSafe}')`, trimBtn);
|
||
});
|
||
html += '</div>';
|
||
}
|
||
html += `<div class="sb-sep"></div>
|
||
<div style="display:flex;flex-direction:column;gap:6px;margin-bottom:10px">
|
||
<label style="display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer">
|
||
<input type="checkbox" id="sbFsEnhance" checked>
|
||
<span>Enhance faces</span>
|
||
</label>
|
||
<label style="display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer" title="Requires install_facefusion.sh">
|
||
<input type="checkbox" id="sbFsHair" disabled>
|
||
<span>FaceFusion</span>
|
||
<span id="sbFsHairBadge" style="font-size:10px;color:#f87;background:#2a1a1a;padding:1px 5px;border-radius:3px">not installed</span>
|
||
</label>
|
||
<label style="display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer" title="Generate at lower resolution for quick preview">
|
||
<input type="checkbox" id="sbFsPreview" onchange="document.getElementById('sbFsPreviewOpts').style.display=this.checked?'flex':'none'">
|
||
<span>Preview mode</span>
|
||
</label>
|
||
<div id="sbFsPreviewOpts" style="display:none;align-items:center;gap:6px;padding-left:20px">
|
||
<span style="font-size:11px;color:#888">Scale:</span>
|
||
<select id="sbFsPreviewScale" style="font-size:11px;background:#1a1a1a;color:#ccc;border:1px solid #333;border-radius:3px;padding:1px 4px">
|
||
<option value="0.5">50%</option>
|
||
<option value="0.25">25%</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:8px">
|
||
<span id="sbFsJobStatus" style="flex:1;font-size:11px;color:#888;display:none">
|
||
<span class="sb-spinner"></span><span id="sbFsJobText">Processing…</span>
|
||
</span>
|
||
<button class="sb-btn primary" id="sbFsStartBtn" onclick="submitFaceswap()"
|
||
${_fsSelectedVideo ? '' : 'disabled'}>Start Faceswap</button>
|
||
</div>`;
|
||
|
||
panel.innerHTML = html;
|
||
// Cards show static poster frames; videos mount only on hover (_tplPlay).
|
||
// Check FaceFusion availability
|
||
fetch(`${API}/faceswap/check`).then(r => r.json()).then(s => {
|
||
const hairCb = document.getElementById('sbFsHair');
|
||
const hairBdg = document.getElementById('sbFsHairBadge');
|
||
if (!hairCb) return;
|
||
if (s.facefusion) { hairCb.disabled = false; hairBdg.style.display = 'none'; }
|
||
else { hairCb.disabled = true; hairBdg.style.display = ''; }
|
||
}).catch(()=>{});
|
||
}
|
||
|
||
function sbSelectTemplate(videoName) {
|
||
_fsSelectedVideo = videoName;
|
||
renderSidebarFaceswap();
|
||
}
|
||
|
||
function openTrimPanel(videoName) {
|
||
_fsTrimVideo = videoName;
|
||
renderSidebarFaceswap();
|
||
}
|
||
|
||
function closeTrimPanel() {
|
||
_fsTrimVideo = null;
|
||
renderSidebarFaceswap();
|
||
}
|
||
|
||
async function submitFaceswap() {
|
||
if (!_fsModelFilename || !_fsSelectedVideo) return;
|
||
const enhance = document.getElementById('sbFsEnhance')?.checked ?? true;
|
||
const hairEl = document.getElementById('sbFsHair');
|
||
const hair = (hairEl?.checked && !hairEl?.disabled) ?? false;
|
||
const previewCb = document.getElementById('sbFsPreview');
|
||
const previewScale = (previewCb?.checked)
|
||
? parseFloat(document.getElementById('sbFsPreviewScale')?.value ?? '0.5')
|
||
: 1.0;
|
||
const btn = document.getElementById('sbFsStartBtn');
|
||
if (btn) { btn.disabled = true; btn.textContent = 'Starting…'; }
|
||
const statusEl = document.getElementById('sbFsJobStatus');
|
||
const textEl = document.getElementById('sbFsJobText');
|
||
if (statusEl) statusEl.style.display = 'inline-flex';
|
||
const prevTag = previewScale < 1.0 ? ` preview ${Math.round(previewScale * 100)}%` : '';
|
||
const modeLabel = (hair ? 'FaceFusion + hair' : (enhance ? 'insightface + enhance' : 'insightface')) + prevTag;
|
||
if (textEl) textEl.textContent = `Starting (${modeLabel})…`;
|
||
try {
|
||
const r = await fetch(`${API}/faceswap`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ model_filename: _fsModelFilename, video_name: _fsSelectedVideo, enhance, hair, preview_scale: previewScale }),
|
||
});
|
||
if (!r.ok) {
|
||
if (textEl) textEl.textContent = `Error: ${await r.text()}`;
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Start Faceswap'; }
|
||
return;
|
||
}
|
||
const { job_id } = await r.json();
|
||
_fsJobId = job_id;
|
||
showToast(`Faceswap started (${_fsSelectedVideo})`, 'info');
|
||
pollFaceswapJob(job_id);
|
||
} catch (e) {
|
||
if (textEl) textEl.textContent = `API error: ${e}`;
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Start Faceswap'; }
|
||
}
|
||
}
|
||
|
||
function pollFaceswapJob(job_id) {
|
||
clearTimeout(_fsJobPollTimer);
|
||
_fsJobPollTimer = setTimeout(async () => {
|
||
try {
|
||
const r = await fetch(`${API}/batch/${job_id}`);
|
||
if (!r.ok) { pollFaceswapJob(job_id); return; }
|
||
const job = await r.json();
|
||
const pct = job.total > 1 ? ` ${job.done}/${job.total} frames` : '';
|
||
const textEl = document.getElementById('sbFsJobText');
|
||
const btn = document.getElementById('sbFsStartBtn');
|
||
if (job.status === 'running') {
|
||
if (textEl) textEl.textContent = `Processing${pct}…`;
|
||
pollFaceswapJob(job_id);
|
||
} else if (job.status === 'done') {
|
||
if (textEl) textEl.textContent = `Done! ${job.output || ''}`;
|
||
if (btn) btn.textContent = 'Done ✓';
|
||
showToast(`Faceswap complete: ${job.output || ''}`, 'success');
|
||
_followLatestGid = lbCurrentGid;
|
||
refreshNow(true);
|
||
} else {
|
||
if (textEl) textEl.textContent = `Error: ${job.error || 'unknown'}`;
|
||
if (btn) { btn.disabled = false; btn.textContent = 'Retry'; }
|
||
showToast(`Faceswap failed: ${job.error || ''}`, 'error');
|
||
}
|
||
} catch (e) { pollFaceswapJob(job_id); }
|
||
}, 2500);
|
||
}
|
||
|
||
// ---- clip timeline ----
|
||
let _cltDuration = 0, _cltStart = 0, _cltEnd = 0, _cltDragging = null;
|
||
|
||
function initClipTimeline() {
|
||
const video = document.getElementById('cltVideoPreview');
|
||
const track = document.getElementById('cltTrack');
|
||
if (!video || !track) return;
|
||
video.addEventListener('loadedmetadata', () => {
|
||
_cltDuration = video.duration || 10;
|
||
_cltStart = 0;
|
||
_cltEnd = _cltDuration;
|
||
updateCltUI();
|
||
});
|
||
video.addEventListener('timeupdate', () => {
|
||
if (_cltDragging || _cltDuration <= 0) return;
|
||
const ph = document.getElementById('cltPlayhead');
|
||
if (ph) ph.style.left = (video.currentTime / _cltDuration * 100) + '%';
|
||
});
|
||
track.addEventListener('pointerdown', e => {
|
||
const rect = track.getBoundingClientRect();
|
||
const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
|
||
const startPct = _cltDuration > 0 ? _cltStart / _cltDuration : 0;
|
||
const endPct = _cltDuration > 0 ? _cltEnd / _cltDuration : 1;
|
||
if (e.target.id === 'cltHandleStart' || (e.target.id !== 'cltHandleEnd' && Math.abs(pct - startPct) <= Math.abs(pct - endPct))) {
|
||
_cltDragging = 'start';
|
||
} else if (e.target.id === 'cltHandleEnd' || Math.abs(pct - endPct) < Math.abs(pct - startPct)) {
|
||
_cltDragging = 'end';
|
||
} else {
|
||
if (video) video.currentTime = pct * _cltDuration;
|
||
return;
|
||
}
|
||
track.setPointerCapture(e.pointerId);
|
||
e.preventDefault();
|
||
});
|
||
track.addEventListener('pointermove', e => {
|
||
if (!_cltDragging || _cltDuration <= 0) return;
|
||
const rect = track.getBoundingClientRect();
|
||
const t = Math.max(0, Math.min(_cltDuration, (e.clientX - rect.left) / rect.width * _cltDuration));
|
||
if (_cltDragging === 'start') _cltStart = Math.min(t, _cltEnd - 0.1);
|
||
else _cltEnd = Math.max(t, _cltStart + 0.1);
|
||
updateCltUI();
|
||
if (video) video.currentTime = _cltDragging === 'start' ? _cltStart : _cltEnd;
|
||
});
|
||
track.addEventListener('pointerup', e => {
|
||
_cltDragging = null;
|
||
track.releasePointerCapture(e.pointerId);
|
||
});
|
||
}
|
||
|
||
function updateCltUI() {
|
||
if (_cltDuration <= 0) return;
|
||
const sp = (_cltStart / _cltDuration) * 100;
|
||
const ep = (_cltEnd / _cltDuration) * 100;
|
||
const sh = document.getElementById('cltHandleStart');
|
||
const eh = document.getElementById('cltHandleEnd');
|
||
const rn = document.getElementById('cltRange');
|
||
const sl = document.getElementById('cltStartLabel');
|
||
const el = document.getElementById('cltEndLabel');
|
||
if (sh) sh.style.left = sp + '%';
|
||
if (eh) eh.style.left = ep + '%';
|
||
if (rn) { rn.style.left = sp + '%'; rn.style.width = (ep - sp) + '%'; }
|
||
if (sl) sl.textContent = formatSecs(_cltStart);
|
||
if (el) el.textContent = formatSecs(_cltEnd);
|
||
}
|
||
|
||
function formatSecs(s) {
|
||
const m = Math.floor(s / 60), sec = (s % 60).toFixed(1);
|
||
return m + ':' + (parseFloat(sec) < 10 ? '0' : '') + sec;
|
||
}
|
||
|
||
function cltPreviewRange() {
|
||
const video = document.getElementById('cltVideoPreview');
|
||
if (!video || _cltDuration <= 0) return;
|
||
video.currentTime = _cltStart;
|
||
video.play();
|
||
const stop = () => { if (video.currentTime >= _cltEnd) { video.pause(); video.currentTime = _cltStart; } };
|
||
video.addEventListener('timeupdate', stop);
|
||
setTimeout(() => video.removeEventListener('timeupdate', stop), (_cltEnd - _cltStart + 2) * 1000);
|
||
}
|
||
|
||
async function submitTrimFromTimeline() {
|
||
if (!_fsTrimVideo) return;
|
||
const name = (document.getElementById('cltOutputName')?.value || '').trim() || null;
|
||
const statusEl = document.getElementById('cltStatus');
|
||
if (_cltEnd <= _cltStart) { if (statusEl) statusEl.textContent = 'End must be > start'; return; }
|
||
if (statusEl) statusEl.textContent = 'Trimming…';
|
||
try {
|
||
const r = await fetch(`${API}/wireframe/trim`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ video_name: _fsTrimVideo, start: _cltStart, end: _cltEnd, output_name: name }),
|
||
});
|
||
if (!r.ok) { if (statusEl) statusEl.textContent = 'Error: ' + await r.text(); return; }
|
||
const d = await r.json();
|
||
showToast('Clip saved: ' + d.output_name, 'success');
|
||
availableVideos = [];
|
||
await loadTemplateVideos();
|
||
closeTrimPanel();
|
||
} catch (e) { if (statusEl) statusEl.textContent = 'Error: ' + e; }
|
||
}
|
||
|
||
// ---- scenery sidebar tab ----
|
||
let _sceneVideo = null, _sceneDuration = 0, _sceneFrameBytes = null, _sceneJobPollTimer = null;
|
||
|
||
let _sceneGridOpen = false; // whether the video picker grid is expanded
|
||
let _sceneLibOpen = false; // whether "previous scenery" library panel is expanded
|
||
let _sceneLib = null; // cached { groups, ungrouped } from /scenery/library
|
||
// 3 positional reference slots: 0=Background(image1), 1=Person(image2), 2=Extra(image3).
|
||
// Each entry is null | {kind:'bytes', data:<base64>} | {kind:'file', name:<filename>}.
|
||
// Backend contract: slot0→scene_bytes (bytes; a file here is fetched→bytes on submit),
|
||
// slot1→model_filename (must be a file), slot2→extra_filename (file, optional).
|
||
let _sceneRefs = [null, null, null];
|
||
let _scenePersonInit = false; // person auto-filled into slot1 once per group
|
||
let _sceneDrag = null; // active drag payload: {type:'slot',idx} | {type:'file',name}
|
||
|
||
function renderSidebarScenery() {
|
||
const panel = document.getElementById('sbPanelScenery');
|
||
if (!panel) return;
|
||
// Preserve a typed prompt across the frequent re-renders (slot changes).
|
||
const prevScenePrompt = document.getElementById('scenePromptInput')?.value || '';
|
||
// Auto-fill the Person slot (image2) once per group with the current image.
|
||
const personName = _fsModelFilename || lbNames[lbIdx] || '';
|
||
if (!_scenePersonInit && !_sceneRefs[1] && personName) {
|
||
_sceneRefs[1] = { kind: 'file', name: personName };
|
||
_scenePersonInit = true;
|
||
}
|
||
|
||
const bgRef = _sceneRefs[0];
|
||
const bgBytes = (bgRef && bgRef.kind === 'bytes') ? bgRef.data : null;
|
||
const haveRef = _sceneVideo || bgBytes;
|
||
// Auto-open the grid when no background source is chosen yet.
|
||
const gridOpen = _sceneGridOpen || !haveRef;
|
||
|
||
// --- Interactive 3-slot reference row (drag to reorder, ✕ to clear) ---
|
||
const SLOT_META = [
|
||
{ n: 1, label: 'Background' },
|
||
{ n: 2, label: 'Person' },
|
||
{ n: 3, label: 'Extra' },
|
||
];
|
||
const slotHtml = SLOT_META.map((m, i) => {
|
||
const ref = _sceneRefs[i];
|
||
const thumb = _sceneThumb(ref);
|
||
const kind = ref ? (ref.kind === 'bytes' ? '📷' : '🖼') : '';
|
||
return `<div class="scene-slot ${ref ? 'filled' : 'empty'}" data-slot="${i}"
|
||
draggable="${ref ? 'true' : 'false'}"
|
||
ondragstart="sceneDragSlotStart(event,${i})"
|
||
ondragover="sceneDragOver(event)" ondragleave="sceneDragLeave(event)" ondrop="sceneDrop(event,${i})">
|
||
<div class="scene-slot-thumb">${thumb
|
||
? `<img src="${thumb}" onerror="this.style.opacity=.3">`
|
||
: `<span class="ph">${m.n}</span>`}
|
||
${ref ? `<button class="scene-slot-x" title="Remove" onclick="sceneClearSlot(${i})">✕</button>` : ''}
|
||
${kind ? `<span class="scene-slot-kind">${kind}</span>` : ''}</div>
|
||
<div class="scene-slot-cap"><b>Image ${m.n}</b>${m.label}</div>
|
||
</div>`;
|
||
}).join('');
|
||
let html = `<div class="sb-label">References <span style="color:#555;font-weight:400;font-size:9px">· Shift+Click filmstrip (fills →) · drag to reorder</span></div>
|
||
<div class="scene-slots">${slotHtml}</div>`;
|
||
|
||
// --- Image 1 source: background scene (video scrub + capture, or upload) ---
|
||
html += `<div class="sb-label">Image 1 source · background</div>`;
|
||
|
||
// --- Selected video: player + slider + capture (always at top) ---
|
||
if (_sceneVideo) {
|
||
html += `
|
||
<div class="scene-frame-preview" id="sceneFramePreview" style="display:block">
|
||
<video id="sceneVideoEl" muted preload="auto" playsinline
|
||
style="${_sceneFrameBytes?'display:none':'display:block'};width:100%;height:100%;object-fit:contain"></video>
|
||
<img id="sceneFrameImg" style="${_sceneFrameBytes?'display:block':'display:none'};width:100%;height:100%;object-fit:contain"
|
||
${_sceneFrameBytes?`src="data:image/png;base64,${_sceneFrameBytes}"`:''}/>
|
||
<div class="scene-captured-badge" id="sceneCapturedBadge" style="${_sceneFrameBytes?'display:block':'display:none'}">✓ frame captured</div>
|
||
</div>
|
||
<input type="range" class="scene-scrubber" id="sceneScrubber" min="0" max="1000" value="0"
|
||
oninput="sceneScrub(this.value)" ${_sceneFrameBytes?'disabled':''}>
|
||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
||
<span id="sceneTimeLabel" style="font-size:11px;color:#888">0:00.0</span>
|
||
<span style="font-size:10px;color:#555">${escHtml(_sceneVideo.replace(/\.[^.]+$/,''))}</span>
|
||
</div>
|
||
<button class="sb-btn primary" id="sceneCaptureBtn" onclick="sceneExtractFrame()"
|
||
style="${_sceneFrameBytes?'display:none':'display:block'};width:100%;margin-bottom:8px">📷 Capture this frame</button>
|
||
<button class="sb-btn" id="sceneReleaseBtn" onclick="sceneReleaseFrame()"
|
||
style="${_sceneFrameBytes?'display:block':'display:none'};width:100%;margin-bottom:8px">✖ Pick a different frame</button>
|
||
<button class="sb-btn" onclick="sceneToggleGrid()" style="width:100%;margin-bottom:10px">
|
||
${gridOpen?'▲ Hide video list':'▼ Change background video'}</button>`;
|
||
}
|
||
|
||
// --- Video picker grid (collapsible) ---
|
||
if (!_sceneVideo || gridOpen) {
|
||
html += '<div class="sb-label">Background video</div>';
|
||
if (availableVideos.length) {
|
||
html += '<div class="sb-template-grid">';
|
||
availableVideos.forEach(v => {
|
||
const vSafe = v.replace(/'/g, "\\'");
|
||
html += _tplCardHTML(v, _sceneVideo === v, `sceneSelectVideo('${vSafe}')`);
|
||
});
|
||
html += '</div>';
|
||
} else {
|
||
html += '<div style="font-size:11px;color:#555;padding:6px 0">No wireframe videos found</div>';
|
||
}
|
||
}
|
||
|
||
// Upload alternative for the background (still image 1)
|
||
html += `<input type="file" id="sceneUploadInput" accept="image/*" style="display:none" onchange="sceneHandleUpload(event)">
|
||
<button class="sb-btn" onclick="document.getElementById('sceneUploadInput').click()" style="width:100%;margin-bottom:10px">⬆ Upload image as background</button>`;
|
||
|
||
// --- Person / Extra: filled via filmstrip Shift+Click (see slots above) ---
|
||
const person = _sceneRefs[1];
|
||
html += `<div class="sb-sep"></div>
|
||
<div style="font-size:10px;color:#666;line-height:1.5;margin-bottom:8px">
|
||
<b style="color:#999">Image 2 (Person)</b> & <b style="color:#999">Image 3 (Extra)</b>:
|
||
Shift+Click a thumbnail in the bottom filmstrip to add it (fills the first open
|
||
slot right→left). Drag the slots above to rearrange, or ✕ to clear.
|
||
</div>`;
|
||
if (!person || person.kind !== 'file') {
|
||
html += `<div style="font-size:11px;color:#a55;padding:0 0 8px">Image 2 needs a gallery image (Shift+Click the filmstrip).</div>`;
|
||
}
|
||
|
||
// --- Prompt + generate ---
|
||
const canGen = !!bgRef && !!person && person.kind === 'file'
|
||
&& (!_sceneRefs[2] || _sceneRefs[2].kind === 'file');
|
||
html += `<div class="sb-sep"></div>
|
||
<div class="sb-label">Prompt (optional) <span style="color:#555;font-weight:400;font-size:9px">· history while typing</span></div>
|
||
<div class="sb-prompt-wrap" style="position:relative;margin-bottom:10px">
|
||
<textarea class="sb-input" id="scenePromptInput" rows="2" autocomplete="off"
|
||
placeholder="Auto: place person in scene… (type to search history)"
|
||
oninput="autoGrowPrompt(this);showPromptSuggest(this,'scenePromptSuggest')"
|
||
onfocus="autoGrowPrompt(this);showPromptSuggest(this,'scenePromptSuggest')"
|
||
onblur="setTimeout(()=>hidePromptSuggest('scenePromptSuggest'),150)"
|
||
onkeydown="if(event.key==='Escape')hidePromptSuggest('scenePromptSuggest')"
|
||
style="resize:none;max-height:300px;overflow-y:auto;line-height:1.4;font-family:inherit"></textarea>
|
||
<div id="scenePromptSuggest" style="display:none;position:absolute;left:0;right:0;z-index:50;max-height:240px;overflow-y:auto;background:#18181b;border:1px solid #2a2a2a;border-radius:6px;margin-top:2px;box-shadow:0 6px 20px rgba(0,0,0,0.6)"></div>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:8px">
|
||
<span id="sceneJobStatus" style="flex:1;font-size:11px;color:#888;display:none">
|
||
<span class="sb-spinner"></span><span id="sceneJobText">Generating…</span>
|
||
</span>
|
||
<button class="sb-btn primary" id="sceneGenBtn" onclick="submitGenerateScenery()"
|
||
${canGen?'':'disabled'}>Create Scenery</button>
|
||
</div>`;
|
||
|
||
// --- Previous scenery library (grouped by source video) ---
|
||
html += `<div class="sb-sep"></div>
|
||
<div style="display:flex;align-items:center;gap:6px;margin-bottom:6px">
|
||
<span class="sb-label" style="margin:0;flex:1">Previous scenery</span>
|
||
<button class="sb-btn" style="padding:2px 8px;font-size:10px" onclick="sceneLibToggle()">${_sceneLibOpen ? '▲ hide' : '▼ browse'}</button>
|
||
</div>`;
|
||
if (_sceneLibOpen) {
|
||
if (!_sceneLib) {
|
||
html += `<div id="sceneLibLoader" style="font-size:11px;color:#666;padding:4px 0">Loading…</div>`;
|
||
} else if (!_sceneLib.groups.length && !_sceneLib.ungrouped.length) {
|
||
html += `<div style="font-size:11px;color:#555;padding:4px 0">No previous scenery found.</div>`;
|
||
} else {
|
||
const renderLibItems = (items) => items.map(it => {
|
||
const url = IMAGE_FOLDER + encodeURIComponent(it.filename);
|
||
const gLabel = it.group_id ? ` <span style="color:#555">${escHtml(it.group_id)}</span>` : '';
|
||
return `<div style="display:inline-block;margin:2px;cursor:pointer;position:relative;vertical-align:top"
|
||
title="Use as background · ${escHtml(it.filename)}${it.group_id?' ('+escHtml(it.group_id)+')':''}"
|
||
onclick="sceneLibUseAsBackground(${JSON.stringify(it.filename)})">
|
||
<img src="${url}" style="width:52px;height:52px;object-fit:cover;border-radius:4px;border:1px solid #333">
|
||
</div>`;
|
||
}).join('');
|
||
_sceneLib.groups.forEach(g => {
|
||
const vName = g.video.replace(/\.[^.]+$/, '');
|
||
html += `<div style="margin-bottom:8px">
|
||
<div style="font-size:10px;color:#888;margin-bottom:3px">📹 ${escHtml(vName)}</div>
|
||
<div>${renderLibItems(g.items)}</div>
|
||
</div>`;
|
||
});
|
||
if (_sceneLib.ungrouped.length) {
|
||
html += `<div style="margin-bottom:8px">
|
||
<div style="font-size:10px;color:#888;margin-bottom:3px">📷 uploaded / other</div>
|
||
<div>${renderLibItems(_sceneLib.ungrouped)}</div>
|
||
</div>`;
|
||
}
|
||
}
|
||
}
|
||
|
||
panel.innerHTML = html;
|
||
|
||
// Lazy-load library data when panel is opened
|
||
if (_sceneLibOpen && !_sceneLib) {
|
||
fetch(`${API}/scenery/library`)
|
||
.then(r => r.ok ? r.json() : null)
|
||
.then(d => { if (d) { _sceneLib = d; renderSidebarScenery(); } })
|
||
.catch(() => {});
|
||
}
|
||
|
||
// Restore the preserved prompt text and size the textarea.
|
||
const spEl = document.getElementById('scenePromptInput');
|
||
if (spEl) { spEl.value = prevScenePrompt; autoGrowPrompt(spEl); }
|
||
|
||
if (_sceneVideo) {
|
||
const vid = document.getElementById('sceneVideoEl');
|
||
vid.src = `${API}/wireframe/${encodeURIComponent(_sceneVideo)}`;
|
||
// Once metadata is known, size the slider so 1000 steps span the whole clip.
|
||
vid.addEventListener('loadedmetadata', () => {
|
||
_sceneDuration = vid.duration || 0;
|
||
const lbl = document.getElementById('sceneTimeLabel');
|
||
if (lbl) lbl.textContent = formatSecs(vid.currentTime || 0);
|
||
}, { once: true });
|
||
// Keep the time label in sync if the frame is seeked any other way.
|
||
vid.addEventListener('seeked', () => {
|
||
const lbl = document.getElementById('sceneTimeLabel');
|
||
if (lbl) lbl.textContent = formatSecs(vid.currentTime || 0);
|
||
});
|
||
}
|
||
}
|
||
|
||
// Scrub the preview video live as the slider moves (no server round-trip).
|
||
function sceneScrub(val) {
|
||
const vid = document.getElementById('sceneVideoEl');
|
||
if (!vid || !vid.duration) return;
|
||
const t = (parseFloat(val) / 1000) * vid.duration;
|
||
vid.pause();
|
||
vid.currentTime = t;
|
||
const lbl = document.getElementById('sceneTimeLabel');
|
||
if (lbl) lbl.textContent = formatSecs(t);
|
||
}
|
||
|
||
function sceneToggleGrid() {
|
||
_sceneGridOpen = !_sceneGridOpen;
|
||
renderSidebarScenery();
|
||
}
|
||
|
||
function sceneLibToggle() {
|
||
_sceneLibOpen = !_sceneLibOpen;
|
||
if (_sceneLibOpen && !_sceneLib) _sceneLib = null; // force re-fetch on open
|
||
renderSidebarScenery();
|
||
}
|
||
|
||
// Use a library scenery image as slot 0 background (fetched to bytes).
|
||
async function sceneLibUseAsBackground(filename) {
|
||
try {
|
||
const url = IMAGE_FOLDER + encodeURIComponent(filename);
|
||
const b64 = await _fetchAsBase64(url);
|
||
_sceneRefs[0] = { kind: 'bytes', data: b64 };
|
||
_sceneVideo = null;
|
||
_sceneFrameBytes = null;
|
||
_sceneGridOpen = false;
|
||
renderSidebarScenery();
|
||
showToast('Background set from previous scenery', 'success');
|
||
} catch (e) {
|
||
showToast('Could not load image: ' + e, 'error');
|
||
}
|
||
}
|
||
|
||
// ---- scenery reference-slot helpers ----
|
||
|
||
// Thumbnail (URL or data-URL) for a slot ref.
|
||
function _sceneThumb(ref) {
|
||
if (!ref) return '';
|
||
if (ref.kind === 'bytes') return 'data:image/png;base64,' + ref.data;
|
||
const idx = lbNames.indexOf(ref.name);
|
||
const u = idx >= 0 ? lbUrls[idx] : (IMAGE_FOLDER + ref.name);
|
||
return isVideo(ref.name) ? posterFor(u) : u;
|
||
}
|
||
function _sceneUrlForName(name) {
|
||
const idx = lbNames.indexOf(name);
|
||
return idx >= 0 ? lbUrls[idx] : (IMAGE_FOLDER + name);
|
||
}
|
||
async function _fetchAsBase64(url) {
|
||
const r = await fetch(url);
|
||
const blob = await r.blob();
|
||
return await new Promise((res, rej) => {
|
||
const fr = new FileReader();
|
||
fr.onload = () => res(String(fr.result).split(',')[1]);
|
||
fr.onerror = rej;
|
||
fr.readAsDataURL(blob);
|
||
});
|
||
}
|
||
|
||
// Refresh both the scenery panel and the filmstrip badges.
|
||
function _sceneRefresh() { renderSidebarScenery(); updateStudio(); }
|
||
|
||
// Assign a filmstrip file to the first empty slot scanning right→left ([2,1,0]).
|
||
// Shift+Click an already-assigned file removes it (toggle).
|
||
function sceneAssignRef(name) {
|
||
const at = _sceneRefs.findIndex(r => r && r.kind === 'file' && r.name === name);
|
||
if (at >= 0) { _sceneRefs[at] = null; _sceneRefresh(); return; }
|
||
for (const i of [2, 1, 0]) {
|
||
if (!_sceneRefs[i]) {
|
||
_sceneRefs[i] = { kind: 'file', name };
|
||
if (i === 0) _sceneFrameBytes = null; // a file now backs slot 0
|
||
_sceneRefresh();
|
||
return;
|
||
}
|
||
}
|
||
showToast('All 3 reference slots are full — ✕ one first', 'info');
|
||
}
|
||
|
||
function sceneClearSlot(i) {
|
||
const ref = _sceneRefs[i];
|
||
_sceneRefs[i] = null;
|
||
if (i === 0 && ref && ref.kind === 'bytes') _sceneFrameBytes = null;
|
||
if (i === 1) _scenePersonInit = true; // don't auto-refill after explicit clear
|
||
_sceneRefresh();
|
||
}
|
||
|
||
// ---- drag & drop between slots / from filmstrip ----
|
||
function sceneDragSlotStart(ev, i) {
|
||
if (!_sceneRefs[i]) { ev.preventDefault(); return; }
|
||
_sceneDrag = { type: 'slot', idx: i };
|
||
ev.dataTransfer.effectAllowed = 'move';
|
||
}
|
||
function sceneFilmDragStart(ev, i) {
|
||
_sceneDrag = { type: 'file', name: lbNames[i] };
|
||
ev.dataTransfer.effectAllowed = 'copy';
|
||
}
|
||
function sceneDragOver(ev) { ev.preventDefault(); ev.currentTarget.classList.add('drop-hover'); }
|
||
function sceneDragLeave(ev) { ev.currentTarget.classList.remove('drop-hover'); }
|
||
function sceneDrop(ev, dst) {
|
||
ev.preventDefault();
|
||
ev.currentTarget.classList.remove('drop-hover');
|
||
const drag = _sceneDrag; _sceneDrag = null;
|
||
if (!drag) return;
|
||
if (drag.type === 'file') {
|
||
// Dropping a filmstrip image onto a slot — remove any duplicate first.
|
||
const dup = _sceneRefs.findIndex(r => r && r.kind === 'file' && r.name === drag.name);
|
||
if (dup >= 0 && dup !== dst) _sceneRefs[dup] = null;
|
||
if (dst === 0 && _sceneRefs[0] && _sceneRefs[0].kind === 'bytes') _sceneFrameBytes = null;
|
||
_sceneRefs[dst] = { kind: 'file', name: drag.name };
|
||
} else {
|
||
const src = drag.idx;
|
||
if (src === dst) return;
|
||
const a = _sceneRefs[src], b = _sceneRefs[dst];
|
||
// Captured/uploaded bytes can only live in slot 0 (the background).
|
||
if ((a && a.kind === 'bytes' && dst !== 0) || (b && b.kind === 'bytes' && src !== 0)) {
|
||
showToast('The captured background can only sit in the Image 1 slot', 'info');
|
||
return;
|
||
}
|
||
_sceneRefs[src] = b; _sceneRefs[dst] = a;
|
||
_sceneFrameBytes = (_sceneRefs[0] && _sceneRefs[0].kind === 'bytes') ? _sceneRefs[0].data : null;
|
||
}
|
||
_sceneRefresh();
|
||
}
|
||
|
||
async function sceneSelectVideo(v) {
|
||
_sceneVideo = v; _sceneFrameBytes = null;
|
||
if (_sceneRefs[0] && _sceneRefs[0].kind === 'bytes') _sceneRefs[0] = null;
|
||
_sceneGridOpen = false; // collapse the picker so the player is front-and-centre
|
||
renderSidebarScenery();
|
||
}
|
||
|
||
function sceneReleaseFrame() {
|
||
_sceneFrameBytes = null;
|
||
if (_sceneRefs[0] && _sceneRefs[0].kind === 'bytes') _sceneRefs[0] = null;
|
||
renderSidebarScenery(); // re-render restores the live video + slider + capture button
|
||
}
|
||
|
||
async function sceneExtractFrame() {
|
||
const vid = document.getElementById('sceneVideoEl');
|
||
if (vid) vid.pause();
|
||
const t = vid ? vid.currentTime : 0;
|
||
const captureBtn = document.getElementById('sceneCaptureBtn');
|
||
if (captureBtn) { captureBtn.disabled = true; captureBtn.textContent = 'Extracting…'; }
|
||
try {
|
||
const r = await fetch(`${API}/wireframe/frame`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ video_name: _sceneVideo, time: t }),
|
||
});
|
||
if (!r.ok) {
|
||
showToast('Frame extract failed: ' + (await r.text()), 'error');
|
||
if (captureBtn) { captureBtn.disabled = false; captureBtn.textContent = '📷 Capture this frame'; }
|
||
} else {
|
||
const d = await r.json();
|
||
_sceneFrameBytes = d.frame_b64;
|
||
_sceneRefs[0] = { kind: 'bytes', data: d.frame_b64 }; // background → slot 0
|
||
_sceneRefresh(); // show the frozen frame + update slot badge
|
||
}
|
||
} catch (e) {
|
||
showToast('Frame extract error: ' + e, 'error');
|
||
if (captureBtn) { captureBtn.disabled = false; captureBtn.textContent = '📷 Capture this frame'; }
|
||
}
|
||
}
|
||
|
||
function sceneHandleUpload(event) {
|
||
const file = event.target.files[0];
|
||
if (!file) return;
|
||
const reader = new FileReader();
|
||
reader.onload = e => {
|
||
_sceneFrameBytes = e.target.result.split(',')[1];
|
||
_sceneRefs[0] = { kind: 'bytes', data: _sceneFrameBytes }; // background → slot 0
|
||
_sceneVideo = null;
|
||
renderSidebarScenery();
|
||
};
|
||
reader.readAsDataURL(file);
|
||
}
|
||
|
||
async function submitGenerateScenery() {
|
||
// Map positional slots → backend contract.
|
||
const bg = _sceneRefs[0], person = _sceneRefs[1], extra = _sceneRefs[2];
|
||
if (!bg) { showToast('Set a background (Image 1) — capture, upload, or Shift+Click', 'error'); return; }
|
||
if (!person || person.kind !== 'file') { showToast('Set a person image (Image 2) from the filmstrip', 'error'); return; }
|
||
if (extra && extra.kind !== 'file') { showToast('Image 3 must be a gallery image', 'error'); return; }
|
||
const prompt = (document.getElementById('scenePromptInput')?.value || '').trim() || null;
|
||
const btn = document.getElementById('sceneGenBtn');
|
||
const statusEl = document.getElementById('sceneJobStatus');
|
||
const textEl = document.getElementById('sceneJobText');
|
||
if (btn) btn.disabled = true;
|
||
if (statusEl) statusEl.style.display = 'inline-flex';
|
||
if (textEl) textEl.textContent = 'Submitting…';
|
||
if (prompt) savePromptHistory(prompt);
|
||
// slot0 → scene_bytes: bytes directly, or fetch a file-as-background to bytes.
|
||
let sceneBytes;
|
||
try {
|
||
sceneBytes = (bg.kind === 'bytes') ? bg.data : await _fetchAsBase64(_sceneUrlForName(bg.name));
|
||
} catch (e) {
|
||
showToast('Could not read background image: ' + e, 'error');
|
||
if (btn) btn.disabled = false; if (statusEl) statusEl.style.display = 'none'; return;
|
||
}
|
||
const payload = {
|
||
model_filename: person.name, // image2 (Picture 2) — person
|
||
scene_bytes: sceneBytes, // image1 (Picture 1) — background
|
||
scene_video: null,
|
||
scene_time: 0,
|
||
extra_filename: (extra && extra.kind === 'file') ? extra.name : null, // image3 (Picture 3)
|
||
prompt,
|
||
seed: -1,
|
||
};
|
||
try {
|
||
const r = await fetch(`${API}/generate-scenery`, {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(payload),
|
||
});
|
||
if (!r.ok) {
|
||
if (textEl) textEl.textContent = 'Error: ' + await r.text();
|
||
if (btn) btn.disabled = false; return;
|
||
}
|
||
const { job_id } = await r.json();
|
||
showToast('Scenery generation started…', 'info');
|
||
if (textEl) textEl.textContent = 'Generating…';
|
||
const pollScene = () => {
|
||
_sceneJobPollTimer = setTimeout(async () => {
|
||
try {
|
||
const jr = await fetch(`${API}/batch/${job_id}`);
|
||
if (!jr.ok) { pollScene(); return; }
|
||
const job = await jr.json();
|
||
if (job.status === 'running') { if (textEl) textEl.textContent = 'Generating…'; pollScene(); return; }
|
||
if (job.status === 'done') {
|
||
showToast('Scenery generated!', 'success');
|
||
if (statusEl) statusEl.style.display = 'none';
|
||
if (btn) btn.disabled = false;
|
||
// Auto-follow so the open studio filmstrip jumps to the
|
||
// new image (matches the Generate tab); refreshNow() alone
|
||
// doesn't update an already-open studio's filmstrip.
|
||
_followLatestGid = lbCurrentGid;
|
||
refreshNow();
|
||
} else {
|
||
const errMsg = job.error || 'unknown error';
|
||
if (textEl) textEl.textContent = 'Error: ' + errMsg;
|
||
if (btn) btn.disabled = false;
|
||
showToast('Scenery failed: ' + errMsg, 'error');
|
||
}
|
||
} catch (e) { pollScene(); }
|
||
}, 2500);
|
||
};
|
||
pollScene();
|
||
} catch (e) {
|
||
if (textEl) textEl.textContent = 'API error: ' + e;
|
||
if (btn) btn.disabled = false;
|
||
}
|
||
}
|
||
|
||
// ---- segment (SAM2 / rembg) sidebar tab ----
|
||
|
||
function renderSidebarSegment() {
|
||
const panel = document.getElementById('sbPanelSegment');
|
||
if (!panel) return;
|
||
const isChecked = document.getElementById('studioViewer')?.classList.contains('show-checker');
|
||
panel.innerHTML = `
|
||
<div style="font-size:11px;color:#666;margin-bottom:12px;line-height:1.6">
|
||
Remove background using SAM2 or rembg fallback. The original file is
|
||
kept intact — a sidecar <code>.nobg.png</code> is created. Restore BG
|
||
simply shows the original again without any file change.
|
||
</div>
|
||
<div style="display:flex;gap:8px;margin-bottom:8px;flex-wrap:wrap">
|
||
<button class="sb-btn" id="sbSam2Btn" onclick="sam2RemoveBg()" title="SAM2 segmentation — preserves hair/glasses/accessories; falls back to rembg if unavailable">Remove BG (SAM2)</button>
|
||
<button class="sb-btn" id="sbRembgBtn" onclick="rembgRemoveBg()" title="rembg (U2Net) — fast background removal without SAM2">Remove BG (rembg)</button>
|
||
<button class="sb-btn" id="sbRestoreBgBtn" onclick="restoreBg()">Restore BG</button>
|
||
</div>
|
||
<div id="sbSegStatus" style="font-size:11px;color:#aaa;min-height:16px;margin-bottom:10px"></div>
|
||
<div class="sb-sep"></div>
|
||
<label style="display:flex;align-items:center;gap:8px;font-size:12px;cursor:pointer;margin-bottom:6px">
|
||
<input type="checkbox" id="sbCheckerboard" onchange="toggleCheckerboard(this.checked)" ${isChecked ? 'checked' : ''}>
|
||
<span>Checkerboard (show transparency)</span>
|
||
</label>
|
||
<div style="font-size:10px;color:#555;line-height:1.4">
|
||
SAM2 merges all non-border masks (preserves hair, glasses, sandals).<br>
|
||
rembg (U2Net) is faster and works without SAM2 checkpoint.<br>
|
||
Requires sam2_checkpoint in config.json for SAM2 path.
|
||
</div>`;
|
||
// Check SAM2 availability
|
||
fetch(`${API}/sam2/check`).then(r => r.json()).then(d => {
|
||
const btn = document.getElementById('sbSam2Btn');
|
||
if (btn) btn.title = d.sam2 ? 'SAM2 available' : 'SAM2 not available — will use rembg fallback';
|
||
}).catch(()=>{});
|
||
}
|
||
|
||
async function sam2RemoveBg() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname) return;
|
||
const btn = document.getElementById('sbSam2Btn');
|
||
const statusEl = document.getElementById('sbSegStatus');
|
||
if (btn) btn.disabled = true;
|
||
if (statusEl) statusEl.textContent = 'Removing background…';
|
||
try {
|
||
const r = await fetch(`${API}/remove-background-sam/${encodeURIComponent(fname)}`, { method: 'POST' });
|
||
if (!r.ok) {
|
||
if (statusEl) statusEl.textContent = 'Error: ' + await r.text();
|
||
} else {
|
||
const d = await r.json();
|
||
const label = d.used_sam2 ? 'Done (SAM2)' : 'Done (rembg)';
|
||
if (statusEl) statusEl.textContent = label;
|
||
showToast('Background removed', 'success');
|
||
refreshNow(true);
|
||
// Build URL same way as all gallery images: IMAGE_FOLDER + filename
|
||
const nobgUrl = IMAGE_FOLDER + d.nobg_filename + '?t=' + Date.now();
|
||
_nobgSidecars.set(fname, nobgUrl);
|
||
// Preload before swap to avoid black flash
|
||
const preload = new Image();
|
||
preload.onload = () => {
|
||
const img = document.getElementById('lbImg');
|
||
if (img) img.src = nobgUrl;
|
||
const cb = document.getElementById('sbCheckerboard');
|
||
if (cb && !cb.checked) { cb.checked = true; toggleCheckerboard(true); }
|
||
};
|
||
preload.src = nobgUrl;
|
||
}
|
||
} catch (e) { if (statusEl) statusEl.textContent = 'Error: ' + e; }
|
||
if (btn) btn.disabled = false;
|
||
}
|
||
|
||
function restoreBg() {
|
||
// Non-destructive: just revert the viewer to the original URL.
|
||
// The sidecar .nobg.png stays on disk for future use.
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname) return;
|
||
_nobgSidecars.delete(fname);
|
||
const img = document.getElementById('lbImg');
|
||
if (img) img.src = lbUrls[lbIdx] + (lbUrls[lbIdx].includes('?') ? '&' : '?') + 't=' + Date.now();
|
||
const statusEl = document.getElementById('sbSegStatus');
|
||
if (statusEl) statusEl.textContent = 'Original restored';
|
||
// Turn off checkerboard
|
||
const cb = document.getElementById('sbCheckerboard');
|
||
if (cb && cb.checked) { cb.checked = false; toggleCheckerboard(false); }
|
||
showToast('Original image shown', 'success');
|
||
}
|
||
|
||
async function rembgRemoveBg() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname) return;
|
||
const btn = document.getElementById('sbRembgBtn');
|
||
const statusEl = document.getElementById('sbSegStatus');
|
||
if (btn) btn.disabled = true;
|
||
if (statusEl) statusEl.textContent = 'Removing background (rembg)…';
|
||
try {
|
||
const r = await fetch(`${API}/remove-background/${encodeURIComponent(fname)}`, { method: 'POST' });
|
||
if (!r.ok) {
|
||
if (statusEl) statusEl.textContent = 'Error: ' + await r.text();
|
||
} else {
|
||
const d = await r.json();
|
||
if (statusEl) statusEl.textContent = 'Done (rembg)';
|
||
showToast('Background removed (rembg)', 'success');
|
||
refreshNow(true);
|
||
const nobgUrl = IMAGE_FOLDER + d.nobg_filename + '?t=' + Date.now();
|
||
_nobgSidecars.set(fname, nobgUrl);
|
||
const preload = new Image();
|
||
preload.onload = () => {
|
||
const img = document.getElementById('lbImg');
|
||
if (img) img.src = nobgUrl;
|
||
const cb = document.getElementById('sbCheckerboard');
|
||
if (cb && !cb.checked) { cb.checked = true; toggleCheckerboard(true); }
|
||
};
|
||
preload.src = nobgUrl;
|
||
}
|
||
} catch (e) { if (statusEl) statusEl.textContent = 'Error: ' + e; }
|
||
if (btn) btn.disabled = false;
|
||
}
|
||
|
||
function toggleCheckerboard(on, isRetry=false) {
|
||
const viewer = document.getElementById('studioViewer');
|
||
const cbLayer = document.getElementById('studioCheckerboard');
|
||
const img = document.getElementById('lbImg');
|
||
|
||
if (viewer) viewer.classList.toggle('show-checker', on);
|
||
|
||
if (cbLayer) {
|
||
if (on && img && img.style.display !== 'none') {
|
||
const rect = getImageDisplayRect(img);
|
||
if (rect) {
|
||
cbLayer.style.display = 'block';
|
||
cbLayer.style.left = rect.left + 'px';
|
||
cbLayer.style.top = rect.top + 'px';
|
||
cbLayer.style.width = rect.width + 'px';
|
||
cbLayer.style.height = rect.height + 'px';
|
||
} else {
|
||
cbLayer.style.display = 'none';
|
||
}
|
||
} else {
|
||
cbLayer.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
const cb = document.getElementById('sbCheckerboard');
|
||
if (cb) cb.checked = on;
|
||
}
|
||
|
||
// ---- set preferred ----
|
||
|
||
async function lbSetPreferred() {
|
||
const fname = lbNames[lbIdx];
|
||
if (!fname) return;
|
||
try {
|
||
const r = await fetch(`${API}/images/${encodeURIComponent(fname)}/set-preferred`, {method:'POST'});
|
||
if (!r.ok) return;
|
||
fileSortOrders[fname] = 0;
|
||
const url = lbUrls[lbIdx];
|
||
const otherUrls = lbUrls.filter((_, i) => i !== lbIdx);
|
||
const otherNames = lbNames.filter((_, i) => i !== lbIdx);
|
||
lbUrls = [url, ...otherUrls];
|
||
lbNames = [fname, ...otherNames];
|
||
lbIdx = 0;
|
||
updateStudio();
|
||
refreshNow(true);
|
||
// Fire-and-forget face extraction for face-book
|
||
fetch(`${API}/images/${encodeURIComponent(fname)}/extract-face`, { method: 'POST' })
|
||
.then(r => r.ok ? r.json() : null)
|
||
.then(d => {
|
||
if (d?.status === 'queued') {
|
||
showToast('Extracting face reference…', 'info');
|
||
loadFaceThumb(lbCurrentGid, 8); // poll until the crop lands
|
||
}
|
||
})
|
||
.catch(() => {});
|
||
} catch(e) { console.error(e); }
|
||
}
|
||
|
||
function showPasteGroupDialog(files) {
|
||
document.getElementById('pasteDialog')?.remove();
|
||
const gName = (lbCurrentGid && groupNames[lbCurrentGid]) || lbCurrentGid || 'current group';
|
||
const gidSafe = escHtml(lbCurrentGid || '');
|
||
const gNameSafe = escHtml(gName);
|
||
const d = document.createElement('div');
|
||
d.id = 'pasteDialog';
|
||
d.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#1a1a1a;border:1px solid #333;border-radius:8px;padding:20px;z-index:600;min-width:280px;text-align:center;box-shadow:0 8px 32px rgba(0,0,0,0.7)';
|
||
d.innerHTML = `<div style="font-size:13px;color:#ccc;margin-bottom:14px">Add ${files.length} image(s)?</div>
|
||
<div style="display:flex;flex-direction:column;gap:8px">
|
||
<button class="btn primary" onclick="handleUploadToGroup(window._pasteFiles,'${gidSafe}');document.getElementById('pasteDialog')?.remove()">Add to “${gNameSafe}” — no poses</button>
|
||
<button class="btn" onclick="handleUpload(window._pasteFiles);document.getElementById('pasteDialog')?.remove()">New group (run poses)</button>
|
||
<button class="btn" onclick="document.getElementById('pasteDialog')?.remove()">Cancel</button>
|
||
</div>`;
|
||
window._pasteFiles = files;
|
||
document.body.appendChild(d);
|
||
setTimeout(() => document.getElementById('pasteDialog')?.remove(), 12000);
|
||
}
|
||
|
||
async function handleUploadToGroup(files, groupId) {
|
||
showToast(`Adding ${files.length} image(s) to group…`);
|
||
const added = [];
|
||
for (const file of files) {
|
||
const fd = new FormData();
|
||
fd.append('image', file);
|
||
fd.append('group_id', groupId);
|
||
fd.append('skip_poses', 'true');
|
||
try {
|
||
const r = await fetch(`${API}/upload`, { method: 'POST', body: fd });
|
||
if (r.ok) {
|
||
const d = await r.json().catch(() => ({}));
|
||
if (d.filename) added.push(d.filename);
|
||
} else {
|
||
showToast('Upload failed: ' + await r.text(), 'error');
|
||
}
|
||
} catch (e) { showToast('Upload error: ' + e, 'error'); }
|
||
}
|
||
|
||
// Optimistically show the new images immediately — the static images.json the
|
||
// gallery reads from is rebuilt on a background thread, so refreshNow() alone
|
||
// would race ahead of it and the images wouldn't appear until the next refresh.
|
||
if (added.length) {
|
||
const fresh = groupData.get(groupId);
|
||
added.forEach(fn => {
|
||
const url = IMAGE_FOLDER + fn + '?t=' + Date.now();
|
||
if (fresh && !fresh.names.includes(fn)) {
|
||
fresh.urls.push(url);
|
||
fresh.names.push(fn);
|
||
if (fresh.visibleIdx) fresh.visibleIdx.push(fresh.names.length - 1);
|
||
}
|
||
if (_isStudioOpen() && lbCurrentGid === groupId && !lbNames.includes(fn)) {
|
||
lbUrls.push(url);
|
||
lbNames.push(fn);
|
||
}
|
||
});
|
||
if (_isStudioOpen() && lbCurrentGid === groupId) updateStudio();
|
||
}
|
||
|
||
showToast('Added to group', 'success');
|
||
// Reconcile with the backend once the static data file has been regenerated.
|
||
setTimeout(refreshNow, 600);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |