diff --git a/src/components/Player.jsx b/src/components/Player.jsx index e34c906..ca4e9cc 100644 --- a/src/components/Player.jsx +++ b/src/components/Player.jsx @@ -18,10 +18,12 @@ export default function Player() { {p.current.title} - {p.current.artist} + + {p.missing ? 'Audio unavailable — re-import' : p.current.artist} + e.stopPropagation()}> - @@ -82,25 +84,31 @@ function NowPlaying({ p, onClose }) { -
- p.seek(Number(e.target.value))} - style={{ background: `linear-gradient(to right, var(--accent) ${pct}%, var(--surface-2) ${pct}%)` }} - /> -
- {formatTime(p.progress)} - {formatTime(p.duration)} + {p.missing ? ( +

+ Audio unavailable — the file for this track is missing. Re-import it to play. +

+ ) : ( +
+ p.seek(Number(e.target.value))} + style={{ background: `linear-gradient(to right, var(--accent) ${pct}%, var(--surface-2) ${pct}%)` }} + /> +
+ {formatTime(p.progress)} + {formatTime(p.duration)} +
-
+ )}
- diff --git a/src/index.css b/src/index.css index afa4660..6b061c5 100644 --- a/src/index.css +++ b/src/index.css @@ -483,6 +483,8 @@ body { box-shadow: 0 8px 24px rgba(233, 162, 59, 0.35); } .playbtn:active { transform: scale(0.96); } +.playbtn:disabled, .mini__controls .iconbtn:disabled { opacity: 0.4; cursor: default; box-shadow: none; } +.now__missing { margin: 14px 0; padding: 0 24px; text-align: center; font-size: 13px; color: var(--accent); } .loopbtn { display: inline-flex; align-items: center; gap: 8px; diff --git a/src/state/PlayerProvider.jsx b/src/state/PlayerProvider.jsx index 5bfc46c..610d3d5 100644 --- a/src/state/PlayerProvider.jsx +++ b/src/state/PlayerProvider.jsx @@ -80,6 +80,10 @@ export function PlayerProvider({ children }) { let cancelled = false countedRef.current = false setMissing(false) + // Reset the scrubber immediately so it doesn't show the previous track's + // position/length until the new metadata arrives. + setProgress(0) + setDuration(0) const revokePrev = () => { if (objectUrlRef.current) { @@ -93,7 +97,17 @@ export function PlayerProvider({ children }) { if (!url) { const blob = await getAudioBlob(current.id) if (!blob) { - if (!cancelled) setMissing(true) + // Bytes are gone (cleared storage / failed import). Stop the element + // so it doesn't keep playing the PREVIOUS track under a now-missing + // `current`, and reset state so the UI can show an honest message. + if (!cancelled) { + audio.pause() + audio.removeAttribute('src') + audio.load() + revokePrev() // release the previous track's blob URL too + setIsPlaying(false) + setMissing(true) + } return } if (cancelled) return