"""Blenderに依存しないSEライブラリの走査・設定処理。""" from __future__ import annotations import json import os from pathlib import Path AUDIO_EXTENSIONS = {".mp3", ".wav", ".flac", ".ogg", ".m4a", ".aac"} CONFIG_FILENAME = ".se_palette.json" def scan_audio_files(folder: str) -> list[Path]: """フォルダ直下の対応音声ファイルを名前順で返す。""" root = Path(folder).expanduser() if not root.is_dir(): return [] return sorted( ( path for path in root.iterdir() if path.is_file() and path.suffix.lower() in AUDIO_EXTENSIONS ), key=lambda path: path.name.casefold(), ) def default_label(path: Path) -> str: """拡張子と管理用の先頭番号を除いた表示名を作る。""" label = path.stem if len(label) > 3 and label[:2].isdigit() and label[2] in {"_", "-", " "}: label = label[3:] return label.replace("_", " ") def load_config(folder: str) -> dict: config_path = Path(folder).expanduser() / CONFIG_FILENAME if not config_path.is_file(): return {"items": {}} try: data = json.loads(config_path.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError): return {"items": {}} if not isinstance(data, dict) or not isinstance(data.get("items", {}), dict): return {"items": {}} return data def save_config(folder: str, items: list[dict]) -> Path: root = Path(folder).expanduser() root.mkdir(parents=True, exist_ok=True) config_path = root / CONFIG_FILENAME payload = { "version": 1, "items": { item["filename"]: { "label": item.get("label", ""), "icon": item.get("icon", "PLAY_SOUND"), "icon_path": item.get("icon_path", ""), } for item in items }, } temporary_path = config_path.with_suffix(config_path.suffix + ".tmp") temporary_path.write_text( json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8", ) os.replace(temporary_path, config_path) return config_path