From d874fcd11756bbdf344ee406309bc48921011f72 Mon Sep 17 00:00:00 2001 From: JellyBrick Date: Mon, 12 May 2025 00:16:53 +0900 Subject: [PATCH] fix(music-together): fix duplicate queue listener --- src/plugins/music-together/connection.ts | 22 ++++++- src/plugins/music-together/index.ts | 73 +++++++++++++---------- src/plugins/music-together/queue/queue.ts | 4 +- 3 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/plugins/music-together/connection.ts b/src/plugins/music-together/connection.ts index d634ab66..c06bcd8d 100644 --- a/src/plugins/music-together/connection.ts +++ b/src/plugins/music-together/connection.ts @@ -14,6 +14,7 @@ export type ConnectionEventMap = { | { progress?: number; state?: number; index?: number } | undefined; PERMISSION: Permission | undefined; + CONNECTION_CLOSED: null; }; export type ConnectionEventUnion = { [Event in keyof ConnectionEventMap]: { @@ -31,7 +32,7 @@ type PromiseUtil = { export type ConnectionListener = ( event: ConnectionEventUnion, - conn: DataConnection, + conn: DataConnection | null, ) => void; export type ConnectionMode = 'host' | 'guest' | 'disconnected'; export class Connection { @@ -59,6 +60,16 @@ export class Connection { this._mode = 'host'; await this.registerConnection(conn); }); + this.peer.on('close', () => { + for (const listener of this.listeners) { + listener({ type: 'CONNECTION_CLOSED', payload: null }, null); + } + this.listeners = []; + this.connectionListeners = []; + this.connections = {}; + this.peer.disconnect(); + this.peer.destroy(); + }); this.peer.on('error', async (err) => { if (err.type === PeerErrorType.Network) { // retrying after 10 seconds @@ -99,6 +110,10 @@ export class Connection { this._mode = 'disconnected'; this.connections = {}; this.connectionListeners = []; + for (const listener of this.listeners) { + listener({ type: 'CONNECTION_CLOSED', payload: null }, null); + } + this.listeners = []; this.peer.disconnect(); this.peer.destroy(); } @@ -126,7 +141,9 @@ export class Connection { } public on(listener: ConnectionListener) { - this.listeners.push(listener); + if (!this.listeners.includes(listener)) { + this.listeners.push(listener); + } } public onConnections(listener: (connections?: DataConnection) => void) { @@ -172,7 +189,6 @@ export class Connection { delete this.connections[conn.connectionId]; this.connectionListeners.forEach((listener) => listener(conn)); - this.connectionListeners = []; if (conn.open) { conn.close({ diff --git a/src/plugins/music-together/index.ts b/src/plugins/music-together/index.ts index d208cec6..2bd8cfb2 100644 --- a/src/plugins/music-together/index.ts +++ b/src/plugins/music-together/index.ts @@ -208,7 +208,7 @@ export default createPlugin< const listener = async ( event: ConnectionEventUnion, - conn?: DataConnection, + conn?: DataConnection | null, ) => { this.ignoreChange = true; @@ -311,6 +311,10 @@ export default createPlugin< break; } + case 'CONNECTION_CLOSED': { + this.queue?.off(listener); + break; + } default: { console.warn('Music Together [Host]: Unknown Event', event); break; @@ -359,6 +363,37 @@ export default createPlugin< }); let resolveIgnore: number | null = null; + const queueListener = async (event: ConnectionEventUnion) => { + this.ignoreChange = true; + switch (event.type) { + case 'ADD_SONGS': { + await this.connection?.broadcast('ADD_SONGS', event.payload); + await this.connection?.broadcast('SYNC_QUEUE', undefined); + break; + } + case 'REMOVE_SONG': { + await this.connection?.broadcast('REMOVE_SONG', event.payload); + break; + } + case 'MOVE_SONG': { + await this.connection?.broadcast('MOVE_SONG', event.payload); + await this.connection?.broadcast('SYNC_QUEUE', undefined); + break; + } + case 'SYNC_PROGRESS': { + if (this.permission === 'host-only') + await this.connection?.broadcast('SYNC_QUEUE', undefined); + else + await this.connection?.broadcast('SYNC_PROGRESS', event.payload); + break; + } + } + + if (typeof resolveIgnore === 'number') clearTimeout(resolveIgnore); + resolveIgnore = window.setTimeout(() => { + this.ignoreChange = false; + }, 16); // wait 1 frame + }; const listener = async (event: ConnectionEventUnion) => { this.ignoreChange = true; switch (event.type) { @@ -448,6 +483,10 @@ export default createPlugin< ); break; } + case 'CONNECTION_CLOSED': { + this.queue?.off(queueListener); + break; + } default: { console.warn('Music Together [Guest]: Unknown Event', event); break; @@ -461,37 +500,7 @@ export default createPlugin< }; this.connection.on(listener); - this.queue?.on(async (event: ConnectionEventUnion) => { - this.ignoreChange = true; - switch (event.type) { - case 'ADD_SONGS': { - await this.connection?.broadcast('ADD_SONGS', event.payload); - await this.connection?.broadcast('SYNC_QUEUE', undefined); - break; - } - case 'REMOVE_SONG': { - await this.connection?.broadcast('REMOVE_SONG', event.payload); - break; - } - case 'MOVE_SONG': { - await this.connection?.broadcast('MOVE_SONG', event.payload); - await this.connection?.broadcast('SYNC_QUEUE', undefined); - break; - } - case 'SYNC_PROGRESS': { - if (this.permission === 'host-only') - await this.connection?.broadcast('SYNC_QUEUE', undefined); - else - await this.connection?.broadcast('SYNC_PROGRESS', event.payload); - break; - } - } - - if (typeof resolveIgnore === 'number') clearTimeout(resolveIgnore); - resolveIgnore = window.setTimeout(() => { - this.ignoreChange = false; - }, 16); // wait 1 frame - }); + this.queue?.on(queueListener); if (!this.me) this.me = getDefaultProfile(this.connection.id); this.queue?.injection(); diff --git a/src/plugins/music-together/queue/queue.ts b/src/plugins/music-together/queue/queue.ts index ef91fe51..5bdf2ed2 100644 --- a/src/plugins/music-together/queue/queue.ts +++ b/src/plugins/music-together/queue/queue.ts @@ -252,7 +252,9 @@ export class Queue { } on(listener: QueueEventListener) { - this.listeners.push(listener); + if (!this.listeners.includes(listener)) { + this.listeners.push(listener); + } } off(listener: QueueEventListener) {