o
    i5                     @   s  d Z ddlmZ ddlmZ G dd dZeh dZeh dZde	d	e
fd
dZdd Zded	efddZdZde	d	efddZded	eee ee	 f fddZde	ded	e
fddZdede	d	efddZd:d e	d!ed"e
d	efd#d$Zded	eeee f fd%d&Zd'ee d(e	d)e	d	ee fd*d+Zd e	d,ed-ed.ed	ee f
d/d0Z	1	1	2	3	1	d;d,ed-ed.ed4eee	  d5ee	 d(e	d)e	d6ee d7ed	eee ef fd8d9Zd1S )<zp
Job utilities for the /api/jobs endpoint.
Provides normalization and helper functions for job status tracking.
    )Optional)
prune_dictc                   @   s2   e Zd ZdZdZdZdZdZdZeeeeegZ	dS )	JobStatuszJob status constants.pendingin_progress	completedfailed	cancelledN)
__name__
__module____qualname____doc__PENDINGIN_PROGRESS	COMPLETEDFAILED	CANCELLEDALL r   r   2/mnt/c/Users/fbmor/ComfyUI/comfy_execution/jobs.pyr      s    r   >   3dtextaudiovideoimages>   .fbx.glb.obj.gltf.usdzfilenamereturnc                    s   |    t fddtD S )Nc                 3       | ]}  |V  qd S Nendswith.0extlowerr   r   	<genexpr>       z#has_3d_extension.<locals>.<genexpr>)r*   anyTHREE_D_EXTENSIONSr    r   r)   r   has_3d_extension   s   r0   c                 C   sB   | du rdS t | trt| r| ddddS dS t | tr| S dS )zNormalize a single output list item for the jobs API.

    Returns the normalized item, or None to exclude it.
    String items with 3D extensions become {filename, type, subfolder} dicts.
    Noutput r   )r    type	subfolder	mediaType)
isinstancestrr0   dict)itemr   r   r   normalize_output_item"   s   

r:   outputsc           
      C   s   i }|   D ]J\}}t|ts|||< qi }|  D ]1\}}|dks't|ts,|||< qg }|D ]}|du r7q0t|}	||	durC|	n| q0|||< q|||< q|S )zNormalize raw node outputs for the jobs API.

    Transforms string 3D filenames into file output dicts and removes
    None items. All other items (non-3D strings, dicts, etc.) are
    preserved as-is.
    animatedN)itemsr6   r8   listr:   append)
r;   
normalizednode_idnode_outputsnormalized_node
media_typer=   normalized_itemsr9   normr   r   r   normalize_outputs3   s&   


rG   i   valuec                 C   s&   t | tkr
d| iS | dt ddS )zCreate a text preview dict with optional truncation.

    Returns:
        dict with 'content' and optionally 'truncated' flag
    contentNT)rI   	truncated)lenTEXT_PREVIEW_MAX_LENGTH)rH   r   r   r   _create_text_previewR   s
   
rM   
extra_datac                 C   s0   |  d}|  di }| di  d}||fS )zqExtract create_time and workflow_id from extra_data.

    Returns:
        tuple: (create_time, workflow_id)
    create_timeextra_pnginfoworkflowidget)rN   rO   rP   workflow_idr   r   r   _extract_job_metadata`   s   
rV   rD   r9   c                    sb   | t v rdS |dd}|r|ds|drdS |dd  t fddtD r/dS d	S )
au  
    Check if an output item is previewable.
    Matches frontend logic in ComfyUI_frontend/src/stores/queueStore.ts
    Maintains backwards compatibility with existing logic.

    Priority:
    1. media_type is 'images', 'video', 'audio', or '3d'
    2. format field starts with 'video/' or 'audio/'
    3. filename has a 3D extension (.obj, .fbx, .gltf, .glb, .usdz)
    Tformatr2   zvideo/zaudio/r    c                 3   r"   r#   r$   r&   r/   r   r   r+      r,   z!is_previewable.<locals>.<genexpr>F)PREVIEWABLE_MEDIA_TYPESrT   
startswithr*   r-   r.   )rD   r9   fmtr   r/   r   is_previewablel   s   r[   statusc                 C   s0   | \}}}}}t |\}}t||||d|dS )zvConvert queue item tuple to unified job dict.

    Expects item with sensitive data already removed (5 elements).
    r   )rR   r\   priorityrO   outputs_countrU   )rV   r   )r9   r\   r]   	prompt_id_rN   rO   rU   r   r   r   normalize_queue_item   s   ra   Fr_   history_iteminclude_outputsc                 C   sp  |d }|\}}}}}t |\}}	|di }
|
r|
dnd}|di }t|\}}d}d}d}d}|
r||
dg }|D ]=}t|ttfr{t|dkr{|d	 |d
 }}t|tr{|dkre|d}q>|dv r{|d}|dkru|}q>|dkr{d}q>|dkrtj	}n|dkr|rtj
ntj}ntj	}t| |||||||||	d
}|rt||d< |
|d< ||d|d< |S )zConvert history item dict to unified job dict.

    History items have sensitive data already removed (prompt tuple has 5 elements).
    promptr\   
status_strNr;   Fmessages   r      execution_start	timestamp)execution_successexecution_errorexecution_interruptedrl   rm   Tsuccesserror)
rR   r\   r]   rO   execution_start_timeexecution_end_timerl   r^   preview_outputrU   execution_status)rd   rN   rQ   )rV   rT   get_outputs_summaryr6   r>   tuplerK   r8   r   r   r   r   r   rG   )r_   rb   rc   prompt_tupler]   r`   rd   rN   rO   rU   status_infore   r;   r^   rr   rl   rp   rq   was_interruptedrf   entry
event_name
event_datar\   jobr   r   r   normalize_history_item   sd   


