diff --git a/listener.css b/listener.css new file mode 100644 index 0000000..287b161 --- /dev/null +++ b/listener.css @@ -0,0 +1,363 @@ +/* ========== TechDJ Listener Stylesheet ========== */ +/* Standalone styles — no DJ panel CSS loaded. */ + +@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Rajdhani:wght@300;500;700&display=swap'); + +:root { + --bg-dark: #0a0a12; + --panel-bg: rgba(20, 20, 30, 0.8); + --primary-cyan: #00f3ff; + --secondary-magenta: #bc13fe; + --text-main: #e0e0e0; + --text-dim: #888; + --glass-border: 1px solid rgba(255, 255, 255, 0.1); + --glow-opacity: 0.3; + --glow-spread: 30px; + --glow-border: 5px; +} + +* { + -webkit-overflow-scrolling: touch; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + margin: 0; + background-color: var(--bg-dark); + color: var(--text-main); + font-family: 'Rajdhani', sans-serif; + height: 100vh; + display: flex; + flex-direction: column; + overflow: hidden; + background-image: + radial-gradient(circle at 10% 20%, rgba(0, 243, 255, 0.15) 0%, transparent 25%), + radial-gradient(circle at 90% 80%, rgba(188, 19, 254, 0.15) 0%, transparent 25%), + radial-gradient(circle at 50% 50%, rgba(0, 243, 255, 0.05) 0%, transparent 50%); +} + +body::before { + content: ''; + position: fixed; + inset: 0; + pointer-events: none; + z-index: 1; + opacity: var(--glow-opacity, 0.3); + transition: all 1s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Listener atmospheric glow */ +body.listener-glow::before { + animation: pulse-listener 4s ease-in-out infinite; +} + +@keyframes pulse-listener { + 0%, 100% { + filter: hue-rotate(0deg) brightness(1.2); + box-shadow: + 0 0 var(--glow-spread) rgba(0, 80, 255, calc(var(--glow-opacity) * 1.5)), + 0 0 calc(var(--glow-spread) * 1.5) rgba(188, 19, 254, calc(var(--glow-opacity) * 1.5)), + inset 0 0 var(--glow-spread) rgba(0, 80, 255, calc(var(--glow-opacity) * 1)), + inset 0 0 calc(var(--glow-spread) * 1.5) rgba(188, 19, 254, calc(var(--glow-opacity) * 1)); + } + 50% { + filter: hue-rotate(15deg) brightness(1.8); + box-shadow: + 0 0 calc(var(--glow-spread) * 1.5) rgba(0, 120, 255, calc(var(--glow-opacity) * 2.2)), + 0 0 calc(var(--glow-spread) * 2) rgba(220, 50, 255, calc(var(--glow-opacity) * 2.2)), + 0 0 calc(var(--glow-spread) * 4) rgba(0, 243, 255, calc(var(--glow-opacity) * 1)), + inset 0 0 calc(var(--glow-spread) * 1.5) rgba(0, 120, 255, calc(var(--glow-opacity) * 1.5)), + inset 0 0 calc(var(--glow-spread) * 2) rgba(220, 50, 255, calc(var(--glow-opacity) * 1.5)); + } +} + +/* ========== Listener Mode Layout ========== */ + +.listener-mode { + position: fixed; + inset: 0; + background: linear-gradient(135deg, #0a0a12 0%, #1a0a1a 100%); + z-index: 10; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 40px; +} + +.listener-header { + text-align: center; + margin-bottom: 40px; +} + +.listener-header h1 { + font-family: 'Orbitron', sans-serif; + font-size: 3rem; + color: var(--secondary-magenta); + text-shadow: 0 0 30px var(--secondary-magenta); + margin: 0 0 20px 0; +} + +/* ========== Glow Text ========== */ + +.glow-text { + color: #fff; + text-shadow: 0 0 10px var(--secondary-magenta), 0 0 20px var(--secondary-magenta); + animation: text-glow-pulse 2s infinite ease-in-out; +} + +@keyframes text-glow-pulse { + 0%, 100% { + opacity: 0.8; + text-shadow: 0 0 10px var(--secondary-magenta); + } + 50% { + opacity: 1; + text-shadow: 0 0 15px var(--secondary-magenta), 0 0 30px var(--secondary-magenta); + } +} + +/* ========== Live Indicator ========== */ + +.live-indicator { + display: inline-flex; + align-items: center; + gap: 10px; + padding: 10px 20px; + background: rgba(188, 19, 254, 0.2); + border: 2px solid var(--secondary-magenta); + border-radius: 25px; + font-family: 'Orbitron', sans-serif; + font-size: 1.2rem; + color: var(--secondary-magenta); + box-shadow: 0 0 20px rgba(188, 19, 254, 0.4); +} + +.pulse-dot { + width: 12px; + height: 12px; + background: var(--secondary-magenta); + border-radius: 50%; + animation: pulse-dot 1.5s ease-in-out infinite; +} + +@keyframes pulse-dot { + 0%, 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.3); + opacity: 0.7; + } +} + +/* ========== Listener Content Card ========== */ + +.listener-content { + max-width: 600px; + width: 100%; + background: rgba(10, 10, 20, 0.8); + border: 2px solid var(--secondary-magenta); + border-radius: 20px; + padding: 40px; + box-shadow: 0 0 40px rgba(188, 19, 254, 0.3); + backdrop-filter: blur(10px); +} + +/* ========== Visualiser ========== */ + +#viz-listener { + width: 100%; + height: 80px; + display: block; + margin: 20px 0; +} + +/* ========== Now Playing ========== */ + +.now-playing { + text-align: center; + font-family: 'Orbitron', sans-serif; + font-size: 1.5rem; + color: var(--text-main); + margin-bottom: 30px; + padding: 20px; + background: rgba(0, 0, 0, 0.3); + border-radius: 10px; + min-height: 60px; + display: flex; + align-items: center; + justify-content: center; +} + +/* ========== Volume ========== */ + +.volume-control { + margin-bottom: 20px; +} + +.volume-control label { + display: block; + margin-bottom: 10px; + font-size: 1.1rem; + color: var(--text-dim); +} + +.volume-control input[type="range"] { + width: 100%; + height: 8px; + -webkit-appearance: none; + appearance: none; + background: rgba(188, 19, 254, 0.3); + border-radius: 4px; + outline: none; +} + +.volume-control input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 20px; + height: 20px; + border-radius: 50%; + background: var(--secondary-magenta); + cursor: pointer; + box-shadow: 0 0 10px var(--secondary-magenta); +} + +.volume-control input[type="range"]::-moz-range-thumb { + width: 20px; + height: 20px; + border-radius: 50%; + background: var(--secondary-magenta); + cursor: pointer; + border: none; + box-shadow: 0 0 10px var(--secondary-magenta); +} + +/* ========== Connection Status ========== */ + +.connection-status { + text-align: center; + font-family: 'Rajdhani', sans-serif; + font-size: 0.9rem; + color: var(--text-dim); + padding: 10px; + background: rgba(0, 0, 0, 0.3); + border-radius: 5px; +} + +.connection-status.connected { + color: #00ff00; + text-shadow: 0 0 10px #00ff00; +} + +.connection-status.disconnected { + color: #ff4444; +} + +/* ========== Enable Audio Button ========== */ + +.enable-audio-btn { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + padding: 30px 40px; + margin: 30px 0; + background: linear-gradient(145deg, #1a1a1a, #0a0a0a); + border: 3px solid var(--secondary-magenta); + border-radius: 15px; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 0 30px rgba(188, 19, 254, 0.3); + font-family: 'Orbitron', sans-serif; +} + +.enable-audio-btn:hover { + background: linear-gradient(145deg, #2a2a2a, #1a1a1a); + box-shadow: 0 0 50px rgba(188, 19, 254, 0.6); + transform: translateY(-3px); + border-color: #ff00ff; +} + +.enable-audio-btn:active { + transform: translateY(-1px); + box-shadow: 0 0 40px rgba(188, 19, 254, 0.5); +} + +.enable-audio-btn .audio-icon { + font-size: 3rem; + animation: pulse-icon 2s ease-in-out infinite; +} + +@keyframes pulse-icon { + 0%, 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.1); + opacity: 0.8; + } +} + +.enable-audio-btn .audio-text { + font-size: 1.5rem; + font-weight: bold; + color: var(--secondary-magenta); + text-shadow: 0 0 10px var(--secondary-magenta); +} + +.enable-audio-btn .audio-subtitle { + font-size: 0.9rem; + color: var(--text-dim); + font-family: 'Rajdhani', sans-serif; +} + +/* ========== Mobile Responsiveness ========== */ + +@media (max-width: 768px) { + .listener-mode { + padding: 20px; + justify-content: flex-start; + padding-top: 60px; + } + + .listener-header h1 { + font-size: 2.2rem; + margin-bottom: 10px; + } + + .live-indicator { + font-size: 0.9rem; + padding: 6px 15px; + } + + .listener-content { + padding: 25px; + margin-top: 10px; + border-radius: 15px; + } + + .now-playing { + font-size: 1.1rem; + min-height: 80px; + line-height: 1.4; + margin-bottom: 20px; + } + + .volume-control label { + font-size: 0.9rem; + } + + #viz-listener { + height: 60px !important; + margin: 15px 0; + } +} diff --git a/listener.html b/listener.html new file mode 100644 index 0000000..359727f --- /dev/null +++ b/listener.html @@ -0,0 +1,46 @@ + + + + + + + TechDJ Live + + + + + +
+
+

