@@ -417,10 +417,15 @@ def download_and_cache_model(
417417 model_path .mkdir (parents = True , exist_ok = True )
418418 model_source = self ._guess_source (str (source ))
419419 remote_files , _ = self ._remote_files_from_source (model_source )
420+ # Handle multiple subfolders for HFModelSource
421+ subfolders = model_source .subfolders if isinstance (model_source , HFModelSource ) else []
420422 job = self ._multifile_download (
421423 dest = model_path ,
422424 remote_files = remote_files ,
423- subfolder = model_source .subfolder if isinstance (model_source , HFModelSource ) else None ,
425+ subfolder = model_source .subfolder
426+ if isinstance (model_source , HFModelSource ) and len (subfolders ) <= 1
427+ else None ,
428+ subfolders = subfolders if len (subfolders ) > 1 else None ,
424429 )
425430 files_string = "file" if len (remote_files ) == 1 else "files"
426431 self ._logger .info (f"Queuing model download: { source } ({ len (remote_files )} { files_string } )" )
@@ -438,10 +443,13 @@ def _remote_files_from_source(
438443 if isinstance (source , HFModelSource ):
439444 metadata = HuggingFaceMetadataFetch (self ._session ).from_id (source .repo_id , source .variant )
440445 assert isinstance (metadata , ModelMetadataWithFiles )
446+ # Use subfolders property which handles '+' separated multiple subfolders
447+ subfolders = source .subfolders
441448 return (
442449 metadata .download_urls (
443450 variant = source .variant or self ._guess_variant (),
444- subfolder = source .subfolder ,
451+ subfolder = source .subfolder if len (subfolders ) <= 1 else None ,
452+ subfolders = subfolders if len (subfolders ) > 1 else None ,
445453 session = self ._session ,
446454 ),
447455 metadata ,
@@ -741,10 +749,13 @@ def _import_remote_model(
741749 install_job ._install_tmpdir = destdir
742750 install_job .total_bytes = sum ((x .size or 0 ) for x in remote_files )
743751
752+ # Handle multiple subfolders for HFModelSource
753+ subfolders = source .subfolders if isinstance (source , HFModelSource ) else []
744754 multifile_job = self ._multifile_download (
745755 remote_files = remote_files ,
746756 dest = destdir ,
747- subfolder = source .subfolder if isinstance (source , HFModelSource ) else None ,
757+ subfolder = source .subfolder if isinstance (source , HFModelSource ) and len (subfolders ) <= 1 else None ,
758+ subfolders = subfolders if len (subfolders ) > 1 else None ,
748759 access_token = source .access_token ,
749760 submit_job = False , # Important! Don't submit the job until we have set our _download_cache dict
750761 )
@@ -771,31 +782,69 @@ def _multifile_download(
771782 remote_files : List [RemoteModelFile ],
772783 dest : Path ,
773784 subfolder : Optional [Path ] = None ,
785+ subfolders : Optional [List [Path ]] = None ,
774786 access_token : Optional [str ] = None ,
775787 submit_job : bool = True ,
776788 ) -> MultiFileDownloadJob :
777789 # HuggingFace repo subfolders are a little tricky. If the name of the model is "sdxl-turbo", and
778790 # we are installing the "vae" subfolder, we do not want to create an additional folder level, such
779791 # as "sdxl-turbo/vae", nor do we want to put the contents of the vae folder directly into "sdxl-turbo".
780792 # So what we do is to synthesize a folder named "sdxl-turbo_vae" here.
781- if subfolder :
793+ #
794+ # For multiple subfolders (e.g., text_encoder+tokenizer), we create a combined folder name
795+ # (e.g., sdxl-turbo_text_encoder_tokenizer) and keep each subfolder's contents in its own
796+ # subdirectory within the model folder.
797+
798+ if subfolders and len (subfolders ) > 1 :
799+ # Multiple subfolders: create combined name and keep subfolder structure
800+ top = Path (remote_files [0 ].path .parts [0 ]) # e.g. "Z-Image-Turbo/"
801+ subfolder_names = [sf .name .replace ("/" , "_" ).replace ("\\ " , "_" ) for sf in subfolders ]
802+ combined_name = "_" .join (subfolder_names )
803+ path_to_add = Path (f"{ top } _{ combined_name } " )
804+
805+ parts : List [RemoteModelFile ] = []
806+ for model_file in remote_files :
807+ assert model_file .size is not None
808+ # Determine which subfolder this file belongs to
809+ file_path = model_file .path
810+ new_path : Optional [Path ] = None
811+ for sf in subfolders :
812+ try :
813+ # Try to get relative path from this subfolder
814+ relative = file_path .relative_to (top / sf )
815+ # Keep the subfolder name as a subdirectory
816+ new_path = path_to_add / sf .name / relative
817+ break
818+ except ValueError :
819+ continue
820+
821+ if new_path is None :
822+ # File doesn't match any subfolder, keep original path structure
823+ new_path = path_to_add / file_path .relative_to (top )
824+
825+ parts .append (RemoteModelFile (url = model_file .url , path = new_path ))
826+ elif subfolder :
827+ # Single subfolder: flatten into renamed folder
782828 top = Path (remote_files [0 ].path .parts [0 ]) # e.g. "sdxl-turbo/"
783829 path_to_remove = top / subfolder # sdxl-turbo/vae/
784830 subfolder_rename = subfolder .name .replace ("/" , "_" ).replace ("\\ " , "_" )
785831 path_to_add = Path (f"{ top } _{ subfolder_rename } " )
786- else :
787- path_to_remove = Path ("." )
788- path_to_add = Path ("." )
789-
790- parts : List [RemoteModelFile ] = []
791- for model_file in remote_files :
792- assert model_file .size is not None
793- parts .append (
794- RemoteModelFile (
795- url = model_file .url , # if a subfolder, then sdxl-turbo_vae/config.json
796- path = path_to_add / model_file .path .relative_to (path_to_remove ),
832+
833+ parts = []
834+ for model_file in remote_files :
835+ assert model_file .size is not None
836+ parts .append (
837+ RemoteModelFile (
838+ url = model_file .url ,
839+ path = path_to_add / model_file .path .relative_to (path_to_remove ),
840+ )
797841 )
798- )
842+ else :
843+ # No subfolder specified - pass through unchanged
844+ parts = []
845+ for model_file in remote_files :
846+ assert model_file .size is not None
847+ parts .append (RemoteModelFile (url = model_file .url , path = model_file .path ))
799848
800849 return self ._download_queue .multifile_download (
801850 parts = parts ,
0 commit comments