Files
ComicOPDS/app/fs_index.py
2025-09-04 21:12:06 +02:00

101 lines
2.8 KiB
Python

from dataclasses import dataclass, field
from pathlib import Path
from typing import List, Iterable, Dict, Any
import json, os
from .meta import read_comicinfo
COMIC_EXTS = {".cbz"}
@dataclass
class Item:
path: Path
is_dir: bool
name: str
rel: str
size: int = 0
mtime: float = 0.0
meta: Dict[str, Any] = field(default_factory=dict)
def _cache_file() -> Path:
return Path("/data/index.json")
def load_cache() -> Dict[str, dict]:
p = _cache_file()
if p.exists():
try:
return json.loads(p.read_text(encoding="utf-8"))
except Exception:
return {}
return {}
def save_cache(items: List[Item]):
p = _cache_file()
data = {
it.rel: {
"is_dir": it.is_dir,
"name": it.name,
"size": it.size,
"mtime": it.mtime,
"meta": it.meta
}
for it in items
}
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(json.dumps(data), encoding="utf-8")
def scan(root: Path) -> List[Item]:
root = root.resolve()
cached = load_cache()
items: List[Item] = []
items.append(Item(path=root, is_dir=True, name=root.name or "/", rel=""))
for dirpath, _, filenames in os.walk(root):
base = Path(dirpath)
rel_dir = str(base.relative_to(root)).replace("\\", "/")
if rel_dir != ".":
items.append(Item(path=base, is_dir=True, name=base.name, rel=rel_dir))
for fn in filenames:
p = base / fn
ext = p.suffix.lower()
if ext not in COMIC_EXTS:
continue
rel = str(p.relative_to(root)).replace("\\", "/")
try:
st = p.stat()
except FileNotFoundError:
continue
size, mtime = st.st_size, st.st_mtime
c = cached.get(rel)
meta, name = {}, p.stem
if c and not c.get("is_dir") and c.get("mtime") == mtime and c.get("size") == size:
name = c.get("name") or name
meta = c.get("meta") or {}
else:
meta = read_comicinfo(p)
if meta.get("title"):
name = meta["title"]
items.append(Item(path=p, is_dir=False, name=name, rel=rel, size=size, mtime=mtime, meta=meta))
save_cache(items)
return items
def children(items: List[Item], rel_folder: str) -> Iterable[Item]:
prefix = (rel_folder.strip("/") + "/") if rel_folder else ""
depth = 0 if rel_folder == "" else rel_folder.count("/") + 1
for it in items:
if not it.rel.startswith(prefix):
continue
if it.rel == rel_folder:
continue
if it.is_dir and it.rel.count("/") == depth:
yield it
elif not it.is_dir and it.rel.count("/") == depth:
yield it