r}   c                 C   sD  d}d}d}|   D ]\}}t|tsq
|  D ]\}}|dks%t|ts&q|D ]q}t|tskt|}	|	du ri|dkrh|d7 }|du rht|trQ|rN|d nd}
nt|}
t|
}i |||d}|du rh|}q(|	}|d7 }|durtq(t||ri |d|i}d	|vr||d	< |	d
dkr|}q(|du r|}q(qq
||p|fS )z
    Count outputs and find preview in a single pass.
    Returns (outputs_count, preview_output).

    Preview priority (matching frontend):
    1. type="output" with previewable media
    2. Any previewable media
    r   Nr<   r   rh   r2   )nodeIdr5   r~   r5   r3   r1   )
r=   r6   r8   r>   r:   ru   r7   rM   r[   rT   )r;   countrr   fallback_previewrA   rB   rD   r=   r9   r@   
text_valuetext_previewenrichedr   r   r   rt      sb   	



/rt   jobssort_by
sort_orderc                 C   s0   |dk}|dkrdd }ndd }t | ||dS )z,Sort jobs list by specified field and order.descexecution_durationc                 S   s,   |  dd}|  dd}|r|r|| S dS )Nrp   r   rq   rS   )r|   startendr   r   r   get_sort_key"  s   z#apply_sorting.<locals>.get_sort_keyc                 S   s   |  ddS )NrO   r   rS   )r|   r   r   r   r   '  s   )keyreverse)sorted)r   r   r   r   r   r   r   r   apply_sorting  s
   
r   runningqueuedhistoryc                 C   sj   | |v rt | ||  ddS |D ]}|d | krt|tj  S q|D ]}|d | kr2t|tj  S q"dS )aY  
    Get a single job by prompt_id from history or queue.

    Args:
        prompt_id: The prompt ID to look up
        running: List of currently running queue items
        queued: List of pending queue items
        history: Dict of history items keyed by prompt_id

    Returns:
        Job dict with full details, or None if not found
    T)rc   rh   N)r}   ra   r   r   r   )r_   r   r   r   r9   r   r   r   get_job-  s   r   N
created_atr   status_filterrU   limitoffsetc	                    s  g }	|du r	t j}t j|v r| D ]}
|	t|
t j qt j|v r/|D ]}
|	t|
t j q#t jt jt jh}|t	|@ }|rY|
 D ]\}}t||}|d|v rX|	| qC rd fdd|	D }	t|	||}	t|	}|dkrx|	|d }	|dur|	d| }	|	|fS )ae  
    Get all jobs (running, pending, completed) with filtering and sorting.

    Args:
        running: List of currently running queue items
        queued: List of pending queue items
        history: Dict of history items keyed by prompt_id
        status_filter: List of statuses to include (from JobStatus.ALL)
        workflow_id: Filter by workflow ID
        sort_by: Field to sort by ('created_at', 'execution_duration')
        sort_order: 'asc' or 'desc'
        limit: Maximum number of items to return
        offset: Number of items to skip

    Returns:
        tuple: (jobs_list, total_count)
    Nr\   c                    s   g | ]}| d  kr|qS rU   rS   )r'   jr   r   r   
<listcomp>z  s    z get_all_jobs.<locals>.<listcomp>r   )r   r   r   r?   ra   r   r   r   r   setr=   r}   rT   r   rK   )r   r   r   r   rU   r   r   r   r   r   r9   history_statusesrequested_history_statusesr_   rb   r|   total_countr   r   r   get_all_jobsH  s4   



r   )F)NNr   r   Nr   )r   typingr   comfy_api.internalr   r   	frozensetrX   r.   r7   boolr0   r:   r8   rG   rL   rM   ru   intrV   r[   ra   r}   rt   r>   r   r   r   r   r   r   r   <module>   sZ    "A"B"
	
