|
7 | 7 | import hashlib |
8 | 8 | import math |
9 | 9 | import os |
| 10 | +import sqlite3 |
10 | 11 | import tarfile |
11 | 12 | import xml.etree.ElementTree as ET |
12 | 13 | import zipfile |
@@ -1378,6 +1379,34 @@ def _video_thumb(filepath: Path) -> Image.Image | None: |
1378 | 1379 | logger.error("Couldn't render thumbnail", filepath=filepath, error=type(e).__name__) |
1379 | 1380 | return im |
1380 | 1381 |
|
| 1382 | + @staticmethod |
| 1383 | + def _clip_thumb(filepath: Path) -> Image.Image | None: |
| 1384 | + """Extract the thumbnail from the SQLite database embedded in a .clip file. |
| 1385 | +
|
| 1386 | + Args: |
| 1387 | + filepath (Path): The path of the .clip file. |
| 1388 | +
|
| 1389 | + Returns: |
| 1390 | + Image: The embedded thumbnail, if extractable. |
| 1391 | + """ |
| 1392 | + im: Image.Image | None = None |
| 1393 | + try: |
| 1394 | + with open(filepath, "rb") as f: |
| 1395 | + blob = f.read() |
| 1396 | + sqlite_index = blob.find(b"SQLite format 3") |
| 1397 | + if sqlite_index == -1: |
| 1398 | + return im |
| 1399 | + |
| 1400 | + with sqlite3.connect(":memory:") as conn: |
| 1401 | + conn.deserialize(blob[sqlite_index:]) |
| 1402 | + thumbnail = conn.execute("SELECT ImageData FROM CanvasPreview").fetchone() |
| 1403 | + if thumbnail: |
| 1404 | + im = Image.open(BytesIO(thumbnail[0])) |
| 1405 | + except Exception as e: |
| 1406 | + logger.error("Couldn't render thumbnail", filepath=filepath, error=type(e).__name__) |
| 1407 | + |
| 1408 | + return im |
| 1409 | + |
1381 | 1410 | def render( |
1382 | 1411 | self, |
1383 | 1412 | timestamp: float, |
@@ -1628,6 +1657,11 @@ def _render( |
1628 | 1657 | ext, MediaCategories.KRITA_TYPES, mime_fallback=True |
1629 | 1658 | ): |
1630 | 1659 | image = self._krita_thumb(_filepath) |
| 1660 | + # Clip Studio Paint ============================================ |
| 1661 | + elif MediaCategories.is_ext_in_category( |
| 1662 | + ext, MediaCategories.CLIP_STUDIO_PAINT_TYPES |
| 1663 | + ): |
| 1664 | + image = self._clip_thumb(_filepath) |
1631 | 1665 | # VTF ========================================================== |
1632 | 1666 | elif MediaCategories.is_ext_in_category( |
1633 | 1667 | ext, MediaCategories.SOURCE_ENGINE_TYPES, mime_fallback=True |
|
0 commit comments