feat: move analysis cache to recordings/analyses/, prune orphans on startup
- Cache files now live in recordings/analyses/<filename>.analysis.json (mirroring the relative path for files in subdirectories) rather than alongside each audio file. - _api_delete now removes the corresponding cache file after deleting audio. - prune_orphan_analyses() runs at startup and removes any .analysis.json whose audio file no longer exists (handles files deleted outside the UI). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -203,6 +203,33 @@ def analyze_flac(path: Path, window_samples: int = WINDOW_SAMPLES,
|
||||
return _package_result(rms_values, framerate, n_frames, window_samples, threshold, min_gap)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Analysis cache helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _analysis_cache_path(base: Path, audio_path: Path) -> Path:
|
||||
rel = audio_path.relative_to(base)
|
||||
return base / 'analyses' / rel.parent / (rel.name + '.analysis.json')
|
||||
|
||||
|
||||
def prune_orphan_analyses(base: Path):
|
||||
analyses_dir = base / 'analyses'
|
||||
if not analyses_dir.exists():
|
||||
return
|
||||
removed = 0
|
||||
for cache in analyses_dir.rglob('*.analysis.json'):
|
||||
rel = cache.relative_to(analyses_dir)
|
||||
audio_path = base / rel.parent / rel.name[:-len('.analysis.json')]
|
||||
if not audio_path.exists():
|
||||
try:
|
||||
cache.unlink()
|
||||
removed += 1
|
||||
except Exception:
|
||||
pass
|
||||
if removed:
|
||||
print(f'Pruned {removed} orphaned analysis cache file(s)')
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# File listing
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -331,7 +358,8 @@ class _Handler(BaseHTTPRequestHandler):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
cache_path = path.parent / (path.name + '.analysis.json')
|
||||
base = Path(self.recordings_dir).resolve()
|
||||
cache_path = _analysis_cache_path(base, path)
|
||||
try:
|
||||
cached = json.loads(cache_path.read_text('utf-8'))
|
||||
if cached.get('threshold') == threshold and cached.get('min_gap') == min_gap:
|
||||
@@ -353,6 +381,7 @@ class _Handler(BaseHTTPRequestHandler):
|
||||
return
|
||||
|
||||
try:
|
||||
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp = cache_path.with_suffix('.tmp')
|
||||
tmp.write_text(json.dumps({'threshold': threshold, 'min_gap': min_gap, 'result': result}), 'utf-8')
|
||||
os.replace(tmp, cache_path)
|
||||
@@ -481,6 +510,11 @@ class _Handler(BaseHTTPRequestHandler):
|
||||
self._json_err(500, f'Failed to delete: {e}')
|
||||
return
|
||||
|
||||
try:
|
||||
_analysis_cache_path(Path(self.recordings_dir).resolve(), path).unlink()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._send(200, json.dumps({'deleted': filename}).encode(), 'application/json')
|
||||
|
||||
def _api_cut(self, qs):
|
||||
@@ -1567,6 +1601,8 @@ def main():
|
||||
if not rec_dir.exists():
|
||||
print(f"Warning: recordings directory '{args.dir}' does not exist yet.")
|
||||
|
||||
prune_orphan_analyses(rec_dir.resolve())
|
||||
|
||||
class Handler(_Handler):
|
||||
recordings_dir = str(rec_dir.resolve())
|
||||
threshold = args.threshold
|
||||
|
||||
Reference in New Issue
Block a user