From 3e9921a1012df92106cc0ea98226b078672a8b7a Mon Sep 17 00:00:00 2001 From: Thenlie Date: Mon, 15 Sep 2025 21:53:56 -0600 Subject: [PATCH 1/3] create audio error event --- public/gapless.cjs | 1 + src/components/Player.tsx | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/public/gapless.cjs b/public/gapless.cjs index dab8d95..fe9318d 100644 --- a/public/gapless.cjs +++ b/public/gapless.cjs @@ -485,6 +485,7 @@ // basic event handlers audioOnError = (e) => { this.debug('audioOnError', e); + window.dispatchEvent(new CustomEvent('gapless-audio-error', { error: e })); }; onEnded(from) { diff --git a/src/components/Player.tsx b/src/components/Player.tsx index 81690a2..42e4612 100644 --- a/src/components/Player.tsx +++ b/src/components/Player.tsx @@ -2,7 +2,7 @@ import Head from 'next/head'; import Link from 'next/link'; -import React, { useRef, useState } from 'react'; +import React, { useRef, useState, useEffect } from 'react'; import { useSelector } from 'react-redux'; import player from '../lib/player'; @@ -71,6 +71,17 @@ const Player = ({ artistSlugsToName }: Props) => { localStorage.volume = Math.max(0, Math.min(1, nextVolume)); }; + useEffect(() => { + const handleAudioError = (e: CustomEvent) => { + console.log('Audio error detected', e); + }; + + window.addEventListener('gapless-audio-error', handleAudioError); + return () => { + window.removeEventListener('gapless-audio-error', handleAudioError); + }; + }); + return ( {false && activeTrack && ( From 76196d0a9b3e16fbc087ad2131e4c12da030dd4d Mon Sep 17 00:00:00 2001 From: Thenlie Date: Mon, 15 Sep 2025 22:10:17 -0600 Subject: [PATCH 2/3] add onError queue method --- public/gapless.cjs | 9 ++++++++- src/components/Player.tsx | 26 +++++++++++++++++--------- src/lib/player.ts | 3 +++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/public/gapless.cjs b/public/gapless.cjs index fe9318d..69b0bb7 100644 --- a/public/gapless.cjs +++ b/public/gapless.cjs @@ -42,6 +42,7 @@ onPlayPreviousTrack, onStartNewTrack, webAudioIsDisabled = false, + onError, } = props; this.props = { @@ -50,6 +51,7 @@ onPlayNextTrack, onPlayPreviousTrack, onStartNewTrack, + onError, }; this.state = { @@ -209,6 +211,10 @@ this.tracks.map((track) => track.setVolume(nextVolume)); } + + onError() { + if (this.props.onError) this.props.onError(); + } } class Track { @@ -485,7 +491,8 @@ // basic event handlers audioOnError = (e) => { this.debug('audioOnError', e); - window.dispatchEvent(new CustomEvent('gapless-audio-error', { error: e })); + // window.dispatchEvent(new CustomEvent('gapless-audio-error', { error: e })); + this.queue.onError(); }; onEnded(from) { diff --git a/src/components/Player.tsx b/src/components/Player.tsx index 42e4612..c4a143b 100644 --- a/src/components/Player.tsx +++ b/src/components/Player.tsx @@ -9,6 +9,7 @@ import player from '../lib/player'; import { durationToHHMMSS, removeLeadingZero, splitShowDate } from '../lib/utils'; import Flex from './Flex'; import { + AlertCircle, ChevronDown, FastForwardIcon, ListMusicIcon, @@ -29,6 +30,7 @@ const Player = ({ artistSlugsToName }: Props) => { const [volume, setVolume] = useState( (typeof localStorage !== 'undefined' && localStorage.volume) || 1 ); + // const [hasAudioError, setHasAudioError] = useState(false); const { year, month, day } = splitShowDate(playback.showDate); const { artistSlug, source } = playback; @@ -71,16 +73,16 @@ const Player = ({ artistSlugsToName }: Props) => { localStorage.volume = Math.max(0, Math.min(1, nextVolume)); }; - useEffect(() => { - const handleAudioError = (e: CustomEvent) => { - console.log('Audio error detected', e); - }; + // useEffect(() => { + // const handleAudioError = () => { + // setHasAudioError(true); + // }; - window.addEventListener('gapless-audio-error', handleAudioError); - return () => { - window.removeEventListener('gapless-audio-error', handleAudioError); - }; - }); + // window.addEventListener('gapless-audio-error', handleAudioError); + // return () => { + // window.removeEventListener('gapless-audio-error', handleAudioError); + // }; + // }, []); return ( @@ -199,6 +201,12 @@ const Player = ({ artistSlugsToName }: Props) => { )} + {/* {hasAudioError && ( + + +

