@@ -59,6 +59,8 @@ export class PlaylistPlayerSession {
5959
6060 private selectedIndex = 0 ;
6161
62+ private operationQueue : Promise < void > = Promise . resolve ( ) ;
63+
6264 private readonly unsubscribeBackend : ( ) => void ;
6365
6466 constructor (
@@ -188,72 +190,86 @@ export class PlaylistPlayerSession {
188190 }
189191
190192 async playSelected ( ) {
191- await this . playIndex ( this . selectedIndex ) ;
193+ const selectedIndex = this . selectedIndex ;
194+ await this . enqueueOperation ( async ( ) => {
195+ await this . playIndex ( selectedIndex ) ;
196+ } ) ;
192197 }
193198
194199 async playNext ( ) {
195- const nextIndex = this . getAdjacentIndex ( 1 ) ;
196- if ( nextIndex === null ) {
197- return ;
198- }
200+ await this . enqueueOperation ( async ( ) => {
201+ const nextIndex = this . getAdjacentIndex ( 1 ) ;
202+ if ( nextIndex === null ) {
203+ return ;
204+ }
199205
200- await this . playIndex ( nextIndex ) ;
206+ await this . playIndex ( nextIndex ) ;
207+ } ) ;
201208 }
202209
203210 async playPrevious ( ) {
204- const previousIndex = this . getAdjacentIndex ( - 1 ) ;
205- if ( previousIndex === null ) {
206- return ;
207- }
211+ await this . enqueueOperation ( async ( ) => {
212+ const previousIndex = this . getAdjacentIndex ( - 1 ) ;
213+ if ( previousIndex === null ) {
214+ return ;
215+ }
208216
209- await this . playIndex ( previousIndex ) ;
217+ await this . playIndex ( previousIndex ) ;
218+ } ) ;
210219 }
211220
212221 async togglePause ( ) {
213- if ( this . playlist . length === 0 ) {
214- return ;
215- }
216-
217- const { status } = this . backend . getSnapshot ( ) ;
218- if ( status === "stopped" ) {
219- const restartIndex = this . currentIndex ?? this . selectedIndex ;
220- await this . playIndex ( restartIndex ) ;
221- return ;
222- }
222+ const selectedIndex = this . selectedIndex ;
223+ await this . enqueueOperation ( async ( ) => {
224+ if ( this . playlist . length === 0 ) {
225+ return ;
226+ }
223227
224- try {
225- this . clearError ( ) ;
226- await this . backend . togglePause ( ) ;
227- } catch ( error ) {
228- this . reportError ( error ) ;
229- }
228+ const { status } = this . backend . getSnapshot ( ) ;
229+ if ( status === "stopped" ) {
230+ const restartIndex = this . currentIndex ?? selectedIndex ;
231+ await this . playIndex ( restartIndex ) ;
232+ return ;
233+ }
234+
235+ try {
236+ this . clearError ( ) ;
237+ await this . backend . togglePause ( ) ;
238+ } catch ( error ) {
239+ this . reportError ( error ) ;
240+ }
241+ } ) ;
230242 }
231243
232244 async seekBy ( seconds : number ) {
233- if ( seconds === 0 ) {
234- return ;
235- }
236-
237- const { status } = this . backend . getSnapshot ( ) ;
238- if ( status === "stopped" ) {
239- return ;
240- }
245+ await this . enqueueOperation ( async ( ) => {
246+ if ( seconds === 0 ) {
247+ return ;
248+ }
241249
242- try {
243- this . clearError ( ) ;
244- await this . backend . seekBy ( seconds ) ;
245- } catch ( error ) {
246- this . reportError ( error ) ;
247- }
250+ const { status } = this . backend . getSnapshot ( ) ;
251+ if ( status === "stopped" ) {
252+ return ;
253+ }
254+
255+ try {
256+ this . clearError ( ) ;
257+ await this . backend . seekBy ( seconds ) ;
258+ } catch ( error ) {
259+ this . reportError ( error ) ;
260+ }
261+ } ) ;
248262 }
249263
250264 async stop ( ) {
251- try {
252- this . clearError ( ) ;
253- await this . backend . stop ( ) ;
254- } catch ( error ) {
255- this . reportError ( error ) ;
256- }
265+ await this . enqueueOperation ( async ( ) => {
266+ try {
267+ this . clearError ( ) ;
268+ await this . backend . stop ( ) ;
269+ } catch ( error ) {
270+ this . reportError ( error ) ;
271+ }
272+ } ) ;
257273 }
258274
259275 reportError ( error : unknown ) {
@@ -317,7 +333,7 @@ export class PlaylistPlayerSession {
317333 return nextIndex ;
318334 }
319335
320- private async handleBackendEvent ( event : PlayerBackendEvent ) {
336+ private handleBackendEvent ( event : PlayerBackendEvent ) {
321337 switch ( event . type ) {
322338 case "state" :
323339 this . emit ( ) ;
@@ -326,18 +342,26 @@ export class PlaylistPlayerSession {
326342 this . reportError ( event . error ) ;
327343 return ;
328344 case "ended" :
329- if ( event . reason === "eof" ) {
330- const nextIndex = this . getAdjacentIndex ( 1 ) ;
331- if ( nextIndex !== null ) {
332- await this . playIndex ( nextIndex ) ;
333- return ;
345+ void this . enqueueOperation ( async ( ) => {
346+ if ( event . reason === "eof" ) {
347+ const nextIndex = this . getAdjacentIndex ( 1 ) ;
348+ if ( nextIndex !== null ) {
349+ await this . playIndex ( nextIndex ) ;
350+ return ;
351+ }
334352 }
335- }
336353
337- this . emit ( ) ;
354+ this . emit ( ) ;
355+ } ) ;
338356 }
339357 }
340358
359+ private enqueueOperation ( operation : ( ) => Promise < void > ) {
360+ const queuedOperation = this . operationQueue . then ( operation , operation ) ;
361+ this . operationQueue = queuedOperation . catch ( ( ) => { } ) ;
362+ return queuedOperation ;
363+ }
364+
341365 private syncSelectionToSearch ( ) {
342366 const matchedIndex = findTrackIndexByQuery ( this . playlist , this . searchQuery ) ;
343367 if ( matchedIndex !== - 1 ) {
0 commit comments