[MusicBox] Abspielen von mp3's, Internetradio, Bluetoothgeräten

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Guten Abend zusammen,

    in letzter Zeit gab es von mir mehrere Themen die alle von Problem zu meinem aktuellen Projekt handelten. Ich bin damit noch nicht fertig, aber es so weit das man es benutzen kann.

    Es handelt sich bei meinem Projekt um einen Ersatz für unsere altes Radio. Die MusicBox besteht aus einem Raspberry Pi 4 (2GB) mit einem Hifiberry MiniAmp und einem WaveShare 4,3" Touch-Display.

    Programmiert wurde die MusicBox mit Python. Für die Touchbedienung habe ich GUI mit Kivy erstellt.

    Die MusicBox hat ein Hauptmenü mit vier Funktionen:

    1. Funktion: Internetradio

    Für diese Funktion muss an in einem definierten Pfad eine *csv - Datei gespeichert sein, die die gewünschten Radiostreams mit Sendernamen enthält. Diese Datei wird von Python eingelesen und die beinhalteten Sender kann man in einem DropDown-Menü auswählen. Die Wiedergabe kann mit einem Button pausiert werden und über einen Slider kann die Lautstärke eingestellt werden.

    2. Funktion: Playlist-Player

    Für diese Funktion wird in einem definierten Pfad unterschiedliche Ordner erstellt und mit Musik-Dateien befüllt. Zum Beispiel ein Ordner "Party" mit Partymusik und ein Ordner "Rock" mit Rockmusik und so weiter, was man eben gern hören will. Diese Ordner kann man ebenfalls in einem DropDown-Menü auswählen. Und der Inhalt der sich in dem ausgewählten Ordner befindet wird abgespielt. Über Buttons kann man die Wiedergabe stoppen und pausieren sowie den nächsten oder den vorherigen Titel auswählen. Mit einem Slider wird die Lautstärke eingestellt.

    3. Funktion Bluetooth

    Diese Funktion baiserit auf diesem Rpi Audio Receiver. Damit ist es möglich die MusicBox als Bluetooth-Speaker zu verwenden. Es wird einfach an dem gewünschten Abspielgerät Bluetooth aktivert und "MusicBox" ausgewählt. Die Verbindung wird automatisch hergestellt und die Musik wird über die MusicBox ausgegeben. Die Lautstärke wird dabei über das Abspielgerät gesteuert. Genaueres findet ihr in dem Link. Mein Programm macht in diesem Fall nicht viel, es wird lediglich Bluetooth aktiviert und beim verlassen dieser Funktion wird das Bluetooth wieder deaktiviert.

    4. Funktion Shutdown

    Die MusicBox wird ausgeschaltet bzw. der Pi wird herunter gefahren.

    Was ich noch machen will:

    -Beim Abspielen vom Internetradio hätte ich gerne ein Label, dass mir den aktuellen Titel anzeigt. Ein passende Funktion für python-vlc habe ich leider noch nicht finden können. Wenn hier jemand etwas passendes weis, wäre ich euch dankbar.

    -Was passiert wenn beispielsweise die Internetverbindung nicht da ist oder auf den gewünschten Dateienpfad nicht zugegriffen werden kann. Ich habe bis jetzt 'try' und 'except' Blöcke in meinem Code, allerdings steht nach 'except' noch 'pass', da ich noch nicht weis, wie ich am besten damit umgehen soll. Da fehlt es mir noch etwas an Ideen/Erfahrung was eine sinnvolle Ausnahmebedingung wäre. Auch hier bin ich für Tipps dankbar.

    -Der Pi braucht echt lange bis er hochgefahren ist, bis jetzt ist Raspberry-OS with Desktop installiert und die MusicBox startet automatisch in den Vollbildmodus. Den Start muss ich noch beschleunigen.

    -Ausgeschaltet wird die Box über die Shutdown-Funktion, es ist aber nicht so toll den Stecker aus und wieder einzustecken, wenn man sie anschalten will. Dazu habe ich schon einen kleinen Taster bestellt, mit dem ich die Box dann starten will.

    - Zum Schluss soll noch ein 3D-Druck Gehäuse kommen.

    Ich habe euch hier noch ein paar Screenshots:

    Hauptmenü/Startbildschirm:

    Internetradio:

    Playlistplayer:

    Bluetooth:

    Shutdown:

    Die benötigte *csv-Datei muss so aufgebaut sein:

    Code
    Sendername,URL
    RadioBob,http://streams.radiobob.de/bob-live/mp3-192/mediaplayer
    RockAntenne,http://mp3channels.webradio.rockantenne.de/alternative

    Und zum Schluss noch der aktuelle Code:

    So das war (ich hoffe) vorerst mal alles :)

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

    Einmal editiert, zuletzt von Dennis89 (20. Februar 2021 um 00:15)

  • [MusicBox] Abspielen von mp3's, Internetradio, Bluetoothgeräten? Schau mal ob du hier fündig wirst!

  • Hallo Dennis,

    Dein Projekt finde ich klasse:thumbup:

    Zur Fehlerbehandlung :

    Du verwendest öfter

    Code
        except:        
            pass

    ich benutze hierfür gerne Signale, so was wie

    und die Fehlerbehandlung, in diesem Fall mit einem Dialog, der den Fehlertext anzeigt

    so "landen" alle Fehler an der selben Stelle und Du kannst Dich zentral darum kümmern und lokal reagieren. Außerdem kannst Du in gleicher Weise auch Fehler berücksichtigen und ggf. anzeigen, die systemtechnisch gar keine sind (also ohne try:), sondern z.B. logische, Timeouts, oder durch falsche Eingaben im UI entstehen etc.

    Signale funktionieren Thread-übergreifend !

    Gruß,

    Holger

    "Probleme kann man meist nicht mit derselben Denkweise lösen, durch die sie entstanden sind."

    Wetterstation Nievenheim

    Einmal editiert, zuletzt von insight-er (20. Februar 2021 um 04:28)

  • Signale funktionieren Thread-übergreifend !

    Signale *koennen* Thread-Uebergreifend funktionieren. Aber nicht so wie von dir mit dem simplen connect gezeigt - die muessen "QueuedConnections" sein, damit sie aus dem einen Thread in die Event-Queue des anderen gepackt werden. Sonst sind sie direkt, und koennen Abstuerze verursachen.

    Vor allem aber sind sie eine Eigenheit von Qt, Dennis verwendet aber Kivy - da gibt es die also nicht.

    Dennis89 nackte try/except wie von dir gezeigt sollte man in erster Naehrung *niemals* machen, weil die auch beliebige Fehler die du beim Programmieren machst maskieren koennen. Die Faustregel ist, immer so spezifisch auf Fehler zu reagieren wie moeglich. Es gibt Ausnahmen, aber das sind Sonderfaelle bei zB Webservern, die wiederum Code beliebiger Entwickler laufen lassen, und bei Fehlern nicht abstuerzen, sondern nur einen "Error 500" werfen, und weitermachen. Das liegt bei dir aber nicht vor.

    Und zum Thema Darstellung von Fehlern: auch da wuerde ich anders vorgehen. Python hat das logging-Modul. Das sollte man benutzen, inklusive seiner logging.exception()-Funktion. Und erstmal geht damit alles in STDOUT, einen Datei, oder das SYSLOG, je nach Konfiguration.

    Wenn man dann tatsaechlich zB eine Konsole in seiner GUI haben will, kann man auch einen Handler definieren, der die eintreffenden Fehlermeldungen in ein GUI-Element packt. Aber eine fest verdrahtete Behandlung wuerde ich nicht empfehlen.

  • Hallo @__deets__

    danke für deine Antwort.

    nackte try/except wie von dir gezeigt sollte man in erster Naehrung *niemals* machen,

    Kurz zur Erklärung, warum die nackten try/except im Code sind: Ich hatte zu Beginn gar keine try/except im Code, da ich im am Laptop erstellt habe und da war bei den Testläufen immer alle Rahmenbedingungen gegeben. Als mir beim testen mit dem Pi das Programm abgebrochen ist, weil dieser zum Beispiel nicht auf einen Netzwerkpfad zugreifen konnte, habe ich überall ein 'try' reingesetzt, wo ich es für sinnvoll gehalten habe. So dass das Programm niemals abbricht und im schlimmsten Fall die Funktion nicht nicht gegeben ist. Dadurch sind dann aber natürlich meine Log-Dateien die Kivy erstellt nicht mehr hilfreich geworden.

    Die Faustregel ist, immer so spezifisch auf Fehler zu reagieren wie moeglich.

    Das heißt ich mache mir Gedanken, welche Fehler auftreten können und reagiere dann mit einer passenden Exception von hier (Python Doc - Exception) ?

    Python hat das logging-Modul. Das sollte man benutzen,

    Danke, dann werde ich diesen Ansatz weiterverfolgen :thumbup:

    Wenn ein Fehler auftritt, dann ist es meiner Meinung nach nicht nötig diesen komplett im GUI auszugeben. Ein kleines Fenster mit "Error" würde mir genügen. Wenn es mir passiert kann ich in den Log-Dateien nachschauen und wenn es bei meiner Freundin auftritt gibt sie mir Bescheid und ich schaue wieder in den Log-Dateien nach. :)

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Guten Abend,

    ich habe die nackten try/except ersetzt bzw. bin dabei dies zu tun. Um das mit 'logging'-Modul zu testen, habe ich ein Mini-Programm auf dem Laptop geschrieben, dass einen Fehler werfen muss. Dabei würde ich auf die Fehler so reagieren:

    Das wäre bis jetzt auch mein Plan für die MusicBox. War das von dir @__deets__ (wenn ich so direkt fragen darf?) so gemeint?

    Die 'test_log.log'-Datei enthält jetzt diese Zeilen:

    Code
    ERROR:root:Command '['ls', '-l', PosixPath('/home/dennis/apfelmus')]' returned non-zero exit status 2.
    Traceback (most recent call last):
      File "/home/dennis/Dokumente/testt.py", line 11, in <module>
        subprocess.run(["ls", "-l", PATH], check=True)
      File "/usr/lib64/python3.9/subprocess.py", line 524, in run
        raise CalledProcessError(retcode, process.args,
    subprocess.CalledProcessError: Command '['ls', '-l', PosixPath('/home/dennis/apfelmus')]' returned non-zero exit status 2.

    Dass der Ordner 'apfelmus' nicht vorhanden ist steht jetzt leider nicht direkt drin. Im Vergleich zu der Fehlermeldung, wenn ich den Code ohne try/except starte:

    ls: Zugriff auf '/home/dennis/apfelmus' nicht möglich: Datei oder Verzeichnis nicht gefunden


    Du hattest noch geschrieben "immer so spezifisch wie möglich auf Fehler reagieren". In meinem kleinen Beispielprogramm wäre der Fehler ja 'FileNotFoundError', nur das habe ich noch nicht "eingebaut" bekommen.


    Bitte nicht falsch verstehen, ich freue mich über jeden der mir helfen kann/will.


    Danke schon mal und Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Nein, das ist CalledProcessError. FileNotFound waere es nur, wenn Python es selbst macht, mit pathlib oder os.path. Dein Beispiel ist natuerlich konstruiert, in echt wuerde man ls ja nicht aufrufen, sondern das mit Python machen.

    Und was die Eintraege des logs angeht: du kannst ja auch die anderen logging-Aufrufe nutzen. Die exception ist ja viel Information, und eigentlich eher fuer Fehlerfaelle gedacht, die man gar nicht behandelt hat. Du kannst aber vorher oder stattdessen ja auch logger.error oder warn oder info oder debug ausgeben.


    ZB wenn du in einer config einen Pfad zu einem USB-Stick stehen hast, der aber nicht eingesteckt ist - dann kannst du warnen, dass die darauf befindlichen MP3s jetzt nicht zur Verfuegung stehen, aber eine exception wuerde ich dafuer nicht loggen.

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!