Aus Linux-Magazin 09/2014

Spotify-Playlisten archivieren

© konstantin32, 123RF

Der Musikstreaming-Service Spotify pumpt gegen eine monatliche Gebühr Musik auf den Desktop und aufs Smartphone. Noch mehr Groove hat, dass Perl-Hacker Michael Schilli per Oauth-geschütztem Web-API seine Spotify-Playlisten für die Ewigkeit archiviert.

Der mediale Beat lässt klassische Informationsträger hinter sich – und geht lieber online. Schneller noch als die Papier- der Online-Zeitung weicht, verlieren Silberscheiben ihre ursprüngliche Bedeutung, denn Musik und Filme strömen mehr und mehr über die Datenleitung zum Konsumenten.

Zugleich bahnt sich ein Wandel der Lizenznutzung an: Eine gekaufte und einmal gesehene Blu-ray-Disk stellt sich doch nach einmaligem Genuss oft als Staubfänger heraus, der in extra zu diesem Zweck teuer erworbenen Designerregalen wertvollen Wohnraum besetzt. Drei Umzüge später verscherbelt der Besitzer die selten berührte Schachtel auf dem Flohmarkt. Wozu also sollte man Kopien von Musik- oder Filmerzeugnissen tatsächlich besitzen wollen?

Online PLUS

In einem Screencast demonstriert Michael Schilli das Beispiel: https://www.linux-magazin.de/2014/09/plus

Vermarkter in Schockstarre

Die etablierten Musikvermarkter haben den Trend erwartungsgemäß verschlafen. Die Grammophongesellschaften würden lieber an ihrem 100 Jahre alten Geschäftsmodell festhalten, auch manche Künstler mosern [2] – geschenkt, meine Weichen sind bereits auf Zukunft gestellt. Denn Drittanbieter wie der Videostreamer Netflix in den USA (demnächst auch in Deutschland, [3]) oder reine Musikangebote wie Pandora, Rhapsody oder Spotify [4] verdienen bereits ganz gut beim Online-Vermieten.

Bei ihnen erwirbt der Kunde keine digitalen Inhalte im Einzelkauf, sondern entrichtet eine monatliche Abogebühr. Im Gegenzug verpflichtet sich der Anbieter, den Kunden per Internet mit einem Repertoire aus Millionen Film- oder Musiktiteln zu bedienen. Die laufen entweder auf dem PC, über kleine Kästchen wie dem Roku, Apple TV, Chromecast oder Amazon Fire im Fernseher oder gleich direkt auf dem Smartphone.

Einige Anbieter dieser Mietmusik schalten ein kostenloses Radioprogramm, bei dem der Kunde nur ungefähr die Richtung vorgibt, aber keine Titel direkt auswählt. Auch trüben lästige Werbespots den Musikgenuss. Zahlungspflichtige Varianten dagegen bieten freie Titelwahl aus einem je nach Anbieter mehr oder weniger beeindruckenden Katalog an.

Der Abonnent darf sich so genannte Playlisten (Abbildungen 1 und 2) zusammenstellen, damit seine Lieblinge die digitale Bühne schneller erklimmen können. Das System ist ungemein praktisch – so lange man zahlender Kunde bleibt. Wer dagegen kündigt, verliert aber fatalerweise seine mühevoll zusammengesuchten Playlisten.

Abbildung 1: Im Webbrowser lässt Spotify die Mietmusik laufen, die der Benutzer zuvor in seiner höchst persönlichen Playliste zusammengetragen hat.

Abbildung 1: Im Webbrowser lässt Spotify die Mietmusik laufen, die der Benutzer zuvor in seiner höchst persönlichen Playliste zusammengetragen hat.

Abbildung 2: Eine Spotify-Playliste von Michael Schilli auf dem iPhone.

Abbildung 2: Eine Spotify-Playliste von Michael Schilli auf dem iPhone.

Über den Wolken

Da die Playlisten aber im Rechenzentrum des Anbieters liegen und nicht auf meinem heimischen Rechner, beschleicht mich ein leichtes Gefühl des Unwohlseins. Was würde mit meinen mühsam zusammengestellten Listen passieren, sollte Spotify einpacken? Dank des ebenfalls angebotenen Web-API [5] gelingt es mir aber, die Daten lokal zu sichern und Verlustängste zu überwinden.

