1- import { useEffect , useRef , useState } from "react" ;
1+ import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
22import cx from "clsx" ;
33
44import { ReactComponent as IconPlay } from "../assets/play.svg" ;
@@ -11,6 +11,8 @@ import { defaultThemeColor } from "../constants";
1111import { PlaylistLoop , PlaylistOrder , usePlaylist } from "../hooks/usePlaylist" ;
1212import { Lyrics } from "./lyrics" ;
1313import { useThemeColor } from "../hooks/useThemeColor" ;
14+ import { useNotice } from "../hooks/useNotice" ;
15+ import { useSetTimeout } from "../hooks/useSetTimeout" ;
1416
1517/**
1618 * @see https://aplayer.js.org/#/home?id=options
@@ -54,13 +56,36 @@ export function APlayer({
5456 getSongId : ( song ) => song . url ,
5557 } ) ;
5658
59+ const [ notice , showNotice ] = useNotice ( ) ;
60+
61+ const autoSkipTimeoutRef = useRef <
62+ ReturnType < typeof setTimeout > | undefined
63+ > ( ) ;
64+ const setTimeout = useSetTimeout ( ) ;
65+
66+ const cancelAutoSkip = useCallback ( ( ) => {
67+ if ( autoSkipTimeoutRef . current ) {
68+ clearTimeout ( autoSkipTimeoutRef . current ) ;
69+ autoSkipTimeoutRef . current = undefined ;
70+ }
71+ } , [ ] ) ;
72+
5773 const audioControl = useAudioControl ( {
5874 src : playlist . currentSong . url ,
5975 initialVolume : volume ,
6076 autoPlay,
61- onError ( ) {
77+ onError ( e ) {
78+ const { error } = e . target as HTMLAudioElement ;
79+
80+ if ( error ) {
81+ showNotice (
82+ "An audio error has occurred, player will skip forward in 2 seconds."
83+ ) ;
84+ }
6285 if ( playlist . hasNextSong ) {
63- playlist . next ( ) ;
86+ autoSkipTimeoutRef . current = setTimeout ( ( ) => {
87+ playlist . next ( ) ;
88+ } , 2000 ) ;
6489 }
6590 } ,
6691 onEnded ( ) {
@@ -88,11 +113,29 @@ export function APlayer({
88113 }
89114 } , [ playlist . currentSong , audioControl . playAudio ] ) ;
90115
116+ const handlePlayButtonClick = useCallback ( ( ) => {
117+ cancelAutoSkip ( ) ;
118+ audioControl . togglePlay ( playlist . currentSong . url ) ;
119+ } , [ audioControl , cancelAutoSkip , playlist . currentSong . url ] ) ;
120+
91121 const hasPlaylist = playlist . length > 1 ;
92122
93123 const [ isPlaylistOpen , setPlaylistOpen ] = useState ( ( ) => hasPlaylist ) ;
94124
95125 const themeColor = useThemeColor ( playlist . currentSong , theme ) ;
126+ const playlistAudioProp = useMemo (
127+ ( ) => ( Array . isArray ( audio ) ? audio : [ audio ] ) ,
128+ [ audio ]
129+ ) ;
130+
131+ const { prioritize } = playlist ;
132+ const handlePlayAudioFromList = useCallback (
133+ ( audioInfo : AudioInfo ) => {
134+ cancelAutoSkip ( ) ;
135+ prioritize ( audioInfo ) ;
136+ } ,
137+ [ cancelAutoSkip , prioritize ]
138+ ) ;
96139
97140 return (
98141 < div
@@ -114,7 +157,7 @@ export function APlayer({
114157 "aplayer-button" ,
115158 audioControl . isPlaying ? "aplayer-pause" : "aplayer-play"
116159 ) }
117- onClick = { ( ) => audioControl . togglePlay ( playlist . currentSong . url ) }
160+ onClick = { handlePlayButtonClick }
118161 >
119162 { audioControl . isPlaying ? < IconPause /> : < IconPlay /> }
120163 </ div >
@@ -150,16 +193,18 @@ export function APlayer({
150193 onLoopChange = { playlist . setLoop }
151194 />
152195 </ div >
153- < div className = "aplayer-notice" > </ div >
196+ < div className = "aplayer-notice" style = { notice . style } >
197+ { notice . text }
198+ </ div >
154199 < div className = "aplayer-miniswitcher" > </ div >
155200 </ div >
156201 { hasPlaylist ? (
157202 < Playlist
158203 themeColor = { themeColor }
159204 open = { isPlaylistOpen }
160- audio = { Array . isArray ( audio ) ? audio : [ audio ] }
205+ audio = { playlistAudioProp }
161206 playingAudioUrl = { playlist . currentSong . url }
162- onPlayAudio = { ( audioInfo ) => playlist . prioritize ( audioInfo ) }
207+ onPlayAudio = { handlePlayAudioFromList }
163208 />
164209 ) : null }
165210 </ div >
0 commit comments