# Reddit → Spotify / Bandcamp linker A Firefox extension that, while you browse a Reddit thread, finds **artist / band names and album titles** in the comments (and the post body) and turns them into inline links to **Spotify** and **Bandcamp** — so a recommendation thread like [this one](https://www.reddit.com/r/SlamDeathMetal/comments/1txh2r1/) becomes a list of clickable artists and albums instead of names you have to copy-paste into search. The hard part is *"which words are artists/albums and which aren't?"*. Rather than guess with client-side rules, the extension hands candidate names to a tiny local **resolver service** that confirms each one and only links the names that are really artists or albums. The resolver confirms names two ways (you enable either or both): **Spotify** (exact-name match across artists **and** albums → direct Spotify pages) and an optional **local LLM** via ollama (classifies which candidates are artists/albums). A confirmed name links to its Spotify artist/album page when available, otherwise to a Google search for " bandcamp". Album links are italicised so you can tell an album from an artist at a glance. ``` reddit page content script resolver service ─────────── ────────────── ──────────────── comment text ──extract──> candidate names ──POST /resolve──> Spotify API (exact-match + direct links) and/or links <──rewrite── confirmed links <───────────────── local LLM (ollama, optional) ``` ## Layout ``` service/ tiny Node.js resolver (one file, zero npm deps) resolver.js package.json .env.example extension/ Firefox MV2 extension manifest.json background.js (does the resolver fetch; talks to the content script) content.js styles.css icon.svg ``` ## 1. Run the resolver service Requirements: **Node 18+** (uses the built-in `fetch`). No `npm install` needed — there are no dependencies. ```sh cd service node resolver.js # resolver listening on http://localhost:8787 (spotify: off, llm: off) ``` For anything to link you need **at least one** of two gates configured — **Spotify credentials** or the **local LLM** (or both). With neither, the resolver warns at startup and nothing links. ### Spotify credentials (one of the two gates) Spotify does two jobs: it **gates** a name (a candidate links only when Spotify has that **artist or album** by exact name match) and supplies the **direct** Spotify page link. A single search call covers both types, so albums confirm and link with no extra setup. To enable it: 1. Create a free app at and copy its **Client ID** and **Client Secret**. 2. `cp .env.example .env` and fill them in (the `.env` file is gitignored): ``` SPOTIFY_CLIENT_ID=your_id SPOTIFY_CLIENT_SECRET=your_secret ``` 3. Restart the service. Startup reports `(spotify: on, llm: off)`. With Spotify alone, names that aren't on Spotify don't link — precise, but it misses real artists Spotify doesn't carry. The local LLM gate (below) catches those. Quick check that it works: ```sh curl -s localhost:8787/resolve \ -H 'content-type: application/json' \ -d '{"candidates":["Cerebral Bore","Devourment","Notarealband Xyzzy"]}' # {"Cerebral Bore":{...},"Devourment":{...},"Notarealband Xyzzy":null} ``` A confirmed name links to its Spotify artist page when available, otherwise to a Google search for " bandcamp" (which surfaces the artist's Bandcamp page). ## Better matching with a local LLM (optional) The optional LLM gate is the **matching-quality lever**: it classifies which candidate strings are real artists/albums, so confirmed names that aren't on Spotify still link (via the " bandcamp" Google fallback). The resolver calls ollama over **plain HTTP**, so it stays **zero npm dependencies**. On a 16GB MacBook (M2): 1. Install ollama — or `brew install ollama`. 2. Pull a small model: `ollama pull qwen2.5:3b` (~2GB; runs comfortably on a 16GB M2; the first resolve call warms the model so it's a little slow once, then fast). 3. In `service/.env`, point the resolver at ollama (and optionally override the model): ``` OLLAMA_URL=http://localhost:11434 OLLAMA_MODEL=qwen2.5:3b ``` 4. Restart the resolver. Startup reports `(spotify: off, llm: qwen2.5:3b)` (or `(spotify: on, llm: qwen2.5:3b)` if you also set Spotify creds). 5. Confirm: `curl localhost:8787/health` should show `"llm":"qwen2.5:3b"`. With both gates, the LLM decides which names are artists and Spotify supplies the direct links; with the LLM alone, confirmed names get a Spotify direct link if one exists, else the Google fallback. ## 2. Install the extension in Firefox The extension is unpacked / unsigned, so load it as a **temporary add-on**: 1. Open `about:debugging#/runtime/this-firefox`. 2. **Load Temporary Add-on…** 3. Select `extension/manifest.json`. It's now active until you restart Firefox. (To keep it permanently you'd sign it via [AMO](https://addons.mozilla.org/developers/) or use Firefox Developer/ESR with `xpinstall.signatures.required = false`.) ## 3. Use it 1. Make sure the resolver service is running. 2. Open any Reddit comments thread (a URL with `/comments/` in it). 3. Artist names and album titles become a single link — to **Spotify** if it's on Spotify, otherwise to **Bandcamp** — colour-coded (green = Spotify, blue = Google/Bandcamp); album links are italicised so they read as albums. New comments loaded as you scroll are linked automatically. 4. The toolbar icon shows the extension is active; its badge shows how many links were added on the current page. ## Troubleshooting If nothing gets linked: 1. Confirm the resolver is running — `curl localhost:8787/health` should return `{"ok":true,"spotify":,"llm":,"cached":}`. If both `spotify` and `llm` are off, nothing can link — configure at least one gate above. 2. On the reddit thread, open the browser console (F12) and look for `[rsl]` logs: they report the root element, candidate count, and resolver responses on each scan. A `resolve failed` line means the background script can't reach the resolver (is it on `:8787`?); `candidates found: 0` means the comment text wasn't matched (please report the thread). 3. The content script reaches the resolver **through the extension's background script** — a page-context fetch to `http://localhost` is blocked by reddit's CSP — so make sure the add-on loaded without errors in `about:debugging`. ## Configuration The resolver URL is the constant `RESOLVER_URL` at the top of `extension/background.js` (default `http://localhost:8787`). If you change the host or port, update **both** that constant **and** the matching entry in `extension/manifest.json` → `permissions` (Firefox needs the host listed there to let the content script reach it). ## Limitations (MVP) - **Artist detection is mostly capitalization-based**, with a cue-phrase pass that also catches lowercase names right after "check out / by / fan of / recommend …". Names split across inline formatting (e.g. **bold** mid-name) can still be missed. The resolver's exact-name match keeps false *positives* low; the trade-off is some false *negatives*. - **Album detection leans on two precise signals** — quoted titles (`"Reek of Putrefaction"`) and album cue phrases ("the album X", "X LP/EP"). Album free-text is noisier than artist names, so it deliberately over-generates only from these cues and lets the Spotify exact-album match (and/or the LLM) be the precision filter. A bare album title written with no quotes or cue may be missed. - **Links are direct to Spotify when Spotify has the artist/album**; otherwise a confirmed name links to a Google search for " bandcamp" (which surfaces the Bandcamp page). - **Without Spotify creds or the LLM, nothing links** — at least one gate must be configured. - The optional LLM adds a little latency on the **first** call (ollama warms the model), then is fast. - The resolver caches results in memory; it resets when you restart it. - Built and tested as a Firefox MV2 extension. ## License Personal project — do as you like.