fix: review mode playback line, model restore dedup, auto-rescan on rollback
- Show bright green playback position line in review mode - Model history button next to scan model dropdown - Skip backup on restore if identical timestamped copy already exists - Auto-rescan when restoring a model version Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+15
-4
@@ -489,16 +489,27 @@ def list_model_versions(profile_name: str = "default",
|
|||||||
def restore_model_version(version_path: str, profile_name: str = "default",
|
def restore_model_version(version_path: str, profile_name: str = "default",
|
||||||
embed_model: str | None = None) -> None:
|
embed_model: str | None = None) -> None:
|
||||||
"""Restore a backup version as the active model."""
|
"""Restore a backup version as the active model."""
|
||||||
import shutil
|
import filecmp, shutil
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
current = default_model_path(profile_name, embed_model)
|
current = default_model_path(profile_name, embed_model)
|
||||||
if version_path == current:
|
if version_path == current:
|
||||||
return
|
return
|
||||||
# Back up current before replacing
|
# Back up current before replacing — but only if no identical backup exists
|
||||||
if os.path.exists(current):
|
if os.path.exists(current):
|
||||||
stem, ext = os.path.splitext(current)
|
stem, ext = os.path.splitext(current)
|
||||||
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
already_saved = False
|
||||||
shutil.move(current, f"{stem}_{ts}{ext}")
|
if os.path.isdir(_MODEL_DIR):
|
||||||
|
import re
|
||||||
|
pat = re.compile(re.escape(os.path.basename(stem)) + r"_\d{8}_\d{6}" + re.escape(ext) + "$")
|
||||||
|
for fname in os.listdir(_MODEL_DIR):
|
||||||
|
if pat.match(fname):
|
||||||
|
candidate = os.path.join(_MODEL_DIR, fname)
|
||||||
|
if filecmp.cmp(current, candidate, shallow=False):
|
||||||
|
already_saved = True
|
||||||
|
break
|
||||||
|
if not already_saved:
|
||||||
|
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
shutil.move(current, f"{stem}_{ts}{ext}")
|
||||||
shutil.copy2(version_path, current)
|
shutil.copy2(version_path, current)
|
||||||
_log(f"audio_scan: restored {os.path.basename(version_path)} as active model")
|
_log(f"audio_scan: restored {os.path.basename(version_path)} as active model")
|
||||||
|
|
||||||
|
|||||||
@@ -1218,10 +1218,16 @@ class TimelineWidget(QWidget):
|
|||||||
p.drawText(mx + 1, rh + 2, 13, 12,
|
p.drawText(mx + 1, rh + 2, 13, 12,
|
||||||
Qt.AlignmentFlag.AlignCenter, str(num))
|
Qt.AlignmentFlag.AlignCenter, str(num))
|
||||||
|
|
||||||
# ── scan mode cursor line ─────────────────────────────────────
|
# ── scan mode cursor + playback line ─────────────────────────
|
||||||
if self._scan_mode:
|
if self._scan_mode:
|
||||||
p.setPen(QPen(QColor(255, 255, 255, 200), 2))
|
# Export cursor (dim)
|
||||||
|
p.setPen(QPen(QColor(255, 255, 255, 80), 1))
|
||||||
p.drawLine(x_start, rh, x_start, h)
|
p.drawLine(x_start, rh, x_start, h)
|
||||||
|
# Playback position (bright green)
|
||||||
|
if self._play_pos is not None and self._play_pos >= 0:
|
||||||
|
px = int(self._play_pos / self._duration * w)
|
||||||
|
p.setPen(QPen(QColor(80, 255, 80, 220), 2))
|
||||||
|
p.drawLine(px, rh, px, h)
|
||||||
|
|
||||||
# ── crop keyframe diamonds ────────────────────────────────────
|
# ── crop keyframe diamonds ────────────────────────────────────
|
||||||
if self._crop_keyframes and self._duration > 0:
|
if self._crop_keyframes and self._duration > 0:
|
||||||
@@ -2429,10 +2435,16 @@ class MainWindow(QMainWindow):
|
|||||||
self._scan_all_queue: list[str] = []
|
self._scan_all_queue: list[str] = []
|
||||||
|
|
||||||
self._cmb_scan_model = QComboBox()
|
self._cmb_scan_model = QComboBox()
|
||||||
self._cmb_scan_model.setToolTip("Trained embedding model to use for scanning\nRight-click to rollback to a previous version")
|
self._cmb_scan_model.setToolTip("Trained embedding model to use for scanning")
|
||||||
self._cmb_scan_model.setMinimumWidth(120)
|
self._cmb_scan_model.setMinimumWidth(120)
|
||||||
self._cmb_scan_model.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
self._cmb_scan_model.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||||
self._cmb_scan_model.customContextMenuRequested.connect(self._show_model_versions_menu)
|
self._cmb_scan_model.customContextMenuRequested.connect(self._show_model_versions_menu)
|
||||||
|
self._btn_model_history = QPushButton("\u23f2")
|
||||||
|
self._btn_model_history.setFixedWidth(28)
|
||||||
|
self._btn_model_history.setToolTip("Rollback to a previous model version")
|
||||||
|
self._btn_model_history.clicked.connect(
|
||||||
|
lambda: self._show_model_versions_menu(None)
|
||||||
|
)
|
||||||
|
|
||||||
self._spn_auto_fuse = QDoubleSpinBox()
|
self._spn_auto_fuse = QDoubleSpinBox()
|
||||||
self._spn_auto_fuse.setDecimals(1)
|
self._spn_auto_fuse.setDecimals(1)
|
||||||
@@ -2591,6 +2603,7 @@ class MainWindow(QMainWindow):
|
|||||||
settings_row.addWidget(self._chk_rand_square)
|
settings_row.addWidget(self._chk_rand_square)
|
||||||
settings_row.addWidget(self._chk_track)
|
settings_row.addWidget(self._chk_track)
|
||||||
settings_row.addWidget(self._cmb_scan_model)
|
settings_row.addWidget(self._cmb_scan_model)
|
||||||
|
settings_row.addWidget(self._btn_model_history)
|
||||||
settings_row.addWidget(self._btn_scan)
|
settings_row.addWidget(self._btn_scan)
|
||||||
settings_row.addWidget(self._btn_scan_mode)
|
settings_row.addWidget(self._btn_scan_mode)
|
||||||
settings_row.addWidget(self._btn_auto_export)
|
settings_row.addWidget(self._btn_auto_export)
|
||||||
@@ -3480,10 +3493,13 @@ class MainWindow(QMainWindow):
|
|||||||
display = f"{label[:4]}-{label[4:6]}-{label[6:8]} {label[9:11]}:{label[11:13]}"
|
display = f"{label[:4]}-{label[4:6]}-{label[6:8]} {label[9:11]}:{label[11:13]}"
|
||||||
act = menu.addAction(f"Restore {display}")
|
act = menu.addAction(f"Restore {display}")
|
||||||
act.setData(path)
|
act.setData(path)
|
||||||
chosen = menu.exec(self._cmb_scan_model.mapToGlobal(pos))
|
global_pos = (self._btn_model_history.mapToGlobal(self._btn_model_history.rect().bottomLeft())
|
||||||
|
if pos is None
|
||||||
|
else self._cmb_scan_model.mapToGlobal(pos))
|
||||||
|
chosen = menu.exec(global_pos)
|
||||||
if chosen and chosen.data():
|
if chosen and chosen.data():
|
||||||
restore_model_version(chosen.data(), self._profile, embed_name)
|
restore_model_version(chosen.data(), self._profile, embed_name)
|
||||||
self._show_status(f"Restored model version — rescan to use it")
|
self._start_scan()
|
||||||
|
|
||||||
def _cleanup_scan_worker(self) -> None:
|
def _cleanup_scan_worker(self) -> None:
|
||||||
"""Disconnect signals, cancel, and schedule deletion of old scan worker."""
|
"""Disconnect signals, cancel, and schedule deletion of old scan worker."""
|
||||||
|
|||||||
Reference in New Issue
Block a user