Externe M3U Playlist

Moin zusammen,

folgende Idee:

Wie aufwändig wäre das? Bzw. würde sich das wohl überhaupt umsetzen lassen?

Bei Fragen oder wenn ich noch irgendwie helfen oder Infos nachlegen kann, meldet euch gerne.

Viele Grüße
Jan

Bedenke:
Das Problem an dem Ganzen ist halt, dass es voraussetzt, dass die WLAN-Verbindung brauchbar ist. Der ESP32 kann hier auch durchaus mal zickig sein. Ich bin mir nicht so sicher, ob das wirklich was ist, wo man sich ständig drauf verlassen will. Das ist halt mit SD irgendwie sicherer.

Also grundsätzlich entfernte M3Us öffnen kann ESPuino ja, weil es sich hierbei auf die interne Funktionalität von der Audiolib. Problem an dieser Stelle ist halt, dass einfach das erste File geöffnet wird und überhaupt nur eine Playlist mit genau einem Titel entsteht.

Grundsätzlich klingt’s erstmal easy: Verknüpfe ein m3u und sorge dafür, dass es vorher runtergeladen wird. Nur muss man halt beides speichern: die m3u-Verknüpfung zur Karte und auch den Link zum m3u. Darauf ist weder die GUI vorbereitet, noch der NVS-Parser. Die Struktur im NVS (pro Eintrag) sieht zB so aus:

Da musst du also:

  • Den Parser anpassen
  • Die GUI anpassen
  • Das m3u runterladen an die Stelle, wo es gespeichert werden soll
  • Die NVS-Write-Funktion anpassen

Und im Anschluss musst du dann testen, ob das Schreiben und Wiederherstellen der NVS-Einträge in das Backup-File auf der SD-Karte noch funktioniert.

Moin,

ich hab jetzt selbst etwas mehr experimentiert, wenn ich Webstream als Quelle auswähle und einen M3U-Link angebe wird diese Datei auch richtig verarbeitet, interpretiert und der erste Track davon korrekt abgespielt, das scheint ja auch alles in der AudioLib zu laufen. Das ist schonmal die halbe Miete. Die M3U-Datei brauche ich ja gar nicht lokal, die kann gerne jedes Mal neu eingelesen werden. Aber leider spielt er den ersten Song immer und immer wieder ab, statt dann den zweiten der M3U-Datei zu nutzen. Das Vorwärts/Zurück nicht klappt ist mir klar weil bei Webstream ja immer von einem einzelnen Track ausgegangen wird. Da muss ich nochmal genauer schauen warum nur der erste Track funktioniert und dann wiederholt wird.

Natürlich alles unter der Premise das, das WLAN funktioniert und der ESP nicht zickt das ist klar :slight_smile:

Weil die Audiolib halt so arbeitet. Sie schnappt sich aus der m3u den ersten Eintrag.
Diese Playlist-Sache habe ich quasi überstülpt, aber die Audiolib kapselt das. Anders gesagt: ESPuino kriegt davon nix mit, dass da mehrere Einträge drin sind. Für ESPuino ist das nur ein m3u-File. Lokal ist das anders: Da ist jede Zeile ein Eintrag der Playlist.

Irgendwie war es gestern glaube ich schon zu spät :slight_smile:.
Also Parser und GUI muss man natürlich nicht erweitern. Man will ja einfach nur einen Link zu einem externen m3u-File machen. Das tut man aktuell ja auch schon, nur will man den Inhalt ausparsen und nicht die Kontrolle vollständig an die Audiolib übergeben. Man will in diesem Fall also, dass man selbst die Kontrolle hat, eine Playlist generiert und das Ganze dann Eintrag für Eintrag an die Audiolib übergibt.

Die Strukturierung muss man sich halt überlegen. Weil aktuell gibt es den Modus Webstream und gibt „alles Andere“ (z.B. auch lokales m3u). Die Stelle, an der sich der Pfad teilt, ist hier

Da ist alles, was nicht Webradio ist, als lokal zu betrachten. D.h. hier wird Kram ausgeparst und Playlists generiert. Dann gibt es den Modus Webradio: Da besteht die Playlist immer aus exakt einem Eintrag:

Da muss man dann ansetzen. D.h. man nimmt die URL, macht einen HTTP-Call, parst die Payload aus und generiert daraus eine Playlist. Man muss sich aber die AudioPlayer.cpp genauer anschauen dann. Weil es gibt Stellen, die sich drauf verlassen, dass Webradio immer nur einen Titel besitzt. Z.B. hier:

