Skip to content

Commit 37164c2

Browse files
committed
WIP queue layout
1 parent 0832bb9 commit 37164c2

2 files changed

Lines changed: 312 additions & 7 deletions

File tree

modules/ui/queue.py

Lines changed: 269 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,45 @@
44
import datetime
55
from modules.video_queue import JobStatus
66

7+
def debug_job_data(jobs):
8+
"""Debug function to inspect all available job data"""
9+
if not jobs:
10+
print("No jobs to inspect")
11+
return
12+
13+
print("\n" + "="*50)
14+
print("DEBUG: JOB DATA INSPECTION")
15+
print("="*50)
16+
17+
for i, job in enumerate(jobs):
18+
print(f"\n--- Job {i+1} ---")
19+
print(f"Job ID: {getattr(job, 'id', 'N/A')}")
20+
print(f"Job Type: {type(job).__name__}")
21+
22+
# Get all attributes
23+
all_attrs = dir(job)
24+
# Filter out built-in attributes and methods
25+
user_attrs = [attr for attr in all_attrs if not attr.startswith('_') and not callable(getattr(job, attr))]
26+
27+
print(f"Available attributes ({len(user_attrs)}):")
28+
for attr in sorted(user_attrs):
29+
try:
30+
value = getattr(job, attr)
31+
# Don't truncate the "params" attribute - show full value
32+
if attr == "params":
33+
print(f" {attr}: {value}")
34+
else:
35+
# Truncate long values for readability
36+
if isinstance(value, str) and len(value) > 100:
37+
value = value[:100] + "..."
38+
elif isinstance(value, (list, dict)) and len(str(value)) > 100:
39+
value = str(value)[:100] + "..."
40+
print(f" {attr}: {value}")
41+
except Exception as e:
42+
print(f" {attr}: <Error accessing: {e}>")
43+
44+
print("\n" + "="*50)
45+
746
def format_queue_status(jobs):
847
rows = []
948
for job in jobs:
@@ -22,6 +61,231 @@ def format_queue_status(jobs):
2261
rows.append([job.id[:6] + '...', generation_type, job.status.value, created, started, completed, elapsed_time, thumbnail_html])
2362
return rows
2463

