fix: 6 bugs — profile isolation, export stashing, auto-negative guard
- Stash profile and crop_center at export start for async safety - Scope get_group/delete_group by profile to prevent cross-profile leaks - Guard auto-negative sampling when no markers exist (prevents flood) - Wrap ffmpeg subprocess with clean timeout error message - Fix scan-all panel reload to use stashed profile, not live value - Remove dead warnings import Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+9
-6
@@ -3,7 +3,6 @@
|
||||
import hashlib
|
||||
import os
|
||||
import subprocess
|
||||
import warnings
|
||||
import numpy as np
|
||||
|
||||
from .paths import _bin, _log
|
||||
@@ -22,7 +21,10 @@ def _load_audio_ffmpeg(path: str, sr: int = _SR) -> np.ndarray:
|
||||
"-loglevel", "error",
|
||||
"pipe:1",
|
||||
]
|
||||
proc = subprocess.run(cmd, capture_output=True, timeout=300)
|
||||
try:
|
||||
proc = subprocess.run(cmd, capture_output=True, timeout=300)
|
||||
except subprocess.TimeoutExpired:
|
||||
raise RuntimeError(f"ffmpeg timed out (300s) on {os.path.basename(path)}")
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(f"ffmpeg failed: {proc.stderr.decode().strip()}")
|
||||
return np.frombuffer(proc.stdout, dtype=np.float32)
|
||||
@@ -240,11 +242,12 @@ def _extract_w2v_targeted(y: np.ndarray, sr: int, gt_intense: list[float],
|
||||
# Don't let manual negatives overlap with positives
|
||||
manual_neg_times -= pos_times
|
||||
|
||||
# Auto negative windows: every 4s, far from any marker (skip if margin <= 0)
|
||||
# Auto negative windows: every 4s, far from any marker (skip if margin <= 0 or no markers)
|
||||
neg_times = set()
|
||||
for t in range(0, int(duration - _WINDOW), 4):
|
||||
if neg_margin > 0 and min((abs(t - g) for g in all_gt), default=9999) > neg_margin:
|
||||
neg_times.add(t)
|
||||
if all_gt and neg_margin > 0:
|
||||
for t in range(0, int(duration - _WINDOW), 4):
|
||||
if min(abs(t - g) for g in all_gt) > neg_margin:
|
||||
neg_times.add(t)
|
||||
|
||||
all_times = sorted(pos_times | neg_times | manual_neg_times)
|
||||
# Filter out windows that go past the end
|
||||
|
||||
+17
-13
@@ -159,43 +159,47 @@ class ProcessedDB:
|
||||
self._con.execute("DELETE FROM processed WHERE output_path = ?", (output_path,))
|
||||
self._con.commit()
|
||||
|
||||
def get_group(self, output_path: str) -> list[str]:
|
||||
"""Return all output_paths sharing the same (filename, start_time) as *output_path*."""
|
||||
def get_group(self, output_path: str, profile: str = "") -> list[str]:
|
||||
"""Return all output_paths sharing the same (filename, start_time, profile) as *output_path*."""
|
||||
if not self._enabled:
|
||||
return []
|
||||
row = self._con.execute(
|
||||
"SELECT filename, start_time FROM processed WHERE output_path = ?",
|
||||
"SELECT filename, start_time, profile FROM processed WHERE output_path = ?",
|
||||
(output_path,),
|
||||
).fetchone()
|
||||
if not row:
|
||||
return []
|
||||
filename, start_time, row_profile = row
|
||||
p = profile or row_profile
|
||||
rows = self._con.execute(
|
||||
"SELECT output_path FROM processed"
|
||||
" WHERE filename = ? AND start_time = ? ORDER BY output_path",
|
||||
(row[0], row[1]),
|
||||
" WHERE filename = ? AND start_time = ? AND profile = ? ORDER BY output_path",
|
||||
(filename, start_time, p),
|
||||
).fetchall()
|
||||
return [r[0] for r in rows]
|
||||
|
||||
def delete_group(self, output_path: str) -> list[str]:
|
||||
"""Delete all rows sharing the same (filename, start_time) as *output_path*.
|
||||
def delete_group(self, output_path: str, profile: str = "") -> list[str]:
|
||||
"""Delete all rows sharing the same (filename, start_time, profile) as *output_path*.
|
||||
Returns list of deleted output_paths."""
|
||||
if not self._enabled:
|
||||
return []
|
||||
with self._lock:
|
||||
row = self._con.execute(
|
||||
"SELECT filename, start_time FROM processed WHERE output_path = ?",
|
||||
"SELECT filename, start_time, profile FROM processed WHERE output_path = ?",
|
||||
(output_path,),
|
||||
).fetchone()
|
||||
if not row:
|
||||
return []
|
||||
filename, start_time = row
|
||||
filename, start_time, row_profile = row
|
||||
p = profile or row_profile
|
||||
paths = [r[0] for r in self._con.execute(
|
||||
"SELECT output_path FROM processed WHERE filename = ? AND start_time = ?",
|
||||
(filename, start_time),
|
||||
"SELECT output_path FROM processed"
|
||||
" WHERE filename = ? AND start_time = ? AND profile = ?",
|
||||
(filename, start_time, p),
|
||||
).fetchall()]
|
||||
self._con.execute(
|
||||
"DELETE FROM processed WHERE filename = ? AND start_time = ?",
|
||||
(filename, start_time),
|
||||
"DELETE FROM processed WHERE filename = ? AND start_time = ? AND profile = ?",
|
||||
(filename, start_time, p),
|
||||
)
|
||||
self._con.commit()
|
||||
return paths
|
||||
|
||||
Reference in New Issue
Block a user