mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-01-28 22:01:24 +00:00
feat: render .clip thumbnails. (#1150)
* feat: render .clip thumbnails. * doc: document .clip support. * fix: add CLIP_STUDIO_PAINT_TYPES to ALL_CATEGORIES. * explicitly close connection.
This commit is contained in:
@@ -79,8 +79,9 @@ Audio thumbnails will default to embedded cover art (if any) and fallback to gen
|
||||
Preview support for office documents or well-known project file formats varies by the format and whether or not embedded thumbnails are available to be read from. OpenDocument-based files are typically supported.
|
||||
|
||||
| Filetype | Extensions | Preview Type |
|
||||
| ------------------------------------ | --------------------- | -------------------------------------------------------------------------- |
|
||||
|--------------------------------------| --------------------- | -------------------------------------------------------------------------- |
|
||||
| Blender | `.blend`, `.blend<#>` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
|
||||
| Clip Studio Paint | `.clip` | Embedded thumbnail |
|
||||
| Keynote (Apple iWork) | `.key` | Embedded thumbnail |
|
||||
| Krita[^3] | `.kra`, `.krz` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
|
||||
| Mdipack (FireAlpaca, Medibang Paint) | `.mdp` | Embedded thumbnail |
|
||||
|
||||
@@ -33,6 +33,7 @@ class MediaType(str, Enum):
|
||||
AUDIO_MIDI = "audio_midi"
|
||||
AUDIO = "audio"
|
||||
BLENDER = "blender"
|
||||
CLIP_STUDIO_PAINT = "clip_studio_paint"
|
||||
CODE = "code"
|
||||
DATABASE = "database"
|
||||
DISK_IMAGE = "disk_image"
|
||||
@@ -177,6 +178,7 @@ class MediaCategories:
|
||||
".blend31",
|
||||
".blend32",
|
||||
}
|
||||
_CLIP_STUDIO_PAINT_SET: set[str] = {".clip"}
|
||||
_CODE_SET: set[str] = {
|
||||
".bat",
|
||||
".cfg",
|
||||
@@ -456,6 +458,12 @@ class MediaCategories:
|
||||
is_iana=False,
|
||||
name="blender",
|
||||
)
|
||||
CLIP_STUDIO_PAINT_TYPES = MediaCategory(
|
||||
media_type=MediaType.CLIP_STUDIO_PAINT,
|
||||
extensions=_CLIP_STUDIO_PAINT_SET,
|
||||
is_iana=False,
|
||||
name="clip studio paint",
|
||||
)
|
||||
CODE_TYPES = MediaCategory(
|
||||
media_type=MediaType.CODE,
|
||||
extensions=_CODE_SET,
|
||||
@@ -644,6 +652,7 @@ class MediaCategories:
|
||||
AUDIO_MIDI_TYPES,
|
||||
AUDIO_TYPES,
|
||||
BLENDER_TYPES,
|
||||
CLIP_STUDIO_PAINT_TYPES,
|
||||
DATABASE_TYPES,
|
||||
DISK_IMAGE_TYPES,
|
||||
DOCUMENT_TYPES,
|
||||
|
||||
@@ -8,6 +8,7 @@ import contextlib
|
||||
import hashlib
|
||||
import math
|
||||
import os
|
||||
import sqlite3
|
||||
import struct
|
||||
import tarfile
|
||||
import xml.etree.ElementTree as ET
|
||||
@@ -1458,6 +1459,35 @@ class ThumbRenderer(QObject):
|
||||
|
||||
return im
|
||||
|
||||
@staticmethod
|
||||
def _clip_thumb(filepath: Path) -> Image.Image | None:
|
||||
"""Extract the thumbnail from the SQLite database embedded in a .clip file.
|
||||
|
||||
Args:
|
||||
filepath (Path): The path of the .clip file.
|
||||
|
||||
Returns:
|
||||
Image: The embedded thumbnail, if extractable.
|
||||
"""
|
||||
im: Image.Image | None = None
|
||||
try:
|
||||
with open(filepath, "rb") as f:
|
||||
blob = f.read()
|
||||
sqlite_index = blob.find(b"SQLite format 3")
|
||||
if sqlite_index == -1:
|
||||
return im
|
||||
|
||||
with sqlite3.connect(":memory:") as conn:
|
||||
conn.deserialize(blob[sqlite_index:])
|
||||
thumbnail = conn.execute("SELECT ImageData FROM CanvasPreview").fetchone()
|
||||
if thumbnail:
|
||||
im = Image.open(BytesIO(thumbnail[0]))
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
logger.error("Couldn't render thumbnail", filepath=filepath, error=type(e).__name__)
|
||||
|
||||
return im
|
||||
|
||||
def render(
|
||||
self,
|
||||
timestamp: float,
|
||||
@@ -1708,6 +1738,11 @@ class ThumbRenderer(QObject):
|
||||
ext, MediaCategories.KRITA_TYPES, mime_fallback=True
|
||||
):
|
||||
image = self._krita_thumb(_filepath)
|
||||
# Clip Studio Paint ============================================
|
||||
elif MediaCategories.is_ext_in_category(
|
||||
ext, MediaCategories.CLIP_STUDIO_PAINT_TYPES
|
||||
):
|
||||
image = self._clip_thumb(_filepath)
|
||||
# VTF ==========================================================
|
||||
elif MediaCategories.is_ext_in_category(
|
||||
ext, MediaCategories.SOURCE_ENGINE_TYPES, mime_fallback=True
|
||||
|
||||
Reference in New Issue
Block a user