
    +jAP                     B   S SK r S SKrS SKJr  S SKJrJrJr  S SK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JrJrJrJr  S SKJrJr  S SKJrJrJ r J!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,  S SK-J.r.   " S S\5      r/ " S S\5      r0\S   r1S\1S\2\3   4S jr4S\2\3   4S jr5S\2\3   4S jr6  S7S\1S\7S\7S\8\3   S-  4S jjr9S\1S\8\3   4S jr:S\2\3   S\;4S jr<S\=\1S4   S\2\3   4S jr>  S8S\2\3   S \8\3   S!\7S"\7S\=\2\   \8\3   \;4   4
S# jjr?S$\2\   S%\8\3   S\;4S& jr@S rAS'rBS(rC\AS)4S\=\1S4   S*\;S+\;S\24S, jjrD    S9S-\3S.\3S/\3S0\7S1\7S2\/ \74   S-  S3\E\3\#4   S-  S\;4S4 jjrF    S9S5\2S0\7S1\7S2\/ \74   S-  S3\E\3\#4   S-  S\=\;\2\3   4   4S6 jjrGg):    N)Path)CallableLiteral	TypedDict)add_missing_tag_for_asset_idbulk_update_enrichment_levelbulk_update_is_missingbulk_update_needs_verifydelete_orphaned_seed_assetdelete_references_by_idsensure_tags_existget_asset_by_hashget_reference_by_idget_references_for_prefixesget_unenriched_references(mark_references_missing_outside_prefixesreassign_asset_referencesremove_missing_tag_for_asset_idset_reference_system_metadataupdate_asset_hash_and_mime)SeedAssetSpecbatch_insert_seed_assets)get_mtime_ns
is_visiblelist_files_recursivelyverify_file_unchanged)HashCheckpointcompute_blake3_hash)extract_image_dimensions)extract_file_metadata)compute_relative_filenameget_comfy_models_folders!get_name_and_tags_from_asset_path)create_sessionc                   H    \ rS rSr% \\S'   \\S'   \\S'   \\S'   \\S'   Srg)	_RefInfo.   ref_id	file_pathexistsstat_unchangedneeds_verify N)__name__
__module____qualname____firstlineno__str__annotations__bool__static_attributes__r-       2/home/wildlama/comfy/ComfyUI/app/assets/scanner.pyr&   r&   .   s    KNLr6   r&   c                   @    \ rS rSr% \S-  \S'   \\S'   \\   \S'   Sr	g)_AssetAccumulator6   Nhashsize_dbrefsr-   )
r.   r/   r0   r1   r2   r3   intlistr&   r5   r-   r6   r7   r9   r9   6   s    
*L
x.r6   r9   modelsinputoutputrootreturnc                    U S:X  aV  / n[        5        H  u  p#UR                  U5        M     U Vs/ s H"  n[        R                  R	                  U5      PM$     sn$ U S:X  a3  [        R                  R	                  [
        R                  " 5       5      /$ U S:X  a3  [        R                  R	                  [
        R                  " 5       5      /$ / $ s  snf )NrA   rB   rC   )r"   extendospathabspathfolder_pathsget_input_directoryget_output_directory)rD   bases_bucketpathsps        r7   get_prefixes_for_rootrR   ?   s    x68NGLL 9,12Eq"E22w @ @ BCDDx A A CDEEI 3s   )Cc                  `    Sn U  VVs/ s H  n[        U5        H  o"PM     M     snn$ s  snnf )z3Get all known asset prefixes across all root types.r@   )rR   )	all_rootsrD   rQ   s      r7   get_all_known_prefixesrU   L   s.    &CI#I)$-B4-HA-HA)IIIs   *c                  $   / n [        5        H  u  p[        R                  " U5      =(       d    / nU H  n[        S [	        U5      R
                   5       5      (       d  M/  [        R                  " X5      nU(       d  MN  [        R                  R                  U5      nSn[	        U5      nU H:  nUR                  [        R                  R                  U5      5      (       d  M8  Sn  O   U(       d  M  U R                  U5        M     GM     U $ )Nc              3   8   #    U  H  n[        U5      v   M     g 7f)N)r   ).0parts     r7   	<genexpr>'collect_models_files.<locals>.<genexpr>W   s     I4HDz$''4Hs   FT)r"   rK   get_filename_listallr   partsget_full_pathrH   rI   rJ   is_relative_toappend)	outfolder_namerN   	rel_filesrel_pathabs_pathallowedabs_pbs	            r7   collect_models_filesrj   R   s    C68 22;?E2	!HIDN4H4HIII#11+HHwwx0HGNE''(:;;"G  w

8$ " 9" Jr6   collect_existing_pathsupdate_missing_tagsc           
      P   [        U5      nU(       d  U(       a
  [        5       $ S$ [        XUS9n0 nU H  nUR                  UR                  5      nUc(  UR
                  UR                  / S.nXUR                  '   Sn	 Sn
[        UR                  US   [        R                  " UR                  SS9S9n	US   R#                  UR$                  UR                  U
U	UR&                  S.5        M     / n/ n/ n/ n/ n[        5       nUR)                  5        GH  u  nnUS   nUS   n[+        S U 5       5      n[-        S U 5       5      nU H  nUS   (       d  UR#                  US   5        M#  US   (       a2  UR#                  US   5        US   (       a  UR#                  US   5        US   (       a  Mk  US   (       a  Mw  UR#                  US   5        M     Ucd  U(       a  U(       a  [/        U U5        OFU H@  nUS   (       d  M  UR1                  [        R2                  R5                  US   5      5        MB     GM-  U(       a=  U H#  nUS   (       a  M  UR#                  US   5        M%     U(       a   [7        U US9  OOU(       a   [=        U USS9  U H@  nUS   (       d  M  UR1                  [        R2                  R5                  US   5      5        MB     GM     [?        X5        [        U5      nU Vs/ s H  nUU;  d  M  UPM     nn[A        XSS9  [A        U USS9  [C        XSS9  [C        XSS9  U(       a  U$ S$ ! [         a    Sn
 GN[         a'    Sn
[        R                  " S	UR                  5         GN[          a/  nSn
[        R                  " S
UR                  U5         SnAGNSnAff = f! [8         a#  n[        R:                  " SUU5         SnAGNPSnAff = f! [8         a#  n[        R:                  " SUU5         SnAGNSnAff = fs  snf )a  Reconcile asset references with filesystem for a root.

- Toggle needs_verify per reference using mtime/size stat check
- For hashed assets with at least one stat-unchanged ref: delete stale missing refs
- For seed assets with all refs missing: delete Asset and its references
- Optionally add/remove 'missing' tags based on stat check in this root
- Optionally return surviving absolute paths

Args:
    session: Database session
    root: Root type to scan
    collect_existing_paths: If True, return set of surviving file paths
    update_missing_tags: If True, update 'missing' tags based on file status

Returns:
    Set of surviving absolute paths if collect_existing_paths=True, else None
N)include_missing)r;   r<   r=   FTr<   follow_symlinks)mtime_dbr<   stat_resultzPermission denied accessing %szOSError checking %s: %sr=   )r(   r)   r*   r+   r,   r;   c              3   *   #    U  H	  oS    v   M     g7f)r+   Nr-   rX   rs     r7   rZ   2sync_references_with_filesystem.<locals>.<genexpr>   s     >A./s   c              3   4   #    U  H  oS    (       + v   M     g7f)r*   Nr-   rt   s     r7   rZ   rv      s     84ak//4s   r*   r(   r+   r,   r)   )asset_idz-Failed to remove missing tag for asset %s: %s	automatic)rx   originz*Failed to add missing tag for asset %s: %s)value)"rR   setr   getrx   
asset_hash
size_bytesr   mtime_nsrH   statr)   FileNotFoundErrorPermissionErrorloggingdebugOSErrorra   reference_idr,   itemsanyr]   r   addrI   rJ   r   	Exceptionwarningr   r   r	   r
   )sessionrD   rk   rl   prefixesrowsby_assetrowaccr+   r*   eto_set_verifyto_clear_verifystale_ref_idsto_mark_missingto_clear_missing	survivorsaida_hashr=   any_unchangedall_missingru   	stale_setr(   s                             r7   sync_references_with_filesystemr   h   s   . %T*H.su8D8&+>D .0Hll3<<(;>>cnnbQC%(S\\"	GF2IGGCMM4HN 	F** ]] "0 # 0 0	
/ B  "M!#O!M!#O"$%INN$SV6{>>>8488AX;&&q{3!" ''(4^$#**1X;7%&&q/@/@$$Qx[1  >*7C8A{{!bggooan&EF  {{!((85  #3GcJ # !V,Ws;W A{{bggooan=> W %^ W4M"I,;WO&vY?VvOOW74@7$4EBW4@WUC.98D8c ! 	F 	KFMM:CMMJ 	GFMM3S]]AFF	Gl ! OOGa   V LcSTUUV Xs`    7M
<
OO3 
P#P#
O ,O 	O $N;;O 
O0O++O03
P =PP c                     [        5        n[        UU SSS9nUR                  5         U=(       d
    [        5       sSSS5        $ ! , (       d  f       g= f! [         a+  n[
        R                  " SX5        [        5       s SnA$ SnAff = f)zrSync a single root's references with the filesystem.

Returns survivors (existing paths) or empty set on failure.
T)rk   rl   Nzfast DB scan failed for %s: %s)r$   r   commitr|   r   r   	exception)rD   sessr   r   s       r7   sync_root_safelyr      so    
7'+$(	I KKM%   :DDus9   
A /A	A 
AA A 
B  B BBr   c                      [        5        n[        X5      nUR                  5         UsSSS5        $ ! , (       d  f       g= f! [         a!  n[        R
                  " SU5         SnAgSnAff = f)zMark references as missing when outside the given prefixes.

This is a non-destructive soft-delete. Returns count marked or 0 on failure.
Nz!marking missing assets failed: %sr   )r$   r   r   r   r   r   )r   r   countr   s       r7   $mark_missing_outside_prefixes_safelyr      sV    
<TLEKKM   =qAs0   
A 3	A 
AA A 
A/A**A/roots.c                    / nSU ;   a  UR                  [        5       5        SU ;   a-  UR                  [        [        R                  " 5       5      5        SU ;   a-  UR                  [        [        R
                  " 5       5      5        U$ )z+Collect all file paths for the given roots.rA   rB   rC   )rG   rj   r   rK   rL   rM   )r   rP   s     r7   collect_paths_for_rootsr     sj    E5)+,%+L,L,L,NOP5+L,M,M,OPQLr6   rP   existing_pathsenable_metadata_extractioncompute_hashesc                    / n[        5       nSnU  H  n[        R                  R                  U5      nX;   a  US-  nM.   [        R                  " USS9n	U	R                  (       d  MW  [        U5      u  p[        U5      nSnU(       a  [        UU	US9nSnU(       a   [        U5      u  nnSU-   nU(       a  UR                  OSnUR                  UU	R                  [!        U	5      U
UUUUUSS	.
5        UR#                  U5        M     XEU4$ ! [
         a     GM  f = f! [         a"  n[        R                  " SUU5         SnANSnAff = f)
aM  Build asset specs from paths, returning (specs, tag_pool, skipped_count).

Args:
    paths: List of file paths to process
    existing_paths: Set of paths that already exist in the database
    enable_metadata_extraction: If True, extract tier 1 & 2 metadata
    compute_hashes: If True, compute blake3 hashes (slow for large files)
r      Tro   Nrr   relative_filenameblake3:Failed to hash %s: %s)
rf   r   r   	info_nametagsfnamemetadatar;   	mime_typejob_id)r|   rH   rI   rJ   r   r   st_sizer#   r!   r    r   r   r   r   content_typera   r   update)rP   r   r   r   specstag_poolskippedrQ   rh   stat_pnamer   	rel_fnamer   r~   digest_r   r   s                      r7   build_asset_specsr     sa    "$EHG""qLG	WWUD9F ~~6u=
-e4	 %,""+H "&
C/6	&/
 .6H))4	!$nn(0!"$"&	
 	_ b G##U  		,  C 7BBCs*    DD 
DD 
E*EEr   r   c                     U (       d  g[        5        nU(       a  [        X!5        [        X SS9nUR                  5         UR                  sSSS5        $ ! , (       d  f       g= f)zBInsert asset specs into database, returning count of created refs.r    )r   owner_idN)r$   r   r   r   inserted_refs)r   r   r   results       r7   insert_asset_specsr   `  sE    		Td-)$bI## 
		s   8A
A#r      i  	max_levellimitc                     / nU  H  nUR                  [        U5      5        M     U(       d  / $ [        5        n[        XSXS9sSSS5        $ ! , (       d  f       g= f)zGet assets that need enrichment for the given roots.

Args:
    roots: Tuple of root types to scan
    max_level: Maximum enrichment level to include
    limit: Maximum number of rows to return

Returns:
    List of UnenrichedReferenceRow
)r   r   N)rG   rR   r$   r   )r   r   r   r   rD   r   s         r7   get_unenriched_assets_for_rootsr   r  sP     H-d34  			T(i
 
		s   
A
Ar)   r   rx   extract_metadatacompute_hashinterrupt_checkhash_checkpointsc                    [         n [        R                  " USS9n	[	        U	5      n
[        U5      nSnSnU(       a$  [        UU	US9nU(       a  UR                  n[        nSnU(       Ga;   [	        U	5      nU	R                  nSnUb|  UR                  U5      nUbh  [        R                  " USS9nUR                  [	        U5      :w  d  UR                  UR                  :w  a  SnUR                  US5        O[	        U5      n[        UUUS9u  nnUc  Ub  Ub  UUl        UUl        UXq'   U$ Ub  UR                  US5        [        R                  " USS9n[	        U5      nUU:w  a  [        R                   " SU5        O$SU 3nU(       + =(       d    USLnU(       a  ["        n['        X5      nUb  UR                  U
:w  a-  U R)                  5         [        R*                  " S	U5        [         $ U(       aa  U(       aZ  UR-                  5       nU(       a7  UR/                  S
5      (       a!  [1        XS9nU(       a  UR3                  U5        [5        XU5        U(       an  [7        X5      nU(       aO  UR8                  U:w  a?  [;        XUR8                  U5        [=        X5        U(       a  [?        U UR8                  US9  O[?        XX5        OU(       a
  [?        XUS9  [A        X/U5        U RC                  5         U$ ! [         a    Us $ f = f! [$         a#  n[        R                   " SUU5         SnAGNSnAff = f)a  Enrich a single asset with metadata and/or hash.

Args:
    session: Database session (caller manages lifecycle)
    file_path: Absolute path to the file
    reference_id: ID of the reference to update
    asset_id: ID of the asset to update (for mime_type and hash)
    extract_metadata: If True, extract safetensors header and mime type
    compute_hash: If True, compute blake3 hash
    interrupt_check: Optional non-blocking callable that returns True if
        the operation should be interrupted (e.g. paused or cancelled)
    hash_checkpoints: Optional dict for saving/restoring hash progress
        across interruptions, keyed by file path

Returns:
    New enrichment level achieved
Tro   Nr   )r   
checkpointz1File modified during hashing, discarding hash: %sr   r   z?Ref %s mtime changed during enrichment, discarding stale resultzimage/)r   )"ENRICHMENT_STUBrH   r   r   r   r!   r    r   ENRICHMENT_METADATAr   r}   r   	file_sizepopr   r   r   ENRICHMENT_HASHEDr   r   rollbackinfoto_user_metadata
startswithr   r   r   r   idr   r   r   r   r   )r   r)   r   rx   r   r   r   r   	new_levelr   initial_mtime_nsr   r   r   	full_hashmtime_beforesize_beforer   cur_statr   new_checkpoint
stat_aftermtime_aftermetadata_okr   refsystem_metadatadimsexistings                                r7   enrich_assetr     s   6  ID9 $F+))4I IH('

  --I+I I-	C'/L ..K J+-11)<
)!wwy$GH"++|H/EE)33x7G7GG%)
(,,Y='3H'=%8 /%&"FN ~#/N4N.:N+/:N,2@$/    + $$Y5DAJ&z2K{* SU^_%fX.	"22Jhd6J 1I g
4C
{cll&66M	
 H"335--h77+IKD&&t,%g_M$W8x/%glS&w9*7HKK9U&w)O	"7	J .)DNNM  @  	COO3YBB	Cs7   K? -CL 0AL $L ?LL
L>L99L>r   c                    Sn/ n[        5        nU  Ht  nUb  U" 5       (       a    Od [        UUR                  UR                  UR                  UUUUS9n	XR
                  :  a  US-  nMY  UR                  UR                  5        Mv     SSS5        XV4$ ! [         aX  n
[        R                  " SUR                  U
5        UR                  5         UR                  UR                  5         Sn
A
M  Sn
A
ff = f! , (       d  f       XV4$ = f)a  Enrich a batch of assets.

Uses a single DB session for the entire batch, committing after each
individual asset to avoid long-held transactions while eliminating
per-asset session creation overhead.

Args:
    rows: List of UnenrichedReferenceRow from get_unenriched_assets_for_roots
    extract_metadata: If True, extract metadata for each asset
    compute_hash: If True, compute hash for each asset
    interrupt_check: Optional non-blocking callable that returns True if
        the operation should be interrupted (e.g. paused or cancelled)
    hash_checkpoints: Optional dict for saving/restoring hash progress
        across interruptions, keyed by file path

Returns:
    Tuple of (enriched_count, failed_reference_ids)
r   N)r)   r   rx   r   r   r   r   r   zFailed to enrich %s: %s)r$   r   r)   r   rx   enrichment_levelra   r   r   r   r   )r   r   r   r   r   enriched
failed_idsr   r   r   r   s              r7   enrich_assets_batchr     s    2 HJ		TC*/@/@4(!mm!$!1!1 \\%5!-$3%5		 333MH%%c&6&67%  
2   4 93==!L!!#"2"2334) 
	2 sB   C:AB)C:+BC:
C7AC2,C:2C77C::
D
)FF)TF)TFNN)Hr   rH   pathlibr   typingr   r   r   rK   app.assets.database.queriesr   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   app.assets.services.bulk_ingestr   r   app.assets.services.file_utilsr   r   r   r   app.assets.services.hashingr   r   $app.assets.services.image_dimensionsr   $app.assets.services.metadata_extractr    app.assets.services.path_utilsr!   r"   r#   app.database.dbr$   r&   r9   RootTyper?   r2   rR   rU   rj   r4   r|   r   r   r>   r   tupler   r   r   r   r   r   r   dictr   r   r-   r6   r7   <module>r     s    	  / /     $  L I F 
 +y 	  ./
 
T#Y 
JS	 Jd3i 2 $) %	9
9 !9 	9
 	X_9D8 C (49  	53#7 	DI 	 (, 	C$9C$HC$ !%C$ 	C$
 4C#-.C$N	$d=1 	$SX 	$# 	$   
 %
3

 
 
	
B "159=EE E 	E
 E E b$h'$.E 3./$6E 	ET "159=5 
5 5  5  b$h'$.	5 
 3./$65  3S	>5 r6   