"""위성 조각 저장본 한도(590-2 · 요구 452 · 453) — 프로젝트당 한 벌 `_derived/plan_tile`. 다시 받을 수 있는 저장본 · 프로젝트당 `limit` 바이트를 넘으면 오래 안 본 것부터 한도 80 % 까지 지움. 크기는 서버 메모리에 두고 프로젝트를 처음 볼 때 한 번만 폴더를 셈(그때 넘쳤으면 바로 지움). 본 때 = 파일 수정시각 · 볼 때 하루 넘게 묵은 것만 새로 찍음(시놀로지 동기 일감을 줄임). """ from __future__ import annotations import os import threading import time from pathlib import Path TOUCH_AFTER_S = 86400 KEEP_RATIO = 0.8 _SIZES: dict[Path, int] = {} _LOCK = threading.Lock() def _tiles(folder: Path) -> list[tuple[float, int, Path]]: """(본 때, 크기, 경로) — 받는 중인 `.part` 는 뺌.""" out = [] for path in folder.rglob("*.jpeg"): try: stat = path.stat() except FileNotFoundError: continue out.append((stat.st_mtime, stat.st_size, path)) return out def seen(path: Path) -> None: """조각을 봄 — 하루 넘게 묵었으면 본 때를 지금으로.""" try: if time.time() - path.stat().st_mtime > TOUCH_AFTER_S: os.utime(path) except OSError: pass def prune(folder: Path, limit: int) -> int: """한도를 넘으면 오래 안 본 것부터 한도 80 % 까지 지움 → 지운 수.""" tiles = sorted(_tiles(folder)) total = sum(size for _, size, _ in tiles) removed = 0 if total > limit: for _, size, path in tiles: if total <= limit * KEEP_RATIO: break path.unlink(missing_ok=True) total -= size removed += 1 _SIZES[folder] = total return removed def note(folder: Path, added: int, limit: int) -> int: """`folder`(프로젝트 조각 뿌리)에 `added` 바이트가 늘었음(0 = 열기만) → 지운 수.""" with _LOCK: if folder not in _SIZES: return prune(folder, limit) _SIZES[folder] += added return prune(folder, limit) if _SIZES[folder] > limit else 0