Wie eine eigene HTML Playlist für das Spotify Modul bauen?

Ich würde gerne eine Suche auch benutzen ohne dazu jedes mal die Instanz öffnen zu müssen.

Es gibt zwar die Methode SPO_Search, diese besitzt allerdings keinen Rückgabewert, ist also für das benutzen z.B. aus einem Skript zur Zeit nutzlos.

Jetzt könnte ich mir zwar eine eigene Methode schreiben mit Hilfe von SPO_MakeAPIRequest, ist aber mehr als umständlich, wenn es an sich schon eine Methode gibt, der lediglich der Rückgabe Wert fehlt.

Habe ich bemerkt, ich bräuchte aber für andere Zwecke die URL des Covers. Ich nutzte zur Zeit eine eigene Abfrage über

SPO_MakeAPIRequest(13233, 'GET', 'me/player?additional_types=episode', '');

und lese da die URL aus. Es wäre aber zumindest aus meiner Sicht schön, wenn man dazu nicht zwingend einen eigenen API Request nutzten muss, sondern es auch eine Methode in dem Modul gibt, die bei Aufruf über ein Skript die URL zurückgibt.

Ich würde gerne eine eigene Playlistansicht benutzten mit dem Spotify SDK

Der Entwurfcode sieht zunächst mal so aus:

<?php

<!DOCTYPE html>
<html>
<head>
    <title>Meine Spotify-Playlist</title>
    <script src="https://sdk.scdn.co/spotify-player.js"></script>
</head>
<body>
    <h1>Meine Spotify-Playlist</h1>
    <div id="player"></div>
    
    <script>
        window.onSpotifyWebPlaybackSDKReady = () => {
            const token = 'IHR_SPOTIFY_AUTH_TOKEN'; // Ersetzen durch eigenen Spotify-Authentifizierungstoken
            
            const player = new Spotify.Player({
                name: 'Mein Spotify-Player',
                getOAuthToken: cb => { cb(token); }
            });
            
            // Fehlerbehandlung
            player.addListener('initialization_error', ({ message }) => {
                console.error('Fehler bei der Initialisierung:', message);
            });
            player.addListener('authentication_error', ({ message }) => {
                console.error('Authentifizierungsfehler:', message);
            });
            player.addListener('account_error', ({ message }) => {
                console.error('Accountfehler:', message);
            });
            player.addListener('playback_error', ({ message }) => {
                console.error('Wiedergabefehler:', message);
            });
            
            // Das Gerät ist bereit
            player.addListener('ready', ({ device_id }) => {
                console.log('Gerät ist bereit mit ID:', device_id);
                
                // Stellen Sie sicher, dass Ihre Spotify-Playlist-URI hier korrekt ist
                const playlistUri = 'spotify:playlist:PLAYLIST_ID'; // Ersetzen der 'PLAYLIST_ID' durch eigene Playlist-URI
                
                // Spielt die Playlist ab
                player
                    .play({
                        uris: [playlistUri],
                        device_id: device_id,
                    })
                    .then(() => {
                        console.log(`Jetzt wird ${playlistUri} abgespielt.`);
                    })
                    .catch(error => {
                        console.error('Fehler beim Abspielen der Playlist:', error);
                    });
            });
            
            player.connect();
        };
    </script>
</body>
</html>

Ich muss in dem Code die Playlist ID und den Token übergeben, den Rest macht der Spotify SDK.

Gibt es eine Möglichkeit einfach an den Token zu kommen? Dieser ist ja in einem Attribut gespeichert. Ich habe nur eine Methode gefunden den Token zurückzusetzen, aber keine den Token in einem Skript auszulesen.

Wie komme ich an die aktuelle Playlist ID, gibt es da eine Methode im Modul für oder könnte man diese ergänzen, um diese aus einem Skript abzufragen?

Wenn es um dein Script hier geht:

Dann hol den Inhalt aus dem Medienobjekt mit IPS_GetMediaContent
und dann wieder mit

