perf: cut DB scans, timeline repaints, and per-frame allocations
Database: - Enable WAL + synchronous=NORMAL + bigger cache pragmas - Add (profile, filename) index covering the hot queries - _refresh_playlist_checks: one get_clip_counts_grouped() scan for the whole profile instead of one query per file (was O(N) full scans per keystroke/ tab switch/file load) Timeline (60fps playback): - set_play_position only repaints when the playhead moves a whole pixel or the view scrolls (≈30x fewer full repaints in non-zoomed playback) - Cache all per-paint QColor/QPen objects and the other-folder color table in __init__ instead of allocating them every frame; drop the per-paint visible-markers list comprehension File load / startup: - PlaylistWidget stats files for the missing-set only when paths change, not on every filter keystroke - Cache the vid-folder lookup (DB + os.listdir) per (file, folder) so spinner ticks don't repeat it; m-counter still recomputed so it stays correct - Swap the waveform worker without blocking the UI thread (no wait(1000)) - Defer the changelog modal so the window is interactive first Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+36
@@ -24,6 +24,18 @@ class ProcessedDB:
|
||||
self._lock = threading.Lock()
|
||||
try:
|
||||
self._con = sqlite3.connect(db_path, check_same_thread=False)
|
||||
# Performance pragmas: WAL cuts lock contention and fsync cost,
|
||||
# a bigger page cache keeps hot scans in memory.
|
||||
for pragma in (
|
||||
"PRAGMA journal_mode = WAL",
|
||||
"PRAGMA synchronous = NORMAL",
|
||||
"PRAGMA temp_store = MEMORY",
|
||||
"PRAGMA cache_size = -65536", # ~64 MB
|
||||
):
|
||||
try:
|
||||
self._con.execute(pragma)
|
||||
except sqlite3.Error:
|
||||
pass
|
||||
self._migrate()
|
||||
self._enabled = True
|
||||
_log(f"DB opened: {db_path}")
|
||||
@@ -85,6 +97,11 @@ class ProcessedDB:
|
||||
self._con.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_filename ON processed(filename)"
|
||||
)
|
||||
# Most hot queries filter by profile, often with filename too.
|
||||
self._con.execute(
|
||||
"CREATE INDEX IF NOT EXISTS idx_profile_filename"
|
||||
" ON processed(profile, filename)"
|
||||
)
|
||||
self._con.execute(
|
||||
"CREATE TABLE IF NOT EXISTS hidden_files ("
|
||||
" filename TEXT NOT NULL,"
|
||||
@@ -552,6 +569,25 @@ class ProcessedDB:
|
||||
counts[folder] = counts.get(folder, 0) + 1
|
||||
return counts
|
||||
|
||||
def get_clip_counts_grouped(self, profile: str = "default"
|
||||
) -> dict[str, dict[str, int]]:
|
||||
"""Return ``{filename: {export_folder: count}}`` for a whole profile
|
||||
in a single scan (replaces N per-file queries on the hot path)."""
|
||||
if not self._enabled:
|
||||
return {}
|
||||
rows = self._con.execute(
|
||||
"SELECT filename, output_path FROM processed WHERE profile = ?",
|
||||
(profile,),
|
||||
).fetchall()
|
||||
out: dict[str, dict[str, int]] = {}
|
||||
for fn, op in rows:
|
||||
folder = os.path.basename(os.path.dirname(os.path.dirname(op)))
|
||||
d = out.get(fn)
|
||||
if d is None:
|
||||
d = out[fn] = {}
|
||||
d[folder] = d.get(folder, 0) + 1
|
||||
return out
|
||||
|
||||
def get_all_folder_counts(self, profile: str = "default") -> dict[str, int]:
|
||||
"""Return clip counts per export folder across all videos in *profile*.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user