mymusics

retro MySpace-style music player
Log | Files | Refs | README

README.md (7532B)


      1 # MyMusics
      2 
      3 Retro-styled web player that picks random tracks from [`data/metadata.tsv`](data/metadata.tsv) and streams MP3s from the Internet Archive item [**The Myspace Dragon Hoard (2008–2010)**](https://archive.org/details/myspace_dragon_hoard_2010). URLs follow the same pattern as the official IA “Hobbit” player (ZIP member paths).
      4 
      5 The API uses a **SQLite index** (`data/tracks.db`) built from the TSV for fast random selection, search, and lookup by id — without loading hundreds of thousands of rows into RAM.
      6 
      7 ## Requirements
      8 
      9 - Node.js 20+
     10 - `data/metadata.tsv` (Dragon Hoard export). No local MP3 mirror is required.
     11 - After clone/deploy: run **`npm run index-metadata`** once (or on each TSV update) to create `data/tracks.db`.
     12 
     13 ## Configuration
     14 
     15 1. Copy `.env.example` to `.env` and adjust:
     16 
     17    - `METADATA_TSV` — path to `metadata.tsv` (default: `data/metadata.tsv`).
     18    - `TRACKS_DB` — SQLite index (default: `data/tracks.db`).
     19    - **Ports** — [`config/ports.ts`](config/ports.ts): `PORT_INDEX` (0–3) or explicit `PORT` / `VITE_DEV_PORT`. Defaults: API `38471`, Vite `38472`.
     20    - `IA_ITEM_ID` (optional) — Internet Archive item id (default `myspace_dragon_hoard_2010`).
     21    - `SERVE_STATIC` — serve SPA from `dist/` on the API port when built.
     22    - `CORS_ORIGINS` — comma-separated API CORS origins (production defaults to the public site).
     23    - `PUBLIC_SITE_URL` / `VITE_PUBLIC_SITE_URL` — canonical URL for share links, oEmbed, embed snippet.
     24    - `VITE_EMBED_PARENT_ORIGIN` — restrict embed `postMessage` target (optional).
     25 
     26 2. Install dependencies:
     27 
     28    ```bash
     29    npm install
     30    ```
     31 
     32 3. Build the track index (required before first API start):
     33 
     34    ```bash
     35    npm run index-metadata
     36    ```
     37 
     38    Use `npm run index-metadata -- --if-stale` to skip when the DB is newer than the TSV (used by `npm run build`). Use `--force` to rebuild always.
     39 
     40 ## Development
     41 
     42 Fast dev with a small TSV sample (optional):
     43 
     44 ```bash
     45 npm run sample-metadata
     46 # set METADATA_TSV=data/metadata.sample.tsv in .env, then:
     47 npm run index-metadata -- --force
     48 ```
     49 
     50 Start API + Vite:
     51 
     52 ```bash
     53 npm run dev
     54 ```
     55 
     56 Open the URL Vite prints (default `http://localhost:38472`).
     57 
     58 ## Production (static build + API)
     59 
     60 ```bash
     61 npm run build
     62 SERVE_STATIC=true npm run start
     63 ```
     64 
     65 `npm run build` runs `index-metadata --if-stale`, compiles the server, and builds the SPA.
     66 
     67 **PM2** (after `npm run build`):
     68 
     69 ```bash
     70 npm run pm2:prod
     71 pm2 save
     72 ```
     73 
     74 Set `METADATA_TSV` / `TRACKS_DB` in `ecosystem.config.cjs` → `env_production` on the VPS. Re-run **`npm run index-metadata`** when `metadata.tsv` changes.
     75 
     76 ### Docker
     77 
     78 ```bash
     79 docker compose build
     80 docker compose up
     81 ```
     82 
     83 Ensure `data/metadata.tsv` is present under `./data` (volume). The image runs `index-metadata --if-stale` during build when the TSV is copied in.
     84 
     85 ### VPS: “No tracks available” / `trackCount: 0`
     86 
     87 1. Ensure **`data/metadata.tsv`** exists and run **`npm run index-metadata`**.
     88 2. Check **`GET /api/health`**: `tracksReady: true`, `tracksDbExists: true`, `trackCount` > 0, `ftsReady: true`, and `hint` if something failed.
     89 3. Restart PM2 after env changes: `pm2 restart mymusics --update-env`.
     90 
     91 ### Reverse proxy (e.g. `mymusics.murad.gg`)
     92 
     93 Point HTTPS at the Node port (default `38471`). Example **nginx**:
     94 
     95 ```nginx
     96 server {
     97   server_name mymusics.murad.gg;
     98   location / {
     99     proxy_pass http://127.0.0.1:38471;
    100     proxy_http_version 1.1;
    101     proxy_set_header Host $host;
    102     proxy_set_header X-Real-IP $remote_addr;
    103     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    104     proxy_set_header X-Forwarded-Proto $scheme;
    105   }
    106 }
    107 ```
    108 
    109 **Embed on third-party sites:** HTML responses use `Content-Security-Policy: frame-ancestors *`. Ensure the proxy does not add `X-Frame-Options: DENY`.
    110 
    111 ## Embed (`/embed`) — priority
    112 
    113 Iframe URL: `https://mymusics.murad.gg/embed` with optional query params:
    114 
    115 | Param | Default | Effect |
    116 |-------|---------|--------|
    117 | `autoplay` | `1` | `0` disables auto-advance and skips random load on mount |
    118 | `theme` | `default` | `compact` — smaller layout |
    119 | `start` | — | Track id — loads `GET /api/track/:id` |
    120 | `brand` | `1` | `0` hides footer logo |
    121 | `muted` | `0` | `1` starts **audio** muted |
    122 | `preset` | `default` | Color preset: `light`, `dark`, `midnight`, `minimal` |
    123 | `accent` | — | Accent hex (with/without `#`) |
    124 | `bg` | — | Page background hex |
    125 | `panel` | — | Panel/card background hex |
    126 | `text` | — | Primary text hex |
    127 | `fgMuted` | — | Secondary text hex (not audio mute) |
    128 | `radius` | preset | Border radius 0–24 (px) |
    129 | `font` | `sans` | `system`, `sans`, or `serif` |
    130 
    131 Full customization guide (presets, CSS tokens, postMessage theme): **[docs/EMBED-CUSTOMIZATION.md](docs/EMBED-CUSTOMIZATION.md)**.
    132 
    133 **postMessage** (iframe → parent), payload `{ source: "mymusics", type, ... }`:
    134 
    135 - `mymusics:ready` — `{ trackCount }`
    136 - `mymusics:track` — `{ id, title, artist, streamUrl }`
    137 - `mymusics:state` — `{ state: "playing" \| "paused" \| "buffering" \| "error" }`
    138 - `mymusics:error` — `{ code, message }`
    139 - `mymusics:theme-applied` — `{ tokens }` after a theme change
    140 
    141 Parent → iframe: `{ source: "mymusics-host", type: "mymusics:command", command: "play" \| "pause" \| "next" }`.
    142 
    143 Parent → iframe (theme): `{ source: "mymusics-host", type: "mymusics:theme", theme: { preset, accent, ... } }`.
    144 
    145 **oEmbed:** `GET /api/oembed?url=https://mymusics.murad.gg/embed`
    146 
    147 The Home and About pages include a snippet generator with preset, accent, and live preview.
    148 
    149 ## API
    150 
    151 | Method | Path | Description |
    152 |--------|------|-------------|
    153 | GET | `/api/health` | Diagnostics (`tracksDb`, `ftsReady`, `blockedCount`, …) |
    154 | GET | `/api/track/random` | Random track + `streamUrl` |
    155 | GET | `/api/track/up-next?exclude=` | Next track (prefers different id) |
    156 | GET | `/api/track/:id` | Track by id |
    157 | GET | `/api/track/search?q=&limit=` | Search by title/artist (FTS) |
    158 | POST | `/api/reload` | Reload DB / paths from env |
    159 | POST | `/api/events` | Client metrics (`stream_error`, `time_to_play`) |
    160 | GET | `/api/oembed?url=` | oEmbed JSON for `/embed` |
    161 
    162 ## Share links
    163 
    164 - `https://mymusics.murad.gg/?track=TRACK_ID`
    165 - `https://mymusics.murad.gg/t/TRACK_ID` (redirects to `/?track=`)
    166 
    167 ## Maintenance
    168 
    169 Weekly cron (optional) — sample Archive URLs and block failures:
    170 
    171 ```bash
    172 npm run verify-tracks
    173 ```
    174 
    175 Env: `VERIFY_SAMPLE_SIZE` (default `50`).
    176 
    177 ## Troubleshooting
    178 
    179 ### HTTP 503 on `/api/track/random`
    180 
    181 The pool is empty. Run `npm run index-metadata` and verify `/api/health`.
    182 
    183 ### Console: TensorFlow / `content.js` messages
    184 
    185 These come from **browser extensions**, not MyMusics.
    186 
    187 ## Playback notes
    188 
    189 - Audio streams from `https://archive.org/download/...`. First play can be slow (ZIP member extraction).
    190 - Archive **503** errors are retried automatically a few times; use **Next** if needed.
    191 - Volume is remembered in `localStorage`. Shortcuts on Home: Space, N, M.
    192 
    193 ## Scripts
    194 
    195 | Command | Description |
    196 |---------|-------------|
    197 | `npm run dev` | Vite + API |
    198 | `npm run index-metadata` | Build `data/tracks.db` from TSV |
    199 | `npm run index-metadata:force` | Force rebuild index |
    200 | `npm run sample-metadata` | Write `data/metadata.sample.tsv` (500 lines) |
    201 | `npm run verify-tracks` | HEAD sample URLs → `blocked_ids` |
    202 | `npm run build` | Index (if stale) + server + SPA |
    203 | `npm run start` | Production Node server |
    204 | `npm run test` | Vitest (metadata + track store) |
    205 | `npm run pm2:prod` | Build + PM2 |
    206 
    207 ## Logo
    208 
    209 `public/mymusics.png` is the MyMusics logo asset.