CastQueue: Wie ich eine Chromecast Playlist App gebaut habe
Und am Ende alles anders kam. Eine Geschichte über Next.js, Chromecast und den Weg zur nativen iOS App.
Kennt ihr das? Man liegt abends auf der Couch, will ein paar Folgen einer Serie auf dem Chromecast schauen und denkt sich: Warum gibt es eigentlich keine App, bei der ich einfach ein paar Video Links reinklatschen kann und die dann schön brav nacheinander abgespielt werden? Genau diesen Gedanken hatte ich letztens und statt einfach die Folgen einzeln anzuschmeißen, habe ich natürlich das gemacht was jeder vernünftige Entwickler tut. Ich habe angefangen eine eigene App zu programmieren.
Die Idee war simpel. Eine Web App, die ich am iPhone im Browser aufrufe, dort ein paar Direct Links zu Videos reinkopiere und dann auf einen Knopf drücke. Die App soll dann meinen Chromecast im Netzwerk finden und alle Videos nacheinander abspielen. Wie eine Playlist halt, nur für Direct Links. Klingt machbar, oder?
Beim Tech Stack habe ich mich für Next.js mit dem neuen App Router entschieden, TypeScript, Tailwind CSS und shadcn/ui für die Komponenten. Dazu Framer Motion für hübsche Animationen und dnd kit für Drag and Drop in der Playlist. Das ganze sollte modern aussehen, viel mit Popups und Modals gelöst werden und natürlich auch am iPhone gut funktionieren.
Jetzt kommt der spannende Teil: Wie steuert man einen Chromecast von einer Web App aus? Die offizielle Google Cast SDK funktioniert nämlich nur in Chrome. Nicht in Safari, nicht in Brave. Und ich wollte die App ja explizit am iPhone nutzen. Die Lösung: Man baut die Chromecast Steuerung ins Backend. Der Node.js Server redet direkt mit dem Chromecast über das CASTV2 Protokoll, und der Browser redet nur mit dem Server. So ist es dem Browser komplett egal ob er Chrome, Safari oder Brave ist.
Dafür habe ich zwei Libraries genutzt. Erstens castv2-client, eine Node.js Library die das Chromecast Protokoll implementiert. Die ist zwar schon ein paar Jahre alt, funktioniert aber einwandfrei weil sich das Protokoll nicht geändert hat. Und zweitens bonjour-service für die mDNS Discovery, also das automatische Finden von Chromecasts im Netzwerk.

Die App ist am Ende ziemlich cool geworden. Man kann Links einfügen, einzeln oder einfach alle auf einmal reinkopieren. Die App erkennt automatisch wo ein neuer Link anfängt wenn man alles in einer Wurst einfügt, indem sie an jeder http:// Grenze splittet. Die Playlist kann man per Drag and Drop sortieren, was am iPhone auch mit Touch funktioniert. Man kann eine Skip Intro Zeit setzen, zum Beispiel 1 Minute 40 Sekunden, und jede Folge überspringt dann automatisch das Intro. Dazu gibt es Multiselect um die Skip Intro Zeit für alle Folgen auf einmal zu setzen. Und natürlich Play, Pause, eine klickbare Seek Bar und einen Skip 30 Sekunden Button.
Passwortschutz habe ich auch eingebaut. Die App ist komplett hinter einem Login gesichert mit HMAC signiertem Cookie, Rate Limiting gegen Brute Force und ordentlichen Security Headers. Soll ja nicht jeder im Internet meine Serien steuern können.
Dann kam der Moment wo ich die App auf meinen Server deployen wollte. Ich nutze Coolify als Self Hosting Plattform, also Docker Image gebaut, Dockerfile geschrieben, alles schön mit Multi Stage Build. Und dann deployed. Login Seite kam, sah super aus, Passwort eingegeben, Playlist erstellt, auf Stream gedrückt und dann... Connection Timeout.
Hm. Nochmal. Connection Timeout.
Nach einigem Debugging bin ich draufgekommen: Mein Coolify Server steht nicht bei mir zuhause. Der steht irgendwo in einem Rechenzentrum. Und mein Chromecast steht im Wohnzimmer. Der Server hat natürlich keine Ahnung was 192.168.0.25 sein soll, weil das eine lokale IP in meinem Heimnetzwerk ist die der Server niemals erreichen kann. Der Chromecast ist ja nicht im Internet, sondern bei mir zuhause im WLAN.
Ich hab dann noch versucht mit Docker network_mode host das irgendwie hinzubiegen, aber das ändert nichts an der Tatsache dass der Server physisch woanders steht als der Chromecast.
Und dann hatte ich diesen Moment. Diesen wunderbaren Moment wo man sich zurücklehnt und denkt: Verdammt. Ich muss die App doch nativ für mein iPhone entwickeln. Wenn das iPhone direkt mit dem Chromecast redet, sind beide im selben WLAN und das Problem existiert einfach nicht.
Also sitze ich jetzt hier und plane eine native iOS App in SwiftUI mit dem Google Cast SDK. Die ganze Architektur dreht sich um. Kein Server mehr, kein Docker, kein Coolify. Einfach nur das iPhone und der Chromecast die direkt miteinander reden. Eigentlich hätte ich auch gleich darauf kommen können, aber naja, manchmal muss man den langen Weg gehen um den kurzen zu finden.
Die Web App war trotzdem nicht umsonst. Die ganze Logik, das URL Parsing, die Skip Intro Funktion, die sequenzielle Wiedergabe, das muss ich in Swift alles genauso nachbauen. Und jetzt heißt es: Swift lernen. Ich meine, wie schwer kann das schon sein. Berühmte letzte Worte.
In diesem Sinne, schönen Abend und bis zum nächsten Mal wenn ich dann hoffentlich berichten kann dass die App im App Store ist.