
    +jr                        d Z ddlZddlZddlZddlZddlmZmZ ddlm	Z	 ddl
mZ ddlmZmZmZmZmZmZmZmZmZmZmZmZ ddlmZ  G d d	e          Z G d
 de	          Z G d de	          Ze G d d                      Ze G d d                      Z eegdf         Z! G d d          Z" e"            Z#dS )zHBackground asset seeder with thread management and cancellation support.    N)	dataclassfield)Enum)Callable)ENRICHMENT_METADATAENRICHMENT_STUBRootTypebuild_asset_specscollect_paths_for_rootsenrich_assets_batchget_all_known_prefixesget_prefixes_for_rootget_unenriched_assets_for_rootsinsert_asset_specs$mark_missing_outside_prefixes_safelysync_root_safely)dependencies_availablec                       e Zd ZdZdS )ScanInProgressErrorzBRaised when an operation cannot proceed because a scan is running.N)__name__
__module____qualname____doc__     1/home/wildlama/comfy/ComfyUI/app/assets/seeder.pyr   r      s        LLLLr   r   c                   "    e Zd ZdZdZdZdZdZdS )StatezSeeder state machine states.IDLERUNNINGPAUSED
CANCELLINGN)r   r   r   r   r   r    r!   r"   r   r   r   r   r       s(        &&DGFJJJr   r   c                       e Zd ZdZdZdZdZdS )	ScanPhasezScan phase options.fastenrichfullN)r   r   r   r   FASTENRICHFULLr   r   r   r$   r$   )   s#        DFDDDr   r$   c                   L    e Zd ZU dZdZeed<   dZeed<   dZeed<   dZ	eed<   dS )Progressz*Progress information for a scan operation.r   scannedtotalcreatedskippedN)
r   r   r   r   r-   int__annotations__r.   r/   r0   r   r   r   r,   r,   1   sU         44GSE3NNNGSGSr   r,   c                   \    e Zd ZU dZeed<   edz  ed<    ee          Z	ee
         ed<   dS )
ScanStatusz#Current status of the asset seeder.stateNprogress)default_factoryerrors)r   r   r   r   r   r2   r,   r   listr8   strr   r   r   r4   r4   ;   sR         --LLLod333FDI33333r   r4   c                      e Zd ZdZd1dZd1dZdefdZdej	        dddfd	e
ed
f         dededz  dededefdZ	 	 	 d2d	e
ed
f         dedz  dedefdZ	 	 	 d2d	e
ed
f         dedz  dedefdZ	 	 d3d	e
ed
f         dedefdZdefdZdefdZdefdZdefdZ	 	 	 	 	 	 d4d	e
ed
f         dz  dedz  dedz  dedz  dedz  dedefdZd5dedz  defdZdefdZd6deddfdZdefdZd1dZdefdZdefd Zdefd!Z d"e!d#e"ddfd$Z#	 	 	 	 d7d%edz  d&edz  d'edz  d(edz  ddf
d)Z$d*Z%d+e!ddfd,Z&d	e
ed
f         ddfd-Z'd1d.Z(d	e
ed
f         de
eeef         fd/Z)d	e
ed
f         de
eef         fd0Z*dS )8_AssetSeederzBackground asset scanning manager.

    Spawns ephemeral daemon threads for scanning.
    Each scan creates a new thread that exits when complete.
    Use the module-level ``asset_seeder`` instance.
    returnNc                    t          j                    | _        t          j        | _        d | _        d | _        g | _        d | _	        t          j
                    | _        t          j
                    | _        | j                                         d| _        t          j        | _        d| _        d| _        d | _        d| _        d | _        d S )Nr   F)	threadingRLock_lockr   r   _state	_progress_last_progress_errors_threadEvent_cancel_event	_run_gateset_rootsr$   r*   _phase_compute_hashes_prune_first_progress_callback	_disabled_pending_enrichselfs    r   __init__z_AssetSeeder.__init__O   s     _&&
