mymusics

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

commit 751272017c9e579f763b8c07d63f3f617449ec92
parent d79e1e7e6b8cc5ce593c3133beec5723a6e596d9
Author: Pablo Murad <pblmrd@gmail.com>
Date:   Fri,  1 May 2026 12:10:12 -0300

metadata

Diffstat:
MREADME.md | 30++++++++++++++++++++++++++++++
Msrc/App.css | 39+++++++++++++++++++++++++++++++++++++++
Msrc/App.tsx | 46++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 115 insertions(+), 0 deletions(-)

diff --git a/README.md b/README.md @@ -77,6 +77,36 @@ server { No extra `vite.config` base URL is needed when the site is served at the domain root. +## Troubleshooting + +### HTTP 503 on `/api/track/random` (“No tracks available”) + +The API returns **503** only when the in-memory track pool is **empty** (`trackCount: 0`). The browser request is reaching Node; fix **metadata path and env** on the server. + +1. **On the VPS (SSH)**, call health locally (replace `38471` if you use another `PORT`): + + ```bash + curl -sS http://127.0.0.1:38471/api/health + ``` + + Check `metadataTsv`, `metadataExists`, `metadataSizeBytes`, `trackCount`, `tracksReady`, and `hint`. + +2. **Align `METADATA_TSV`** with the real file (e.g. `/opt/mymusics/data/metadata.tsv`). Wrong paths such as `/opt/data/metadata.tsv` look “almost right” but **fail** if the file lives under the app directory. Alternatively **remove** `METADATA_TSV` from `.env` / PM2 so the app uses the default `data/metadata.tsv` next to the project. + +3. **Restart the process** after editing env so variables reload: + + ```bash + pm2 restart mymusics --update-env + ``` + + (Use your PM2 app name if different.) + +4. Re-run `curl` until `tracksReady` is `true` and `trackCount` > 0. + +### Console: `content.js` and TensorFlow / WebGL kernel messages + +Messages like **“The kernel '…' for backend 'webgl' is already registered”** in **`content.js`** come from a **browser extension** (not from MyMusics). To verify the site without that noise, use a **private/incognito window** with extensions disabled for that window, or temporarily disable extensions. + ## Playback notes - The browser loads audio directly from `https://archive.org/download/...` URLs. First play may be slow while the Archive serves the file from inside large ZIPs. diff --git a/src/App.css b/src/App.css @@ -160,6 +160,45 @@ font-weight: 500; } +.health-banner { + margin-bottom: 1.25rem; + padding: 1rem 1.1rem; + border-radius: 12px; + border: 2px solid color-mix(in srgb, var(--orange) 55%, transparent); + background: color-mix(in srgb, var(--orange) 12%, var(--panel)); + box-shadow: 0 8px 28px rgba(0, 0, 0, 0.3); +} + +.health-banner strong { + display: block; + margin-bottom: 0.5rem; + color: var(--yellow); + font-size: 0.95rem; + letter-spacing: 0.03em; + text-transform: uppercase; +} + +.health-banner p { + margin: 0 0 0.65rem; + font-size: 0.9rem; + line-height: 1.45; + word-break: break-word; +} + +.health-banner p:last-child { + margin-bottom: 0; +} + +.health-banner-hint { + color: var(--muted); + font-size: 0.82rem !important; +} + +.health-banner code { + font-size: 0.8em; + color: var(--sky); +} + .footer { margin-top: 2.5rem; text-align: center; diff --git a/src/App.tsx b/src/App.tsx @@ -14,12 +14,21 @@ type RandomResponse = { type ErrBody = { error?: string }; +type HealthBody = { + tracksReady?: boolean; + hint?: string; + metadataTsv?: string; + metadataExists?: boolean; + trackCount?: number; +}; + export default function App() { const audioRef = useRef<HTMLAudioElement>(null); const [track, setTrack] = useState<TrackInfo | null>(null); const [status, setStatus] = useState<string>(""); const [history, setHistory] = useState<TrackInfo[]>([]); const [autoPlay, setAutoPlay] = useState(true); + const [healthWarn, setHealthWarn] = useState<string | null>(null); const playUrl = useCallback((url: string) => { const a = audioRef.current; @@ -61,6 +70,32 @@ export default function App() { }, [playUrl]); useEffect(() => { + void (async () => { + try { + const res = await fetch("/api/health"); + const h = (await res.json()) as HealthBody; + if (!h.tracksReady) { + const parts = [ + h.hint, + h.metadataTsv && `Path: ${h.metadataTsv}`, + h.metadataExists === false && "File not found at configured path.", + typeof h.trackCount === "number" && `Tracks loaded: ${h.trackCount}.`, + ].filter(Boolean); + setHealthWarn( + parts.length > 0 + ? parts.join(" ") + : "No tracks loaded. Check server metadata and /api/health.", + ); + } else { + setHealthWarn(null); + } + } catch { + setHealthWarn(null); + } + })(); + }, []); + + useEffect(() => { // eslint-disable-next-line react-hooks/set-state-in-effect -- intentional mount bootstrap void loadNext(); }, [loadNext]); @@ -71,6 +106,17 @@ export default function App() { return ( <div className="page"> + {healthWarn ? ( + <div className="health-banner" role="alert"> + <strong>Server metadata</strong> + <p>{healthWarn}</p> + <p className="health-banner-hint"> + On the host, run <code>curl -sS http://127.0.0.1:38471/api/health</code> (adjust + port) and fix <code>METADATA_TSV</code> or remove it to use the default{" "} + <code>data/metadata.tsv</code>. + </p> + </div> + ) : null} <header className="header"> <img className="logo"