imagecreatefromjpeg('data://image/jpeg;base64,'.IPS_GetMediaContent(12345));

Dann habe ich ein Bild, dann muss ich ja aber immer noch ein Webbhook bauen, wenn ich das von IP-Symcon abrufen will. Einfacher ist es gleich die URL auszulesen und diese externe an einem Img Tag an eine externe Webseite zu übergeben. So mache ich das zur Zeit. Geht ja auch mit einer eigenen Methode.

Es gibt ja eine public Methode des Moduls
public function UpdateVariables()

Aber leider hat diese Methode auch keinerlei Rückgabewert. Das ist schade, so kann man zwar die Daten aktualisieren, was das Modul aber so oder so selber macht, hat aber wieder keine Möglichkeit die Werte, die das Modul so oder so abruft, selber aus einem Skript abzurufen. Statt dessen muss man wieder eine eigene Funktion schreiben.

Es wäre schon wünschenswert wenn es auch eine public Methode gibt, die einfach ein Array an ein aufrufendes Skript ausgibt, ohne das man wieder alles nochmals selber schreiben muss.

Das Modul selbst merkt sich die URL überhaupt nicht. Da dein Fall schon sehr speziell ist, würde ich dafür jetzt auch keinen einfacheren Weg anbieten wollen. @Nall-chan hat schon ein paar schöne Ideen skizziert, wie man das umsetzen kann.

Ist ja Eure Entscheidung was ihr anbietet oder nicht. Ich bin zumindest froh das es die Möglichkeit gibt eigene API Request über SPO_MakeAPIRequest zu erstellen. Ich habe mir jetzt als so beholfen das ich mir selber eine Anfrage erstellt habe, mit der ich die URL abhole, ebenso eine eigene Funktion zur Suche, da eure Methode ja keinen Rückgabewert liefert.

Eine Frage hätte ich noch zur Playlist bzw. dem Inhalt der Variable. Ist dies jetzt ein Standardformat was so dann von jedem möglichen Medienplayern abgelegt werden soll und ist diese Playlistdarstellung in der neuen Visualisierung dann universell ?

Wenn ich mir meine eigene Playlistdarstellung benutzten will mit Coveransicht in der Playlist, dann muss ich wohl wieder einen eigenen Request über SPO_MakeAPIRequest erstellen, oder?

Wenn ich dann aus der selber erstellenden Liste den Song auch starten will muss ich dann wieder SPO_PlayURI() benutzten? Wird dann von SPO_PlayURI() automatisch auf dem Gerät abgespielt das in IP-Symcon selektiert wurde?

Was wäre den der aktuelle Weg aus einer Liste dann so einen Befehl an IP-Symcon abzusetzen? Nutzen von Javascript und aufrufen der JSON RPC und absetzten von SPO_PlayURI() ?

Hätte jemand dazu vielleicht eine Code Beispiel?

Die neue Visu zeigt doch Playlist und Cover an wenn ich mich nicht irre. Was gefällt dir denn daran nicht?

paresy

Zunächst mal finde ich das ein riesigen Fortschritt das dies jetzt mit der neuen Visualisierung kommt, danke dafür.

Über Präferenzen wie man was angezeigt bekommen will, kann man sicher lange diskutieren und sich austauschen, da hat ja jeder andere Vorlieben.

Ich persönlich möchte die Freiheit haben das so anzupassen, wie mir das vom Layout gefällt. In sofern ist die Lösung die Ihr anbietet toll für alle Benutzer die keine individuellen Ansprüche haben.

Ich möchte eben die Ansicht gerne mit CSS anpassen können was Farbe, Hintergrund, Schriftart, Schriftgröße usw. anbelangt. Das ist wohl nur möglich wenn ich mir die Playlist selber erstelle. Ansonsten wäre das höchstens eine Anregung für die Zukunft das man irgendwie über CSS auch solche Dinge im neuen Webfront anpassen kann.