j*./3"$04&_.."**,.!*%*"';?$,0r   c                 <    d| _         t          j        d           dS )z=Disable the asset seeder, preventing any scans from starting.TzAsset seeder disabledN)rP   logginginforR   s    r   disablez_AssetSeeder.disablec   s     ,-----r   c                     | j         S )z&Check if the asset seeder is disabled.)rP   rR   s    r   is_disabledz_AssetSeeder.is_disabledh   s
    ~r   modelsinputoutputFroots.phaseprogress_callbackprune_firstcompute_hashesc                    | j         rt          j        d           dS t          j        d||j                   | j        5  | j        t          j        k    r"t          j        d           	 ddd           dS t          j	        | _        t                      | _        g | _        || _        || _        || _        || _        || _        | j                                         | j                                         t-          j        | j        dd          | _        | j                                         	 ddd           dS # 1 swxY w Y   dS )	a  Start a background scan for the given roots.

        Args:
            roots: Tuple of root types to scan (models, input, output)
            phase: Scan phase to run (FAST, ENRICH, or FULL for both)
            progress_callback: Optional callback called with progress updates
            prune_first: If True, prune orphaned assets before scanning
            compute_hashes: If True, compute blake3 hashes (slow)

        Returns:
            True if scan was started, False if already running
        z(Asset seeder is disabled, skipping startFz!Seeder start (roots=%s, phase=%s)z,Asset seeder already running, skipping startNr<   T)targetnamedaemon)rP   rV   debugrW   valuerA   rB   r   r   r    r,   rC   rE   rK   rL   rN   rM   rO   rH   clearrI   rJ   r?   Thread	_run_scanrF   start)rS   r_   r`   ra   rb   rc   s         r   rm   z_AssetSeeder.startl   s   ( > 	MDEEE58%MMMZ 	 	{ej((KLLL	 	 	 	 	 	 	 	  -DK%ZZDNDLDKDK +D#1D &7D#$$&&&N   $+~#  DL
 L   )	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s    +E 8B;E  EEc                 J    |                      |t          j        ||d          S )a^  Start a fast scan (phase 1 only) - creates stub records.

        Args:
            roots: Tuple of root types to scan
            progress_callback: Optional callback for progress updates
            prune_first: If True, prune orphaned assets before scanning

        Returns:
            True if scan was started, False if already running
        Fr_   r`   ra   rb   rc   )rm   r$   r(   )rS   r_   ra   rb   s       r   
