1919from tagstudio .core .library .ignore import Ignore
2020from tagstudio .core .media_types import MediaTypeGroup , MediaTypes , slugify
2121from tagstudio .core .query_lang .file_groups import SEARCH
22+ from tagstudio .core .utils .decompression import decompressed , get_inner_ext , is_compressed
2223from tagstudio .core .utils .types import unwrap
2324from tagstudio .previews .base_preview import RENDER , BasePreview
2425from tagstudio .previews .effects import apply_overlay_color
@@ -104,15 +105,15 @@ def __init__(self, library: Library, settings: AppSettings) -> None:
104105 # Key: ("name", UiColor, 512, 512, 1.25)
105106 self .icons : dict [tuple [str , UiColor , int , int , float ], Image .Image ] = {}
106107
107- def _get_resource_id (self , url : Path ) -> str :
108+ def _get_resource_id (self , path : Path ) -> str :
108109 """Return the name of the icon resource to use for a file type.
109110
110111 Special terms will return special resources.
111112
112113 Args:
113- url (Path): The file url to assess. "$LOADING" will return the loading graphic.
114+ path (Path): The file path to assess. "$LOADING" will return the loading graphic.
114115 """
115- ext = url . suffix . lower ( )
116+ ext = get_inner_ext ( path ) or MediaTypes . get_ext ( path )
116117 groups = MediaTypes .find (ext , SEARCH ) # Fallback icons use the SEARCH context
117118 groups .sort ( # Sort by priority and most specific dot-separated subgroup.
118119 key = lambda g : (
@@ -731,27 +732,19 @@ def _render(
731732
732733 if filepath and filepath .is_file ():
733734 try :
734- ext = filepath .suffix .lower () if filepath .suffix else filepath .stem .lower ()
735- for preview in FileRenderer ._preview_renderers :
736- media_type : MediaTypeGroup | None = getattr (
737- MediaTypes , preview .media_type_name , None
735+ preview = FileRenderer ._find_preview (filepath )
736+ if preview :
737+ image = preview .render (
738+ filepath = filepath ,
739+ is_small = is_small ,
740+ theme = theme ,
741+ size = (scaled_size , scaled_size ),
742+ dpi_scale = dpi_scale ,
743+ )
744+ elif is_compressed (filepath ):
745+ image = FileRenderer ._render_decompressed (
746+ filepath , is_small , theme , (scaled_size , scaled_size ), dpi_scale
738747 )
739- if media_type is None :
740- logger .error (
741- f"[FileRenderer] "
742- f"Attribute '{ preview .media_type_name } ' not registered with MediaTypes" ,
743- )
744- break
745-
746- if media_type .contains (ext , RENDER ):
747- image = preview .render (
748- filepath = filepath ,
749- is_small = is_small ,
750- theme = theme ,
751- size = (scaled_size , scaled_size ),
752- dpi_scale = dpi_scale ,
753- )
754- break
755748
756749 if image :
757750 image = self ._resize_image (image , (scaled_size , scaled_size ))
@@ -763,6 +756,44 @@ def _render(
763756
764757 return image
765758
759+ @staticmethod
760+ def _find_preview (filepath : Path ) -> type [BasePreview ] | None :
761+ """Return the highest priority preview renderer for a file's extension, if any."""
762+ ext = MediaTypes .get_ext (filepath ) if filepath .suffix else filepath .stem .lower ()
763+ for preview in FileRenderer ._preview_renderers :
764+ media_type : MediaTypeGroup | None = getattr (MediaTypes , preview .media_type_name , None )
765+ if media_type is None :
766+ logger .error (
767+ f"[FileRenderer] "
768+ f"Attribute '{ preview .media_type_name } ' not registered with MediaTypes" ,
769+ )
770+ return None
771+
772+ if media_type .contains (ext , RENDER ):
773+ return preview
774+
775+ return None
776+
777+ @staticmethod
778+ def _render_decompressed (
779+ filepath : Path , is_small : bool , theme : Theme , size : tuple [int , int ], dpi_scale : float
780+ ) -> Image .Image | None :
781+ """Render a compressed file with the preview renderer for the uncompressed file."""
782+ preview = FileRenderer ._find_preview (Path (filepath .stem ))
783+ if preview is None :
784+ return None
785+
786+ with decompressed (filepath ) as inner_path :
787+ if inner_path is None :
788+ return None
789+
790+ image = preview .render (
791+ filepath = inner_path , is_small = is_small , theme = theme , size = size , dpi_scale = dpi_scale
792+ )
793+ if image :
794+ image .load () # Read the image data before the temporary file is deleted
795+ return image
796+
766797 def _resize_image (self , image : Image .Image , size : tuple [int , int ]) -> Image .Image :
767798 orig_x , orig_y = image .size
768799 new_x , new_y = size
0 commit comments