# mp3-com-meta-browser mp3-com-meta-browser is a static single-page web app for exploring the salvaged mp3.com metadata catalog. All queries run entirely in the browser against a read-only SQLite database compiled to WebAssembly, so the site can be hosted on any static file server. ## Highlights - Client-side full-text search (FTS5) across track titles, artists, albums, and genres. - Responsive Bulma-based UI with dedicated views for search, browse (artists, albums, years, genres), and dataset stats. - Overlay detail panels for artists, albums, and tracks with keyboard navigation support. - Zero server dependencies beyond serving static assets with HTTP range support. ## Tech Stack - Bulma (`bulma.min.css`) for layout and components. - Vanilla JavaScript in `script.js` for SPA state management, view controllers, and data access helpers. - `sql.js` WASM build (`sql-wasm.js` / `sql-wasm.wasm`) to run SQLite with FTS5 support in the browser. - Local styling refinements in `site.css`. - Metadata database delivered as `/assets/mp3com-meta.sqlite`. ## Directory Layout ``` / ├─ index.html # Single-page app shell and HTML templates ├─ bulma.min.css # Bundled Bulma CSS (do not modify upstream file) ├─ site.css # Local overrides and micro-utilities ├─ script.js # Application logic, helpers, and view controllers ├─ README.md # Project overview (this file) ├─ AGENTS.md # Orientation and guardrails for automation agents └─ assets/ └─ mp3com-meta.sqlite # Read-only metadata database ``` Add future static assets (icons, fonts, etc.) under `assets/`. ## Running Locally 1. Start a static file server from the repository root. Examples: - `python3 -m http.server 8000` - `npx http-server -p 8000` 2. Ensure the server sends `Accept-Ranges: bytes` for `/assets/mp3com-meta.sqlite` so the WASM virtual file system can page data on demand. 3. Visit `http://localhost:8000/` in a modern browser. 4. Open the developer console to confirm there are no runtime errors. ## Using the App - **Search:** Free-text search with 250 ms debounce. Toggle between relevance and alphabetical sorting. Keyboard: `/` focuses search, `Enter` activates the highlighted row. - **Browse Artists / Albums / Years / Genres:** Paginated listings with quick filters (artists leverage FTS prefix queries for fast prefix matching). Selecting an entry opens the corresponding overlay. - **Stats:** Summary cards pull from pre-computed counters in `site_stats` and link into browse views with preset filters. - **Overlays:** Artist, album, and track overlays show detailed metadata. `Esc` closes the top overlay and focus returns to the invoker. ## Keyboard & Accessibility Notes - Global shortcuts: `/` for search, `Esc` to close overlays. - Lists expose arrow navigation with `Enter` activation when focused. - Focus indicators remain visible; async list regions use polite `aria-live` messages. - Ensure new UI follows Bulma semantics and pairs labels with inputs. ## Database Schema The bundled database enforces foreign keys and ships with an FTS5 index over key text fields. The current schema is: ```sql PRAGMA foreign_keys=ON; CREATE TABLE artists( id INTEGER PRIMARY KEY, name TEXT NOT NULL COLLATE NOCASE UNIQUE ); CREATE TABLE albums( id INTEGER PRIMARY KEY, artist_id INTEGER NOT NULL REFERENCES artists(id), title TEXT COLLATE NOCASE, year INTEGER, UNIQUE(artist_id, title, year) ); CREATE TABLE tracks( id INTEGER PRIMARY KEY, artist_id INTEGER NOT NULL REFERENCES artists(id), album_id INTEGER REFERENCES albums(id), title TEXT NOT NULL COLLATE NOCASE, track_no INTEGER, year INTEGER, genre TEXT, duration_sec INTEGER, bitrate_kbps INTEGER, samplerate_hz INTEGER, channels INTEGER, filesize_bytes INTEGER, sha1 TEXT, relpath TEXT NOT NULL ); CREATE TABLE site_stats ( name TEXT PRIMARY KEY, value TEXT NOT NULL ); CREATE VIRTUAL TABLE fts_tracks USING fts5( title, artist, album, genre, content='tracks', content_rowid='id', tokenize='unicode61 remove_diacritics 2', prefix='2 3 4' ); CREATE TABLE 'fts_tracks_data'(id INTEGER PRIMARY KEY, block BLOB); CREATE TABLE 'fts_tracks_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID; CREATE TABLE 'fts_tracks_docsize'(id INTEGER PRIMARY KEY, sz BLOB); CREATE TABLE 'fts_tracks_config'(k PRIMARY KEY, v) WITHOUT ROWID; CREATE INDEX idx_tracks_artist_id ON tracks(artist_id); CREATE INDEX idx_tracks_album_id ON tracks(album_id); CREATE INDEX idx_tracks_year ON tracks(year); CREATE INDEX idx_tracks_genre ON tracks(genre); CREATE INDEX idx_artists_name ON artists(name COLLATE NOCASE); CREATE INDEX idx_artists_name_nocase ON artists(name COLLATE NOCASE); ``` The `fts_tracks_*` tables are managed by SQLite to back the FTS5 virtual table. `site_stats` stores pre-computed counters that power the stats view. The application issues read-only queries and never mutates the database. ## Development Notes - Keep the app framework-free; do not introduce bundlers or additional dependencies without discussion. - Reuse HTML templates in `index.html` through the `instantiateTemplate` helper instead of building large DOM fragments manually. - Paginate every query (`LIMIT ? OFFSET ?`) and prefer streaming or incremental rendering for large result sets. - Consolidate shared UI logic (pagination, keyboard handlers, async states) in `script.js` utilities. - Restrict new CSS to scoped utility classes in `site.css`; otherwise rely on Bulma components. - Before submitting changes, verify there are no console warnings, layout adapts from mobile to desktop, and keyboard navigation continues to work. ### Debug Instrumentation - Enable verbose query diagnostics by setting `localStorage.setItem('mp3com.debug', 'true')` and reloading the app (clear the key or set it to `'false'` to disable). - With DEBUG on, every SQLite statement logs `Query begin` / `Query end` entries that include the originating view, optional label, normalized SQL text, bound parameters, row counts, and execution duration. - Any statement failure logs `[SQLite error]` details (SQL, params, phase, message, stack) before the exception propagates, making it easier to correlate with UI regressions. - Pagination helpers emit `[Pagination]` traces whenever user inputs are normalized or clamped (including high-page corrections after a total count comes back smaller than expected). - Use the `prepareForView(db, VIEW_NAMES.someView, sql, label)` helper when preparing new statements so that console output remains aligned with the feature area generating the query. ## License Add licensing information here when available.