Solche Sachen müsste man, dann allerdings inkl. der Webstream-spezifischen Fehlermeldungen, aber eigentlich gefahrlos ausbauen können, da es anderweitig auch Checks gibt (muss man aber testen, ob die wirklich greifen), die die Anzahl der Tracks in einer Playlist überwachen. Also letztlich ist das ein 2d-Array (char**) und da muss man natürlich verhindern, dass man dessen Grenzen verlässt.

An deiner Stelle würde ich erstmal so ein m3u auf den ESPuino laden, dann als lokales m3u verknüpfen und schauen, wie gut das mit der Streamerei so funktioniert von YT.

Nochmal eben eine Runde drüber nachgedacht…

Hier könnte man im Prinzip prüfen, ob *fileName mit http oder https anfängt und mit .m3u aufhört. Aktuell würde das ausfehlern, da ESPuino im Dateisystem danach suchen würde, was aber natürlich nicht klappt. Künftig könnte man da einen WebClient starten, mit dem man das File dann runterlädt. Und damit kann man dann hier wieder einsteigen:

Aber das muss dann das einfache .m3u-Format sein, bei dem pro File/Link eine Zeile verwendet wird. Das kann auch (gemischt) lokale Files und Links beinhalten.

Also ist jetzt doch einfacher, als ich es initial gedacht hätte. Ich denke ich schaue mir das mal an.

Moin,

ich hab es mal angefangen zu implementieren um das ganze System besser kennen zu lernen. Leider muss ich gestehen Java / JavaScript / Typescript-Entwickler zu sein und die C++ Kentnisse liegen lange in der Vergangenheit :frowning:

Ich hab es im ersten Wurf als extra „Remote M3U“ Value gebaut, inkl. UI, Values, etc… ich hänge beim parsen des Ergebnises vom HTTP-Client, hab einen String und muss daraus die Playlist parsen, siehe:

char **AudioPlayer_ReturnPlaylistFromM3U(const char* _webUrl) {
	httpClient.begin(_webUrl);
	int httpResponseCode = httpClient.GET();

	if (httpResponseCode >= 200 && httpResponseCode <= 300) {
		String payload = httpClient.getString();
		// TODO parse M3U
		Log_Println(payload.c_str(), LOGLEVEL_INFO);
	}

	httpClient.end();

	static char **urlResult = (char **)x_malloc(sizeof(char *) * 2);
	urlResult[0] = x_strdup("1");
	urlResult[1] = x_strdup("test");
	return ++urlResult;
}

Deine Version in deiner letzten Antwort klingt natürlich auch spannend :slight_smile:

Edit:
Ach und hab nebenbei genau das getestet gehabt, Lokal eine M3U erstellt mit 15 YT Songs und die beim Entwicklen laufen lassen. Hatte da bisher keine Probleme vernommen. Allerdings bin ich auch „recht nah“ am Access Point, was die WLAN-Verbindung angeht. Die Internet-Verbindung spielt eigentlich keine Rolle, da der yt-streaming-api-service bei mir im Netzwerk läuft und die Songs ja lokal cached und erst nach einer gewissen Zeit verwirft.

Der Vorteil an meiner letzten Variante ist, dass der Parser ja eh schon da ist. Man speichert nur ein .m3u ab, dann parst man es, wie vorher, und löscht es ggf wieder.

Dann würde ich einfach den existierenden Modus nehmen und ihn halt nicht mehr lokal nennen.

Ich nehme mich der Sache mal an.

M3U Dateien gibt es viele im Netz. Meistens werden sie von Radiostationen benutzt. Wenn sich der Streamingserver ändert wird die M3U angepasst, ohne dass die Höhrer in ihrer App die neue URL eintragen zu müssen. Häufig gibt es in der M3U (PLS, ASX) nur einen Eintrag, weil der Stream „unendlich“ läuft. Deshalb reicht es der Audio Lib, wenn der erste Eintrag gültig ist.
Kürzlich habe ich mit DLNA experimentiert. Ein DLNA Server hat jeder zu Hause, der z.B eine Fritzbox besitzt. Für die Kommunikation mittels DLNA gibt es die recht gute Lib SoapESP32 von Yellowbyte. Ein DLNA Server sortiert nicht nur nach der Ordnerhiearchie sondern auch nach Interpreten, Genres…
Allerdings sin die Ordner und Audidateien nicht mit Klarnamen abrufbar, sondern haben speziellen IDs. Ist die ID von einen Ordner, Genre etc. bekannt liefert SoapESP32 den gesamten Inhalt, max 100 Einträge. Das erspart das Editieren einer Playlist. Ein DLNA-Beispiel habe ich in meinen Repo hinterlegt.