TECHDJ LIVE

+
+ + LIVE +
+
+
+
Waiting for stream...
+ + + + + + +
+ + +
+
Connecting...
+
+
+ + + + + + diff --git a/listener.js b/listener.js new file mode 100644 index 0000000..d7b7741 --- /dev/null +++ b/listener.js @@ -0,0 +1,476 @@ +// ========== TechDJ Listener ========== +// Standalone listener script — no DJ panel code loaded. + +'use strict'; + +// --- State --- +let socket = null; +let listenerAudioContext = null; +let listenerGainNode = null; +let listenerAnalyserNode = null; +let listenerMediaElementSourceNode = null; +let listenerVuMeterRunning = false; + +// --- Helpers --- + +function getMp3FallbackUrl() { + // Use same-origin so this works behind reverse proxies (e.g. Cloudflare) + return `${window.location.origin}/stream.mp3`; +} + +function updateGlowIntensity(val) { + const opacity = parseInt(val, 10) / 100; + const spread = (parseInt(val, 10) / 100) * 80; + document.documentElement.style.setProperty('--glow-opacity', opacity); + document.documentElement.style.setProperty('--glow-spread', `${spread}px`); +} + +// --- VU Meter --- + +function startListenerVUMeter() { + if (listenerVuMeterRunning) return; + listenerVuMeterRunning = true; + + const draw = () => { + if (!listenerVuMeterRunning) return; + requestAnimationFrame(draw); + + const canvas = document.getElementById('viz-listener'); + if (!canvas || !listenerAnalyserNode) return; + + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + // Keep canvas sized correctly for DPI + const dpr = window.devicePixelRatio || 1; + const rect = canvas.getBoundingClientRect(); + const targetW = Math.max(1, Math.floor(rect.width * dpr)); + const targetH = Math.max(1, Math.floor(rect.height * dpr)); + if (canvas.width !== targetW || canvas.height !== targetH) { + canvas.width = targetW; + canvas.height = targetH; + } + + const analyser = listenerAnalyserNode; + const bufferLength = analyser.frequencyBinCount; + const dataArray = new Uint8Array(bufferLength); + analyser.getByteFrequencyData(dataArray); + + const width = canvas.width; + const height = canvas.height; + const barCount = 32; + const barWidth = width / barCount; + + ctx.fillStyle = '#0a0a12'; + ctx.fillRect(0, 0, width, height); + + // Magenta hue (matches Deck B styling) + const hue = 280; + for (let i = 0; i < barCount; i++) { + const freqIndex = Math.floor(Math.pow(i / barCount, 1.5) * bufferLength); + const value = (dataArray[freqIndex] || 0) / 255; + const barHeight = value * height; + + const lightness = 30 + (value * 50); + const gradient = ctx.createLinearGradient(0, height, 0, height - barHeight); + gradient.addColorStop(0, `hsl(${hue}, 100%, ${lightness}%)`); + gradient.addColorStop(1, `hsl(${hue}, 100%, ${Math.min(lightness + 20, 80)}%)`); + + ctx.fillStyle = gradient; + ctx.fillRect(i * barWidth, height - barHeight, barWidth - 2, barHeight); + } + }; + + draw(); +} + +// --- Socket.IO --- + +function initSocket() { + if (socket) return socket; + + const serverUrl = window.location.origin; + console.log(`[LISTENER] Initializing Socket.IO connection to: ${serverUrl}`); + + socket = io(serverUrl, { + transports: ['websocket'], + reconnection: true, + reconnectionAttempts: 10 + }); + + socket.on('connect', () => { + console.log('[OK] Connected to streaming server'); + socket.emit('get_listener_count'); + }); + + socket.on('connect_error', (error) => { + console.error('[ERROR] Connection error:', error.message); + }); + + socket.on('disconnect', (reason) => { + console.log(`[ERROR] Disconnected: ${reason}`); + }); + + socket.on('listener_count', (data) => { + const el = document.getElementById('listener-count'); + if (el) el.textContent = data.count; + }); + + return socket; +} + +// --- Listener Mode Init --- + +function initListenerMode() { + console.log('[LISTENER] Initializing listener mode (MP3 stream)...'); + + // Apply glow + updateGlowIntensity(30); + + // Clean up old audio element if it exists (e.g. page refresh) + if (window.listenerAudio) { + console.log('[CLEAN] Cleaning up old audio element'); + try { + window.listenerAudio.pause(); + if (window.listenerAudio.src) { + URL.revokeObjectURL(window.listenerAudio.src); + } + window.listenerAudio.removeAttribute('src'); + window.listenerAudio.remove(); + } catch (e) { + console.warn('Error cleaning up old audio:', e); + } + + if (listenerMediaElementSourceNode) { + try { listenerMediaElementSourceNode.disconnect(); } catch (_) { } + listenerMediaElementSourceNode = null; + } + if (listenerAnalyserNode) { + try { listenerAnalyserNode.disconnect(); } catch (_) { } + listenerAnalyserNode = null; + } + if (listenerGainNode) { + try { listenerGainNode.disconnect(); } catch (_) { } + listenerGainNode = null; + } + + window.listenerAudio = null; + window.listenerMediaSource = null; + window.listenerAudioEnabled = false; + } + + // Create fresh audio element + const audio = document.createElement('audio'); + audio.autoplay = false; + audio.muted = false; + audio.controls = false; + audio.playsInline = true; + audio.setAttribute('playsinline', ''); + audio.style.display = 'none'; + audio.crossOrigin = 'anonymous'; + document.body.appendChild(audio); + console.log('[NEW] Created fresh audio element for listener'); + + // --- Stall Watchdog --- + let lastCheckedTime = 0; + let stallCount = 0; + let watchdogInterval = null; + + const stopWatchdog = () => { + if (watchdogInterval) { + clearInterval(watchdogInterval); + watchdogInterval = null; + } + }; + + const startWatchdog = () => { + stopWatchdog(); + lastCheckedTime = audio.currentTime; + stallCount = 0; + + watchdogInterval = setInterval(() => { + if (!window.listenerAudioEnabled || audio.paused) return; + + if (audio.currentTime === lastCheckedTime && audio.currentTime > 0) { + stallCount++; + console.warn(`[WARN] Stream stall detected (${stallCount}/3)...`); + + if (stallCount >= 3) { + console.error('[ALERT] Stream stalled. Force reconnecting...'); + reconnectStream(); + stallCount = 0; + } + } else { + stallCount = 0; + } + lastCheckedTime = audio.currentTime; + }, 2000); + }; + + const reconnectStream = () => { + if (!window.listenerAudioEnabled || !window.listenerAudio) return; + + console.log('[RECONNECT] Reconnecting stream...'); + const statusEl = document.getElementById('connection-status'); + if (statusEl) { + statusEl.textContent = '[WAIT] Connection weak - Reconnecting...'; + statusEl.classList.remove('glow-text'); + } + + const wasPaused = window.listenerAudio.paused; + window.listenerAudio.src = getMp3FallbackUrl() + '?t=' + Date.now(); + window.listenerAudio.load(); + + if (!wasPaused) { + window.listenerAudio.play() + .then(() => { + if (statusEl) { + statusEl.textContent = '[ACTIVE] Reconnected'; + statusEl.classList.add('glow-text'); + } + startListenerVUMeter(); + }) + .catch(e => console.warn('Reconnect play failed:', e)); + } + }; + + // Set MP3 stream source + audio.src = getMp3FallbackUrl(); + audio.load(); + console.log(`[STREAM] Listener source set to MP3 stream: ${audio.src}`); + + // Auto-reconnect on stream error + audio.onerror = () => { + if (!window.listenerAudioEnabled) return; + console.error('[ERROR] Audio stream error!'); + reconnectStream(); + }; + + audio.onplay = () => { + console.log('[PLAY] Stream playing'); + startWatchdog(); + const statusEl = document.getElementById('connection-status'); + if (statusEl) statusEl.classList.add('glow-text'); + }; + + audio.onpause = () => { + console.log('[PAUSE] Stream paused'); + stopWatchdog(); + }; + + // Show enable audio button + const enableAudioBtn = document.getElementById('enable-audio-btn'); + const statusEl = document.getElementById('connection-status'); + + if (enableAudioBtn) { + enableAudioBtn.style.display = 'flex'; + } + if (statusEl) { + statusEl.textContent = '[INFO] Click "Enable Audio" to start listening (MP3)'; + } + + // Store for later activation + window.listenerAudio = audio; + window.listenerMediaSource = null; + window.listenerAudioEnabled = false; + + // Initialise socket and join + initSocket(); + socket.emit('join_listener'); + + // --- Socket event handlers --- + + socket.on('broadcast_started', () => { + const nowPlayingEl = document.getElementById('listener-now-playing'); + if (nowPlayingEl) nowPlayingEl.textContent = 'Stream is live!'; + + if (window.listenerAudio) { + console.log('[BROADCAST] Broadcast started: Refreshing audio stream...'); + const wasPlaying = !window.listenerAudio.paused; + window.listenerAudio.src = getMp3FallbackUrl(); + window.listenerAudio.load(); + if (wasPlaying || window.listenerAudioEnabled) { + window.listenerAudio.play().catch(e => console.warn('Auto-play after refresh blocked:', e)); + } + } + }); + + socket.on('stream_status', (data) => { + const nowPlayingEl = document.getElementById('listener-now-playing'); + if (nowPlayingEl) { + if (data.active) { + const status = data.remote_relay ? 'Remote stream is live!' : 'DJ stream is live!'; + nowPlayingEl.textContent = status; + } else { + nowPlayingEl.textContent = 'Stream offline - waiting for DJ...'; + } + } + }); + + socket.on('broadcast_stopped', () => { + const nowPlayingEl = document.getElementById('listener-now-playing'); + if (nowPlayingEl) nowPlayingEl.textContent = 'Stream ended'; + }); + + socket.on('connect', () => { + const statusEl = document.getElementById('connection-status'); + if (statusEl && window.listenerAudioEnabled) { + statusEl.textContent = '[ACTIVE] Connected'; + } + socket.emit('join_listener'); + }); + + socket.on('disconnect', () => { + const statusEl = document.getElementById('connection-status'); + if (statusEl) statusEl.textContent = '[OFFLINE] Disconnected'; + }); +} + +// --- Enable Audio (user gesture required) --- + +async function enableListenerAudio() { + console.log('[LISTENER] Enabling audio via user gesture...'); + + const enableAudioBtn = document.getElementById('enable-audio-btn'); + const statusEl = document.getElementById('connection-status'); + const audioText = enableAudioBtn ? enableAudioBtn.querySelector('.audio-text') : null; + + if (audioText) audioText.textContent = 'INITIALIZING...'; + + try { + // 1. Create AudioContext if needed + if (!listenerAudioContext) { + listenerAudioContext = new (window.AudioContext || window.webkitAudioContext)(); + } + + // 2. Resume audio context (CRITICAL for Chrome/Safari) + if (listenerAudioContext.state === 'suspended') { + await listenerAudioContext.resume(); + console.log('[OK] Audio context resumed'); + } + + // 3. Bridge Audio Element to AudioContext + if (window.listenerAudio) { + try { + if (!listenerGainNode) { + listenerGainNode = listenerAudioContext.createGain(); + listenerGainNode.gain.value = 0.8; + listenerGainNode.connect(listenerAudioContext.destination); + } + + if (!listenerAnalyserNode) { + listenerAnalyserNode = listenerAudioContext.createAnalyser(); + listenerAnalyserNode.fftSize = 256; + } + + if (!listenerMediaElementSourceNode) { + listenerMediaElementSourceNode = listenerAudioContext.createMediaElementSource(window.listenerAudio); + } + + // Clean single connection chain: media -> analyser -> gain -> destination + try { listenerMediaElementSourceNode.disconnect(); } catch (_) { } + try { listenerAnalyserNode.disconnect(); } catch (_) { } + + listenerMediaElementSourceNode.connect(listenerAnalyserNode); + listenerAnalyserNode.connect(listenerGainNode); + + window.listenerAudio._connectedToContext = true; + console.log('[OK] Connected audio element to AudioContext (with analyser)'); + + startListenerVUMeter(); + } catch (e) { + console.warn('Could not connect to AudioContext:', e.message); + } + } + + // 4. Prepare and start audio playback + if (window.listenerAudio) { + window.listenerAudio.muted = false; + window.listenerAudio.volume = 1.0; + + const volEl = document.getElementById('listener-volume'); + const volValue = volEl ? parseInt(volEl.value, 10) : 80; + setListenerVolume(Number.isFinite(volValue) ? volValue : 80); + + const hasBufferedData = () => { + return window.listenerAudio.buffered && window.listenerAudio.buffered.length > 0; + }; + + window.listenerAudioEnabled = true; + + if (audioText) audioText.textContent = 'STARTING...'; + console.log('[PLAY] Attempting to play audio...'); + + const playTimeout = setTimeout(() => { + if (!hasBufferedData()) { + console.warn('[WARN] Audio play is taking a long time (buffering)...'); + if (audioText) audioText.textContent = 'STILL BUFFERING...'; + window.listenerAudio.load(); + } + }, 8000); + + const playPromise = window.listenerAudio.play(); + + if (!hasBufferedData() && audioText) { + audioText.textContent = 'BUFFERING...'; + } + + try { + await playPromise; + clearTimeout(playTimeout); + console.log('[OK] Audio playback started successfully'); + } catch (e) { + clearTimeout(playTimeout); + throw e; + } + } + + // 5. Hide the button and update status + if (enableAudioBtn) { + enableAudioBtn.style.opacity = '0'; + setTimeout(() => { + enableAudioBtn.style.display = 'none'; + }, 300); + } + + if (statusEl) { + statusEl.textContent = '[ACTIVE] Audio Active - Enjoy the stream'; + statusEl.classList.add('glow-text'); + } + + } catch (error) { + console.error('[ERROR] Failed to enable audio:', error); + const stashedBtn = document.getElementById('enable-audio-btn'); + const stashedStatus = document.getElementById('connection-status'); + const aText = stashedBtn ? stashedBtn.querySelector('.audio-text') : null; + + if (aText) aText.textContent = 'RETRY ENABLE'; + if (stashedStatus) { + let errorMsg = error.name + ': ' + error.message; + if (error.name === 'NotAllowedError') { + errorMsg = 'Browser blocked audio (NotAllowedError). Check permissions.'; + } else if (error.name === 'NotSupportedError') { + errorMsg = 'MP3 stream not supported or unavailable (NotSupportedError).'; + } + stashedStatus.textContent = errorMsg; + + if (error.name === 'NotSupportedError') { + stashedStatus.textContent = 'MP3 stream failed. Is ffmpeg installed on the server?'; + } + } + } +} + +// --- Volume --- + +function setListenerVolume(value) { + if (listenerGainNode) { + listenerGainNode.gain.value = value / 100; + } +} + +// --- Boot --- + +document.addEventListener('DOMContentLoaded', () => { + initListenerMode(); +}); diff --git a/server.py b/server.py index 71af96a..86a1736 100644 --- a/server.py +++ b/server.py @@ -269,7 +269,7 @@ if not os.path.exists(MUSIC_FOLDER): os.makedirs(MUSIC_FOLDER) # Helper for shared routes -def setup_shared_routes(app): +def setup_shared_routes(app, index_file='index.html'): @app.route('/library.json') def get_library(): library = [] @@ -359,7 +359,7 @@ def setup_shared_routes(app): @app.route('/') def index(): - return send_from_directory('.', 'index.html') + return send_from_directory('.', index_file) @app.route('/upload', methods=['POST']) def upload_file(): @@ -664,7 +664,7 @@ def dj_audio(data): listener_app = Flask(__name__, static_folder='.', static_url_path='') listener_app.config['SECRET_KEY'] = CONFIG_SECRET + '_listener' listener_app.config['MAX_CONTENT_LENGTH'] = CONFIG_MAX_UPLOAD_MB * 1024 * 1024 -setup_shared_routes(listener_app) +setup_shared_routes(listener_app, index_file='listener.html') # Block write/admin endpoints on the listener server @listener_app.before_request