Error loading audio

+
+ )} */}
); }; diff --git a/src/lib/player.ts b/src/lib/player.ts index 3ba9b2a..85689a4 100644 --- a/src/lib/player.ts +++ b/src/lib/player.ts @@ -156,6 +156,9 @@ const player = new Gapless.Queue({ } } }, + onError: () => { + console.log('Leithen errors'); + }, }); export function initGaplessPlayer(nextStore, changeURL) { From 87e42861fce42223b024645c029541ec517334ff Mon Sep 17 00:00:00 2001 From: Thenlie Date: Mon, 15 Sep 2025 22:31:12 -0600 Subject: [PATCH 3/3] add sonner, create audio error toast --- package.json | 1 + pnpm-lock.yaml | 14 ++++++++++++++ public/gapless.cjs | 1 - src/app/(browse)/layout.tsx | 2 ++ src/components/Player.tsx | 21 ++------------------- src/lib/player.ts | 6 +++++- 6 files changed, 24 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 01b4c68..30413f7 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "react-timeago": "^8.3.0", "react-use-cookie": "^1.6.1", "redux": "^4.2.1", + "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "thenby": "^1.3.4", "ua-parser-js": "^1.0.37", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0d59300..26d14f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,6 +92,9 @@ importers: redux: specifier: ^4.2.1 version: 4.2.1 + sonner: + specifier: ^2.0.7 + version: 2.0.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1) tailwind-merge: specifier: ^3.3.1 version: 3.3.1 @@ -2538,6 +2541,12 @@ packages: simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + sonner@2.0.7: + resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -5597,6 +5606,11 @@ snapshots: is-arrayish: 0.3.2 optional: true + sonner@2.0.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + source-map-js@1.2.1: {} spdx-correct@3.2.0: diff --git a/public/gapless.cjs b/public/gapless.cjs index 69b0bb7..9f44d49 100644 --- a/public/gapless.cjs +++ b/public/gapless.cjs @@ -491,7 +491,6 @@ // basic event handlers audioOnError = (e) => { this.debug('audioOnError', e); - // window.dispatchEvent(new CustomEvent('gapless-audio-error', { error: e })); this.queue.onError(); }; diff --git a/src/app/(browse)/layout.tsx b/src/app/(browse)/layout.tsx index 3daf129..61fba98 100644 --- a/src/app/(browse)/layout.tsx +++ b/src/app/(browse)/layout.tsx @@ -3,6 +3,7 @@ import NavBar from '@/components/NavBar'; import cn from '@/lib/cn'; import { getIsInIframe } from '@/lib/isInIframe'; import { ReactNode } from 'react'; +import { Toaster } from 'sonner'; export default async function BrowseLayout({ children, @@ -23,6 +24,7 @@ export default async function BrowseLayout({ return ( +
; @@ -30,7 +31,6 @@ const Player = ({ artistSlugsToName }: Props) => { const [volume, setVolume] = useState( (typeof localStorage !== 'undefined' && localStorage.volume) || 1 ); - // const [hasAudioError, setHasAudioError] = useState(false); const { year, month, day } = splitShowDate(playback.showDate); const { artistSlug, source } = playback; @@ -73,17 +73,6 @@ const Player = ({ artistSlugsToName }: Props) => { localStorage.volume = Math.max(0, Math.min(1, nextVolume)); }; - // useEffect(() => { - // const handleAudioError = () => { - // setHasAudioError(true); - // }; - - // window.addEventListener('gapless-audio-error', handleAudioError); - // return () => { - // window.removeEventListener('gapless-audio-error', handleAudioError); - // }; - // }, []); - return ( {false && activeTrack && ( @@ -201,12 +190,6 @@ const Player = ({ artistSlugsToName }: Props) => {
)} - {/* {hasAudioError && ( - - -

Error loading audio

-
- )} */}
); }; diff --git a/src/lib/player.ts b/src/lib/player.ts index 85689a4..b79bdf3 100644 --- a/src/lib/player.ts +++ b/src/lib/player.ts @@ -5,6 +5,7 @@ import { splitShowDate } from './utils'; import { scrobblePlay } from '../redux/modules/live'; import { updatePlayback } from '../redux/modules/playback'; import { ActiveTrack, GaplessMetadata } from '../types'; +import { toast } from 'sonner'; declare global { interface Window { @@ -157,7 +158,10 @@ const player = new Gapless.Queue({ } }, onError: () => { - console.log('Leithen errors'); + toast.error('There was an error loading your audio', { + toasterId: 'audio-error', + duration: Infinity, + }); }, });