diff --git a/sidecar/index.js b/sidecar/index.js index 26c4640..003418f 100644 --- a/sidecar/index.js +++ b/sidecar/index.js @@ -76,10 +76,13 @@ rl.on('line', (line) => { function play(ip, url, metadata) { if (activeClient) { + try { activeClient.removeAllListeners(); } catch (e) { } try { activeClient.close(); } catch (e) { } } activeClient = new Client(); + // Increase max listeners for this client instance to avoid Node warnings + try { if (typeof activeClient.setMaxListeners === 'function') activeClient.setMaxListeners(50); } catch (e) {} activeClient._playMetadata = metadata || {}; activeClient.connect(ip, () => { @@ -109,9 +112,12 @@ function play(ip, url, metadata) { // Join can fail if the session is stale; stop it and retry launch. stopSessions(activeClient, [session], () => launchPlayer(url, activeClient._playMetadata, /*didStopFirst*/ true)); } else { - activePlayer = player; - loadMedia(url, activeClient._playMetadata); - } + // Clean up previous player listeners before replacing + try { if (activePlayer && typeof activePlayer.removeAllListeners === 'function') activePlayer.removeAllListeners(); } catch (e) {} + activePlayer = player; + try { if (typeof activePlayer.setMaxListeners === 'function') activePlayer.setMaxListeners(50); } catch (e) {} + loadMedia(url, activeClient._playMetadata); + } }); } else { // Backdrop or other non-media session present: skip stopping to avoid platform sender crash, just launch. @@ -154,7 +160,9 @@ function launchPlayer(url, metadata, didStopFirst) { try { error(`Launch retry error full: ${JSON.stringify(retryErr)}`); } catch (e) { /* ignore */ } return; } + try { if (activePlayer && typeof activePlayer.removeAllListeners === 'function') activePlayer.removeAllListeners(); } catch (e) {} activePlayer = retryPlayer; + try { if (typeof activePlayer.setMaxListeners === 'function') activePlayer.setMaxListeners(50); } catch (e) {} loadMedia(url, metadata); }); }); @@ -166,7 +174,9 @@ function launchPlayer(url, metadata, didStopFirst) { try { error(`Launch error full: ${JSON.stringify(err)}`); } catch (e) { /* ignore */ } return; } + try { if (activePlayer && typeof activePlayer.removeAllListeners === 'function') activePlayer.removeAllListeners(); } catch (e) {} activePlayer = player; + try { if (typeof activePlayer.setMaxListeners === 'function') activePlayer.setMaxListeners(50); } catch (e) {} loadMedia(url, metadata); }); } @@ -175,19 +185,41 @@ function loadMedia(url, metadata) { if (!activePlayer) return; const meta = metadata || {}; + // Build a richer metadata payload. Many receivers only honor specific + // fields; we set both Music metadata and generic hints via `customData`. const media = { contentId: url, contentType: 'audio/mpeg', streamType: 'LIVE', metadata: { + // Use MusicTrack metadata (common on audio receivers) but include + // a subtitle field in case receivers surface it. metadataType: 3, // MusicTrackMediaMetadata title: meta.title || 'Radio Station', albumName: 'Radio Player', - artist: meta.artist || meta.station || '', - images: meta.image ? [{ url: meta.image }] : [] + artist: meta.artist || meta.subtitle || meta.station || '', + subtitle: meta.subtitle || '', + images: (meta.image ? [ + { url: meta.image }, + // also include a large hint for receivers that prefer big artwork + { url: meta.image, width: 1920, height: 1080 } + ] : []) + }, + // Many receivers ignore `customData`, but some Styled receivers will + // use it. Include background and theming hints here. + customData: { + appName: meta.appName || 'Radio Player', + backgroundImage: meta.backgroundImage || meta.image || undefined, + backgroundGradient: meta.bgGradient || '#6a0dad', + themeHint: { + primary: '#6a0dad', + accent: '#b36cf3' + } } }; + // Ensure we don't accumulate 'status' listeners across loads + try { if (activePlayer && typeof activePlayer.removeAllListeners === 'function') activePlayer.removeAllListeners('status'); } catch (e) {} activePlayer.load(media, { autoplay: true }, (err, status) => { if (err) return error(`Load error: ${err.message}`); log('Media loaded, playing...'); @@ -201,9 +233,11 @@ function loadMedia(url, metadata) { function stop() { if (activePlayer) { try { activePlayer.stop(); } catch (e) { } + try { if (typeof activePlayer.removeAllListeners === 'function') activePlayer.removeAllListeners(); } catch (e) {} log('Stopped playback'); } if (activeClient) { + try { if (typeof activeClient.removeAllListeners === 'function') activeClient.removeAllListeners(); } catch (e) {} try { activeClient.close(); } catch (e) { } activeClient = null; activePlayer = null; diff --git a/src/main.js b/src/main.js index 71ec7f2..55f202d 100644 --- a/src/main.js +++ b/src/main.js @@ -1268,7 +1268,11 @@ async function play() { url: castUrl, title: station.title || 'Radio', artist: station.slogan || undefined, - image: station.logo || undefined + image: station.logo || undefined, + // Additional metadata hints for receivers + subtitle: station.slogan || station.name, + backgroundImage: station.background || station.logo || undefined, + bgGradient: station.bgGradient || 'linear-gradient(135deg,#5b2d91,#b36cf3)' }); isPlaying = true; // Sync volume