Über sein Web-API erlaubt Spotify auch ohne Registrierung generelle Katalogabfragen zu Musikerzeugnissen. 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. 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 umwandelt. Die For-Schleife ab Zeile 15 iteriert über die Array-Einträge unter dem Schlüssel »tracks->items« und gibt den gefundenen Musiktitel sowie 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 das Gros der im Json-Format zurückkommenden Information aus. Wer will, sucht sich Extra-Happen, etwa Beliebtheit oder Spieldauer, und über Links zusätzliche Details wie das Erscheinungsdatum eines Albums heraus.

Abbildung 3: Ohne Registrierung erlaubt Spotify allgemeine Suchabfragen, wie hier nach den veröffentlichten Titeln der Band "Weezer".

Abbildung 3: Ohne Registrierung erlaubt Spotify allgemeine Suchabfragen, wie hier nach den veröffentlichten Titeln der Band “Weezer”.

Listing 1

search

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 }

Und deine Lieblingsmusik?

Interessantere Abfragen tangieren aber die persönlichen Daten des Spotify-Users und erfordern dessen Einwilligung. Um zum Beispiel alle Playlisten eines Nutzers mit deren Titeln einzuholen, muss das anfragende Skript auf Spotifys Developer-Seite [6] als Applikation registriert sein und sich mit gültigen Oauth-Tokens ausweisen, bevor Spotify die Informationen herausrückt. Die Anweisungen für Applikationsentwickler auf [7] beschreiben die Registrierung und die Handhabung der Oauth-Tokens Schritt für Schritt. Allerdings gewährt Spotify nur zahlenden Nutzern Zugriff auf das API.

Nachdem ich in dieser Rubrik schon mehrere Oauth-geregelte Web-APIs vorgestellt habe, zum Beispiel die von Tumblr [8], Google Drive [9] oder Dropbox [10], und überall den ersten Access-Token mit einem Mojolicious-Skript eingeholt habe, ist es diesmal an der Zeit, ein extra CPAN-Modul zu schreiben: Mit »OAuth::Cmdline« können fortan Skripte wie Listing 2 schnell einen Webserver starten und die Einwilligung des Users in einem andockenden Browser einholen. Bei »spotify-token-init« nehmen in den Zeilen 9 und 10 die beiden Hex-Werte Client-ID und Client-Secret entgegen, die Spotifys Developer-Seite an registrierte Applikationen vergeben hat (Abbildungen 4 und 5).

Weiter gibt der Skriptnutzer im Konstruktor von »OAuth::Cmdline« die von Spotify vorgegebenen URLs für Einloggen und Token-Erneuerung als »login_uri« und »token_uri« an. Das Ausmaß des erlaubten Zugriffs bestimmt der Parameter »scope« mit »user-read-private« , lässt also das Lesen privater Daten zu. Käme die Schreibberechtigung hinzu, dürften Skripte Playlisten selbst anlegen und dort Titel einhängen.

Listing 2

spotify-token-init

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 );
Abbildung 4: Nach dem Anmelden der Web-App …

Abbildung 4: Nach dem Anmelden der Web-App …

Abbildung 5: … erscheinen die Client-ID und das Client-Secret.

Abbildung 5: … erscheinen die Client-ID und das Client-Secret.

Jonglieren mit URLs

Startet der User das Skript in Listing 2, informiert es mit

Server available at http://localhost:8082

über die Webadresse, die der Benutzer ins URL-Feld des Browserfensters einzugeben hat. 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 ihn auf, sich mit seinem Usernamen und Passwort einzuloggen.

Zeigt sich der User im Webflow einsichtig (Abbildung 6), schickt Spotify den Browser zurück zum lokalen Testserver – in Listing 2 auf »localhost:8082« – und lässt ihm einen gültigen Access- und einen Refresh-Token zukommen. Mit letzterem gelingt es OAuth::Cmdline später, abgelaufene Access-Tokens zu erneuern. Das Skript (Listing 2) legt die Zugangsdaten in der Datei ».spotify.yml« im Homeverzeichnis ab.

Klickt der User also auf »Okay« , verzweigt der Spotify-Server zurück zu der registrierten »Redirect URI« . Diese setzt der Testserver in OAuth::Cmdline::Mojo auf »http://localhost: 8082/callback« . Auf genau die gleiche Weise muss der Benutzer sie beim Ausfüllen des Antrags auf der Spotify-Developerseite eingetragen haben – andernfalls geht’s schief.

Vorsicht: Nach dem Hinzufügen einer Redirect-URL, wie in Abbildung 5 gezeigt, besteht Spotify ulkigerweise auf dem Klicken des »Save« -Buttons am unteren Rand der Seite. Wer das vergisst, wundert sich später über mit »Bad Request« oder »Invalid Redirect URI« betitelte Fehlerseiten des Servers.

