mirror of
https://git.srhtnin.garden/sarah/SaraTune.git
synced 2026-10-13 18:18:11 +02:00
Initial commit
This commit is contained in:
commit
0eeff23589
25 files changed
+3328
No files matched your search
@@ -0,0 +1,58 @@
|
||||
"""Small versions of cover art. Embedded covers can be several megabytes,
|
||||
and everything shown goes through Flet's connection, so the album grid and
|
||||
play bar get thumbnails, cached in ~/.cache/SaraSuite/SaraTune/Covers/."""
|
||||
|
||||
import hashlib
|
||||
import io
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
|
||||
from Core import CacheDir
|
||||
from Metadata import ReadCover
|
||||
|
||||
CoversDir = CacheDir / "Covers"
|
||||
_NoCover = b"" # remembered in the memory cache so a coverless album isn't read again
|
||||
|
||||
|
||||
def Thumbnail(Data: bytes, Size: int) -> Optional[bytes]:
|
||||
try:
|
||||
with Image.open(io.BytesIO(Data)) as Picture:
|
||||
Picture = Picture.convert("RGB")
|
||||
Picture.thumbnail((Size, Size), Image.LANCZOS)
|
||||
Output = io.BytesIO()
|
||||
Picture.save(Output, "JPEG", quality=88)
|
||||
return Output.getvalue()
|
||||
except (UnidentifiedImageError, OSError, ValueError, Image.DecompressionBombError):
|
||||
return None
|
||||
|
||||
|
||||
_Memory: dict[tuple[str, int], bytes] = {}
|
||||
|
||||
|
||||
def CoverFor(FilePath: str, Size: int) -> Optional[bytes]:
|
||||
"""FilePath's cover at most Size pixels wide. Blocking (run it in a
|
||||
thread). Cached on disk by path, size and the file's mtime."""
|
||||
Key = (FilePath, Size)
|
||||
if Key in _Memory:
|
||||
return _Memory[Key] or None
|
||||
try:
|
||||
Stamp = Path(FilePath).stat().st_mtime_ns
|
||||
except OSError:
|
||||
return None
|
||||
Name = hashlib.sha1(f"{FilePath}|{Size}|{Stamp}".encode("utf-8", "surrogateescape")).hexdigest()
|
||||
CachePath = CoversDir / f"{Name}.jpg"
|
||||
if CachePath.exists():
|
||||
Data = CachePath.read_bytes()
|
||||
else:
|
||||
Original = ReadCover(FilePath)
|
||||
Data = Thumbnail(Original, Size) if Original else None
|
||||
if Data:
|
||||
try:
|
||||
CoversDir.mkdir(parents=True, exist_ok=True)
|
||||
CachePath.write_bytes(Data)
|
||||
except OSError:
|
||||
pass
|
||||
_Memory[Key] = Data or _NoCover
|
||||
return Data
|
||||
Reference in new issue
Block a user