Release v1.0.11: Pause-Fix, OAuth-Assistent, Docs, Cleanup
Pause-Fix (Silence-Injektor): - Python-Skript zwischen librespot und ffmpeg - Bei Pause füllt der Injektor die Stille mit Null-Bytes - Icecast bleibt verbunden, Stream reißt nicht ab OAuth-Einrichtungs-Assistent (Port 5589): - oauth_helper.py: HTTP-Server mit geführtem Anmelde-Prozess - Zeigt Spotify-Auth-Link automatisch sobald librespot ihn ausgibt - URL-Fixer: Nutzer fügt 127.0.0.1-URL ein, Assistent korrigiert auf HA-IP - Erkennt bereits vorhandene Credentials und zeigt Hinweis Dokumentation: - DOCS.md für die HA Add-on Detailseite erstellt - README.md vollständig überarbeitet und aktualisiert Cleanup: - Leere Testdatei entfernt - Veraltete example-config.yaml entfernt - manifest.json der Custom Component auf v1.0.11 gebracht Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,9 +3,11 @@
|
||||
# Busch-Radio Spotify Bridge – Startup-Script
|
||||
#
|
||||
# Startet:
|
||||
# 1. Icecast2 – HTTP-Stream-Server
|
||||
# 2. librespot – Virtueller Spotify Connect-Empfänger (gibt PCM auf stdout aus)
|
||||
# 3. ffmpeg – Kodiert PCM → MP3 und schickt es als Icecast-Quelle
|
||||
# 1. Icecast2 – HTTP-Stream-Server
|
||||
# 2. OAuth-Hilfe – Einrichtungs-Assistent auf Port 5589
|
||||
# 3. librespot – Virtueller Spotify Connect-Empfänger (PCM auf stdout)
|
||||
# 4. Silence-Injektor– Fügt Stille ein wenn Spotify pausiert (hält Stream aufrecht)
|
||||
# 5. ffmpeg – Kodiert PCM → MP3/AAC und schickt es als Icecast-Quelle
|
||||
#
|
||||
# Bei Absturz wird die komplette Pipeline nach 5 Sekunden neu gestartet.
|
||||
# ==============================================================================
|
||||
@@ -32,7 +34,7 @@ else
|
||||
fi
|
||||
|
||||
bashio::log.info "╔══════════════════════════════════════════════╗"
|
||||
bashio::log.info "║ Busch-Radio Spotify Bridge v1.0.10 ║"
|
||||
bashio::log.info "║ Busch-Radio Spotify Bridge v1.0.11 ║"
|
||||
bashio::log.info "╚══════════════════════════════════════════════╝"
|
||||
bashio::log.info "Gerätename : ${DEVICE_NAME}"
|
||||
bashio::log.info "Bitrate : ${BITRATE} kbps"
|
||||
@@ -152,7 +154,7 @@ bashio::log.info "Icecast läuft (PID: ${ICECAST_PID})"
|
||||
CREDENTIAL_CACHE="/data/librespot-cache"
|
||||
mkdir -p "${CREDENTIAL_CACHE}"
|
||||
|
||||
# ── IP-Adresse des Hosts ermitteln (für OAuth-Anleitung) ─────────────────────
|
||||
# ── IP-Adresse des Hosts ermitteln ───────────────────────────────────────────
|
||||
HA_IP=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src") print $(i+1)}' | head -1)
|
||||
if [ -z "${HA_IP}" ]; then
|
||||
HA_IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||
@@ -161,11 +163,14 @@ if [ -z "${HA_IP}" ]; then
|
||||
HA_IP="<homeassistant-ip>"
|
||||
fi
|
||||
|
||||
# ── OAuth-Hilfsserver starten ─────────────────────────────────────────────────
|
||||
# Stellt unter http://<ha-ip>:5589 einen Einrichtungs-Assistenten bereit,
|
||||
# der den Spotify-OAuth-Prozess Schritt für Schritt erklärt.
|
||||
python3 /oauth_helper.py "${HA_IP}" &
|
||||
OAUTH_HELPER_PID=$!
|
||||
bashio::log.info "OAuth-Hilfsseite: http://${HA_IP}:5589"
|
||||
|
||||
# ── Librespot-Argumente zusammenstellen ───────────────────────────────────────
|
||||
# Zeroconf/mDNS funktioniert nicht zuverlässig in Containern, da Port 5353
|
||||
# vom Host (systemd-resolved/avahi) bereits belegt ist.
|
||||
# Stattdessen: OAuth-Authentifizierung. Das Gerät registriert sich bei
|
||||
# Spotifys Servern und erscheint in der App ohne lokales mDNS.
|
||||
LIBRESPOT_ARGS=(
|
||||
"--name" "${DEVICE_NAME}"
|
||||
"--bitrate" "${BITRATE}"
|
||||
@@ -185,24 +190,10 @@ else
|
||||
bashio::log.info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
bashio::log.info "ERSTER START – Spotify-Autorisierung erforderlich!"
|
||||
bashio::log.info ""
|
||||
bashio::log.info "1) Gleich erscheint im Log eine Zeile:"
|
||||
bashio::log.info " Browse to: https://accounts.spotify.com/authorize?..."
|
||||
bashio::log.info " Kopiere diese URL vollständig und öffne sie im Browser."
|
||||
bashio::log.info "Öffne die Einrichtungs-Seite im Browser:"
|
||||
bashio::log.info " http://${HA_IP}:5589"
|
||||
bashio::log.info ""
|
||||
bashio::log.info "2) Melde dich mit deinem Spotify-Konto an"
|
||||
bashio::log.info " und erteile die Berechtigung."
|
||||
bashio::log.info ""
|
||||
bashio::log.info "3) Dein Browser wird danach weitergeleitet auf:"
|
||||
bashio::log.info " http://127.0.0.1:5588/login?code=..."
|
||||
bashio::log.info " Diese Seite schlägt fehl – das ist normal."
|
||||
bashio::log.info ""
|
||||
bashio::log.info "4) WICHTIG: Ersetze in der Adressleiste"
|
||||
bashio::log.info " '127.0.0.1' durch '${HA_IP}'"
|
||||
bashio::log.info " → http://${HA_IP}:5588/login?code=..."
|
||||
bashio::log.info " und drücke Enter."
|
||||
bashio::log.info ""
|
||||
bashio::log.info "5) Bei Erfolg siehst du: 'Autorisierung erfolgreich!'"
|
||||
bashio::log.info " Das Add-on startet danach automatisch."
|
||||
bashio::log.info "Der Assistent führt dich durch die Anmeldung."
|
||||
bashio::log.info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
fi
|
||||
|
||||
@@ -215,7 +206,7 @@ bashio::log.info "Stream-URL für das Busch-Jäger Radio:"
|
||||
bashio::log.info " http://${HA_IP}:${STREAM_PORT}${STREAM_MOUNT}"
|
||||
bashio::log.info ""
|
||||
bashio::log.info "Icecast-Statusseite:"
|
||||
bashio::log.info " http://<homeassistant-ip>:${STREAM_PORT}"
|
||||
bashio::log.info " http://${HA_IP}:${STREAM_PORT}"
|
||||
bashio::log.info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
bashio::log.info "Wähle '${DEVICE_NAME}' in der Spotify-App als Abspielgerät."
|
||||
bashio::log.info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
@@ -223,19 +214,46 @@ bashio::log.info "━━━━━━━━━━━━━━━━━━━━
|
||||
# ── Aufräumen bei SIGTERM ─────────────────────────────────────────────────────
|
||||
cleanup() {
|
||||
bashio::log.info "Add-on wird beendet..."
|
||||
kill "${ICECAST_PID}" 2>/dev/null || true
|
||||
kill "${ICECAST_PID}" "${OAUTH_HELPER_PID}" 2>/dev/null || true
|
||||
exit 0
|
||||
}
|
||||
trap cleanup SIGTERM SIGINT
|
||||
|
||||
# ── Silence-Injektor ─────────────────────────────────────────────────────────
|
||||
# Sitzt zwischen librespot und ffmpeg.
|
||||
# Wenn Spotify pausiert, hört librespot auf zu schreiben. Ohne Injektor würde
|
||||
# ffmpeg/Icecast den Stream nach einigen Sekunden Stille trennen.
|
||||
# Der Injektor füllt die Pause mit Stille-Bytes, damit der Stream bestehen bleibt.
|
||||
SILENCE_INJECTOR='
|
||||
import sys, select, os
|
||||
CHUNK = 8820 # 50 ms @ 44100 Hz, S16LE, Stereo (44100*2*2/20)
|
||||
silence = bytes(CHUNK)
|
||||
fd = sys.stdin.buffer.fileno()
|
||||
while True:
|
||||
r, _, _ = select.select([fd], [], [], 0.05)
|
||||
if r:
|
||||
data = os.read(fd, CHUNK)
|
||||
if not data:
|
||||
break
|
||||
sys.stdout.buffer.write(data)
|
||||
else:
|
||||
sys.stdout.buffer.write(silence)
|
||||
sys.stdout.buffer.flush()
|
||||
'
|
||||
|
||||
# ── Haupt-Schleife: Pipeline starten und bei Absturz neu starten ──────────────
|
||||
#
|
||||
# Signalfluss:
|
||||
# librespot (Spotify Connect) → stdout (raw PCM 44100Hz/S16LE/Stereo)
|
||||
# ↓
|
||||
# ffmpeg (PCM → MP3 Encoding) → Icecast (HTTP-Stream)
|
||||
# ↓
|
||||
# Busch-Jäger Radio (HTTP-Client)
|
||||
# silence-injector (Python) → leitet durch; bei Pause: sendet Stille
|
||||
# ↓
|
||||
# ffmpeg -re (Echtzeit) → PCM → MP3/AAC Encoding → Icecast
|
||||
# ↓
|
||||
# Busch-Jäger Radio (HTTP-Client)
|
||||
#
|
||||
# -re: Liest den Input in Echtzeit – verhindert zu schnelles Vorspulen
|
||||
# und gibt librespot korrekte Positions-Rückmeldung (Pause/Skip).
|
||||
#
|
||||
# Wenn entweder librespot oder ffmpeg abstürzt, wird die komplette Pipeline
|
||||
# nach RESTART_DELAY Sekunden neu gestartet.
|
||||
@@ -246,7 +264,20 @@ while true; do
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
bashio::log.info "Pipeline-Start (Versuch ${ATTEMPT})..."
|
||||
|
||||
# Librespot stderr: Zeilen werden an HA-Log weitergeleitet UND auf
|
||||
# "Browse to:"-URL geprüft → wird in Datei für OAuth-Hilfsserver gespeichert.
|
||||
librespot "${LIBRESPOT_ARGS[@]}" \
|
||||
2> >(while IFS= read -r line; do
|
||||
echo "$line" >&2
|
||||
case "$line" in
|
||||
*"Browse to:"*)
|
||||
echo "$line" | sed 's/.*Browse to: //' | tr -d '\r\n ' \
|
||||
> /tmp/busch-radio/auth_url.txt
|
||||
bashio::log.info "Spotify-Anmeldung: http://${HA_IP}:5589 öffnen"
|
||||
;;
|
||||
esac
|
||||
done) \
|
||||
| python3 -c "${SILENCE_INJECTOR}" \
|
||||
| ffmpeg \
|
||||
-re \
|
||||
-hide_banner \
|
||||
@@ -259,6 +290,9 @@ while true; do
|
||||
"${ICECAST_SOURCE_URL}" \
|
||||
|| true
|
||||
|
||||
# Auth-URL-Datei leeren damit die Hilfsseite beim Neustart wieder wartet
|
||||
rm -f /tmp/busch-radio/auth_url.txt
|
||||
|
||||
bashio::log.warning "Pipeline beendet. Neustart in ${RESTART_DELAY}s..."
|
||||
sleep "${RESTART_DELAY}"
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user