Der Musik-Streamingservice Spotify pumpt gegen eine monatliche Gebühr Musik auf den Desktop und aufs Smartphone. Mittels einer OAuth-geschützten WebAPI archiviert der Perlhacker die Daten seiner Playlists.
Da schau her -- in meiner Kristallkugel erscheint gerade eine Zukunftsvision der Medienlandschaft. Der Trend geht klar weg vom materiellen Datenträger, hin zu ausschließlich digital verfügbarer Information. Über kurz oder lang werden aber nicht nur die Papier- den Online-Zeitungen weichen, Silberscheiben von über die Datenleitung hereinströmender Musik und Film abgelöst, sondern es bahnt sich auch ein Wandel der Eigentumsverhältnisse an.
Eine gekaufte und einmal gesehene Blu-Ray Disk stellt sich doch realistisch gesehen später zum Beispiel oft als Staubfänger heraus, der in extra zu diesem Zweck in Designerläden teuer erworbenen Regalen wertvollen Wohnraum wegnimmt, über drei Umzüge hinweg in Kartons weitergeschleppt und viel später endlich für wenig Geld auf dem Flohmarkt verscherbelt wird. Die Wahrscheinlichkeit, einen Film zweimal anzusehen ist leider oft vernichtend gering, wozu also sollte man Kopien von Musik- oder Filmerzeugnissen tatsächlich besitzen wollen?
Abbildung 1: Im Web-Browser läuft die Mietmusik von Spotify nach vom Kunden erstellten Playlists. |
Die Unterhaltungsindustrie hat den Trend erwartungsgemäß verschlafen, aber Drittanbieter wie der Video-Streamer Netflix in den USA (demnächst auch in Deutschland [8]) oder Musikangebote wie Pandora, Rhapsody oder Spotify verdienen mittlerweile ganz gut beim Online-Vermieten von Medien. Bei diesen Anbietern erwirbt der Kunde keine digitalen Medien mehr im Einzelkauf, sondern entrichtet eine monatliche Gebühr, gegen die der Anbieter sich verpflichtet, den Kunden per Internet mit beliebig vielen Film- oder Musikerzeugnissen aus dicken Online-Katalogen mit Millionen von Titeln zu bedienen. Diese laufen dann entweder auf dem PC, über kleine Kästchen wie dem Roku, Apple TV, Chromecast oder Amazon Fire im Fernsehkasten, oder gleich direkt auf dem Smartphone -- solange der Kunde die Abo-Gebühr entrichtet. Kündigt der Kunde, bleibt ihm nichts als die Erinnerung. Es überrascht wenig, dass die altbackenen Grammophongesellschaften natürlich lieber an ihrem hundert Jahre alten Geschäftsmodell festhalten würden und der Mietmusik Steine in den Weg legen, und auch manche Künstler haben was rumzumosern ([6]) -- geschenkt, meine Weichen sind bereits auf Zukunft gestellt.
Abbildung 2: Eine Spotify-Playlist des Autors auf dem iPhone. |
Einige Anbieter dieser Mietmusik schalten ein kostenloses Radioprogramm, bei dem der Kunde nur ungefähr die Richtung vorgeben, aber keine Titel direkt auswählen kann. Außerdem trüben lästige Werbespots den Musikgenuss. Die zahlungspflichtigen Varianten hingegen bieten freie Titelwahl aus einem je nach Anbieter mehr oder weniger vollständigen Katalog an, und erlauben die Erstellung sogenannter Playlists, auf denen der Abonnent seinen Favoriten lauschen darf.
Da die Playlisten aber im Rechenzentrum des Anbieters liegen und nicht auf meinem Rechner zuhause, stellte sich bei mir ein leichtes Gefühl des Unwohlseins ein. Was würde mit meinen mühsam zusammengestellten Playlists passieren, falls Spotify den Fußball einpackte und heimginge? Dank der ebenfalls angebotenen Web-API gelang es mir aber, die Daten lokal zu sichern und so die Verlustangst zu überwinden.
Auch ohne Registrierung erlaubt Spotify mittels der Web-API generelle Suchabfragen zu Musikerzeugnissen in seinem Katalog. Wer zum Beispiel herausfinden möchte, welche Titel die kalifornische Band "Weezer" auf welchen Alben veröffentlicht hat, kann dies mit einer einfachen HTTP-Anfrage wie in Listing 1 tun.
01 #!/usr/local/bin/perl -w 02 use strict; 03 use LWP::UserAgent; 04 use JSON qw( from_json ); 05 06 my $ua = LWP::UserAgent->new(); 07 08 my $resp = $ua->get( 09 "https://api.spotify.com/v1/search?" . 10 "q=weezer&type=track" 11 ); 12 13 my $data = from_json( $resp->content() ); 14 15 for my $item ( 16 @{ $data->{ tracks }->{ items } } ) { 17 print "$item->{ name } ", 18 "($item->{ album }->{ name })\n"; 19 }
Den Parameter q
(für Query) setzt das Skript auf den
Namen der gesuchten Band, und type
steht auf "track", um dem Server
zu signalisieren, dass der Client an Titeln der Band interessiert ist.
Der Server schickt das Ergebnis im JSON-Format zurück, das die Methode
from_json()
aus dem CPAN-Modul JSON in eine verschachtelte
Hash-Datenstruktur in Perl umwandelt. Die for
-Schleife ab Zeile 15
iteriert
über die Array-Einträge unter dem Schlüssel tracks->items
und
gibt sowohl den gefundenen Musiktitel als auch das zugehörige Album
aus, das sich ebenfalls in der Datenstruktur unter dem Titeleintrag findet.
Abbildung 3 zeigt die auf 20 Einträge begrenzte Ausgabe. Das Skript filtert
den Großteil der im JSON-Format zurückkommenden Information aus, wer
möchte, kann zusätzliche Happen wie Beliebtheit oder Spieldauer extrahieren
und über Links zusätzliche Details wie das Erscheinungsdatum
eines Albums einholen.
Abbildung 3: Ohne Registrierung erlaubt Spotify allgemeine Suchabfragen, wie hier nach den veröffentlichten Titeln der Band "Weezer". |
Interessantere Abfragen bohren aber in den persönlich erstellten Daten eines Spotify-Users und erfordern dessen Einwilligung. Um zum Beispiel alle Playlists eines Nutzers mit deren Titeln einzuholen muss das anfragende Skript auf Spotifys Developer-Site als Applikation registiert sein und sich mittels gültiger OAuth-Tokens ausweisen, bevor Spotify die geschützten Informationen herausrückt. Die Anweisungen für Applikationsentwickler auf [7] führen durch die notwendigen Schritte bei der Registrierung und der anschließenden Verwendung der OAuth-Tokens. Allerdings gewährt Spotify nur zahlenden Nutzern Zugriff auf die API.
Abbildung 4: Eine Applikation, hier "PerlSnapshot", die auf private User-Daten wie Playlists zugreifen möchte, muss sich zunächst auf der Spotify-Developerseite registrieren. |
Nachdem ich in dieser Rubrik schon verschiedene OAuth-geregelte
Web-APIs vorgestellt habe, wie zum Beispiel die von
Tumblr ([3]), Google Drive ([4]) oder
Dropbox ([5]), und den ersten Access-Token jedesmal mit
einem Mojolicious-Skript eingeholt habe, war es diesmal an der Zeit,
zu diesem Zweck ein CPAN-Modul zu schreiben. Mit OAuth::Cmdline
können nun Skripts wie Listing 2 schnell mal einen Webserver starten und
die Einwilligung des Users in einem andockenden Browser einholen.
spotify-token-init
nimmt in den Zeilen 9 und 10 die zwei Hex-Werte
"Client ID" und "Client Secret" entgegen, die Spotifys Developer-Seite
an registrierte Applikationen vergibt (Abbildungen 5 und 6).
Abbildung 5: Nach dem Anmelden der Web-App ... |
Abbildung 6: ... erscheinen die Client-ID und das Client-Secret. |
Weiter gibt der Skriptnutzer im Konstruktor von OAuth::Cmdline
die von Spotify vorgegebenen URLs zum Einloggen und Tokenerneuerung
als login_uri
und token_uri
an. Das Ausmaß des erlaubten
Zugriffs gibt der Parameter scope
mit "user-read-private"
vor,
lässt also das Lesen privater Daten zu. Käme die Schreibberechtigung
hinzu, dürften Skripts auch neue Playlists anlegen und dort
Titel einhängen. Startet der User das Skript in Listing 2, zeigt es mit
Server available at http://localhost:8082
die Webadresse an, die ins URL-Feld des Browserfensters einzugeben ist. Daraufhin zeigt der Browser einen Link an, der mit "Login on Spotify" betitelt ist. Klickt der User darauf, springt der Browser auf die Spotify-Seite und fordert den User auf, sich dort mit seinem User-Namen und dem gültigen Passwort einzuloggen.
01 #!/usr/local/bin/perl -w 02 use strict; 03 use lib 'lib'; 04 05 use OAuth::Cmdline; 06 use OAuth::Cmdline::Mojo; 07 08 my $oauth = OAuth::Cmdline->new( 09 client_id => "XXX", 10 client_secret => "YYY", 11 login_uri => 12 "https://accounts.spotify.com/authorize", 13 token_uri => 14 "https://accounts.spotify.com/api/token", 15 site => "spotify", 16 scope => "user-read-private", 17 ); 18 19 my $app = OAuth::Cmdline::Mojo->new( 20 oauth => $oauth, 21 ); 22 23 $app->start( 'daemon', '-l', $oauth->local_uri );
Klickt der User auf "Okay", verzweigt der Spotify-Server zurück zu der in Abbildung 6 registrierten "Redirect URI". Diese setzt der Testserver in OAuth::Cmdline::Mojo auf http://localhost:8082/callback und genauso muss sie auch beim Ausfüllen des Antrags auf der Spotify-Developerseite eingetragen werden. Vorsicht: Nach dem Hinzufügen einer Redirect-URL (Abbildung 6) besteht Spotify ulkigerweise auf dem Pressen des "Save"-Buttons am unteren Rand der Seite, was man leicht vergisst, und sich anschließend über die mit "Bad Request" und "Invalid Redirect URI" betitelten Fehlerseiten des Servers wundert.
Zeigt sich der User im Webflow einsichtig, lenkt Spotify den Browser
wieder zurück zum lokalen Testserver in Listing 2 auf "localhost:8082",
und lässt diesem einen gültigen Access-Token
sowie einen Refresh-Token zukommen. Mit letzterem kann OAuth::Cmdline
später abgelaufene
Access-Tokens erneuern. Das Skript in Listing 2 legt diese
Zugangsdaten in der Datei .spotify.yml
im Home-Verzeichnis ab.
Nur der jeweilige Unix-User darf die Daten lesen, aber natürlich kann auch ein
neugieriger System-Administrator (oder auch ein Einbrecher) mit
root
-Berechtigung darauf zugreifen, also Vorsicht.
Über diesen Umweg hat also der User dem Server die Erlaubnis erteilt,
bis auf expliziten Widerruf die Userdaten an die Applikation
"Perlsnapshot" weiter zu geben, die sich bei jeder Anfrage im
HTTP-Header über einen gültigen Access-Token ausweist.
Abbildung 7: Der User bestätigt, dass die Applikation "Perlsnapshot" bis auf Widerruf seine privaten Playlist-Daten lesen darf. |
Weitere Skripts wie Listing 3 können nun den Konstruktor der Klasse
OAuth::Cmdline mit dem site
-Parameter "spotify" aufrufen und
einen gültigen Access-Token aus der Cache-Datei ~/.spotify.yml
auslesen.
Zum Auslesen der Playlists eines Users setzt Zeile 16 mit der
Methode authorization_headers()
aus OAuth::Cmdline den
vom Server geforderten Access-Token in der darauffolgenden GET-Anfrage
an die Web-API. Ist der Access-Token mittlerweile verfallen, erneuert
ihn OAuth::Cmdline automatisch hinter den Kulissen mittels des
ebenfalls in der Cache-Datei gespeicherten Refresh-Tokens durch eine
Anfrage an den Refresh-Service am OAuth-Handler des Servers.
01 #!/usr/local/bin/perl -w 02 use strict; 03 use OAuth::Cmdline; 04 use LWP::UserAgent; 05 use JSON qw( from_json ); 06 07 my( $user ) = @ARGV; 08 die "usage: $0 user" if !defined $user; 09 10 my $oauth = OAuth::Cmdline->new( 11 site => "spotify" 12 ); 13 14 my $ua = LWP::UserAgent->new(); 15 $ua->default_header( 16 $oauth->authorization_headers ); 17 18 my $resp = $ua->get( 19 "https://api.spotify.com/v1" . 20 "/users/$user/playlists" ); 21 22 if( $resp->is_error ) { 23 die "Error: ", $resp->message(); 24 } 25 26 my $result = from_json( $resp->content() ); 27 28 for my $item ( @{ $result->{ items } } ) { 29 print "$item->{ name }\n"; 30 }
Die im JSON-Format zurückkommende Antwort enthält die Namen aller Playlists des Users und deren Hex-IDs, mit denen Skripts wie Listing 4 später die dort referenzierten Musikstücke einholen können. Dank OAuth::Cmdline müssen sie sich nicht mehr mit dem OAuth-Tanz befassen, sondern können sich auf das Lesen der Daten konzentrieren.
01 #!/usr/local/bin/perl -w 02 use strict; 03 use warnings; 04 use OAuth::Cmdline; 05 use LWP::UserAgent; 06 use JSON qw( from_json ); 07 use YAML qw( Dump ); 08 09 my( $user ) = @ARGV; 10 die "usage: $0 user" if !defined $user; 11 12 my $oauth = OAuth::Cmdline->new( 13 site => "spotify" 14 ); 15 16 my $ua = LWP::UserAgent->new(); 17 $ua->default_header( 18 $oauth->authorization_headers ); 19 20 my $resp = $ua->get( 21 "https://api.spotify.com/v1" . 22 "/users/$user/playlists/" . 23 "022PMTci8phXA4CTJjwWEF" ); 24 25 if( $resp->is_error ) { 26 die "Error: ", $resp->message(); 27 } 28 29 my $result = from_json( $resp->content() ); 30 31 my @playlists = (); 32 33 for my $item ( 34 @{ $result->{ tracks }->{ items } } ) { 35 my $track = $item->{ track }; 36 37 push @playlists, { 38 track => $track->{ name }, 39 artist => 40 $track->{ artists }->[0]->{ name }, 41 album => $track->{ album }->{ name } 42 }; 43 } 44 45 print Dump( \@playlists );
Abbildung 8: In der YAML-Datei stehen nun die Playlist-Daten als lokale Sicherungskopie. |
Listing 4 legt nach dem Einholen der Playlist-Daten diese im sowohl von Menschen also auch Maschinen leicht lesbaren YAML-Format ab. So kann der besorgte Musikfreund sie regelmäßig privat sichern und ruhig schlafen, denn die hineingesteckte Arbeit bei der Auswahl der Musikstücke geht nun niemals verloren, unabhängig vom gerade gewählten Mietmusik-Anbieter.
Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2014/09/Perl
"Zimmer mit Aussicht", Tumblr API im Perl Snapshot, Michael Schilli, http://www.linux-magazin.de/Ausgaben/2013/12/Perl-Snapshot
"Papierbuch am Ende", Google Drive API im Perl Snapshot, Michael Schilli, http://www.linux-magazin.de/Ausgaben/2012/12/Perl-Snapshot
"Ab in die Kiste", Dropbox API im Perl Snapshot, Michael Schilli, http://www.linux-magazin.de/Ausgaben/2011/07/Perl-Snapshot
"David Byrne: 'The internet will suck all creative content out of the world'", The Guardian, 2013, http://www.theguardian.com/music/2013/oct/11/david-byrne-internet-content-world
"Spotify Web API Authorization Guide", https://developer.spotify.com/web-api/authorization-guide/
"Netflix to expand to Germany, France and Switzerland", http://www.bbc.com/news/technology-27496055