From 055d825544f04b7df0aa94d6aeb97538e150710a Mon Sep 17 00:00:00 2001 From: leafx54 Date: Thu, 14 May 2026 01:50:24 -0400 Subject: [PATCH 1/2] fix: bootstrap deadlock and silent empty merge on fresh install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs prevented bootstrap from working on a clean install: 1. InitUserDB() did not create the stations, tracks, indexes, or FTS virtual table. SQLite's OR IGNORE silently swallows "no such table" errors, so INSERT OR IGNORE INTO stations/tracks did nothing and returned nil — bootstrap reported success with 0 tracks merged. Fix: add the full stations/tracks schema to InitUserDB(). 2. seedDemoPlaylists() held a Query cursor open while issuing follow-up QueryRow/Exec calls on the same connection. With SetMaxOpenConns(1) this deadlocked. Fix: drain cursor into a []string slice, close it, then process names. Closes #7, closes #10 Co-Authored-By: Claude Sonnet 4.6 --- internal/db/catalog.go | 43 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/internal/db/catalog.go b/internal/db/catalog.go index 20be5e8..1640167 100644 --- a/internal/db/catalog.go +++ b/internal/db/catalog.go @@ -17,6 +17,32 @@ func (db *DB) InitUserDB() error { key TEXT PRIMARY KEY, value TEXT NOT NULL ); + CREATE TABLE IF NOT EXISTS stations ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + description TEXT, + color TEXT, + youtube_channel_id TEXT NOT NULL, + uploads_playlist_id TEXT NOT NULL, + last_synced INTEGER, + created_at INTEGER + ); + CREATE TABLE IF NOT EXISTS tracks ( + id TEXT PRIMARY KEY, + station_id TEXT NOT NULL REFERENCES stations(id), + youtube_id TEXT NOT NULL UNIQUE, + song_title TEXT, + artist TEXT, + raw_title TEXT NOT NULL, + search_text TEXT, + thumbnail TEXT, + published_at INTEGER, + created_at INTEGER + ); + CREATE INDEX IF NOT EXISTS tracks_station_idx ON tracks(station_id); + CREATE INDEX IF NOT EXISTS tracks_published_idx ON tracks(published_at DESC); + CREATE INDEX IF NOT EXISTS tracks_youtube_id_idx ON tracks(youtube_id); + CREATE VIRTUAL TABLE IF NOT EXISTS tracks_fts USING fts5(song_title, artist, content=tracks, content_rowid=rowid); CREATE TABLE IF NOT EXISTS playlists ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, @@ -143,14 +169,21 @@ func (db *DB) seedDemoPlaylists() (int, error) { if err != nil { return 0, err } - defer rows.Close() - - created := 0 + var names []string for rows.Next() { var name string if err := rows.Scan(&name); err != nil { - return created, err + rows.Close() + return 0, err } + names = append(names, name) + } + if err := rows.Close(); err != nil { + return 0, err + } + + created := 0 + for _, name := range names { var count int db.conn.QueryRow("SELECT COUNT(*) FROM playlists WHERE name=?", name).Scan(&count) if count > 0 { @@ -172,7 +205,7 @@ func (db *DB) seedDemoPlaylists() (int, error) { } created++ } - return created, rows.Err() + return created, nil } // DefaultCatalogPath returns the system catalog path, falling back to a local From efd476590247ca955d9a8937ae9f80dc637c4a6d Mon Sep 17 00:00:00 2001 From: dev Date: Thu, 14 May 2026 04:10:29 -0400 Subject: [PATCH 2/2] fix: add raw_title to FTS schema, match live DB definition Co-Authored-By: Claude Sonnet 4.6 --- internal/db/catalog.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/db/catalog.go b/internal/db/catalog.go index 1640167..c6b9fbd 100644 --- a/internal/db/catalog.go +++ b/internal/db/catalog.go @@ -42,7 +42,7 @@ func (db *DB) InitUserDB() error { CREATE INDEX IF NOT EXISTS tracks_station_idx ON tracks(station_id); CREATE INDEX IF NOT EXISTS tracks_published_idx ON tracks(published_at DESC); CREATE INDEX IF NOT EXISTS tracks_youtube_id_idx ON tracks(youtube_id); - CREATE VIRTUAL TABLE IF NOT EXISTS tracks_fts USING fts5(song_title, artist, content=tracks, content_rowid=rowid); + CREATE VIRTUAL TABLE IF NOT EXISTS tracks_fts USING fts5(song_title, artist, raw_title, content='tracks', content_rowid='rowid'); CREATE TABLE IF NOT EXISTS playlists ( id INTEGER PRIMARY KEY, name TEXT NOT NULL,