15. März 2025
Ein Webradio-Streaming-Server fürs LAN war genau das, was ich brauchte, um meine Smooth-Jazz-Playlists überall im Haus zu hören – ohne großen Aufwand. Dafür habe ich ein kleines Python-Skript entwickelt, das MP3-Dateien oder M3U-Playlists aus einem Ordner streamt. Es ist minimalistisch, ohne überflüssige Funktionen, und ideal für alle, die schnell einen Stream im lokalen Netzwerk aufsetzen möchten. Mein Setup ist simpel: Ich empfange den Stream mit VLC auf meinem Smartphone und leite ihn per Bluetooth an meine Stereoanlage weiter – eine praktische Lösung für entspannte Musikmomente.

Skript zum Download in einer Zip-Datei: Webradio-Streaming-Server.py.zip
Bedienung: Das Skript ist bewusst einfach gehalten, um den Einstieg so unkompliziert wie möglich zu machen. Man kopiert es einfach in den Ordner, in dem sich die MP3-Dateien oder eine M3U-Playlist befinden, und startet es mit Python (z. B. python Webradio-Streaming-Server.py
). Der Server läuft dann auf Port 8000 und gibt die lokale Adresse aus – bei mir z. B. http://192.168.1.233:8000/stream
. Diese URL öffnet man in einem Player wie VLC, sei es auf dem Smartphone, einem Laptop oder einem anderen Gerät im LAN. Der Stream startet sofort und spielt die Songs in einer festen Schleife ab, entweder alphabetisch aus dem Ordner oder in der Reihenfolge der M3U-Datei. Ich nutze es so, dass VLC auf meinem Smartphone den Stream empfängt und per Bluetooth an meine Stereoanlage weiterleitet – das Setup ist super praktisch und funktioniert einwandfrei für meinen Anwendungsfall.
Funktion: Technisch ist das Skript ein einfacher HTTP-Server, der auf Python basiert und die http.server
-Bibliothek nutzt, um einen Streaming-Endpunkt unter /stream
bereitzustellen. Der Server bindet an 0.0.0.0:8000
, sodass er im gesamten LAN erreichbar ist. Die Songs werden als HTTP-Chunks gesendet, was für die meisten Player wie VLC gut funktioniert. Der Code prüft zuerst, ob eine playlist.m3u
im Ordner liegt, und spielt diese ab; falls nicht, nimmt er alle MP3-Dateien aus dem Verzeichnis in alphabetischer Reihenfolge. Fehler wie Verbindungsabbrüche („Broken Pipe“) werden abgefangen, sodass der Server stabil weiterläuft. Alles ist bewusst minimalistisch gehalten – kein Overhead, nur das, was man für einen LAN-Stream braucht.
Entwicklung und Schwierigkeiten: Die Entwicklung war nicht ohne Herausforderungen. Am Anfang hatte ich Probleme mit der Verbindung: Wenn ein Client (z. B. VLC) den Stream abbrach, stürzte der Server ab, weil der „Broken Pipe“-Fehler nicht abgefangen wurde. Das konnte ich durch eine bessere Fehlerbehandlung lösen. Ein weiteres Problem war das Abspielen der Songs: In einer frühen Version wurden alle Songs gleichzeitig gesendet, weil ich die Daten nicht in Chunks aufgeteilt hatte. Das führte dazu, dass VLC die Songs übereinander abspielte – ein echtes Chaos! Durch das Einführen von Chunks konnte ich das beheben, aber das brachte neue Schwierigkeiten: Mit Pausen zwischen den Chunks lief der Stream abgehackt, was bei meinem Bluetooth-Setup nicht gut funktionierte. Letztendlich bin ich bei einer simpleren Lösung geblieben, bei der die gesamte Datei auf einmal gesendet wird – das funktioniert bei VLC und meiner Stereoanlage einwandfrei, ist aber technisch nicht optimal für echtes Streaming. Eine weitere Hürde war die Unterstützung mehrerer Clients: Der aktuelle Code kann nur einen Empfänger gleichzeitig bedienen. Ich habe zwar eine Version mit ThreadingMixIn
getestet, um mehrere Clients zu unterstützen, aber die Synchronisation zwischen den Clients war nicht stabil genug, und der Stream brach oft ab. Das ist also ein Punkt, der noch nicht klappt.
Was klappt und was nicht: Der aktuelle Stand des Skripts ist für meinen Anwendungsfall perfekt: Ein einzelner Empfänger (mein Smartphone mit VLC) kann den Stream zuverlässig empfangen, und die Songs werden in einer festen Schleife nacheinander abgespielt – genau wie ich es wollte. Die Fehlerbehandlung ist robust, sodass der Server nicht abstürzt, wenn ein Client die Verbindung trennt. Auch die Unterstützung für M3U-Playlists funktioniert einwandfrei, und ich kann die Reihenfolge der Songs einfach anpassen. Was noch nicht klappt, ist die Unterstützung für mehrere Empfänger gleichzeitig – wenn ein zweites Gerät verbindet, wird der erste Client oft getrennt. Außerdem fehlen noch Funktionen wie Zufallswiedergabe oder das Anzeigen von Metadaten (z. B. Titel und Künstler) im Player. Ich habe versucht, Metadaten mit der mutagen
-Bibliothek einzubauen, aber das hat bei mir nicht funktioniert, und für meinen Zweck ist es auch nicht zwingend nötig.
Zukünftige Pläne: Es gibt noch einiges, was man mit dem Skript machen könnte. Als Nächstes würde ich gerne die Multi-Client-Unterstützung stabil bekommen, damit mehrere Geräte gleichzeitig den Stream hören können – etwa ein Smartphone und ein Laptop. Dafür müsste man einen zentralen Streaming-Thread einbauen, der die Songs für alle Clients synchronisiert. Eine Zufallswiedergabe wäre auch eine nette Erweiterung, um die Playlist etwas abwechslungsreicher zu gestalten. Außerdem könnte man eine kleine Web-Oberfläche hinzufügen, um die Playlist zu steuern oder Songs zu überspringen – das wäre praktisch, wenn man den Stream von verschiedenen Geräten aus verwalten will. Eine weitere Idee ist die Unterstützung für Metadaten, sodass VLC den Titel und Künstler anzeigt – das habe ich mit mutagen
versucht, aber es hat nicht geklappt; vielleicht wäre eine andere Bibliothek besser geeignet. Langfristig könnte man den Stream auch über das Internet zugänglich machen, mit Portforwarding und DynDNS, aber das ist für meinen LAN-Fokus aktuell nicht nötig.
import os import time from http.server import HTTPServer, BaseHTTPRequestHandler import socketserver import errno # Verzeichnis ist der aktuelle Ordner (wo das Skript liegt) MUSIC_DIR = os.path.dirname(os.path.abspath(__file__)) M3U_FILE = os.path.join(MUSIC_DIR, "playlist.m3u") class RadioStreamHandler(BaseHTTPRequestHandler): def do_GET(self): if self.path == "/stream": self.send_response(200) self.send_header("Content-type", "audio/mpeg") self.send_header("Transfer-Encoding", "chunked") self.send_header("Cache-Control", "no-cache") self.send_header("Connection", "keep-alive") self.send_header("icy-name", "Mein LAN-Radio") self.end_headers() playlist = self.server.get_playlist() if not playlist: self.wfile.write(b"0\r\n\r\n") return while True: for mp3_file in playlist: try: with open(mp3_file, "rb") as f: audio_data = f.read() chunk_size = len(audio_data) self.wfile.write(f"{chunk_size:x}\r\n".encode()) self.wfile.write(audio_data) self.wfile.write(b"\r\n") self.wfile.flush() print(f"Spiele: {os.path.basename(mp3_file)}") except IOError as e: if e.errno == errno.EPIPE: print(f"Client getrennt bei {mp3_file}, fahre fort...") return else: print(f"Fehler bei {mp3_file}: {e}") return except Exception as e: print(f"Unbekannter Fehler bei {mp3_file}: {e}") return time.sleep(0.1) # Pause zwischen Durchläufen else: self.send_error(404, "Nur /stream verfügbar") class RadioServer(HTTPServer): def __init__(self, server_address, handler_class): super().__init__(server_address, handler_class) self.playlist = self.load_playlist() def load_playlist(self): if os.path.exists(M3U_FILE): with open(M3U_FILE, "r") as f: return [os.path.join(MUSIC_DIR, line.strip()) if not os.path.isabs(line.strip()) else line.strip() for line in f if line.strip().endswith(".mp3")] else: return [os.path.join(MUSIC_DIR, f) for f in os.listdir(MUSIC_DIR) if f.endswith(".mp3")] def get_playlist(self): return self.playlist def run(): server_address = ("0.0.0.0", 8000) httpd = RadioServer(server_address, RadioStreamHandler) local_ip = get_local_ip() print(f"LAN-Radio läuft auf http://{local_ip}:8000/stream") httpd.serve_forever() def get_local_ip(): import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.connect(("8.8.8.8", 80)) ip = s.getsockname()[0] except Exception: ip = "localhost" finally: s.close() return ip if __name__ == "__main__": run()
Notwendige Abhängigkeiten: Das Skript ist sehr schlank und benötigt nur Python 3, das auf den meisten Systemen bereits vorhanden ist. Es gibt keine externen Bibliotheken, da alles mit Standardmodulen wie http.server
und socketserver
umgesetzt wurde. Unter Ubuntu reicht es, Python 3 zu haben – z. B. mit sudo apt install python3
. Für den Betrieb im LAN muss der Port 8000 in der Firewall freigegeben sein, falls eine aktive Firewall (wie UFW) genutzt wird: sudo ufw allow 8000/tcp
. Wenn man später Funktionen wie Metadaten hinzufügen will, könnte man Bibliotheken wie mutagen
installieren, aber das ist aktuell nicht nötig.
Dieser Linux-Befehl installiert die einzige Abhängigkeit, falls nötig:
sudo apt-get update && sudo apt-get install -y python3
Fazit: Für alle, die einen simplen Webradio-Stream im LAN aufsetzen wollen, ist dieses Skript eine großartige Basis. Es ist leichtgewichtig, einfach zu bedienen und macht genau das, was es soll – zumindest für einen einzelnen Empfänger. Ich nutze es täglich, um meine Smooth-Jazz-Sammlung im ganzen Haus zu hören, und es funktioniert einwandfrei mit VLC und meiner Bluetooth-Stereoanlage. Die Entwicklung war eine kleine Herausforderung, vor allem wegen der Multi-Client-Unterstützung und der Streaming-Logik, aber für meinen Zweck ist es genau richtig. Für die Zukunft gibt es noch viel Potenzial, etwa mit Zufallswiedergabe oder einer Web-Oberfläche – aber selbst in dieser Form ist es schon ein praktisches Tool für Musikliebhaber wie mich.
Das Skript wurde zusammen mit Grok 3 entwickelt, basierend auf meinen Anforderungen an einen minimalistischen LAN-Stream. Solche Artikel bringen kaum Werbeeinnahmen, da die Zielgruppe klein ist, aber sie sind ein nützlicher Beitrag für Bastler wie mich.