52 lines
1.7 KiB
Python
52 lines
1.7 KiB
Python
import hashlib
|
|
import re
|
|
import threading
|
|
from pathlib import Path
|
|
|
|
from server.services.http import http
|
|
|
|
CACHE_DIR = Path(__file__).resolve().parents[2] / "cache" / "covers"
|
|
_lock = threading.Lock()
|
|
|
|
|
|
class CoverCache:
|
|
def local_url(self, remote: str) -> str:
|
|
remote = (remote or "").strip()
|
|
if not remote.startswith(("http://", "https://")):
|
|
return remote
|
|
digest = hashlib.sha256(remote.encode("utf-8")).hexdigest()
|
|
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
meta = CACHE_DIR / f"{digest}.url"
|
|
if not meta.is_file():
|
|
meta.write_text(remote, encoding="utf-8")
|
|
return f"/api/covers/{digest}"
|
|
|
|
def read(self, digest: str) -> tuple[bytes, str]:
|
|
if not re.fullmatch(r"[0-9a-f]{64}", digest):
|
|
raise RuntimeError("封面不存在")
|
|
meta = CACHE_DIR / f"{digest}.url"
|
|
body = CACHE_DIR / digest
|
|
if not meta.is_file():
|
|
raise RuntimeError("封面不存在")
|
|
if body.is_file() and body.stat().st_size > 0:
|
|
return body.read_bytes(), _media_type(body)
|
|
remote = meta.read_text(encoding="utf-8").strip()
|
|
data, media = http.get_bytes(remote)
|
|
with _lock:
|
|
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
body.write_bytes(data)
|
|
(CACHE_DIR / f"{digest}.type").write_text(media, encoding="utf-8")
|
|
return data, media
|
|
|
|
|
|
def _media_type(path: Path) -> str:
|
|
sidecar = Path(f"{path}.type")
|
|
if sidecar.is_file():
|
|
media = sidecar.read_text(encoding="utf-8").strip()
|
|
if media.startswith("image/"):
|
|
return media
|
|
return "image/jpeg"
|
|
|
|
|
|
covers = CoverCache()
|