Vom Aussehen hätte ich das gerne so in der Form selber genutzt
Bildschirmfoto 2023-10-05 um 10.03.55

Also links eine Zahl und daneben ein Cover. Wie ihr das gestaltet bzw. allgemein anbietet obliegt ja Euch, ich hätte das halt gerne etwas anders für mich dargestellt und die Möglichkeit das individueller anzupassen.

Die Playlist Daten, die in der Variable liegen, enthalten ja kein Cover. Ist es angedacht so was in Zukunft vielleicht noch zu ergänzen, wenn man aus den Daten selber etwas erstellen will oder muss ich mir dann eine eigene Abfrage bauen mit der ich die Playlist inclusive der Cover URLs auslese?

Die Liste mit PHP aus dem Rückgabewert von Spotify oder auch aus der Variable Playlist zu erstellen bekomme ich noch so weit hin.

Die Frage wäre was wäre der aktuell empfohlene Ansatz dann aus dieser Ansicht einen Befehl an IP-Symcon zu schicken, sprich den passenden Track zu starten?

Dazu müsste ich wohl, wenn ich das richtig verstanden habe die Methode SPO_PlayURI() mit der passenden URI aufrufen aus der eigenen erstellenden Webseite aufrufen.

Das müsste ja irgendwie mit Javascript gehen, da habe ich mich aber noch nicht damit beschäftigt wie das genau funktioniert.

Gibt es dazu ein kurzes Codebeispiel, wie man aus einer eigenes erstellen Webseite, einen Song über IP-Symcon abspielt?

Ich habe mir jetzt was geschrieben, was mir die Elemente über die API und einen SPO_MakeAPI Request abfragt und vom Design eine Playlist setzt. Sieht zur Zeit so aus

Jetzt muss ich nur noch auf den Eintrag jeweils eine Aktion legen um den passenden Song zu starten. Kann mir da jemand einen Tipp geben wie so ein Code aussehen muss? Verwenden muss ich denke ich SPO_PlayURI, nur wie muss die URI genau aussehen die ich dort an die SPO Methode übergeben muss?

a href kann ich ja nicht einfach benutzten ich will ja das der Code bzw. die SPO Methode ausgeführt wird oder das einem Link gefolgt wird. Es muss also wohl Javascript verwendet werden. Nur wie sieht so ein Code dann aus?

Die beste Variante wäre wahrscheinlich, dass du einen Webhook für den Aufruf bereitstellst und diesen mit der passenden URI aufruft. Die URI solltest du bei deinen ganzen Songanfragen eigentlich mit bekommen, die ist üblicherweise im Feld „uri“ und sieht irgendwie so aus: „spotify:album:2up3OPMp9Tb4dAKM2erWXQ“. Und die packst du denn halt bei SPO_PlayURI rein.

Gefällt mir!
Cheers Seppm

Momentan ist das ja nur das Layout, aber noch ohne Funktion, da ja noch der Funktionsaufruf des Songs zur Zeit fehlt. Ich werde das mal wie von Dr.Niels vorgeschlagen über einen Webhook versuchen zu lösen. Wenn das so weit dann funktionieren sollte, stelle ich das fertige Skript gerne zur Verfügung, dann kann das ja derjenige, der das benutzen will, an individuelle Bedürfnisse anpassen bzw. das CSS und das Skript editieren.

Das ist der jetzige PHP Code, falls da jemand mit rumspielen will, bzw. was ergänzen will. Es fehlt wie gesagt noch der Funktionsaufruf. Mit dem Layout bin ich auch noch nicht ganz glücklich. Also wer da Verbesserungen und Anmerkungen hat, immer her damit.

<?php

// https://developer.spotify.com/documentation/web-api/reference/get-playlist

// Cover mit Playlist

$spotify_id = 13233;

$currentTrackData = CurrentlyPlaying();
// var_dump($currentTrackData);