64+
def format_queue_cards(jobs):
65+
"""Generate HTML cards for job queue display"""
66+
# DEBUG: Uncomment the next line to see all available job data
67+
debug_job_data(jobs)
68+
69+
if not jobs:
70+
return '<div style="text-align: center; padding: 20px; color: #666;">No jobs in queue</div>'
71+
72+
cards_html = '<div style="padding: 16px;">'
73+
74+
# Default values for comparison
75+
default_values = {
76+
'latent_window_size': 9,
77+
'steps': 25,
78+
'cfg': 1.0,
79+
'gs': 10.0,
80+
'rs': 0.0,
81+
'total_second_length': 6,
82+
'resolutionW': 640,
83+
'resolutionH': 640,
84+
'blend_sections': 4,
85+
'cache_type': 'MagCache',
86+
'use_magcache': True,
87+
'use_teacache': False,
88+
'magcache_threshold': 0.1,
89+
'magcache_max_consecutive_skips': 2,
90+
'magcache_retention_ratio': 0.25,
91+
'teacache_num_steps': 25,
92+
'teacache_rel_l1_thresh': 0.15,
93+
'latent_type': 'Noise',
94+
'combine_with_source': True,
95+
'num_cleaned_frames': 5
96+
}
97+
98+
for job in jobs:
99+
# Get job parameters
100+
params = job.params or {}
101+
102+
# Get prompt
103+
prompt = params.get('prompt_text', params.get('prompt', 'No prompt available'))
104+
if not prompt or prompt.strip() == '':
105+
prompt = 'No prompt available'
106+
107+
prompt_short = (prompt[:255] + '...') if len(prompt) > 255 else prompt
108+
prompt_needs_expand = len(prompt) > 255
109+
110+
# Extract all available data from job object
111+
job_id = getattr(job, 'id', 'Unknown')[:8] + '...'
112+
generation_type = getattr(job, 'generation_type', 'Original')
113+
status = getattr(job, 'status', JobStatus.PENDING).value
114+
thumbnail = getattr(job, 'thumbnail', None)
115+
116+
# Time formatting
117+
created = time.strftime('%H:%M:%S', time.localtime(job.created_at)) if getattr(job, 'created_at', None) else "N/A"
118+
started = time.strftime('%H:%M:%S', time.localtime(job.started_at)) if getattr(job, 'started_at', None) else "N/A"
119+
completed = time.strftime('%H:%M:%S', time.localtime(job.completed_at)) if getattr(job, 'completed_at', None) else "N/A"
120+
121+
# Calculate elapsed time
122+
elapsed_time = ""
123+
if getattr(job, 'started_at', None):
124+
end_time = getattr(job, 'completed_at', None) or time.time()
125+
elapsed_seconds = end_time - job.started_at
126+
status_suffix = "" if getattr(job, 'completed_at', None) else " (running)"
127+
elapsed_time = f"{elapsed_seconds:.2f}s{status_suffix}"
128+
129+
# Queue position for pending jobs
130+
queue_position = getattr(job, 'queue_position', None)
131+
132+
# Additional job data
133+
steps = params.get('steps', 'N/A')
134+
seed = params.get('seed', 'N/A')
135+
resolution = f"{params.get('resolutionW', 'N/A')}x{params.get('resolutionH', 'N/A')}"
136+
duration = f"{params.get('total_second_length', 'N/A')}s"
137+
cache_type = 'MagCache' if params.get('use_magcache', False) else 'TeaCache' if params.get('use_teacache', False) else 'None'
138+
139+
# Status-based styling
140+
status_colors = {
141+
'PENDING': '#ffa500',
142+
'RUNNING': '#007bff',
143+
'COMPLETED': '#28a745',
144+
'FAILED': '#dc3545',
145+
'CANCELLED': '#6c757d'
146+
}
147+
status_color = status_colors.get(status.upper(), '#6c757d')
148+
149+
status_icon = {
150+
'PENDING': '⏳',
151+
'RUNNING': '🔄',
152+
'COMPLETED': '✅',
153+
'FAILED': '❌',
154+
'CANCELLED': '❌'
155+
}[status.upper()]
156+
157+
# Generate settings pills for non-default values
158+
settings_pills = []
159+
160+
# Helper function to add setting pill if different from default
161+
def add_setting_if_different(key, label, value, default_value):
162+
if value is not None and value != default_value:
163+
settings_pills.append(f'''
164+
<div class="setting-pill">
165+
<div class="setting-pill-label">{label}</div>
166+
<div class="setting-pill-value">{value}</div>
167+
</div>
168+
''')
169+
170+
# Check each setting against defaults
171+
add_setting_if_different('latent_window_size', 'LWS', params.get('latent_window_size'), default_values['latent_window_size'])
172+
add_setting_if_different('steps', 'Steps', params.get('steps'), default_values['steps'])
173+
add_setting_if_different('cfg', 'CFG', params.get('cfg'), default_values['cfg'])
174+
add_setting_if_different('gs', 'DCS', params.get('gs'), default_values['gs'])
175+
add_setting_if_different('rs', 'RS', params.get('rs'), default_values['rs'])
176+
add_setting_if_different('total_second_length', 'Length', f"{params.get('total_second_length')}s", f"{default_values['total_second_length']}s")
177+
178+
# Resolution
179+
res_w = params.get('resolutionW')
180+
res_h = params.get('resolutionH')
181+
if res_w and res_h and (res_w != default_values['resolutionW'] or res_h != default_values['resolutionH']):
182+
settings_pills.append(f'''
183+
<div class="setting-pill">
184+
<div class="setting-pill-label">Size</div>
185+
<div class="setting-pill-value">{res_w}x{res_h}</div>
186+
</div>
187+
''')
188+
189+
add_setting_if_different('blend_sections', 'Blend', params.get('blend_sections'), default_values['blend_sections'])
190+
191+
# Cache settings
192+
cache_type = 'None'
193+
if params.get('use_magcache', False):
194+
cache_type = 'MagCache'
195+
elif params.get('use_teacache', False):
196+
cache_type = 'TeaCache'
197+
198+
if cache_type != default_values['cache_type']:
199+
settings_pills.append(f'''
200+
<div class="setting-pill">
201+
<div class="setting-pill-label">Cache</div>
202+
<div class="setting-pill-value">{cache_type}</div>
203+
</div>
204+
''')
205+
206+
# Cache-specific settings
207+
if cache_type == 'MagCache':
208+
add_setting_if_different('magcache_threshold', 'MagThresh', params.get('magcache_threshold'), default_values['magcache_threshold'])
209+
add_setting_if_different('magcache_max_consecutive_skips', 'MagSkips', params.get('magcache_max_consecutive_skips'), default_values['magcache_max_consecutive_skips'])
210+
add_setting_if_different('magcache_retention_ratio', 'MagRet', params.get('magcache_retention_ratio'), default_values['magcache_retention_ratio'])
211+
elif cache_type == 'TeaCache':
212+
add_setting_if_different('teacache_num_steps', 'TeaSteps', params.get('teacache_num_steps'), default_values['teacache_num_steps'])
213+
add_setting_if_different('teacache_rel_l1_thresh', 'TeaThresh', params.get('teacache_rel_l1_thresh'), default_values['teacache_rel_l1_thresh'])
214+
215+
add_setting_if_different('latent_type', 'Latent', params.get('latent_type'), default_values['latent_type'])
216+
add_setting_if_different('combine_with_source', 'Combine', params.get('combine_with_source'), default_values['combine_with_source'])
217+
add_setting_if_different('num_cleaned_frames', 'Frames', params.get('num_cleaned_frames'), default_values['num_cleaned_frames'])
218+
219+
# Seed (always show if present)
220+
seed = params.get('seed')
221+
if seed is not None:
222+
settings_pills.append(f'''
223+
<div class="setting-pill">
224+
<div class="setting-pill-label">Seed</div>
225+
<div class="setting-pill-value">{seed}</div>
226+
</div>
227+
''')
228+
229+
# Generation type
230+
generation_type = getattr(job, 'generation_type', 'Original')
231+
if generation_type != 'Original':
232+
settings_pills.append(f'''
233+
<div class="setting-pill">
234+
<div class="setting-pill-label">Type</div>
235+
<div class="setting-pill-value">{generation_type}</div>
236+
</div>
237+
''')
238+
239+
safe_job_id = job.id.replace('-', '')
240+
expand_button = f'<button class="queue-prompt-expand" id="prompt-toggle-{safe_job_id}" onclick="togglePrompt(\'{safe_job_id}\')">Expand +</button>' if prompt_needs_expand else ''
241+
242+
# Generate card HTML - 3 columns: status, prompt and params, and thumbnail / output video link
243+
card_html = f'''
244+
<div style="display: flex; flex-direction: row; gap: 16px; justify-content: space-between; border: 1px solid #ddd; border-radius: 8px; padding: 16px; background: var(--border-color-primary); box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
245+
<div class="queue-status-indicator" style="background: {status_color}; color: white; padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: bold;">
246+
<div class="status-icon">{status_icon}</div>
247+
</div>
248+
<div class="queue-card-content">
249+
<div class="queue-prompt-section">
250+
<p class="queue-prompt-text">
251+
<span id="prompt-short-{job_id}">{prompt_short}</span>
252+
<span id="prompt-full-{job_id}" style="display: none;">{prompt}</span>
253+
</p>
254+
{expand_button}
255+
</div>
256+
<div class="queue-settings-section">
257+
{''.join(settings_pills)}
258+
</div>
259+
</div>
260+
<div class="queue-media-section">
261+
<div>
262+
{f'<img src="{thumbnail}" alt="Preview">' if thumbnail else '<div class="queue-preview-placeholder">No Preview</div>'}
263+
</div>
264+
</div>
265+
266+
<div style="margin-bottom: 12px;">
267+
<strong>Type:</strong> {generation_type}<br>
268+
<strong>Steps:</strong> {steps} | <strong>Seed:</strong> {seed}<br>
269+
<strong>Resolution:</strong> {resolution} | <strong>Duration:</strong> {duration}
270+
</div>
271+
272+
<div style="margin-bottom: 12px; font-size: 12px; color: #666;">
273+
<strong>Created:</strong> {created}<br>
274+
<strong>Started:</strong> {started}<br>
275+
<strong>Completed:</strong> {completed}<br>
276+
<strong>Elapsed:</strong> {elapsed_time}
277+
{f'<br><strong>Queue Position:</strong> #{queue_position}' if queue_position is not None else ''}
278+
</div>
279+
280+
{f'<div style="text-align: center;"><img src="{thumbnail}" style="max-width: 100%; max-height: 120px; object-fit: contain; border-radius: 4px;"></div>' if thumbnail else ''}
281+
</div>
282+
'''
283+
284+
cards_html += card_html
285+
286+
cards_html += '</div>'
287+
return cards_html
288+
25289
def update_queue_status_with_thumbnails():
26290
try:
27291
from __main__ import job_queue
@@ -31,13 +295,13 @@ def update_queue_status_with_thumbnails():
31295
job.queue_position = job_queue.get_queue_position(job.id)
32296
if job_queue.current_job:
33297
job_queue.current_job.status = JobStatus.RUNNING
34-
return format_queue_status(jobs)
298+
return format_queue_cards(jobs)
35299
except ImportError:
36300
print("Error: Could not import job_queue. Queue status update might fail.")
37-
return []
301+
return '<div style="text-align: center; padding: 20px; color: #dc3545;">Error: Could not load job queue</div>'
38302
except Exception as e:
39303
print(f"Error updating queue status: {e}")
40-
return []
304+
return f'<div style="text-align: center; padding: 20px; color: #dc3545;">Error updating queue: {str(e)}</div>'
41305

