Replaces old list-format tags (which duplicated prompt content) with structured dict tags per category (origin_series, outfit_type, participants, style_type, scene_type, etc.). Tags are now purely organizational metadata — removed from the prompt pipeline entirely. Adds is_favourite and is_nsfw columns to all 8 resource models. Favourite is DB-only (user preference); NSFW is mirrored in JSON tags for rescan persistence. All library pages get filter controls and favourites-first sorting. Introduces a parallel LLM job queue (_enqueue_task + _llm_queue_worker) for background tag regeneration, with the same status polling UI as ComfyUI jobs. Fixes call_llm() to use has_request_context() fallback for background threads. Adds global search (/search) across resources and gallery images, with navbar search bar. Adds gallery image sidecar JSON for per-image favourite/NSFW metadata. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
115 lines
4.2 KiB
Python
115 lines
4.2 KiB
Python
import logging
|
|
from services.job_queue import (
|
|
_job_queue_lock, _job_queue, _llm_queue, _job_history,
|
|
_queue_worker_event, _llm_worker_event,
|
|
)
|
|
|
|
logger = logging.getLogger('gaze')
|
|
|
|
# Both queues for iteration
|
|
_ALL_QUEUES = (_job_queue, _llm_queue)
|
|
|
|
|
|
def register_routes(app):
|
|
|
|
@app.route('/api/queue')
|
|
def api_queue_list():
|
|
"""Return the current queue as JSON."""
|
|
with _job_queue_lock:
|
|
jobs = []
|
|
for q in _ALL_QUEUES:
|
|
for j in q:
|
|
jobs.append({
|
|
'id': j['id'],
|
|
'label': j['label'],
|
|
'status': j['status'],
|
|
'error': j['error'],
|
|
'created_at': j['created_at'],
|
|
'job_type': j.get('job_type', 'comfyui'),
|
|
})
|
|
return {'jobs': jobs, 'count': len(jobs)}
|
|
|
|
@app.route('/api/queue/count')
|
|
def api_queue_count():
|
|
"""Return just the count of active (non-done, non-failed) jobs."""
|
|
with _job_queue_lock:
|
|
count = sum(
|
|
1 for q in _ALL_QUEUES for j in q
|
|
if j['status'] in ('pending', 'processing', 'paused')
|
|
)
|
|
return {'count': count}
|
|
|
|
@app.route('/api/queue/<job_id>/remove', methods=['POST'])
|
|
def api_queue_remove(job_id):
|
|
"""Remove a pending or paused job from the queue."""
|
|
with _job_queue_lock:
|
|
job = _job_history.get(job_id)
|
|
if not job:
|
|
return {'error': 'Job not found'}, 404
|
|
if job['status'] == 'processing':
|
|
return {'error': 'Cannot remove a job that is currently processing'}, 400
|
|
for q in _ALL_QUEUES:
|
|
try:
|
|
q.remove(job)
|
|
break
|
|
except ValueError:
|
|
continue
|
|
job['status'] = 'removed'
|
|
return {'status': 'ok'}
|
|
|
|
@app.route('/api/queue/<job_id>/pause', methods=['POST'])
|
|
def api_queue_pause(job_id):
|
|
"""Toggle pause/resume on a pending job."""
|
|
with _job_queue_lock:
|
|
job = _job_history.get(job_id)
|
|
if not job:
|
|
return {'error': 'Job not found'}, 404
|
|
if job['status'] == 'pending':
|
|
job['status'] = 'paused'
|
|
elif job['status'] == 'paused':
|
|
job['status'] = 'pending'
|
|
# Signal the appropriate worker
|
|
if job.get('job_type') == 'llm':
|
|
_llm_worker_event.set()
|
|
else:
|
|
_queue_worker_event.set()
|
|
else:
|
|
return {'error': f'Cannot pause/resume job with status {job["status"]}'}, 400
|
|
return {'status': 'ok', 'new_status': job['status']}
|
|
|
|
@app.route('/api/queue/clear', methods=['POST'])
|
|
def api_queue_clear():
|
|
"""Clear all pending jobs from the queue (allows current processing jobs to finish)."""
|
|
removed_count = 0
|
|
with _job_queue_lock:
|
|
for q in _ALL_QUEUES:
|
|
pending_jobs = [j for j in q if j['status'] == 'pending']
|
|
for job in pending_jobs:
|
|
try:
|
|
q.remove(job)
|
|
job['status'] = 'removed'
|
|
removed_count += 1
|
|
except ValueError:
|
|
pass
|
|
logger.info("Cleared %d pending jobs from queue", removed_count)
|
|
return {'status': 'ok', 'removed_count': removed_count}
|
|
|
|
@app.route('/api/queue/<job_id>/status')
|
|
def api_queue_job_status(job_id):
|
|
"""Return the status of a specific job."""
|
|
with _job_queue_lock:
|
|
job = _job_history.get(job_id)
|
|
if not job:
|
|
return {'error': 'Job not found'}, 404
|
|
resp = {
|
|
'id': job['id'],
|
|
'label': job['label'],
|
|
'status': job['status'],
|
|
'error': job['error'],
|
|
'job_type': job.get('job_type', 'comfyui'),
|
|
'comfy_prompt_id': job.get('comfy_prompt_id'),
|
|
}
|
|
if job.get('result'):
|
|
resp['result'] = job['result']
|
|
return resp
|