feat: Dedup enhancements (streaming, retry, filename mode, scroll, auto-select)
Iterazioni sul feature Dedup (v1.10.0) da subito-post-scaffolding: - compute_fingerprint espone il vero errore invece del generico None, worker propaga l'err_msg via progress_callback esteso - Retry automatico: -length 30 su "invalid data / decoding frame", -length 60 + -algorithm 1 su "fingerprint vuoto" - Nuovo metodo "filename" (Jaccard sui token nome file) con algoritmo incrementale O(N·K) — utile per file corrotti o per anteprima veloce - Streaming groups: sia fingerprint che filename emettono `dedup:group` appena un gruppo raggiunge >=2 file; JS accumula in Map per update in-place. Progress bar avanza in tempo reale - Fix shape bug che dava "NaN duplicati" nella summary line - .dedup-groups-scroll: max-height 55vh + overflow-y auto per non perdere l'header/footer scrollando molti gruppi - Bottone "Seleziona tutti i consigliati" nel footer: ripristina la selezione di default (tutti tranne il TIENI marcati per cancellazione) - .gitignore: dedup_cache.db (SQLite locale per macchina utente)
This commit is contained in:
1 parent
00ff5a6220
commit
e7d11d0235
8 files changed
+315
-47
No files matched your search
+10
-7
@@ -160,12 +160,13 @@ class TestMoveToTrash:
|
||||
# compute_fingerprint
|
||||
# ------------------------------------------------------------------
|
||||
class TestComputeFingerprint:
|
||||
def test_timeout_returns_none(self):
|
||||
"""Timeout di fpcalc -> None (non alza eccezione)."""
|
||||
def test_timeout_returns_error(self):
|
||||
"""Timeout di fpcalc -> dict con _error, no fingerprint."""
|
||||
with mock.patch("core.dedup.subprocess.run",
|
||||
side_effect=subprocess.TimeoutExpired(cmd="fpcalc", timeout=30)):
|
||||
result = dedup.compute_fingerprint("/fake/fpcalc", "/some/file.mp3")
|
||||
assert result is None
|
||||
assert result and "_error" in result and "timeout" in result["_error"]
|
||||
assert "fingerprint" not in result
|
||||
|
||||
def test_success_returns_dict(self):
|
||||
"""Output JSON valido -> {duration, fingerprint}."""
|
||||
@@ -176,15 +177,17 @@ class TestComputeFingerprint:
|
||||
result = dedup.compute_fingerprint("/fake/fpcalc", "/some/file.mp3")
|
||||
assert result == {"duration": 123.4, "fingerprint": "ABCDEF"}
|
||||
|
||||
def test_bad_json_returns_none(self):
|
||||
def test_bad_json_returns_error(self):
|
||||
fake_proc = mock.Mock()
|
||||
fake_proc.returncode = 0
|
||||
fake_proc.stdout = "not json at all"
|
||||
with mock.patch("core.dedup.subprocess.run", return_value=fake_proc):
|
||||
result = dedup.compute_fingerprint("/fake/fpcalc", "/some/file.mp3")
|
||||
assert result is None
|
||||
assert result and "_error" in result and "JSON" in result["_error"]
|
||||
assert "fingerprint" not in result
|
||||
|
||||
def test_missing_fpcalc_returns_none(self):
|
||||
def test_missing_fpcalc_returns_error(self):
|
||||
# Nessuna chiamata subprocess se fpcalc e' vuoto
|
||||
result = dedup.compute_fingerprint("", "/some/file.mp3")
|
||||
assert result is None
|
||||
assert result and "_error" in result and "fpcalc" in result["_error"]
|
||||
assert "fingerprint" not in result
|
||||
Reference in new issue
Block a user