42306
def create_queue_ui():
43307
with gr.Row():
@@ -61,9 +325,8 @@ def create_queue_ui():
61325
confirm_cancel_yes_btn = gr.Button("❌ Yes, Cancel All", variant="stop")
62326
confirm_cancel_no_btn = gr.Button("↩️ No, Go Back")
63327
with gr.Row():
64-
queue_status = gr.DataFrame(
65-
headers=["Job ID", "Type", "Status", "Created", "Started", "Completed", "Elapsed", "Preview"],
66-
datatype=["str", "str", "str", "str", "str", "str", "str", "html"],
328+
queue_status = gr.HTML(
329+
value='<div style="text-align: center; padding: 20px; color: #666;">Loading queue...</div>',
67330
label="Job Queue"
68331
)
69332
with gr.Accordion("Queue Documentation", open=False):

modules/ui/styles.css

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,46 @@ body, .gradio-container {
204204
border-top: 1px solid #ccc;
205205
border-bottom: 1px solid #ccc;
206206
margin: 12px 0;
207-
}
207+
}
208+
209+
.queue-settings-section {
210+
display: flex;
211+
flex-wrap: wrap;
212+
gap: 8px;
213+
align-items: center;
214+
padding: 0 8px;
215+
}
216+
217+
.setting-pill {
218+
display: flex;
219+
align-items: center;
220+
border-radius: 16px;
221+
overflow: hidden;
222+
font-size: 0.8rem;
223+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
224+
}
225+
226+
.setting-pill-label {
227+
background: var(--block-background-fill, #f8f9fa);
228+
color: var(--body-text-color, #495057);
229+
padding: 4px 8px;
230+
font-weight: 500;
231+
white-space: nowrap;
232+
}
233+
234+
.setting-pill-value {
235+
background: var(--input-background-fill, #ffffff);
236+
color: var(--body-text-color, #1f2937);
237+
padding: 4px 8px;
238+
border-left: 1px solid var(--border-color-primary, #dee2e6);
239+
}
240+
241+
/* Right side - single clickable preview */
242+
.queue-media-section {
243+
display: flex;
244+
flex-direction: column;
245+
height: 150px;
246+
align-items: center;
247+
justify-content: center;
248+
}
249+

0 commit comments

Comments
 (0)