fix: revert listener port to 5001; set SRT port to 5005 via mediamtx_srt_port config
This commit is contained in:
parent
498832bdcb
commit
effb52c5df
20
README.md
20
README.md
|
|
@ -3,7 +3,7 @@
|
||||||
TechDJ Pro is a local DJ web app with a dual-port architecture:
|
TechDJ Pro is a local DJ web app with a dual-port architecture:
|
||||||
|
|
||||||
- **DJ Panel** (mix/load tracks + start broadcast): `http://localhost:5000`
|
- **DJ Panel** (mix/load tracks + start broadcast): `http://localhost:5000`
|
||||||
- **Listener Page** (receive the live stream): `http://localhost:5005`
|
- **Listener Page** (receive the live stream): `http://localhost:5001`
|
||||||
|
|
||||||
It supports:
|
It supports:
|
||||||
- Local library playback (files in `music/`)
|
- Local library playback (files in `music/`)
|
||||||
|
|
@ -104,7 +104,7 @@ If you want to lock it while you’re playing live, create a `config.json` (not
|
||||||
Behavior:
|
Behavior:
|
||||||
- If `dj_panel_password` is empty/missing, the DJ panel is **unlocked** (default).
|
- If `dj_panel_password` is empty/missing, the DJ panel is **unlocked** (default).
|
||||||
- If set, visiting `http://<DJ_MACHINE_IP>:5000` shows a login prompt.
|
- If set, visiting `http://<DJ_MACHINE_IP>:5000` shows a login prompt.
|
||||||
- Listener (`:5005`) is not affected.
|
- Listener (`:5001`) is not affected.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -120,7 +120,7 @@ python server.py
|
||||||
You should see output like:
|
You should see output like:
|
||||||
|
|
||||||
- DJ panel: `http://localhost:5000`
|
- DJ panel: `http://localhost:5000`
|
||||||
- Listener page: `http://localhost:5005`
|
- Listener page: `http://localhost:5001`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -149,10 +149,10 @@ TechDJ can relay live streams from other DJ servers:
|
||||||
### Listener workflow
|
### Listener workflow
|
||||||
|
|
||||||
1. Open the Listener Page:
|
1. Open the Listener Page:
|
||||||
- Same machine: `http://localhost:5005`
|
- Same machine: `http://localhost:5001`
|
||||||
- Another device on your LAN/Wi‑Fi:
|
- Another device on your LAN/Wi‑Fi:
|
||||||
|
|
||||||
`http://<DJ_MACHINE_IP>:5005`
|
`http://<DJ_MACHINE_IP>:5001`
|
||||||
|
|
||||||
2. Click **ENABLE AUDIO** if prompted
|
2. Click **ENABLE AUDIO** if prompted
|
||||||
- Browsers block autoplay by default; user interaction is required.3. Enjoy the live stream with real-time spectrum visualization
|
- Browsers block autoplay by default; user interaction is required.3. Enjoy the live stream with real-time spectrum visualization
|
||||||
|
|
@ -186,7 +186,7 @@ Use the LAN IP (commonly `192.168.x.x` or `10.x.x.x`).
|
||||||
|
|
||||||
Make sure the DJ machine allows inbound connections on:
|
Make sure the DJ machine allows inbound connections on:
|
||||||
- TCP `5000` (DJ Panel)
|
- TCP `5000` (DJ Panel)
|
||||||
- TCP `5005` (Listener)
|
- TCP `5001` (Listener)
|
||||||
|
|
||||||
If listeners can’t connect, this is often the cause.
|
If listeners can’t connect, this is often the cause.
|
||||||
|
|
||||||
|
|
@ -196,7 +196,7 @@ If listeners can’t connect, this is often the cause.
|
||||||
|
|
||||||
TechDJ serves the listener audio as an **MP3 HTTP stream**:
|
TechDJ serves the listener audio as an **MP3 HTTP stream**:
|
||||||
|
|
||||||
- MP3 stream endpoint: `http://<DJ_MACHINE_IP>:5005/stream.mp3`
|
- MP3 stream endpoint: `http://<DJ_MACHINE_IP>:5001/stream.mp3`
|
||||||
|
|
||||||
This requires `ffmpeg` installed on the DJ/server machine.
|
This requires `ffmpeg` installed on the DJ/server machine.
|
||||||
|
|
||||||
|
|
@ -204,7 +204,7 @@ This requires `ffmpeg` installed on the DJ/server machine.
|
||||||
|
|
||||||
- Stream debug JSON:
|
- Stream debug JSON:
|
||||||
|
|
||||||
`http://<DJ_MACHINE_IP>:5005/stream_debug`
|
`http://<DJ_MACHINE_IP>:5001/stream_debug`
|
||||||
|
|
||||||
This shows whether ffmpeg is running and whether MP3 bytes are being produced.
|
This shows whether ffmpeg is running and whether MP3 bytes are being produced.
|
||||||
|
|
||||||
|
|
@ -240,7 +240,7 @@ Example Cloudflare page rule:
|
||||||
- Ensure `ffmpeg` is installed on the server
|
- Ensure `ffmpeg` is installed on the server
|
||||||
- Try opening the MP3 fallback directly:
|
- Try opening the MP3 fallback directly:
|
||||||
|
|
||||||
`http://<DJ_MACHINE_IP>:5005/stream.mp3`
|
`http://<DJ_MACHINE_IP>:5001/stream.mp3`
|
||||||
|
|
||||||
### DJ says broadcast is live but listeners hear nothing
|
### DJ says broadcast is live but listeners hear nothing
|
||||||
|
|
||||||
|
|
@ -248,7 +248,7 @@ Example Cloudflare page rule:
|
||||||
- A deck is actually playing
|
- A deck is actually playing
|
||||||
- Crossfader isn’t fully on the silent side
|
- Crossfader isn’t fully on the silent side
|
||||||
- Volumes aren’t at 0
|
- Volumes aren’t at 0
|
||||||
- Check `http://<DJ_MACHINE_IP>:5005/stream_debug` and see if `transcoder_bytes_out` increases
|
- Check `http://<DJ_MACHINE_IP>:5001/stream_debug` and see if `transcoder_bytes_out` increases
|
||||||
### Spectrum visualiser not showing
|
### Spectrum visualiser not showing
|
||||||
|
|
||||||
- Ensure the listener page is loaded and audio is enabled.
|
- Ensure the listener page is loaded and audio is enabled.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"dj_port": 5000,
|
"dj_port": 5000,
|
||||||
"listener_port": 5005,
|
"listener_port": 5001,
|
||||||
|
|
||||||
"dj_panel_password": "",
|
"dj_panel_password": "",
|
||||||
"secret_key": "",
|
"secret_key": "",
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
"_comment_mediamtx": "MediaMTX / SRT integration. mediamtx_webhook_secret is the shared secret sent in the X-MediaMTX-Webhook-Secret header by webhook callers (leave blank to disable auth). mediamtx_rtsp_url is the RTSP URL MediaMTX exposes for the incoming SRT path.",
|
"_comment_mediamtx": "MediaMTX / SRT integration. mediamtx_webhook_secret is the shared secret sent in the X-MediaMTX-Webhook-Secret header by webhook callers (leave blank to disable auth). mediamtx_rtsp_url is the RTSP URL MediaMTX exposes for the incoming SRT path.",
|
||||||
"mediamtx_webhook_secret": "",
|
"mediamtx_webhook_secret": "",
|
||||||
|
"_comment_mediamtx_srt_port": "SRT ingest port MediaMTX listens on. Flask does not bind this port — set it in mediamtx.yml. Stored here for reference.",
|
||||||
|
"mediamtx_srt_port": 5005,
|
||||||
"mediamtx_rtsp_url": "rtsp://127.0.0.1:8554/live",
|
"mediamtx_rtsp_url": "rtsp://127.0.0.1:8554/live",
|
||||||
"mediamtx_hls_url": "http://techy.music:8888/aussie_dj/index.m3u8",
|
"mediamtx_hls_url": "http://techy.music:8888/aussie_dj/index.m3u8",
|
||||||
"_comment_srt_allowed_ips": "List of DJ source IPs allowed to publish an SRT stream. Empty = allow all.",
|
"_comment_srt_allowed_ips": "List of DJ source IPs allowed to publish an SRT stream. Empty = allow all.",
|
||||||
|
|
|
||||||
|
|
@ -392,7 +392,7 @@
|
||||||
<div class="stream-url-section">
|
<div class="stream-url-section">
|
||||||
<label>Share this URL:</label>
|
<label>Share this URL:</label>
|
||||||
<div class="url-copy-group">
|
<div class="url-copy-group">
|
||||||
<input type="text" id="stream-url" readonly value="http://localhost:5005">
|
<input type="text" id="stream-url" readonly value="http://localhost:5001">
|
||||||
<button onclick="copyStreamUrl(event)" class="copy-btn">COPY</button>
|
<button onclick="copyStreamUrl(event)" class="copy-btn">COPY</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -366,7 +366,7 @@ function animateVUMeters() {
|
||||||
requestAnimationFrame(animateVUMeters);
|
requestAnimationFrame(animateVUMeters);
|
||||||
|
|
||||||
const anyPlaying = decks.A.playing || decks.B.playing;
|
const anyPlaying = decks.A.playing || decks.B.playing;
|
||||||
const isListener = window.location.port === '5005' || window.location.search.includes('listen=true');
|
const isListener = window.location.port === '5001' || window.location.search.includes('listen=true');
|
||||||
|
|
||||||
// When nothing is playing, clear canvases to transparent so they don't show as dark blocks
|
// When nothing is playing, clear canvases to transparent so they don't show as dark blocks
|
||||||
if (!anyPlaying && !isListener) {
|
if (!anyPlaying && !isListener) {
|
||||||
|
|
@ -1880,7 +1880,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||||
if (host.startsWith('dj.')) {
|
if (host.startsWith('dj.')) {
|
||||||
return `${window.location.protocol}//${host.slice(3)}`;
|
return `${window.location.protocol}//${host.slice(3)}`;
|
||||||
}
|
}
|
||||||
return `${window.location.protocol}//${host}:5005`;
|
return `${window.location.protocol}//${host}:5001`;
|
||||||
};
|
};
|
||||||
fetch('/client_config')
|
fetch('/client_config')
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
|
|
|
||||||
14
server.py
14
server.py
|
|
@ -38,7 +38,7 @@ CONFIG = _load_config()
|
||||||
# --- Config-driven settings (config.json > env vars > defaults) ---
|
# --- Config-driven settings (config.json > env vars > defaults) ---
|
||||||
CONFIG_HOST = (CONFIG.get('host') or os.environ.get('HOST') or '0.0.0.0').strip()
|
CONFIG_HOST = (CONFIG.get('host') or os.environ.get('HOST') or '0.0.0.0').strip()
|
||||||
CONFIG_DJ_PORT = int(CONFIG.get('dj_port') or os.environ.get('DJ_PORT') or 5000)
|
CONFIG_DJ_PORT = int(CONFIG.get('dj_port') or os.environ.get('DJ_PORT') or 5000)
|
||||||
CONFIG_LISTENER_PORT = int(CONFIG.get('listener_port') or os.environ.get('LISTEN_PORT') or 5005)
|
CONFIG_LISTENER_PORT = int(CONFIG.get('listener_port') or os.environ.get('LISTEN_PORT') or 5001)
|
||||||
CONFIG_SECRET = (CONFIG.get('secret_key') or '').strip() or 'dj_panel_secret'
|
CONFIG_SECRET = (CONFIG.get('secret_key') or '').strip() or 'dj_panel_secret'
|
||||||
CONFIG_CORS = CONFIG.get('cors_origins', '*')
|
CONFIG_CORS = CONFIG.get('cors_origins', '*')
|
||||||
CONFIG_MAX_UPLOAD_MB = int(CONFIG.get('max_upload_mb') or 500)
|
CONFIG_MAX_UPLOAD_MB = int(CONFIG.get('max_upload_mb') or 500)
|
||||||
|
|
@ -48,6 +48,8 @@ CONFIG_LISTENER_URL = (CONFIG.get('listener_url') or '').strip()
|
||||||
# MediaMTX / SRT integration
|
# MediaMTX / SRT integration
|
||||||
# secret shared with MediaMTX webhook requests (leave blank to disable auth)
|
# secret shared with MediaMTX webhook requests (leave blank to disable auth)
|
||||||
_SRT_WEBHOOK_SECRET = (CONFIG.get('mediamtx_webhook_secret') or '').strip()
|
_SRT_WEBHOOK_SECRET = (CONFIG.get('mediamtx_webhook_secret') or '').strip()
|
||||||
|
# SRT ingest port MediaMTX listens on (Flask does not bind this — configure in mediamtx.yml)
|
||||||
|
_MEDIAMTX_SRT_PORT = int(CONFIG.get('mediamtx_srt_port') or 5005)
|
||||||
# RTSP URL MediaMTX exposes for the incoming SRT path (used by ffmpeg to pull audio)
|
# RTSP URL MediaMTX exposes for the incoming SRT path (used by ffmpeg to pull audio)
|
||||||
_MEDIAMTX_RTSP_URL = (CONFIG.get('mediamtx_rtsp_url') or 'rtsp://127.0.0.1:8554/live').strip()
|
_MEDIAMTX_RTSP_URL = (CONFIG.get('mediamtx_rtsp_url') or 'rtsp://127.0.0.1:8554/live').strip()
|
||||||
_MEDIAMTX_HLS_URL = (CONFIG.get('mediamtx_hls_url') or 'http://techy.music:8888/aussie_dj/index.m3u8').strip()
|
_MEDIAMTX_HLS_URL = (CONFIG.get('mediamtx_hls_url') or 'http://techy.music:8888/aussie_dj/index.m3u8').strip()
|
||||||
|
|
@ -712,7 +714,7 @@ setup_shared_routes(dj_app)
|
||||||
def _protect_dj_panel():
|
def _protect_dj_panel():
|
||||||
"""Optionally require a password for the DJ panel only (port 5000).
|
"""Optionally require a password for the DJ panel only (port 5000).
|
||||||
|
|
||||||
This does not affect the listener server (port 5005).
|
This does not affect the listener server (port 5001).
|
||||||
"""
|
"""
|
||||||
if not DJ_AUTH_ENABLED:
|
if not DJ_AUTH_ENABLED:
|
||||||
return None
|
return None
|
||||||
|
|
@ -1103,11 +1105,11 @@ def mediamtx_webhook():
|
||||||
MediaMTX path config example (mediamtx.yml):
|
MediaMTX path config example (mediamtx.yml):
|
||||||
paths:
|
paths:
|
||||||
live:
|
live:
|
||||||
runOnPublish: 'curl -s -X POST http://127.0.0.1:5005/api/webhook -H "Content-Type: application/json" -d \'{"event":"publish","path":"%MTX_PATH%","id":"%MTX_ID%"}\''
|
runOnPublish: 'curl -s -X POST http://127.0.0.1:5001/api/webhook -H "Content-Type: application/json" -d \'{"event":"publish","path":"%MTX_PATH%","id":"%MTX_ID%"}\''
|
||||||
runOnUnpublish: 'curl -s -X POST http://127.0.0.1:5005/api/webhook -H "Content-Type: application/json" -d \'{"event":"unpublish","path":"%MTX_PATH%","id":"%MTX_ID%"}\''
|
runOnUnpublish: 'curl -s -X POST http://127.0.0.1:5001/api/webhook -H "Content-Type: application/json" -d \'{"event":"unpublish","path":"%MTX_PATH%","id":"%MTX_ID%"}\''
|
||||||
|
|
||||||
Or if using MediaMTX >= 1.x webhook integration, point:
|
Or if using MediaMTX >= 1.x webhook integration, point:
|
||||||
api > hooks > [publish|unpublish] > url: http://127.0.0.1:5005/api/webhook
|
api > hooks > [publish|unpublish] > url: http://127.0.0.1:5001/api/webhook
|
||||||
"""
|
"""
|
||||||
global _mp3_broadcast_announced
|
global _mp3_broadcast_announced
|
||||||
|
|
||||||
|
|
@ -1197,7 +1199,7 @@ def srt_auth():
|
||||||
attempt. Returning HTTP 200 allows the connection; anything else rejects it.
|
attempt. Returning HTTP 200 allows the connection; anything else rejects it.
|
||||||
|
|
||||||
mediamtx.yml:
|
mediamtx.yml:
|
||||||
externalAuthenticationURL: http://127.0.0.1:5005/api/srt_auth
|
externalAuthenticationURL: http://127.0.0.1:5001/api/srt_auth
|
||||||
|
|
||||||
MediaMTX sends JSON like:
|
MediaMTX sends JSON like:
|
||||||
{"ip": "1.2.3.4", "action": "publish", "protocol": "srt",
|
{"ip": "1.2.3.4", "action": "publish", "protocol": "srt",
|
||||||
|
|
|
||||||
|
|
@ -2686,7 +2686,7 @@ class DJApp(QMainWindow):
|
||||||
def get_listener_base_url(self):
|
def get_listener_base_url(self):
|
||||||
"""Return the listener server base URL (no auth required).
|
"""Return the listener server base URL (no auth required).
|
||||||
|
|
||||||
The listener server runs on DJ_port + 1 by convention (default 5005).
|
The listener server runs on DJ_port + 1 by convention (default 5001).
|
||||||
Library JSON and music_proxy routes are available there without any
|
Library JSON and music_proxy routes are available there without any
|
||||||
password, so we use this for all library / track-download requests.
|
password, so we use this for all library / track-download requests.
|
||||||
"""
|
"""
|
||||||
|
|
@ -2702,8 +2702,8 @@ class DJApp(QMainWindow):
|
||||||
|
|
||||||
# Normalise: if the configured port IS the listener port, keep it;
|
# Normalise: if the configured port IS the listener port, keep it;
|
||||||
# otherwise derive listener port as DJ port + 1.
|
# otherwise derive listener port as DJ port + 1.
|
||||||
if dj_port == 5005:
|
if dj_port == 5001:
|
||||||
listener_port = 5005
|
listener_port = 5001
|
||||||
else:
|
else:
|
||||||
# Remap any reverse-proxy or listener port back to DJ port first,
|
# Remap any reverse-proxy or listener port back to DJ port first,
|
||||||
# then add 1 to get the listener port.
|
# then add 1 to get the listener port.
|
||||||
|
|
@ -2726,7 +2726,7 @@ class DJApp(QMainWindow):
|
||||||
port = parsed.port or 5000
|
port = parsed.port or 5000
|
||||||
|
|
||||||
# Remap listener port and common reverse-proxy port to the DJ server port
|
# Remap listener port and common reverse-proxy port to the DJ server port
|
||||||
if port in (5005, 8080):
|
if port in (5001, 8080):
|
||||||
port = 5000
|
port = 5000
|
||||||
|
|
||||||
# Always use plain HTTP — the server never terminates TLS directly.
|
# Always use plain HTTP — the server never terminates TLS directly.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue