Skip to content

Commit 341aa92

Browse files
feat(ui): preview support for source engine files
* Fix text and RAW image handling - Fix RAW images not being loaded correctly in the preview panel - Fix trying to read size data from null images - Refactor `os.stat` to `<Path object>.stat()` - Remove unnecessary upper/lower conversions - Improve encoding compatibility beyond UTF-8 when reading text files - Code cleanup * Use chardet for character encoding detection * Add support for waveform + album cover thumbnails * Rename "cover" variables for MyPy * Rename "audio_tags" variables for MyPy + typing * Add # type: ignore to fromstring method * Add GIF preview support * Add rough check for invalid video codecs * Add ".plist" to PLAINTEXT_TYPES * Add readable video tester * Add ".psd" to IMAGE_TYPES; Handle ID3NoHeaderError * Improve and style waveform previews * Add final return statement to _album_artwork() * Add final return statement to _audio_waveform() * Tweak waveform color and size * Fix ItemThumb label text color in light mode * Fix most theme UI legibility issues * Match additional UI to color scheme * ruff format * feat(ui): add UI color palette dict * feat(ui) center and color small font previews * fix(ui): large font previews follow app theme * fix(ui): blender previews follow app theme * feat(ui): add resizable thumbnail options * fix: mkv files with "[0][0][0][0]" codec load properly * fix: missing audio files properly handled * feat(ui): use system accent color for thumb selections * fix(ui): hide gif preview in multi-selections * feat(ui): add dynamic file thumb icons * fix(ui): hide previous thumbnail before resizing * (fix): catch ffmpeg errors in file tester * Squashed commit of the following: commit 9a3c19d Author: Travis Abendshien <[email protected]> Date: Wed Jul 24 22:57:32 2024 -0700 fix: add missing comma + sort extensions commit 53b2db9 Author: Travis Abendshien <[email protected]> Date: Wed Jul 24 14:46:16 2024 -0700 refactor: move type constants to new media classes * feat(ui): add media types and icon resources * feat(ui): add more default media types and icons Add additional default icons for: - Blender - Presentation - Program - Spreadsheet Add/expand additional media types: - PDF - Packages * fix: remove leading dot in preview panel ext * refactor: remove edge from `four_corner_gradient()` * fix: handle missing files in `resource_manager` * fix(ui): thumb edges fading on refresh * feat(ui): add default icons for audio+vector thumbs * feat(ui): apply edge to default icon thumbs * chore: remove unused code * refactor(ui): move loading icon to `ResourceManager` * added support for Source 1 + 2 file thumbnails All plaintext variants and VTF file conversions. * Added render code for VTF files Added support for VTF files by using the vtf2img library for PIL * Add files via upload * fixed Ruff errors * fixed Ruff errors * chore: organize imports, lists; ruff formatting * Change references of Source to Source Engine * Added error handler + changed source to source engine * add struct to import and docstring for source_engine * complying to the demigod ruff * chore: format with ruff --------- Co-authored-by: Travis Abendshien <[email protected]>
1 parent dcb8ded commit 341aa92

File tree

5 files changed

+68
-6
lines changed

5 files changed

+68
-6
lines changed

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ pydub==0.25.1
1414
mutagen==1.47.0
1515
numpy==1.26.4
1616
ffmpeg-python==0.2.0
17+
vtf2img==0.1.0