start_fastz_AssetSeeder.start_fast   s0      zz./#   
 
 	
r   c                 J    |                      |t          j        |d|          S )a`  Start an enrichment scan (phase 2 only) - extracts metadata and hashes.

        Args:
            roots: Tuple of root types to scan
            progress_callback: Optional callback for progress updates
            compute_hashes: If True, compute blake3 hashes

        Returns:
            True if scan was started, False if already running
        Fro   )rm   r$   r)   )rS   r_   ra   rc   s       r   start_enrichz_AssetSeeder.start_enrich   s1      zz"/)  
 
 	
r   c                    | j         5  |                     ||          r	 ddd           dS | j        ^t          | j        d                   }|                    |           t          |          | j        d<   | j        d         p|| j        d<   n
||d| _        t          j        d| j        d                    ddd           n# 1 swxY w Y   dS )a  Start an enrichment scan now, or queue it for after the current scan.

        If the seeder is idle, starts immediately. Otherwise, the enrich
        request is stored and will run automatically when the current scan
        finishes.

        Args:
            roots: Tuple of root types to scan
            compute_hashes: If True, compute blake3 hashes

        Returns:
            True if started immediately, False if queued for later
        r_   rc   NTr_   rc   zEnrich scan queued (roots=%s)F)rA   rr   rQ   rJ   updatetuplerV   rW   )rS   r_   rc   existing_rootss       r   enqueue_enrichz_AssetSeeder.enqueue_enrich   sf   $ Z 	Y 	Y  u^ LL 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y #/!$T%9'%B!C!C%%e,,,05n0E0E$W-()9:Ln $%566
 #&4( ($ L8$:Nw:WXXX	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y 	Y  us   C	BC		CCc                 f   | j         5  | j        t          j        t          j        fvr	 ddd           dS t          j        d| j        j                   t          j        | _        | j	        
                                 | j        
                                 	 ddd           dS # 1 swxY w Y   dS )zRequest cancellation of the current scan.

        Returns:
            True if cancellation was requested, False if not running or paused
        NFz Asset seeder cancelling (was %s)T)rA   rB   r   r    r!   rV   rW   ri   r"   rH   rJ   rI   rR   s    r   cancelz_AssetSeeder.cancel   s    Z 	 	{5=%,"???	 	 	 	 	 	 	 	 L;T[=NOOO*DK""$$$N   	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   !B&A#B&&B*-B*c                 *    |                                  S )zStop the current scan (alias for cancel).

        Returns:
            True if stop was requested, False if not running
        )rz   rR   s    r   stopz_AssetSeeder.stop   s     {{}}r   c                 
   | j         5  | j        t          j        k    r	 ddd           dS t	          j        d           t          j        | _        | j                                         	 ddd           dS # 1 swxY w Y   dS )zPause the current scan.

        The scan will complete its current batch before pausing.

        Returns:
            True if pause was requested, False if not running
        NFzAsset seeder pausingT)	rA   rB   r   r    rV   rW   r!   rI   rj   rR   s    r   pausez_AssetSeeder.pause  s     Z 	 	{em++	 	 	 	 	 	 	 	 L/000,DKN  """	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   A8?A88A<?A<c                 2   | j         5  | j        t          j        k    r	 ddd           dS t	          j        d           t          j        | _        | j                                         ddd           n# 1 swxY w Y   | 	                    di            dS )zResume a paused scan.

        This is a noop if the scan is not in the PAUSED state

        Returns:
            True if resumed, False if not paused
        NFzAsset seeder resumingzassets.seed.resumedT)
rA   rB   r   r!   rV   rW   r    rI   rJ   _emit_eventrR   s    r   resumez_AssetSeeder.resume  s     Z 	! 	!{el**	! 	! 	! 	! 	! 	! 	! 	! L0111-DKN   	! 	! 	! 	! 	! 	! 	! 	! 	! 	! 	! 	! 	! 	! 	! 	.333ts   A6>A66A:=A:      @timeoutc                 f   t          j        d           | j        5  | j        }| j        }| j        }	| j        }
| j        }ddd           n# 1 swxY w Y   |                                  | 	                    |          sdS ||n|	}| 
                    ||n|||n||||n|
||n|          S )a(  Cancel any running scan and start a new one.

        Args:
            roots: Roots to scan (defaults to previous roots)
            phase: Scan phase (defaults to previous phase)
            progress_callback: Progress callback (defaults to previous)
            prune_first: Prune before scan (defaults to previous)
            compute_hashes: Compute hashes (defaults to previous)
            timeout: Max seconds to wait for current scan to stop

        Returns:
            True if new scan was started, False if failed to stop previous
        zAsset seeder restart requestedNr   Fro   )rV   rW   rA   rK   rL   rO   rN   rM   rz   waitrm   )rS   r_   r`   ra   rb   rc   r   
prev_roots
prev_phaseprev_callback
prev_pruneprev_hashescbs                r   restartz_AssetSeeder.restart&  s    , 	5666Z 	/ 	/JJ 3M*J.K	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	yyy)) 	5"3"?]zz ,%%* ,%%* '2'>J"0"<+  
 
 	
s   $AAAc                     | j         5  | j        }ddd           n# 1 swxY w Y   |dS |                    |           |                                 S )zWait for the current scan to complete.

        Args:
            timeout: Maximum seconds to wait, or None for no timeout

        Returns:
            True if scan completed, False if timeout expired or no scan running
        NTr   )rA   rF   joinis_alive)rS   r   threads      r   r   z_AssetSeeder.waitS  s     Z 	" 	"\F	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	">4G$$$??$$$$s     c           
         | j         5  | j        p| j        }t          | j        |r't          |j        |j        |j        |j	                  ndt          | j                            cddd           S # 1 swxY w Y   dS )z2Get the current status and progress of the seeder.r-   r.   r/   r0   N)r5   r6   r8   )rA   rC   rD   r4   rB   r,   r-   r.   r/   r0   r9   rE   )rS   srcs     r   
