const { Client, DefaultMediaReceiver } = require('castv2-client'); const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin, terminal: false }); let activeClient = null; let activePlayer = null; function log(msg) { console.log(JSON.stringify({ type: 'log', message: msg })); } function error(msg) { console.error(JSON.stringify({ type: 'error', message: msg })); } rl.on('line', (line) => { try { const data = JSON.parse(line); const { command, args } = data; switch (command) { case 'play': play(args.ip, args.url); break; case 'stop': stop(); break; case 'volume': setVolume(args.level); break; default: error(`Unknown command: ${command}`); } } catch (e) { error(`Failed to parse line: ${e.message}`); } }); function play(ip, url) { if (activeClient) { try { activeClient.close(); } catch (e) { } } activeClient = new Client(); activeClient.connect(ip, () => { log(`Connected to ${ip}`); // First, check if DefaultMediaReceiver is already running activeClient.getSessions((err, sessions) => { if (err) return error(`GetSessions error: ${err.message}`); // DefaultMediaReceiver App ID is CC1AD845 const session = sessions.find(s => s.appId === 'CC1AD845'); if (session) { log('Session already running, joining...'); activeClient.join(session, DefaultMediaReceiver, (err, player) => { if (err) { log('Join failed, attempting launch...'); launchPlayer(url); } else { activePlayer = player; loadMedia(url); } }); } else { launchPlayer(url); } }); }); activeClient.on('error', (err) => { error(`Client error: ${err.message}`); try { activeClient.close(); } catch (e) { } activeClient = null; activePlayer = null; }); } function launchPlayer(url) { if (!activeClient) return; activeClient.launch(DefaultMediaReceiver, (err, player) => { if (err) { // If launch fails with NOT_ALLOWED, it sometimes means we MUST join or something else is occupying it return error(`Launch error: ${err.message}`); } activePlayer = player; loadMedia(url); }); } function loadMedia(url) { if (!activePlayer) return; const media = { contentId: url, contentType: 'audio/mp3', streamType: 'LIVE' }; activePlayer.load(media, { autoplay: true }, (err, status) => { if (err) return error(`Load error: ${err.message}`); log('Media loaded, playing...'); }); activePlayer.on('status', (status) => { // Optional: track status }); } function stop() { if (activePlayer) { try { activePlayer.stop(); } catch (e) { } log('Stopped playback'); } if (activeClient) { try { activeClient.close(); } catch (e) { } activeClient = null; activePlayer = null; } } function setVolume(level) { if (activeClient && activePlayer) { activeClient.setVolume({ level }, (err, status) => { if (err) return error(`Volume error: ${err.message}`); log(`Volume set to ${level}`); }); } else { log('Volume command ignored: Player not initialized'); } } log('Sidecar initialized and waiting for commands');