1 „Gefällt mir“

@najjannaj Also ich habe da mal ein bisschen rumprobiert, aber aus meiner Sicht ist das problematisch mit dem Speicher des ESP32. Hab’s versucht auf dem psram zu allokieren, aber das hat auch nicht so wirklich was gebracht.

Ab Zeile 214 geht’s los:

Funktioniert so, dass man einen m3u-Link über die GUI anlernt. Dann wird das File abgerufen, lokal gespeichert in ein Tempfile und der Name des Tempfile in die bisherige Funktion gesteckt.

Der Code ist jetzt aber nicht groß aufgeräumt und nur angetestet. Damit kann man .m3u-Files von http- und https-Servern holen. Die Playlists werden auch generiert, aber es läuft noch was schief. D.h. trägt man in das m3u-File lokale SD-Files dann klappt das, aber Webstreams klappen nicht.

Viel Spaß beim Rumprobieren :slight_smile:

@Wolle Das mit DLNA hatte ich die Tage gesehen. Aber ist vermutlich wieder schwer zu integrieren in ESPuino.

Also ich konnte das ganze etwas ans Laufen bringen:

Das parsen scheint mit Metadaten nicht ganz zu funktionieren, da bei mir 5 Lieder erkannt werden statt 2 die wirklich drin sind, der rest sind Metadaten. Aber das sei mal dahingestellt, jetzt habe ich ein anderes Problem:

[ 79693 ]  Modus: Webstream (lokale .m3u-Datei)
[ 79696 ]  Neue Playlist empfangen mit 5 Titel(n)
[ 79696 ]  Free heap: : 88448
[ 79698 ]  Datei oder Verzeichnis existiert nicht : EXTM3U
[ 79703 ]  info        : Connect to new host: "http://192.168.178.20:3008/api/music/stream/icPHcK_cCF4"
[ 79751 ]  info        : PSRAM found, inputBufferSize: 283615 bytes
[ 79752 ]  info        : buffers freed, free Heap: 88044 bytes
[ 79767 ]  info        : Connection has been established in 15 ms, free Heap: 87364 bytes
[ 79771 ]  'http://192.168.178.20:3008/api/music/stream/icPHcK_cCF4' wird abgespielt (2 von 5)
[ 82122 ]  info        : Filename is a5fb24678455f3d9b91b2bbea12ce9e4.mp3
[ 82125 ]  info        : chunked data transfer
[ 82125 ]  info        : MP3Decoder has been initialized, free Heap: 55784 bytes
[ 82129 ]  lasthost    : http://192.168.178.20:3008/api/music/stream/icPHcK_cCF4
[ 82143 ]  info        : stream ready
[ 82145 ]  info        : syncword found at pos 138
[ 82153 ]  info        : syncword found at pos 0
[ 82175 ]  info        : Channels: 2
[ 82175 ]  info        : SampleRate: 44100
[ 82175 ]  info        : BitsPerSample: 16
[ 82176 ]  info        : BitRate: 128000
[ 88199 ]  info        : slow stream, dropouts are possible
[ 89200 ]  info        : slow stream, dropouts are possible
[ 90202 ]  info        : slow stream, dropouts are possible
[ 91203 ]  info        : slow stream, dropouts are possible
[ 91909 ]  info        : Stream lost -> try new connection
[ 91909 ]  info        : Connect to new host: "http://192.168.178.20:3008/api/music/stream/icPHcK_cCF4"
[ 91916 ]  info        : buffers freed, free Heap: 87312 bytes
[ 91928 ]  info        : Connection has been established in 9 ms, free Heap: 87016 bytes
[ 91950 ]  info        : Filename is a5fb24678455f3d9b91b2bbea12ce9e4.mp3
[ 91953 ]  info        : chunked data transfer
[ 91953 ]  info        : MP3Decoder has been initialized, free Heap: 55444 bytes
[ 91957 ]  lasthost    : http://192.168.178.20:3008/api/music/stream/icPHcK_cCF4
[ 91972 ]  info        : stream ready
[ 91972 ]  info        : syncword found at pos 138
[ 91984 ]  info        : syncword found at pos 0
[ 91999 ]  info        : Channels: 2
[ 91999 ]  info        : SampleRate: 44100
[ 91999 ]  info        : BitsPerSample: 16
[ 91999 ]  info        : BitRate: 128000
[ 92211 ]  info        : slow stream, dropouts are possible
[ 98297 ]  info        : slow stream, dropouts are possible
[ 99298 ]  info        : slow stream, dropouts are possible
[ 100300 ]  info        : slow stream, dropouts are possible
[ 101301 ]  info        : slow stream, dropouts are possible
[ 101743 ]  info        : Stream lost -> try new connection
[ 101743 ]  info        : Connect to new host: "http://192.168.178.20:3008/api/music/stream/icPHcK_cCF4"
[ 101750 ]  info        : buffers freed, free Heap: 87036 bytes
[ 101879 ]  info        : Connection has been established in 127 ms, free Heap: 86732 bytes
[ 101901 ]  info        : Filename is a5fb24678455f3d9b91b2bbea12ce9e4.mp3
[ 101903 ]  info        : chunked data transfer
[ 101904 ]  info        : MP3Decoder has been initialized, free Heap: 55152 bytes
[ 101908 ]  lasthost    : http://192.168.178.20:3008/api/music/stream/icPHcK_cCF4
[ 101922 ]  info        : stream ready
[ 101925 ]  info        : syncword found at pos 138
[ 101937 ]  info        : syncword found at pos 0
[ 101957 ]  info        : Channels: 2
[ 101957 ]  info        : SampleRate: 44100
[ 101957 ]  info        : BitsPerSample: 16
[ 101957 ]  info        : BitRate: 128000
[ 102307 ]  info        : slow stream, dropouts are possible
[ 108398 ]  info        : slow stream, dropouts are possible
[ 109399 ]  info        : slow stream, dropouts are possible
[ 110400 ]  info        : slow stream, dropouts are possible
[ 111401 ]  info        : slow stream, dropouts are possible
[ 111688 ]  info        : Stream lost -> try new connection
[ 111688 ]  info        : Connect to new host: "http://192.168.178.20:3008/api/music/stream/icPHcK_cCF4"
[ 111695 ]  info        : buffers freed, free Heap: 82956 bytes
[ 111812 ]  info        : Connection has been established in 115 ms, free Heap: 86400 bytes
[ 111845 ]  info        : Filename is a5fb24678455f3d9b91b2bbea12ce9e4.mp3
[ 111846 ]  info        : chunked data transfer
[ 111847 ]  info        : MP3Decoder has been initialized, free Heap: 54836 bytes
[ 111852 ]  lasthost    : http://192.168.178.20:3008/api/music/stream/icPHcK_cCF4

Da muss ich als nächstes mal prüfen ob das an den MP3-Streams liegt die der Server ausspielt oder der Lib.

Ok, also wenn http und nicht https verwendet wird, macht das die Sache natürlich erheblich einfacher in Sachen Speicher.
Solche Probleme mit „slow stream“ habe ich bei Webradio auch immer mal wieder. Also gibt Tage, da passiert das gar nicht. Und gibt Tage, da ist’s übel. Woran das liegt weiß ich nicht. Die Sachen für meine Kinder liegen auf jeden Fall alle lokal auf SD und das wird auch so bleiben :slight_smile:.

Moin,

ich hatte jetzt nochmal etwas Zeit zu testen und die „slow stream“-Warnings und warum der Track immer wiederholt wird liegt tatsächlich nur am Stream selbst:

  • Der YT-Streaming-Service kann die „content-length“-header beim GET-Request an die MP3-Files nicht setzen, da die Datei wächst und unklar ist, wie lange diese ist.

  • Solange Daten nachkommen (wie bei einem Webradio) funktioniert es auch ohne Probleme. Schließt der Server die Verbindung kommen keine Daten mehr nach, was normalerweise signalisieren sollte das die Daten fertig sind, die Lib wartet aber weiter auf Daten und schreibt dann „slow stream“ als Log-Ausgabe. Ist die MP3 dann komplett abgespielt, aber es kommen keine Daten nach, denkt die Lib das ein Fehler aufgetreten ist und fängt das Streaming erneut an, dann beginnt der Teufelskreis von vorne. Nehme ich MP3 Dateien, bei denen der Content-Length gesetzt ist / werden kann, funktioniert alles.

Soweit erstmal dazu :slight_smile:

Hast jetzt nochmal was an meinem Code modifiziert oder passt das so?