Alle Projekte

Musik-Streaming mit Android-App

AhoyMusic

Mein eigener Musik-Streaming-Dienst mit nativer Android-App, seit zwei Monaten mein Daily Driver.

Seit 2 Monaten mein Daily Driver

10 TRACKS · 85 MB

Good evening

Liked Songs

Nachbau der echten App mit erfundenen Titeln und ohne Ton.

Kernkompetenzen

  • Mobile und Native
  • Backend und APIs
  • Echtzeit-Systeme
  • Infrastruktur und Betrieb
  • Zuverlässigkeit
  • Testen und Verifizieren

Tech-Stack

  • Expo
  • React Native
  • Reanimated
  • expo-audio
  • Kotlin
  • Media3
  • Expo Modules API
  • MediaStore
  • NestJS
  • MongoDB
  • Redis
  • BullMQ
  • ffmpeg
  • Linux
  • pm2
  • nginx
  • Let's Encrypt

Das Schwierigste

Der Sperrbildschirm, der beim ersten Lied hängen blieb

Was eingefroren aussieht, kann auch eine Schleife sein, die zu schnell läuft.

Die ganze Geschichte
  1. Was man sah

    Die Musik lief ganz normal weiter, aber in der Benachrichtigung auf dem Sperrbildschirm stand für immer der Titel des ersten Lieds.

  2. Was wirklich los war

    Eine Endlosschleife: Jede Benachrichtigung startete den Hintergrunddienst neu, und jeder Neustart schickte wieder eine Benachrichtigung. Android warf die Flut einfach weg.

  3. Die Lösung

    Der Dienst meldet sich nur noch bei einer ausdrücklichen Aktion. Bewiesen mit echten Taps auf die Benachrichtigung im Emulator.

846
Tests bestanden, App 548 und API 298
~24k
Zeilen Code, dazu ~10,7k Zeilen Tests
~1.000
Zeilen Kotlin in 4 Dateien
2 Monate
täglich im Einsatz
In einfachen Worten

Statt für ein Musik-Abo zu zahlen, läuft meine Musik auf meinem eigenen Server. Die App spielt sie online oder offline, im Hintergrund und mit Steuerung auf dem Sperrbildschirm, wie bei den großen Anbietern. Ich nutze sie jeden Tag.

Ganz einfach: um Kosten zu sparen. Und weil ein Musik-Player ein fieser Härtetest ist. Audio, Android-Lebenszyklus und Offline-Sync verzeihen nichts, und wenn etwas kaputt ist, merke ich es täglich selbst.

So funktioniert es

Schritt 1 von 5

Import

Ein BullMQ-Worker außerhalb der API verarbeitet Tracks einzeln, damit ein hängender Job keine Anfragen blockiert.

Was gebaut wurde
  • Player mit Queue, Shuffle, Repeat, Hintergrundwiedergabe und Sperrbildschirm
  • Natives Kotlin-Modul mit Media3-Session, das nichts dekodiert und nur die Queue aus JS spiegelt
  • Offline-Modus: Downloads pro Track, Anmeldung ohne Netz, Library-Snapshots, Aufräumen halber Dateien
  • Audio-Streaming mit HTTP Range (206 und 416), damit Vorspulen auch durch nginx funktioniert
  • Import-Worker mit BullMQ außerhalb der API, Fortschritt pro Track, Duplikaterkennung und Dateiupload
  • Getrennte Bibliotheken pro Konto, Deploy-Skript mit Vorabprüfung, nächtliche Backups mit dokumentiertem Restore
Wie die Kompetenzen eingesetzt wurden
Mobile und Native
Kotlin, Foreground Services, Media3-Sessions, Runtime-Permissions und Config Plugins, dazu Debugging mit adb und logcat
Backend und APIs
NestJS mit MongoDB ohne ORM, Redis und BullMQ, 32 HTTP-Routen
Echtzeit-Systeme
Range-Requests, Proxy-Buffering und asynchrone Status-Ticks, die nicht in falscher Reihenfolge ankommen dürfen
Infrastruktur und Betrieb
Linux, pm2, nginx, Let's Encrypt, logrotate, Disk-Guard und Backups, die vor leeren Dumps warnen
Zuverlässigkeit
Stille Fehlzustände werden zu klaren Ablehnungen, zum Beispiel beim Deploy mit altem Installationspfad
Testen und Verifizieren
846 Unit-Tests, und den nativen Teil habe ich am echten Gerät und im Emulator geprüft
Meine Rolle

Konzept, Architektur, Führung der Agenten und Prüfung. Den Großteil des Codes hat Claude Code in meinem selbst entwickelten Agent-Workflow geschrieben.

Siehe Projekt Context Observer
Nächstes ProjektBrickUp