tagstudio/src/core/media_types.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class MediaType(str, Enum):
3535
PRESENTATION: str = "presentation"
3636
PROGRAM: str = "program"
3737
SHORTCUT: str = "shortcut"
38+
SOURCE_ENGINE: str = "source_engine"
3839
SPREADSHEET: str = "spreadsheet"
3940
TEXT: str = "text"
4041
VIDEO: str = "video"
@@ -236,6 +237,20 @@ class MediaCategories:
236237
".ts",
237238
".txt",
238239
".xml",
240+
".vmt",
241+
".fgd",
242+
".nut",
243+
".cfg",
244+
".conf",
245+
".vdf",
246+
".vcfg",
247+
".gi",
248+
".inf",
249+
".vqlayout",
250+
".qss",
251+
".vsc",
252+
".kv3",
253+
".vsnd_template",
239254
}
240255
_PRESENTATION_SET: set[str] = {
241256
".key",
@@ -244,6 +259,9 @@ class MediaCategories:
244259
".pptx",
245260
}
246261
_PROGRAM_SET: set[str] = {".app", ".exe"}
262+
_SOURCE_ENGINE_SET: set[str] = {
263+
".vtf",
264+
}
247265
_SHORTCUT_SET: set[str] = {".desktop", ".lnk", ".url"}
248266
_SPREADSHEET_SET: set[str] = {
249267
".csv",
@@ -377,6 +395,11 @@ class MediaCategories:
377395
extensions=_SHORTCUT_SET,
378396
is_iana=False,
379397
)
398+
SOURCE_ENGINE_TYPES: MediaCategory = MediaCategory(
399+
media_type=MediaType.SOURCE_ENGINE,
400+
extensions=_SOURCE_ENGINE_SET,
401+
is_iana=False,
402+
)
380403
SPREADSHEET_TYPES: MediaCategory = MediaCategory(
381404
media_type=MediaType.SPREADSHEET,
382405
extensions=_SPREADSHEET_SET,
@@ -416,6 +439,7 @@ class MediaCategories:
416439
PRESENTATION_TYPES,
417440
PROGRAM_TYPES,
418441
SHORTCUT_TYPES,
442+
SOURCE_ENGINE_TYPES,
419443
SPREADSHEET_TYPES,
420444
TEXT_TYPES,
421445
VIDEO_TYPES,

tagstudio/src/core/palette.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,12 @@ class ColorType(int, Enum):
284284
ColorType.LIGHT_ACCENT: "#FFFFFF",
285285
ColorType.DARK_ACCENT: "#1e1e1e",
286286
},
287+
"red": {
288+
ColorType.PRIMARY: "#e22c3c",
289+
ColorType.BORDER: "#e54252",
290+
ColorType.LIGHT_ACCENT: "#f39caa",
291+
ColorType.DARK_ACCENT: "#440d12",
292+
},
287293
"green": {
288294
ColorType.PRIMARY: "#28bb48",
289295
ColorType.BORDER: "#43c568",
@@ -296,12 +302,6 @@ class ColorType(int, Enum):
296302
ColorType.LIGHT_ACCENT: "#EFD4FB",
297303
ColorType.DARK_ACCENT: "#3E1555",
298304
},
299-
"red": {
300-
ColorType.PRIMARY: "#e22c3c",
301-
ColorType.BORDER: "#e54252",
302-
ColorType.LIGHT_ACCENT: "#f39caa",
303-
ColorType.DARK_ACCENT: "#440d12",
304-
},
305305
"theme_dark": {
306306
ColorType.PRIMARY: "#333333",
307307
ColorType.BORDER: "#555555",

tagstudio/src/qt/widgets/fields.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ def __init__(self, title: str = "Field", inline: bool = True) -> None:
4747
self.edit_icon_128 = theme_fg_overlay(FieldContainer.edit_icon_128)
4848
self.trash_icon_128 = theme_fg_overlay(FieldContainer.trash_icon_128)
4949

50+
self.clipboard_icon_128 = theme_fg_overlay(FieldContainer.clipboard_icon_128)
51+
self.edit_icon_128 = theme_fg_overlay(FieldContainer.edit_icon_128)
52+
self.trash_icon_128 = theme_fg_overlay(FieldContainer.trash_icon_128)
53+
5054
self.root_layout = QVBoxLayout(self)
5155
self.root_layout.setObjectName("baseLayout")
5256
self.root_layout.setContentsMargins(0, 0, 0, 0)

tagstudio/src/qt/widgets/thumb_renderer.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from copy import deepcopy
99
from io import BytesIO
1010
from pathlib import Path
11+
import struct
1112

1213
import cv2
1314
import numpy as np
@@ -39,6 +40,7 @@
3940
from src.qt.helpers.gradient import four_corner_gradient
4041
from src.qt.helpers.text_wrapper import wrap_full_text
4142
from src.qt.resource_manager import ResourceManager
43+
from vtf2img import Parser
4244

4345
ImageFile.LOAD_TRUNCATED_IMAGES = True
4446

@@ -600,6 +602,33 @@ def _blender(self, filepath: Path) -> Image.Image:
600602
)
601603
return im
602604

605+
def _source_engine(self, filepath: Path) -> Image.Image:
606+
"""
607+
This is a function to convert the VTF (Valve Texture Format) files to thumbnails, it works using the VTF2IMG library for PILLOW.
608+
"""
609+
parser = Parser(filepath)
610+
im: Image.Image = None
611+
try:
612+
im = parser.get_image()
613+
614+
except (
615+
AttributeError,
616+
UnidentifiedImageError,
617+
FileNotFoundError,
618+
TypeError,
619+
struct.error,
620+
) as e:
621+
if str(e) == "expected string or buffer":
622+
logging.info(
623+
f"[ThumbRenderer][VTF][INFO] {filepath.name} Doesn't have an embedded thumbnail. ({type(e).__name__})"
624+
)
625+
626+
else:
627+
logging.error(
628+
f"[ThumbRenderer][VTF][ERROR]: Couldn't render thumbnail for {filepath.name} ({type(e).__name__})"
629+
)
630+
return im
631+
603632
def _font_short_thumb(self, filepath: Path, size: int) -> Image.Image:
604633
"""Render a small font preview ("Aa") thumbnail from a font file.
605634
@@ -961,6 +990,10 @@ def render(
961990
elif MediaType.BLENDER in MediaCategories.get_types(ext):
962991
image = self._blender(_filepath)
963992

993+
# VTF ==========================================================
994+
elif MediaType.SOURCE_ENGINE in MediaCategories.get_types(ext):
995+
image = self._source_engine(_filepath)
996+
964997
# No Rendered Thumbnail ========================================
965998
if not image:
966999
raise UnidentifiedImageError

0 commit comments

Comments
 (0)