get_statusz_AssetSeeder.get_statusc  s    Z 	 	.7D$7Ck K)KK	    DL))  	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   A A55A9<A9c                     |                                   |                     |           | j        5  d| _        ddd           dS # 1 swxY w Y   dS )zGracefully shutdown: cancel any running scan and wait for thread.

        Args:
            timeout: Maximum seconds to wait for thread to exit
        r   N)rz   r   rA   rF   )rS   r   s     r   shutdownz_AssetSeeder.shutdownt  s     			'	"""Z 	  	 DL	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	 s   AAAc                    | j         5  | j        t          j        k    rt	          d          t          j        | _        ddd           n# 1 swxY w Y   	 t                      sKt          j        d           	 | j         5  | 	                                 ddd           dS # 1 swxY w Y   dS t                      }t          |          }|dk    rt          j        d|           || j         5  | 	                                 ddd           S # 1 swxY w Y   S # | j         5  | 	                                 ddd           w # 1 swxY w Y   w xY w)a  Mark references as missing when outside all known root prefixes.

        This is a non-destructive soft-delete operation. Assets and their
        metadata are preserved, but references are flagged as missing.
        They can be restored if the file reappears in a future scan.

        This operation is decoupled from scanning to prevent partial scans
        from accidentally marking assets belonging to other roots.

        Should be called explicitly when cleanup is desired, typically after
        a full scan of all roots or during maintenance.

        Returns:
            Number of references marked as missing

        Raises:
            ScanInProgressError: If a scan is currently running
        z0Cannot mark missing assets while scan is runningNz:Database dependencies not available, skipping mark missingr   zMarked %d references as missing)rA   rB   r   r   r   r    r   rV   warning_reset_to_idler   r   rW   )rS   all_prefixesmarkeds      r   mark_missing_outside_prefixesz*_AssetSeeder.mark_missing_outside_prefixes  sc   & Z 	( 	({ej(()F    -DK	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	(	&)++ P     & &##%%%& & & & & & & & & & & & & & & & & & 233L9,GGFzz>GGG & &##%%%& & & & & & & & & & & & & & & & & &##%%%& & & & & & & & & & & & & & & &sk   6A

AA"D  B""B&)B&/9D /DD	D	E$E9EE		EE	Ec                 N    | j         | _        t          j        | _        d| _         dS )zFReset state to IDLE, preserving last progress. Caller must hold _lock.N)rC   rD   r   r   rB   rR   s    r   r   z_AssetSeeder._reset_to_idle  s     "njr   c                 4    | j                                         S )z)Check if cancellation has been requested.)rH   is_setrR   s    r   _is_cancelledz_AssetSeeder._is_cancelled  s    !((***r   c                 h    | j                                          p| j                                        S )a>  Non-blocking check: True if paused or cancelled.

        Use as interrupt_check for I/O-bound work (e.g. hashing) so that
        file handles are released immediately on pause rather than held
        open while blocked. The caller is responsible for blocking on
        _check_pause_and_cancel() afterward.
        )rI   r   rH   rR   s    r   _is_paused_or_cancelledz$_AssetSeeder._is_paused_or_cancelled  s/     >((***Id.@.G.G.I.IIr   c                     | j                                         s|                     di            | j                                          |                                 S )a=  Block while paused, then check if cancelled.

        Call this at checkpoint locations in scan loops. It will:
        1. Block indefinitely while paused (until resume or cancel)
        2. Return True if cancelled, False to continue

        Returns:
            True if scan should stop, False to continue
        zassets.seed.paused)rI   r   r   r   r   rR   s    r   _check_pause_and_cancelz$_AssetSeeder._check_pause_and_cancel  sW     ~$$&& 	712666!!###r   
event_typedatac                     	 ddl m} t          |d          r$|j        r|j                            ||           dS dS dS # t
          $ r Y dS w xY w)z.Emit a WebSocket event if server is available.r   )PromptServerinstanceN)serverr   hasattrr   	send_sync	Exception)rS   r   r   r   s       r   r   z_AssetSeeder._emit_event  s    	++++++|Z00 B\5J B%//
DAAAAAB B B B 	 	 	DD	s   8A   
AAr-   r.   r/   r0   c                    d}d}| j         5  | j        	 ddd           dS ||| j        _        ||| j        _        ||| j        _        ||| j        _        | j        rB| j        }t          | j        j        | j        j        | j        j        | j        j                  }ddd           n# 1 swxY w Y   |r!|r!	  ||           dS # t          $ r Y dS w xY wdS dS )z'Update progress counters (thread-safe).Nr   )	rA   rC   r-   r.   r/   r0   rO   r,   r   )rS   r-   r.   r/   r0   callbackr6   s          r   _update_progressz_AssetSeeder._update_progress  s    -1$(Z 	 	~%	 	 	 	 	 	 	 	 ")0& ',$")0&")0&& 2# N2.. N2 N2	  	 	 	 	 	 	 	 	 	 	 	 	 	 	 	(  	 	"""""   	 	 	 	s)   	B/BB//B36B3?C 
CC   messagec                     | j         5  t          | j                  | j        k     r| j                            |           ddd           dS # 1 swxY w Y   dS )z:Add an error message (thread-safe), capped at _MAX_ERRORS.N)rA   lenrE   _MAX_ERRORSappend)rS   r   s     r   
_add_errorz_AssetSeeder._add_error  s    Z 	- 	-4<  4#333##G,,,	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	-s   8AAAc                     ddl }|D ]g}|dk    r8t          j        dt          j                            |j                             @t          |          }|rt          j        d||           hdS )z)Log the directories that will be scanned.r   Nr\   z!Asset scan [models] directory: %szAsset scan [%s] directories: %s)folder_pathsrV   rW   ospathabspath
models_dirr   )rS   r_   r   rootprefixess        r   _log_scan_configz_AssetSeeder._log_scan_config  s     		T 		TDx7GOOL$;<<   
 166 TL!BD(SSS		T 		Tr   c                    t          j                    }| j        }| j        }d}d}d}d}d}	 t	                      s|                     d           |                     dddi           	 |r-|                     d| j        r| j        j        nd||d           | j	        5  | 
                                 | j        }	|	Ed| _        |                     |	d	         |	d
                   st          j        d|	d	                    ddd           dS # 1 swxY w Y   dS | j        r8t!                      }
t#          |
          }|dk    rt          j        d|           |                                 rt          j        d           d}	 |r-|                     d| j        r| j        j        nd||d           | j	        5  | 
                                 | j        }	|	Ed| _        |                     |	d	         |	d
                   st          j        d|	d	                    ddd           dS # 1 swxY w Y   dS |                     |           |t*          j        t*          j        fv r|                     |          \  }}}|||}}}|                                 rd}	 |r-|                     d| j        r| j        j        nd||d           | j	        5  | 
                                 | j        }	|	Ed| _        |                     |	d	         |	d
                   st          j        d|	d	                    ddd           dS # 1 swxY w Y   dS |                     dt3          |          |||d           |t*          j        t*          j        fv r|                                 rd}	 |r-|                     d| j        r| j        j        nd||d           | j	        5  | 
                                 | j        }	|	Ed| _        |                     |	d	         |	d
                   st          j        d|	d	                    ddd           dS # 1 swxY w Y   dS |                     |          \  }}|rd}	 |r-|                     d| j        r| j        j        nd||d           | j	        5  | 
                                 | j        }	|	Ed| _        |                     |	d	         |	d
                   st          j        d|	d	                    ddd           dS # 1 swxY w Y   dS |                     dt3          |          |d           t          j                    |z
  }t          j        d||j        ||||           |                     d|j        ||||t;          |d          d           nh# t<          $ r[}|                     d|            t          j        d           |                     ddtA          |          i           Y d}~nd}~ww xY w|r-|                     d| j        r| j        j        nd||d           | j	        5  | 
                                 | j        }	|	Ed| _        |                     |	d	         |	d
                   st          j        d|	d	                    ddd           dS # 1 swxY w Y   dS # |r-|                     d| j        r| j        j        nd||d           | j	        5  | 
                                 | j        }	|	Ed| _        |                     |	d	         |	d
                   st          j        d|	d	                    ddd           w # 1 swxY w Y   w xY w)z,Main scan loop running in background thread.Fr   z#Database dependencies not availablezassets.seed.errorr   zassets.seed.cancelled)r-   r.   r/   Nr_   rc   rt   z.Pending enrich scan could not start (roots=%s)z%Marked %d refs as missing before scanz(Asset scan cancelled after pruning phaseTzassets.seed.fast_complete)r_   r/   r0   r.   zassets.seed.enrich_complete)r_   enrichedz:Scan(%s, %s) done %.3fs: created=%d enriched=%d skipped=%dzassets.seed.completed   )r`   r.   r/   r   r0   elapsedzScan failed: zAsset scan failed)!timeperf_counterrK   rL   r   r   r   rC   r-   rA   r   rQ   rr   rV   r   rN   r   r   rW   r   r   r$   r(   r*   _run_fast_phaser9   r)   _run_enrich_phaseri   roundr   	exceptionr:   )rS   t_startr_   r`   	cancelledtotal_createdtotal_enrichedskipped_existingtotal_pathspendingr   r   r/   r0   pathsenrich_cancelledr   es                     r   rl   z_AssetSeeder._run_scan  s   #%%	n	)++  EFFF  ' EF   d    +=A^#R4>#9#9QR!,#0      ##%%%.&+/D(,,%g.'./?'@ -     L#G,                   s   R577=lKKA::L!H&QQQ++-- GHHH 	N    +=A^#R4>#9#9QR!,#0      ##%%%.&+/D(,,%g.'./?'@ -     L#G,                   ] !!%((( 888*.*>*>u*E*E'%?FQV///11  $Ix    +=A^#R4>#9#9QR!,#0      ##%%%.&+/D(,,%g.'./?'@ -     L#G,                   G   /!%e#0#3!,	    )9>::://11  $IX    +=A^#R4>#9#9QR!,#0      ##%%%.&+/D(,,%g.'./?'@ -     L#G,                   g 483I3I%3P3P0 .#  $IL    +=A^#R4>#9#9QR!,#0      ##%%%.&+/D(,,%g.'./?'@ -     L#G,                   [   1!%e$2    '))G3GLL    '"[(, ./$Wa00 
 
 
 
  	G 	G 	GOO/A//000122209c!ff2EFFFFFFFF	G
    +=A^#R4>#9#9QR!,#0      ##%%%.&+/D(,,%g.'./?'@ -     L#G,                       +=A^#R4>#9#9QR!,#0      ##%%%.&+/D(,,%g.'./?'@ -     L#G,                 s   ;V; A#DDDA)V; <A#H,,H03H09A%V; A#MM	M	AV; "A#QQQV; 2A#T""T&)T&/BV; :[ ;
X AX[ X  [ A#[		[[7^A#]<0^<^  ^^ ^c           	      j   t          j                    }d}d}t                      }t          j                    }|D ]?}|                                 r||dfc S |                    t          |                     @t          j        dt          j                    |z
  t          |                     |                                 r||dfS t          j                    }t          |          }	t          j        dt          j                    |z
  t          |	                     t          |	          }
| 
                    |
           |                     dt          |          |
dd           t          j                    }t          |	|dd	          \  }}}t          j        d
t          j                    |z
  t          |          |           | 
                    |           |                                 r|||
fS d}t          j                    }d}t          dt          |          |          D ](}|                                 r+t          j        d|t          |          |           |||
fc S ||||z            }d |D             }	 t!          ||          }||z  }nG# t"          $ r:}|                     d| d|            t          j        d|           Y d}~nd}~ww xY w|t          |          z   }t          j                    }| 
                    ||           ||z
  |k    r*|                     dd|t          |          |d           |}*| 
                    t          |          |           t          j        dt          j                    |z
  |||
           |||
fS )zRun phase 1: fast scan to create stub records.

        Returns:
            Tuple of (total_created, skipped_existing, total_paths)
        r   z9Fast scan: sync_root phase took %.3fs (%d existing paths)z4Fast scan: collect_paths took %.3fs (%d paths found))r.   assets.seed.startedr%   )r_   r.   r`   F)enable_metadata_extractionrc   z>Fast scan: build_asset_specs took %.3fs (%d specs, %d skipped))r0   i        ?z2Fast scan cancelled after %d/%d files (created=%d)c                 (    h | ]}|d          D ]}|S )tagsr   ).0spects      r   	<setcomp>z/_AssetSeeder._run_fast_phase.<locals>.<setcomp>  s)    DDDtF|DD!!DDDDr   zBatch insert failed at offset z: z Batch insert failed at offset %dN)r-   r/   assets.seed.progress)r`   r-   r.   r/   zHFast scan complete: %.3fs total (created=%d, skipped=%d, total_paths=%d))r   r   rJ   r   ru   r   rV   rh   r   r   r   r   r9   r
   rangerW   r   r   r   r   )rS   r_   t_fast_startr   r   existing_pathst_syncr	t_collectr   r   t_specsspecstag_pool
batch_sizelast_progress_timeprogress_intervalibatch
batch_tagsr/   r   r-   nows                           r   r   z_AssetSeeder._run_fast_phase  sQ    (**#&55"$$ 	7 	7A++-- :$&69999!!"21"5"56666G&(	
 	
 	
 '')) 	6 "2A55%''	'..B)+JJ	
 	
 	

 %jjK000!5kkK&II	
 	
 	
 #%%,=', 	-
 -
 -
)x) 	L')JJ		
 	
 	
 	&6777'')) 	@ "2K??
!.00q#e**j11 !	) !	)A++-- DHJJ!	   %&6CCCC!a*n,-EDDDDDJI,UJ??( I I I I I Ia I IJJJ!"DaHHHHHHHHI #e**nG#%%C!!'=!III''+<<<  *!'#*!$U#0	    &)"c%jj-HHHV,.	
 	
 	
 .;;s   J((
K,20K''K,c                    d}d}t          j                    }d}| j        st          }nt          }|                     dt          |          dd           t                      d}d}i }		 |                                 rt          j
        d	|           d|fS t          |||
          }
rfd|
D             }
|
snt          |
d| j        | j        |	          \  }}||z  }                    |           |dk    r0|dz  }||k    r$t          j        d|t!                               n;nd}t          j                    }||z
  |k    r|                     dd|d           |}d|fS )zRun phase 2: enrich existing records with metadata and hashes.

        Returns:
            Tuple of (cancelled, total_enriched)
        r   d   r   r   r&   )r_   r`   r   Tz%Enrich scan cancelled after %d assets)	max_levellimitc                 &    g | ]}|j         v|S r   )reference_id)r   r   skip_idss     r   
<listcomp>z2_AssetSeeder._run_enrich_phase.<locals>.<listcomp>%  s%    VVVAq~X7U7Ua7U7U7Ur   )extract_metadatacompute_hashinterrupt_checkhash_checkpoints   zKEnrich phase stopping: %d consecutive batches with no progress (%d skipped)r   )r`   r   F)r   r   rM   r   r   r   r9   rJ   r   rV   rW   r   r   r   ru   r   r   )rS   r_   r   r   r   r   target_max_levelconsecutive_emptymax_consecutive_emptyr   
unenrichedr   
failed_idsr   r   s                 @r   r   z_AssetSeeder._run_enrich_phase  s    
!.00 # 	3.2!5kkH55	
 	
 	

 !UU ! /12	)++-- ,DnUUU^++ 9*   J  WVVVVVVV
 #6!%!1 $ <!1$ $ $ Hj h&NOOJ'''1}}!Q&!$(===Oe)H  
  > %&!#%%C''+<<<  *!)$2    &)"e2	)h n$$r   )r=   N)r[   NF)r[   F)NNNNNr   )N)r   )NNNN)+r   r   r   r   rT   rX   boolrZ   r$   r*   rv   r	   ProgressCallbackrm   rp   rr   rx   rz   r|   r~   r   floatr   r   r4   r   r   r1   r   r   r   r   r   r:   dictr   r   r   r   r   rl   r   r   r   r   r   r<   r<   G   s?        1 1 1 1(. . . .
T     'D$>59!$, ,Xs]#, , ,d2	,
 , , 
, , , ,` 'D59!	
 
Xs]#
 ,d2
 	

 

 
 
 
4 'D59$	
 
Xs]#
 ,d2
 	

 

 
 
 
4 'D$" "Xs]#" " 
	" " " "H    d    t         & .2"&59#'&*+
 +
Xs]#d*+
 4+
 ,d2	+

 D[+
 t+
 +
 
+
 +
 +
 +
Z% %EDL %D % % % % J    "	  	  	  	  	  	  	 (&s (& (& (& (&T   +t + + + +J J J J J$ $ $ $ $c  $     # ""# #t# Tz# t	#
 t# 
# # # #J K-# -$ - - - -TeHcM&: Tt T T T Ty y y yvj<U8S=%9 j<eCcM>R j< j< j< j<XR%uXs]'; R%dCi@P R% R% R% R% R% R%r   r<   )$r   rV   r   r?   r   dataclassesr   r   enumr   typingr   app.assets.scannerr   r   r	   r
   r   r   r   r   r   r   r   r   app.database.dbr   r   r   r   r$   r,   r4   r  r<   asset_seederr   r   r   <module>r     sR   N N  				      ( ( ( ( ( ( ( (                                        3 2 2 2 2 2M M M M M) M M M    D                   4 4 4 4 4 4 4 4 XJ,- D% D% D% D% D% D% D% D%N |~~r   