Files
8-cut/docs/plans/2026-04-06-playlist-db-design.md
T

3.9 KiB

Playlist & Processed-Files Database Design

Overview

Two new features added to the existing 8-cut tool:

  1. Playlist — a queue of source video files; auto-advances to the next file after each export.
  2. Processed-files database — SQLite store of exported source filenames; warns when a newly loaded file is fuzzy-similar to one already processed.

Stack additions

  • sqlite3 (Python built-in) — persistent DB at ~/.8cut.db
  • difflib.SequenceMatcher (Python built-in) — fuzzy filename similarity, threshold 0.75

Layout

┌──────────────────────────────────────────────────────┐
│  [Drop video files here]                             │
├─────────────────┬────────────────────────────────────┤
│ QUEUE           │                                    │
│ ▶ clip_a.mp4   │        mpv video preview           │
│   clip_b.mp4   │                                    │
│   clip_c.mp4   ├────────────────────────────────────┤
│                 │  Timeline ──────|cursor──────      │
│                 ├────────────────────────────────────┤
│                 │  [▶ Play 8s] [⏸]  cursor / dur    │
├─────────────────┴────────────────────────────────────┤
│  Name: [clip]  Folder: [~]  → clip_001.mp4  [Export] │
└──────────────────────────────────────────────────────┘
  • Drop one or multiple files → all added to the queue
  • Clicking a queue item loads it immediately
  • marks the current file
  • Auto-advances to next item after a successful export

Components

ProcessedDB

Wraps sqlite3. DB file: ~/.8cut.db, created on first run.

Table:

CREATE TABLE IF NOT EXISTS processed (
    filename TEXT NOT NULL,
    processed_at TEXT NOT NULL
)

Methods:

  • add(filename: str) — inserts row with filename and current UTC ISO timestamp
  • find_similar(filename: str) -> str | None — loads all filenames, iterates with SequenceMatcher(None, a, b).ratio() >= 0.75, returns the best-matching stored filename or None

PlaylistWidget(QListWidget)

  • Accepts file drops (via dragEnterEvent / dropEvent)
  • Emits file_selected = pyqtSignal(str) when an item is activated (click or auto-advance)
  • add_files(paths: list[str]) — appends paths, skips duplicates already in the list
  • advance() — selects the next item; if at end, does nothing (queue exhausted)
  • Current item shown with a prefix in the display text; others show bare filename

MainWindow changes

  • Drop zone (dragEnterEvent/dropEvent) now calls playlist.add_files(paths) instead of loading directly
  • playlist.file_selected connected to _load_file
  • _after_load: calls db.find_similar(os.path.basename(path)) — if match found, shows "⚠ Similar to already processed: {match}" in status bar
  • _on_export_done: calls db.add(os.path.basename(self._file_path)), then self._playlist.advance()

Warning behavior

  • Warning is informational only — does not block export
  • Shown in the existing status bar with a prefix
  • Shown each time a file is loaded (queue advance or manual click)

Error handling

  • DB connection errors on startup: logged to stderr, app continues without DB (warning feature silently disabled)
  • Queue exhaustion (advance past last item): silently does nothing; user can add more files