const express = require('express'); const http = require('http'); const path = require('path'); const { WebSocketServer } = require('ws'); const app = express(); const server = http.createServer(app); const wss = new WebSocketServer({ server }); const PORT = 9095; // Current now-playing state let currentState = { title: '', artist: '', album: '', albumArt: '', isPlaying: false, progress: 0, duration: 0, }; // CORS middleware app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { return res.sendStatus(204); } next(); }); // Parse JSON bodies app.use(express.json()); // Image proxy — fetches album art server-side so OBS can display it app.get('/api/image-proxy', async (req, res) => { const url = req.query.url; if (!url) return res.status(400).send('Missing url parameter'); try { const response = await fetch(url); if (!response.ok) return res.status(response.status).send('Upstream error'); res.set('Content-Type', response.headers.get('content-type') || 'image/jpeg'); res.set('Cache-Control', 'public, max-age=3600'); const buffer = await response.arrayBuffer(); res.send(Buffer.from(buffer)); } catch (err) { res.status(502).send('Failed to fetch image'); } }); // Health check app.get('/api/health', (req, res) => { res.json({ status: 'ok' }); }); // Get current now-playing state app.get('/api/now-playing', (req, res) => { res.json(currentState); }); // Update now-playing state app.post('/api/now-playing', (req, res) => { const { title, artist, album, albumArt, isPlaying, progress, duration } = req.body; currentState = { title: title ?? currentState.title, artist: artist ?? currentState.artist, album: album ?? currentState.album, albumArt: albumArt ?? currentState.albumArt, isPlaying: isPlaying ?? currentState.isPlaying, progress: progress ?? currentState.progress, duration: duration ?? currentState.duration, }; // Broadcast to all connected WebSocket clients const message = JSON.stringify(currentState); wss.clients.forEach((client) => { if (client.readyState === 1) { // WebSocket.OPEN client.send(message); } }); res.json(currentState); }); // Serve static files from the overlay directory (after API routes) app.use(express.static(path.join(__dirname, '..', 'overlay'))); // Send current state to newly connected WebSocket clients wss.on('connection', (ws) => { ws.send(JSON.stringify(currentState)); }); server.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); });