
    +jr                     D   S r SSKrSSKrSSKrSSKrSSKJrJr  SSKJ	r	  SSK
Jr  SSKJrJrJrJrJrJrJrJrJrJrJrJr  SSKJr   " S S	\5      r " S
 S\	5      r " S S\	5      r\ " S S5      5       r\ " S S5      5       r \\/S4   r! " S S5      r"\"" 5       r#g)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                       \ rS rSrSrSrg)ScanInProgressError   zBRaised when an operation cannot proceed because a scan is running. N)__name__
__module____qualname____firstlineno____doc____static_attributes__r       1/home/wildlama/comfy/ComfyUI/app/assets/seeder.pyr   r      s    Lr   r   c                   (    \ rS rSrSrSrSrSrSrSr	g)	State    zSeeder state machine states.IDLERUNNINGPAUSED
CANCELLINGr   N)
r   r   r   r   r   r#   r$   r%   r&   r   r   r   r   r!   r!       s    &DGFJr   r!   c                   $    \ rS rSrSrSrSrSrSrg)	ScanPhase)   zScan phase options.fastenrichfullr   N)	r   r   r   r   r   FASTENRICHFULLr   r   r   r   r(   r(   )   s    DFDr   r(   c                   R    \ rS rSr% SrSr\\S'   Sr\\S'   Sr	\\S'   Sr
\\S'   Srg	)
Progress1   z*Progress information for a scan operation.r   scannedtotalcreatedskippedr   N)r   r   r   r   r   r3   int__annotations__r4   r5   r6   r   r   r   r   r1   r1   1   s,    4GSE3NGSGSr   r1   c                   P    \ rS rSr% Sr\\S'   \S-  \S'   \" \	S9r
\	\   \S'   Srg)	
ScanStatus;   z#Current status of the asset seeder.stateNprogress)default_factoryerrorsr   )r   r   r   r   r   r!   r8   r1   r   listr?   strr   r   r   r   r:   r:   ;   s'    -Lod3FDI3r   r:   c                      \ rS rSrSrS2S jrS2S jrS\4S jrS\	R                  SS	S	4S
\\S4   S\	S\S-  S\S\S\4S jjr   S3S
\\S4   S\S-  S\S\4S jjr   S3S
\\S4   S\S-  S\S\4S jjr  S4S
\\S4   S\S\4S jjrS\4S jrS\4S jrS\4S jrS\4S jr      S5S
\\S4   S-  S\	S-  S\S-  S\S-  S\S-  S\S\4S jjrS6S\S-  S\4S jjrS\4S jrS7S\SS4S jjrS\4S jrS2S jrS\4S jrS\4S  jr S\4S! jr!S"\"S#\#SS4S$ jr$    S8S%\S-  S&\S-  S'\S-  S(\S-  SS4
S) jjr%S*r&S+\"SS4S, jr'S
\\S4   SS4S- jr(S2S. jr)S
\\S4   S\\\\4   4S/ jr*S
\\S4   S\\\4   4S0 jr+S1r,g)9_AssetSeederG   zBackground 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                    [         R                  " 5       U l        [        R                  U l        S U l        S U l        / U l        S U l	        [         R                  " 5       U l        [         R                  " 5       U l        U R                  R                  5         SU l        [        R                   U l        SU l        SU l        S U l        SU l        S U l        g )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___AssetSeeder.__init__O   s     __&
jj*./3"$04&__."*,.!*%*"';?$,0r   c                 >    SU l         [        R                  " S5        g)z=Disable the asset seeder, preventing any scans from starting.TzAsset seeder disabledN)rX   logginginforZ   s    r   disable_AssetSeeder.disablec   s    ,-r   c                     U R                   $ )z&Check if the asset seeder is disabled.)rX   rZ   s    r   is_disabled_AssetSeeder.is_disabledh   s    ~~r   modelsinputoutputFroots.phaseprogress_callbackprune_firstcompute_hashesc                    U R                   (       a  [        R                  " S5        g[        R                  " SXR                  5        U R
                     U R                  [        R                  :w  a   [        R                  " S5         SSS5        g[        R                  U l        [        5       U l        / U l        Xl        X l        X@l        XPl        X0l        U R$                  R'                  5         U R(                  R+                  5         [,        R.                  " U R0                  SSS9U l        U R2                  R5                  5          SSS5        g! , (       d  f       g= f)	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 startNrC   T)targetnamedaemon)rX   r_   debugr`   valuerI   rJ   r!   r#   r$   r1   rK   rM   rS   rT   rV   rU   rW   rP   clearrQ   rR   rG   Thread	_run_scanrN   start)r[   rj   rk   rl   rm   rn   s         r   rx   _AssetSeeder.startl   s    ( >>MMDE8%MZZ{{ejj(KL Z  --DK%ZDNDLKK +#1 &7#$$&NN $++~~#DL
 LL ) ZZs   6EB=E
E)c                 D    U R                  U[        R                  UUSS9$ )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rj   rk   rl   rm   rn   )rx   r(   r-   )r[   rj   rl   rm   s       r   
start_fast_AssetSeeder.start_fast   s-      zz../#   
 	
r   c                 D    U R                  U[        R                  USUS9$ )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
Fr{   )rx   r(   r.   )r[   rj   rl   rn   s       r   start_enrich_AssetSeeder.start_enrich   s/      zz""/)  
 	
r   c                    U R                      U R                  XS9(       a
   SSS5        gU R                  bg  [        U R                  S   5      nUR	                  U5        [        U5      U R                  S'   U R                  S   =(       d    UU R                  S'   O
UUS.U l        [        R                  " SU R                  S   5        SSS5        g! , (       d  f       g= f)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
rj   rn   NTrj   rn   zEnrich scan queued (roots=%s)F)rI   r   rY   rR   updatetupler_   r`   )r[   rj   rn   existing_rootss       r   enqueue_enrich_AssetSeeder.enqueue_enrich   s    $ ZZ  u L Z ##/!$T%9%9'%B!C%%e,05n0E$$W-(()9:Ln $$%56
 #&4($ LL8$:N:Nw:WX   ! Z  s   CB"C
C%c                    U R                      U R                  [        R                  [        R                  4;  a
   SSS5        g[
        R                  " SU R                  R                  5        [        R                  U l        U R                  R                  5         U R                  R                  5          SSS5        g! , (       d  f       g= f)z{Request cancellation of the current scan.

Returns:
    True if cancellation was requested, False if not running or paused
NFz Asset seeder cancelling (was %s)T)rI   rJ   r!   r$   r%   r_   r`   rt   r&   rP   rR   rQ   rZ   s    r   cancel_AssetSeeder.cancel   s     ZZ{{5==%,,"?? Z LL;T[[=N=NO**DK""$NN  ZZs   0CA5C
Cc                 "    U R                  5       $ )ziStop the current scan (alias for cancel).

Returns:
    True if stop was requested, False if not running
)r   rZ   s    r   stop_AssetSeeder.stop   s     {{}r   c                 ,   U R                      U R                  [        R                  :w  a
   SSS5        g[        R
                  " S5        [        R                  U l        U R                  R                  5          SSS5        g! , (       d  f       g= f)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)	rI   rJ   r!   r$   r_   r`   r%   rQ   ru   rZ   s    r   pause_AssetSeeder.pause  s\     ZZ{{emm+ Z LL/0,,DKNN  " ZZs    BAB
Bc                 N   U R                      U R                  [        R                  :w  a
   SSS5        g[        R
                  " S5        [        R                  U l        U R                  R                  5         SSS5        U R                  S0 5        g! , (       d  f       N!= f)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)
rI   rJ   r!   r%   r_   r`   r$   rQ   rR   _emit_eventrZ   s    r   resume_AssetSeeder.resume  sr     ZZ{{ell* Z LL01--DKNN   	.3 Zs    BAB
B$timeoutc                    [         R                  " S5        U R                     U R                  nU R                  nU R
                  n	U R                  n
U R                  nSSS5        U R                  5         U R                  US9(       d  gUb  UOW	nU R                  Ub  UOWUb  UOWUUb  UOW
Ub  US9$ WS9$ ! , (       d  f       Nc= f)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   Fr{   )r_   r`   rI   rS   rT   rW   rV   rU   r   waitrx   )r[   rj   rk   rl   rm   rn   r   
prev_roots
prev_phaseprev_callback
prev_pruneprev_hashescbs                r   restart_AssetSeeder.restart&  s    , 	56ZZJJ 33M**J..K  	yyy)"3"?]zz ,%* ,%* '2'>J"0"<  
 	
 CN  
 	
 Zs   =B==
Cc                     U R                      U R                  nSSS5        Wc  gUR                  US9  UR                  5       (       + $ ! , (       d  f       N6= f)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   )rI   rN   joinis_alive)r[   r   threads      r   r   _AssetSeeder.waitS  sG     ZZ\\F >G$??$$$ Zs   A


Ac                 T   U R                      U R                  =(       d    U R                  n[        U R                  U(       a4  [        UR                  UR                  UR                  UR                  S9OS[        U R                  5      S9sSSS5        $ ! , (       d  f       g= f)z2Get the current status and progress of the seeder.r3   r4   r5   r6   N)r<   r=   r?   )rI   rK   rL   r:   rJ   r1   r3   r4   r5   r6   r@   rM   )r[   srcs     r   
get_status_AssetSeeder.get_statusc  sr    ZZ..7D$7$7Ckk  "KK))KKKK	 DLL) ZZs   BB
B'c                     U R                  5         U R                  US9  U R                     SU l        SSS5        g! , (       d  f       g= f)zGracefully shutdown: cancel any running scan and wait for thread.

Args:
    timeout: Maximum seconds to wait for thread to exit
r   N)r   r   rI   rN   )r[   r   s     r   shutdown_AssetSeeder.shutdownt  s3     			'	"ZZDL ZZs	   =
Ac                    U R                      U R                  [        R                  :w  a  [	        S5      e[        R
                  U l        SSS5         [        5       (       d=  [        R                  " S5         U R                      U R                  5         SSS5        g[        5       n[        U5      nUS:  a  [        R                  " SU5        UU R                      U R                  5         SSS5        $ ! , (       d  f       N= f! , (       d  f       g= f! , (       d  f       $ = f! U R                      U R                  5         SSS5        f ! , (       d  f       f = f= f)ak  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)rI   rJ   r!   r#   r   r$   r   r_   warning_reset_to_idler   r   r`   )r[   all_prefixesmarkeds      r   mark_missing_outside_prefixes*_AssetSeeder.mark_missing_outside_prefixes  s    & ZZ{{ejj()F   --DK 	&)++P  ##%  23L9,GFz>G##% ) Z( ##% sM   ?C:%D- D!3D-  D:
D
D
D*	-E%:E	E%
E"E%c                 ^    U R                   U l        [        R                  U l        SU l         g)zFReset state to IDLE, preserving last progress. Caller must hold _lock.N)rK   rL   r!   r#   rJ   rZ   s    r   r   _AssetSeeder._reset_to_idle  s     "nnjjr   c                 6    U R                   R                  5       $ )z)Check if cancellation has been requested.)rP   is_setrZ   s    r   _is_cancelled_AssetSeeder._is_cancelled  s    !!((**r   c                     U R                   R                  5       (       + =(       d    U R                  R                  5       $ )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.
)rQ   r   rP   rZ   s    r   _is_paused_or_cancelled$_AssetSeeder._is_paused_or_cancelled  s.     >>((**Id.@.@.G.G.IIr   c                     U R                   R                  5       (       d  U R                  S0 5        U R                   R                  5         U R	                  5       $ )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)rQ   r   r   r   r   rZ   s    r   _check_pause_and_cancel$_AssetSeeder._check_pause_and_cancel  sG     ~~$$&&126!!##r   
event_typedatac                      SSK Jn  [        US5      (       a.  UR                  (       a  UR                  R	                  X5        ggg! [
         a     gf = f)z.Emit a WebSocket event if server is available.r   )PromptServerinstanceN)serverr   hasattrr   	send_sync	Exception)r[   r   r   r   s       r   r   _AssetSeeder._emit_event  sN    	+|Z00\5J5J%%//
A 6K0 		s   AA 
AAr3   r4   r5   r6   c                 b   SnSnU R                      U R                  c
   SSS5        gUb  XR                  l        Ub  X R                  l        Ub  X0R                  l        Ub  X@R                  l        U R                  (       ah  U R                  n[        U R                  R                  U R                  R                  U R                  R                  U R                  R
                  S9nSSS5        U(       a  U(       a
   U" U5        ggg! , (       d  f       N(= f! [         a     gf = f)z'Update progress counters (thread-safe).Nr   )	rI   rK   r3   r4   r5   r6   rW   r1   r   )r[   r3   r4   r5   r6   callbackr=   s          r   _update_progress_AssetSeeder._update_progress  s     -1$(ZZ~~% Z ")0& ',$")0&")0&&&22# NN22.... NN22 NN22	 ( " !8) Z.  s#   DCDD! 
D!
D.-D.   messagec                     U R                      [        U R                  5      U R                  :  a  U R                  R	                  U5        SSS5        g! , (       d  f       g= f)z:Add an error message (thread-safe), capped at _MAX_ERRORS.N)rI   lenrM   _MAX_ERRORSappend)r[   r   s     r   
_add_error_AssetSeeder._add_error  s<    ZZ4<< 4#3#33##G, ZZs   ?A
A#c                     SSK nU Ht  nUS:X  a@  [        R                  " S[        R                  R                  UR                  5      5        MI  [        U5      nU(       d  M]  [        R                  " SX45        Mv     g)z)Log the directories that will be scanned.r   Nrg   z!Asset scan [models] directory: %szAsset scan [%s] directories: %s)folder_pathsr_   r`   ospathabspath
models_dirr   )r[   rj   r   rootprefixess        r   _log_scan_config_AssetSeeder._log_scan_config  s\    Dx7GGOOL$;$;<
 168LL!BDS r   c                 *   [         R                  " 5       nU R                  nU R                  nSnSnSnSnSn [	        5       (       d  U R                  S5        U R                  SSS05         U(       a=  U R                  SU R                  (       a  U R                  R                  OSUUS.5        U R                     U R                  5         U R                  n	U	b<  SU l        U R                  U	S	   U	S
   S9(       d  [        R                  " SU	S	   5        SSS5        gU R                  (       a2  [!        5       n
[#        U
5      nUS:  a  [        R$                  " SU5        U R'                  5       (       a  [        R$                  " S5        Sn U(       a=  U R                  SU R                  (       a  U R                  R                  OSUUS.5        U R                     U R                  5         U R                  n	U	b<  SU l        U R                  U	S	   U	S
   S9(       d  [        R                  " SU	S	   5        SSS5        gU R)                  U5        U[*        R,                  [*        R.                  4;   Ga  U R1                  U5      u  pnXUpnU R'                  5       (       a  Sn U(       a=  U R                  SU R                  (       a  U R                  R                  OSUUS.5        U R                     U R                  5         U R                  n	U	b<  SU l        U R                  U	S	   U	S
   S9(       d  [        R                  " SU	S	   5        SSS5        gU R                  S[3        U5      UUUS.5        U[*        R4                  [*        R.                  4;   Ga  U R'                  5       (       a  Sn U(       a=  U R                  SU R                  (       a  U R                  R                  OSUUS.5        U R                     U R                  5         U R                  n	U	b<  SU l        U R                  U	S	   U	S
   S9(       d  [        R                  " SU	S	   5        SSS5        gU R7                  U5      u  pU(       a  Sn U(       a=  U R                  SU R                  (       a  U R                  R                  OSUUS.5        U R                     U R                  5         U R                  n	U	b<  SU l        U R                  U	S	   U	S
   S9(       d  [        R                  " SU	S	   5        SSS5        gU R                  S[3        U5      US.5        [         R                  " 5       U-
  n[        R$                  " SUUR8                  UUUU5        U R                  SUR8                  UUUU[;        US5      S.5        U(       a=  U R                  SU R                  (       a  U R                  R                  OSUUS.5        U R                     U R                  5         U R                  n	U	b<  SU l        U R                  U	S	   U	S
   S9(       d  [        R                  " SU	S	   5        SSS5        g! , (       d  f       g= f! , (       d  f       g= f! , (       d  f       g= f! , (       d  f       g= f! , (       d  f       g= f! [<         aR  nU R                  SU 35        [        R>                  " S5        U R                  SS[A        U5      05         SnAGNaSnAff = f! , (       d  f       g= f! U(       a=  U R                  SU R                  (       a  U R                  R                  OSUUS.5        U R                     U R                  5         U R                  n	U	b<  SU l        U R                  U	S	   U	S
   S9(       d  [        R                  " SU	S	   5        SSS5        f ! , (       d  f       f = f= f)z,Main scan loop running in background thread.Fr   z#Database dependencies not availablezassets.seed.errorr   zassets.seed.cancelled)r3   r4   r5   Nrj   rn   r   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)rj   r5   r6   r4   zassets.seed.enrich_complete)rj   enrichedz:Scan(%s, %s) done %.3fs: created=%d enriched=%d skipped=%dzassets.seed.completed   )rk   r4   r5   r   r6   elapsedzScan failed: zAsset scan failed)!timeperf_counterrS   rT   r   r   r   rK   r3   rI   r   rY   r   r_   r   rV   r   r   r`   r   r   r(   r-   r/   _run_fast_phaser@   r.   _run_enrich_phasert   roundr   	exceptionrA   )r[   t_startrj   rk   	cancelledtotal_createdtotal_enrichedskipped_existingtotal_pathspendingr   r   r5   r6   pathsenrich_cancelledr   es                     r   rw   _AssetSeeder._run_scan  sR   ##%	n	)++ EF  ' EF d   +=A^^4>>#9#9QR!,#0 ##%..&+/D(,,%g.'./?'@ -   L#G, s   57=lKA:LL!H&Q++--GH 	N   +=A^^4>>#9#9QR!,#0 ##%..&+/D(,,%g.'./?'@ -   L#G, ] !!%( 88*.*>*>u*E'%?FQV//11 $Ix   +=A^^4>>#9#9QR!,#0 ##%..&+/D(,,%g.'./?'@ -   L#G, G   /!%e#0#3!,	 ))9>>:://11 $IX   +=A^^4>>#9#9QR!,#0 ##%..&+/D(,,%g.'./?'@ -   L#G, g 483I3I%3P0 # $IL   +=A^^4>>#9#9QR!,#0 ##%..&+/D(,,%g.'./?'@ -   L#G, [   1!%e$2 '')G3GLLL  '"[[(, ./$Wa0
"   +=A^^4>>#9#9QR!,#0 ##%..&+/D(,,%g.'./?'@ -   L#G,   	GOOmA3/01209c!f2EFF	G    +=A^^4>>#9#9QR!,#0 ##%..&+/D(,,%g.'./?'@ -   L#G, s   4[ >AZ#A0[ $AZ	A%[ ?AZ($A[ AZ96[ #A[
B	[ !A\:
Z
Z%(
Z69
[

[
\7%A\2,] 2\77] :
]A`A`8	`
``c           	         [         R                  " 5       nSnSn[        5       n[         R                  " 5       nU H8  nU R                  5       (       a  X4S4s  $ UR	                  [        U5      5        M:     [        R                  " S[         R                  " 5       U-
  [        U5      5        U R                  5       (       a  X4S4$ [         R                  " 5       n[        U5      n	[        R                  " S[         R                  " 5       U-
  [        U	5      5        [        U	5      n
U R                  U
S9  U R                  S[        U5      U
SS.5        [         R                  " 5       n[        U	USSS	9u  pn[        R                  " S
[         R                  " 5       U-
  [        U5      U5        U R                  US9  U R                  5       (       a  X4U
4$ Sn[         R                  " 5       nSn[        S[        U5      U5       H  nU R                  5       (       a(  [        R                  " SU[        U5      U5        X4U
4s  $ UUUU-    nU VVs1 s H  nUS     H  nUiM     M     nnn [!        UU5      nUU-  nU[        U5      -   n[         R                  " 5       nU R                  UUS9  UU-
  U:  d  M  U R                  SSU[        U5      US.5        UnM     U R                  [        U5      US9  [        R                  " S[         R                  " 5       U-
  UUU
5        X4U
4$ s  snnf ! ["         a8  nU R%                  SU SU 35        [        R&                  " SU5         SnANSnAff = f)zuRun 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))r4   assets.seed.startedr*   )rj   r4   rk   F)enable_metadata_extractionrn   z>Fast scan: build_asset_specs took %.3fs (%d specs, %d skipped))r6   i        ?z2Fast scan cancelled after %d/%d files (created=%d)tagszBatch insert failed at offset z: z Batch insert failed at offset %dN)r3   r5   assets.seed.progress)rk   r3   r4   r5   zHFast scan complete: %.3fs total (created=%d, skipped=%d, total_paths=%d))r   r   rR   r   r   r   r_   rs   r   r   r   r   r@   r
   ranger`   r   r   r   r   )r[   rj   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spect
batch_tagsr5   r   r3   nows                             r   r   _AssetSeeder._run_fast_phase  sl    ((*#&5""$A++--$99!!"21"56  	G&(	
 '')) A55%%'	'.B)+J	

 %jK0!5kK&I	
 ##%,=', 	-
)) 	L')J		
 	&67'')) K??
!..0q#e*j1A++--HJ!	 %CC!a*n-E(-DtF|!!|!JDI,UJ?(
 #e*nG##%C!!'=!I''+<<  *!'#*!$U#0	 &)"C 2F 	c%j-HV,.	
 ;;A E  I"@2aS IJ!!"DaHHIs   L64L<<
M>.M99M>c                    SnSn[         R                  " 5       nSnU R                  (       d  [        nO[        nU R                  S[        U5      SS.5        [        5       nSnSn	0 n
 U R                  5       (       a  [        R                  " S	U5        SU4$ [        UUUS
9nU(       a#  U Vs/ s H  oR                  U;  d  M  UPM     nnU(       d   SU4$ [        USU R                  U R                  U
S9u  pX--  nUR                  U5        US:X  a1  US-  nX:  a&  [        R                   " SU[#        U5      5         SU4$ OSn[         R                  " 5       nX-
  U:  a  U R                  SSUS.5        UnGM  s  snf )zrRun phase 2: enrich existing records with metadata and hashes.

Returns:
    Tuple of (cancelled, total_enriched)
r   d   r   r   r+   )rj   rk   r   Tz%Enrich scan cancelled after %d assets)	max_levellimit)extract_metadatacompute_hashinterrupt_checkhash_checkpoints   zKEnrich phase stopping: %d consecutive batches with no progress (%d skipped)r   )rk   r   F)r   r   rU   r   r   r   r@   rR   r   r_   r`   r   reference_idr   r   r   r   r   )r[   rj   r   r  r  r  target_max_levelskip_idsconsecutive_emptymax_consecutive_emptyr  
unenrichedr   r   
failed_idsr  s                   r   r   _AssetSeeder._run_enrich_phase  s    
!..0 ##.2!5kH5	

 !U ! /1++--DnU^++ 9* J )3VA~~X7Ua
VF n$$C $7!%!11 $ < <!1$ H &NOOJ'1}!Q&!$=OOe)H
  n$$+ > %&!##%C'+<<  *!)$2 &)"e  Ws   /FF)rP   rU   rX   rM   rL   rI   rY   rT   rK   rW   rV   rS   rQ   rJ   rN   )rE   N)rf   NF)rf   F)NNNNN      @)N)r   )NNNN)-r   r   r   r   r   r\   ra   boolrd   r(   r/   r   r	   ProgressCallbackrx   r|   r   r   r   r   r   r   floatr   r   r:   r   r   r7   r   r   r   r   r   rA   dictr   r   r   r   r   rw   r   r   r   r   r   r   rC   rC   G   s\   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$ $c  $  # ""#t# Tz# t	#
 t# 
#J K-# -$ -TeHcM&: Tt Tyvj<U8S=%9 j<eCcM>R j<XR%uXs]'; R%dCi@P R%r   rC   )$r   r_   r   rG   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(   r1   r:   r"  rC   asset_seederr   r   r   <module>r+     s    N  	   (      3M) MD      4 4 4 XJ,- D% D%N ~r   