Abbildung 6: Der User bestätigt, dass die Applikation »PerlSnapshot« bis auf Widerruf seine privaten Playlist-Daten lesen darf.

Abbildung 6: Der User bestätigt, dass die Applikation »PerlSnapshot« bis auf Widerruf seine privaten Playlist-Daten lesen darf.

Zugriff bis auf Widerruf

Nur der jeweilige Unix-User darf die Daten lesen, allerdings auch ein neugieriger Systemadministrator (oder ein erfolgreicher Einbrecher) mit Rootberechtigung – also Vorsicht. Über diesen Umweg hat der User bis zu einem expliziten Widerruf dem Server die Erlaubnis erteilt, die Userdaten an die Applikation »PerlSnapshot« weiterzugeben, die sich bei jeder Anfrage im HTTP-Header mit einem gültigen Access-Token ausweist.

Einfach wiederverwerten

Weitere Skripte wie das in 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 Cachedatei »~/.spotify.yml« auslesen.

Zum Auslesen der Playlists eines Users sendet Zeile 16 mit der Methode »authorization_headers()« aus OAuth::Cmdline den vom Server geforderten Access-Token in der darauf folgenden Get-Anfrage an das Web-API. Sollte der Access-Token zwischenzeitlich verfallen sein, erneuert ihn OAuth::Cmdline automatisch hinter den Kulissen. Dazu stellt das Modul mit dem ebenfalls in der Cachedatei gespeicherten Refresh-Token eine Anfrage an den Refresh-Service am Oauth-Handler des Servers.

Die im Json-Format zurückgelieferte Antwort enthält die Namen aller Playlisten des Users und deren Hex-IDs, mit denen später Skripte wie Listing 4 die dort referenzierten Musikstücke einholen. Dank OAuth::Cmdline müssen sie sich nicht mehr mit dem Oauth-Tanz befassen, sondern dürfen sich auf das Lesen der Daten konzentrieren.

Listing 4 legt nach dem Einholen der Playlist-Daten diese im sowohl von Menschen als auch Maschinen leicht lesbaren Yaml-Format ab (Abbildung 7). Das versetzt besorgte Musikfreunde in die beruhigende Lage, ihre Playlisten regelmäßig bei sich zu sichern. Auf diese Weise bleibt ihre investierte Musik-Sammelarbeit selbst dann erhalten, wenn sie ihrem bisherigen Mietmusik-Anbieter die kalte Schulter zeigen wollen – oder der ihnen.

Abbildung 7: In der Yaml-Datei angekommen dienen die Playlist-Daten als lokale Sicherungskopie.

Abbildung 7: In der Yaml-Datei angekommen dienen die Playlist-Daten als lokale Sicherungskopie.

Listing 3

user-playlists

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 }

Listing 4

songs-in-playlist

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 );

Infos

  1. Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2014/09/Perl
  2. “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
  3. Leo Kelion, “Netflix to expand to Germany, France and Switzerland”: http://www.bbc.com/news/technology-27496055
  4. Spotify: https://www.spotify.com/de/
  5. Libspotify-SDK: https://developer.spotify.com/technologies/libspotify/
  6. Applikationen registrieren: https://developer.spotify.com/my-applications/#!
  7. “Spotify Web API Authorization Guide”: https://developer.spotify.com/web-api/authorization-guide/
  8. Michael Schilli, “Webcam protokolliert auf Tumblr”, Linux-Magazin 12/13, S. 104, https://www.linux-magazin.de/Ausgaben/2013/12/Perl-Snapshot
  9. Michael Schilli, “Bücher scannen und in Google Drive speichern”: Linux-Magazin 12/12, S. 102, https://www.linux-magazin.de/Ausgaben/2012/12/Perl-Snapshot
  10. Michael Schilli, “Perl-Skript automatisiert Datei-Austausch via Dropbox”: Linux-Magazin 07/11, S. 102, https://www.linux-magazin.de/Ausgaben/2011/07/Perl-Snapshot

Der Autor

Michael Schilli arbeitet als Software-Engineer bei Yahoo in Sunnyvale, Kalifornien. In seiner seit 1997 erscheinenden Kolumne forscht er jeden Monat nach praktischen Anwendungen der Skriptsprache Perl. Unter mailto:mschilli@perlmeister.com beantwortet er gerne Fragen.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 4 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben