From 07e2f733b97dbd4d213365e94e83e68c23479d95 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Sat, 9 May 2026 13:47:48 +0200 Subject: [PATCH] feat: bulk update source paths in train dialog Add ProcessedDB.update_source_paths() to re-resolve missing or stale source_path entries by matching filenames against a directory listing and the current playlist. Exposed as "Update paths" button in the train dialog next to the video dir field. Co-Authored-By: Claude Opus 4.6 --- core/db.py | 43 +++++++++++++++++++++++++++++++++++++++++++ main.py | 17 +++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/core/db.py b/core/db.py index 74b6477..628f48f 100644 --- a/core/db.py +++ b/core/db.py @@ -263,6 +263,49 @@ class ProcessedDB: ) self._con.commit() + def update_source_paths(self, new_dir: str, + playlist_paths: list[str] | None = None, + profile: str = "") -> int: + """Re-resolve source_path for all rows whose current path is missing. + + Checks *new_dir* and *playlist_paths* by filename match. + Returns the number of rows updated. + """ + if not self._enabled: + return 0 + lookup: dict[str, str] = {} + if playlist_paths: + for p in playlist_paths: + lookup[os.path.basename(p)] = p + if new_dir and os.path.isdir(new_dir): + for f in os.listdir(new_dir): + fp = os.path.join(new_dir, f) + if os.path.isfile(fp): + lookup[f] = fp + if not lookup: + return 0 + query = "SELECT DISTINCT filename, source_path FROM processed" + params: tuple = () + if profile: + query += " WHERE profile = ?" + params = (profile,) + rows = self._con.execute(query, params).fetchall() + updated = 0 + with self._lock: + for fn, sp in rows: + if sp and os.path.exists(sp): + continue + new_path = lookup.get(fn) + if new_path and os.path.isfile(new_path): + self._con.execute( + "UPDATE processed SET source_path = ? WHERE filename = ?", + (new_path, fn), + ) + updated += 1 + if updated: + self._con.commit() + return updated + def get_labels(self) -> list[str]: """Return distinct non-empty labels ordered by most recently used.""" if not self._enabled: diff --git a/main.py b/main.py index be9f9b5..d4036bb 100755 --- a/main.py +++ b/main.py @@ -517,6 +517,12 @@ class TrainDialog(QDialog): btn_browse.setFixedWidth(30) btn_browse.clicked.connect(self._browse_video_dir) vid_row.addWidget(btn_browse) + self._btn_update_paths = QPushButton("Update paths") + self._btn_update_paths.setToolTip( + "Re-resolve missing source_path entries using the video dir and playlist") + self._btn_update_paths.setFixedWidth(90) + self._btn_update_paths.clicked.connect(self._update_source_paths) + vid_row.addWidget(self._btn_update_paths) self._lbl_video_dir = QLabel("Video dir:") self._video_dir_widget = QWidget() self._video_dir_widget.setLayout(vid_row) @@ -558,6 +564,17 @@ class TrainDialog(QDialog): if d: self._txt_video_dir.setText(d) + def _update_source_paths(self): + video_dir = self._txt_video_dir.text() + n = self._db.update_source_paths( + video_dir, playlist_paths=self._playlist_paths, + profile=self._profile) + if n: + self._lbl_stats.setText(f"Updated {n} source path(s)") + self._debounce.start() + else: + self._lbl_stats.setText("No paths to update (all resolved or no matches)") + def _manage_negatives(self): dlg = HardNegativesDialog(self._db, self._profile, parent=self) dlg.exec()