UNPKG

win-stream-audio

Version:

šŸŽ§ Stream Windows system audio to Android devices over WiFi with professional audio controls, EQ, pitch shifting, and effects

194 lines (167 loc) • 7.91 kB
#!/usr/bin/env node /** * Win Stream Audio - CLI Interface * Professional Windows audio streaming to Android devices */ const chalk = require('chalk'); const inquirer = require('inquirer'); const ora = require('ora'); const boxen = require('boxen'); const path = require('path'); const { spawn } = require('child_process'); const ip = require('ip'); // ASCII Art Banner const banner = ` ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā•šā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā•— ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā–ˆā–ˆā•— ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā–ˆā–ˆā–ˆā–ˆā•”ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā•šā•ā•ā•ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā•”ā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā•šā•ā• ā–ˆā–ˆā•‘ ā•šā•ā•ā•ā•šā•ā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā• šŸŽ§ AudioPilot Professional Audio Streaming šŸŽ§ `; class WinStreamAudio { constructor() { this.defaultPort = 8080; this.selectedPort = null; this.serverProcess = null; } async start() { console.clear(); // Show banner console.log(chalk.cyan(banner)); console.log(boxen( chalk.white.bold('šŸŽµ Stream Windows system audio to Android devices over WiFi\n') + chalk.gray('Professional audio controls • EQ • Pitch shifting • Effects'), { padding: 1, margin: 1, borderStyle: 'round', borderColor: 'cyan' } )); try { await this.promptForSettings(); await this.startServer(); } catch (error) { console.error(chalk.red('āŒ Error:'), error.message); process.exit(1); } } async promptForSettings() { const questions = [ { type: 'input', name: 'port', message: '🌐 Enter port number:', default: this.defaultPort.toString(), validate: (input) => { const port = parseInt(input); if (isNaN(port) || port < 1024 || port > 65535) { return 'Please enter a valid port number (1024-65535)'; } return true; } }, { type: 'confirm', name: 'openBrowser', message: '🌐 Open browser automatically?', default: true } ]; const answers = await inquirer.prompt(questions); this.selectedPort = parseInt(answers.port); this.openBrowser = answers.openBrowser; } async startServer() { const spinner = ora('šŸš€ Starting AudioPilot server...').start(); try { // Get network info const localIP = ip.address(); const serverPath = path.join(__dirname, '..', 'server.js'); // Set environment variables process.env.PORT = this.selectedPort.toString(); process.env.AUTO_OPEN = this.openBrowser ? 'true' : 'false'; // Start server process this.serverProcess = spawn('node', [serverPath], { stdio: 'pipe', env: process.env }); // Handle server output this.serverProcess.stdout.on('data', (data) => { const output = data.toString(); if (output.includes('Server running')) { spinner.succeed('āœ… AudioPilot server started successfully!'); this.showConnectionInfo(localIP); } }); this.serverProcess.stderr.on('data', (data) => { spinner.fail('āŒ Server error'); console.error(chalk.red(data.toString())); }); this.serverProcess.on('close', (code) => { if (code !== 0) { spinner.fail(`āŒ Server exited with code ${code}`); } else { console.log(chalk.yellow('\nšŸ‘‹ AudioPilot server stopped')); } }); // Handle graceful shutdown process.on('SIGINT', () => { console.log(chalk.yellow('\nšŸ›‘ Shutting down AudioPilot server...')); if (this.serverProcess) { this.serverProcess.kill('SIGTERM'); } process.exit(0); }); // Wait a bit for server to start await new Promise(resolve => setTimeout(resolve, 2000)); } catch (error) { spinner.fail('āŒ Failed to start server'); throw error; } } showConnectionInfo(localIP) { const urls = { local: `http://localhost:${this.selectedPort}`, network: `http://${localIP}:${this.selectedPort}`, source: `http://${localIP}:${this.selectedPort}/simple-source.html`, audiopilot: `http://${localIP}:${this.selectedPort}/audiopilot.html` }; console.log('\n' + boxen( chalk.white.bold('šŸŽÆ AudioPilot Connection URLs\n\n') + chalk.cyan('šŸ“± Main Interface:\n') + chalk.white(` ${urls.network}\n\n`) + chalk.cyan('šŸŽ¤ PC Audio Source:\n') + chalk.white(` ${urls.source}\n\n`) + chalk.cyan('šŸŽ§ AudioPilot Receiver (Android):\n') + chalk.white(` ${urls.audiopilot}\n\n`) + chalk.yellow('šŸ’” Quick Start:\n') + chalk.gray(' 1. On PC: Open "PC Audio Source" and select "Stereo Mix"\n') + chalk.gray(' 2. On Android: Open "AudioPilot Receiver" and connect\n') + chalk.gray(' 3. Enjoy professional audio streaming with EQ & effects!'), { padding: 1, margin: 1, borderStyle: 'double', borderColor: 'green' } )); console.log(chalk.gray('\nšŸ“” Server running on:')); console.log(chalk.white(` Local: ${urls.local}`)); console.log(chalk.white(` Network: ${urls.network}`)); console.log(chalk.yellow('\nšŸŽ›ļø Features:')); console.log(chalk.gray(' • Professional cockpit interface')); console.log(chalk.gray(' • 3-band EQ (Bass, Mid, Treble)')); console.log(chalk.gray(' • Pitch shifting with voice presets')); console.log(chalk.gray(' • Audio effects (Reverb, Echo, Distortion, Chorus)')); console.log(chalk.gray(' • Real-time audio visualization')); console.log(chalk.gray(' • Screen wake system for Android')); console.log(chalk.gray(' • Auto-reconnection')); console.log(chalk.cyan('\nšŸ›‘ Press Ctrl+C to stop the server')); } } // Start the application const app = new WinStreamAudio(); app.start().catch(console.error);