$playlistId = ExtractPlaylistFromCurrentlyPlaying($currentTrackData);
var_dump($playlistId);

$playlistData = CurrentPlaylist($spotify_id, $playlistId); 
// var_export($playlistData);

//Playlist Name
$name = $playlistData['name'];
var_dump($name);

// Playlist Beschreibung
$playlist_description = $playlistData['description'];
var_dump($playlist_description);

// Playlist Image
$playlist_img = $playlistData['images'][0]['url'];
var_dump($playlist_img);

$tracks = $playlistData['tracks'];

// Anzahl der Tracks
$anzahl_tracks = $playlistData['tracks']['total'];
var_dump($anzahl_tracks);

// https://cssgradient.io
$html_start = "
<!DOCTYPE html>
<html>
<head>
    <title>Songliste</title>
    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css\">
    <style>
        /* CSS-Stil für Hover-Effekt */
        table tbody tr:hover {
            background-color: #ddd; /* Hellere Farbe für Hover-Effekt */
        }

        /* CSS-Stil für aktive Tabellenzeile */
        table tbody tr:active {
            color: #3498db; /* Schriftfarbe in aktiver Zeile ändern */
        }

        /* CSS-Stil für die Trennlinien zwischen den Zeilen */
        table tbody tr {
            border-bottom: 1px solid white;
        }

        /* CSS-Stil für das linke Feld (Cover) */
        table tbody td.left {
            padding: 5px;
            text-align: left;
            width: 100px; /* Schmalere Spalte für das Cover */
        }

        /* CSS-Stil für das mittlere Feld (Info) */
        table tbody td.info {
            padding: 5px;
        }

        /* CSS-Stil für das mittlere Feld (Album) */
        table tbody td.album {
            padding: 5px;
            text-align: left;
        }

        /* CSS-Stil für das rechte Feld (Laufzeit) */
        table tbody td.right {
            padding: 5px;
            text-align: center;
            vertical-align: middle;
        }

        /* CSS-Stil für das Coverbild */
        table tbody td.left img {
            max-width: 80px;
            width: auto; /* Automatische Breite für das Bild */
            height: auto; /* Automatische Höhe für das Bild */
        }

        /* CSS-Stil für den starken Text (fetter Text) */
        table tbody td.info strong {
            font-weight: bold;
            font-size: 18px; /* Ändern Sie die Schriftgröße nach Bedarf */
            font-family: Arial, sans-serif; /* Ändern Sie die Schriftart nach Bedarf */
        }

        /* CSS-Stil für die gesamte Tabelle */
        table {
            border-collapse: separate; /* Trennen Sie die Rahmen von der Tabelle */
            width: 100%;
            background: rgb(2,0,36);
            background: linear-gradient(0deg, rgba(2,0,36,1) 0%, rgba(21,96,14,1) 35%, rgba(0,212,255,1) 100%);
            border-radius: 10px;
        }

        /* CSS-Stil für Tabellenkopf */
        table th {
            color: #666; /* Graue Schriftfarbe */
            padding-top: 15px;
            padding-left: 20px;
            text-align: left; /* Text linksbündig ausrichten */
        }

        /* CSS-Stil für Tabellenzeilen im Tabellenkopf */
        thead tr:hover {
            background-color: transparent; /* Kein Hover-Effekt für Tabellenüberschrift */
        }
        

        /* CSS-Stil für Tabellenzeilen */
        table tbody tr {
            background-color: transparent; /* Transparente Hintergrundfarbe für Zeilen */
            color: #666; /* Graue Schriftfarbe */
            text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); /* Leichter schwarzer Textschatten */
        }
    </style>
</head>
<body>
    <table style=\"border-collapse: collapse; width: 100%;\">
        <thead>
            <tr>
                <th>Titel</th>
                <th>Info</th>
                <th>Album</th>
                <th><i class=\"fa fa-clock\"></i></th> <!-- Uhrzeichen -->
            </tr>
        </thead>
        <tbody>";
$html = ""; 

echo "Tracks:";
$items = $playlistData['tracks']['items'];
// var_dump($items);

// Schleife durch die Tracks und Erstellung der Tabellenzeilen
foreach($items as $item)
{
    $html .= '<tr>';
    $html .= '<td class="left">';
    $html .= '<img src="' . $item['track']['album']['images'][0]['url'] . '" alt="Cover"></td>'; // Cover-URL
    $html .= '<td class="info"><strong>' . $item['track']['name'] . '</strong><br>'; // Trackname

    // Artist
    $artists = $item['track']['artists'];
    // var_dump($artists);
    $artistlist = "";
    foreach($artists as $artist)
    {
        // var_dump($artist);
        $artist_name = $artist['name'];
        $artistlist .= $artist_name. ", "; 
    }

    // Entfernen Sie das letzte Komma und führende/trailing Leerzeichen
    $artistlist = rtrim($artistlist, ', ');

    $html .= $artistlist;
    $html .= '</td>';

    // Album Feld 
    $html .= '<td class="album">';
    // Albumname
    $albumName = $item['track']['album']['name'];
    $html .= $albumName;
    $html .= '</td>';
    
    // Rechtes Feld mit Laufzeit (zentriert)
    $html .= '<td class="right">';

    // Laufzeit in Millisekunden (umrechnen in Sekunden)
    $durationMs = $item['track']['duration_ms'];
    $durationSec = $durationMs / 1000;
    $duration = formatDuration($durationSec);

    $html .= $duration;
    $html .= '</td>';
    
    $html .= '</tr>';
    
    
}


$html_end = "
        </tbody>
    </table>
</body>
</html>";


$html = $html_start.$html.$html_end;
SetValue(58654, $html);

// Den aktuellen Track abrufen
function CurrentlyPlaying()
        {
            // The response could be false, which cannot be JSON decoded
            // additional_types=episode is required in case the user hears a podcast, otherwise it is irrelevant
            // base url https://api.spotify.com/v1/
            $response = SPO_MakeAPIRequest(13233, 'GET', 'me/player/currently-playing', '');
            if (is_string($response)) {
                $response = json_decode($response, true);
            }
            if (!is_array($response)) {
                return false;
            } elseif (isset($response['error'])) {
                return false;
            } else {
                return $response;
            }
        }

// Extrahieren der Playlist-ID aus dem aktuellen Track
function ExtractPlaylistFromCurrentlyPlaying($currentTrackData)
        {
            $playlistUri = $currentTrackData['context']['uri'];
            $playlistId = substr($playlistUri, strrpos($playlistUri, ':') + 1);
            return $playlistId;
        }
// Abrufen der Playlist-Daten für die aktuelle Playlist
function CurrentPlaylist($spotify_id, $playlistId)
        {
            // The response could be false, which cannot be JSON decoded
            $response = SPO_MakeAPIRequest(13233, 'GET', 'playlists/'. $playlistId, '');
            if (is_string($response)) {
                $response = json_decode($response, true);
            }
            if (!is_array($response)) {
                return false;
            } elseif (isset($response['error'])) {
                return false;
            } else {
                return $response;
            }
        }

function formatDuration($duration)
{
    // Minuten berechnen
    $minuten = floor($duration / 60);

    // Sekunden berechnen
    $sekunden = $duration % 60;

    // Das Format "M:SS" erstellen
    if ($minuten < 10) {
        $laufzeit_formatiert = $minuten . ":" . sprintf("%02d", $sekunden);
    } else {
        $laufzeit_formatiert = $minuten . ":" . $sekunden;
    }
    return $laufzeit_formatiert;
}

Das Skript ist zur Zeit an einem Ablaufplan gebunden, der das Skript nur dann startet, wenn auch etwas abgespielt wird. Ansonsten produziert das Skript in der jetzigen Form einen Fehler wenn kein Spotify Play aktiv ist.

1 „Gefällt mir“