<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>xscDevBlog - LastSharp &#38; Co. &#187; lastfm</title>
	<atom:link href="http://dev.xscheme.de/tag/lastfm/feed/" rel="self" type="application/rss+xml" />
	<link>http://dev.xscheme.de</link>
	<description>Der xscheme-DevelopmentBlog</description>
	<lastBuildDate>Sun, 23 May 2010 11:40:10 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Inside Last.FM: Silent Authentication für API 2.0</title>
		<link>http://dev.xscheme.de/2009/07/inside-lastfm-silent-authentication/</link>
		<comments>http://dev.xscheme.de/2009/07/inside-lastfm-silent-authentication/#comments</comments>
		<pubDate>Wed, 08 Jul 2009 21:47:06 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Lapicon]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[lastfm]]></category>

		<guid isPermaLink="false">http://dev.xscheme.de/?p=774</guid>
		<description><![CDATA[Update (17.07.2009): Das hier ist (abgesehen von den &#8220;Skills&#8221;, die ich dabei gebraucht hab) wohl ziemlich überflüssig, wenn man die API-Methode auth.getMobileSession mal genauer betrachtet. Wieder was gelernt: RTFM!
Da im Moment anscheinend wieder Änderungen am Last.FM-Webservice gemacht werden, die dazu geführt haben, dass LastSharp höchstens noch mithilfe der &#8220;Modifizierten Verbindung&#8221; (unter &#8220;Erweiterte Einstellungen&#8221; &#62;&#62; &#8220;Verschiedenes&#8221;) [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Update (17.07.2009):</strong> Das hier ist (abgesehen von den &#8220;Skills&#8221;, die ich dabei gebraucht hab) wohl ziemlich überflüssig, wenn man die API-Methode auth.getMobileSession mal genauer betrachtet. Wieder was gelernt: RTFM!</p>
<p>Da im Moment anscheinend wieder Änderungen am Last.FM-Webservice gemacht werden, die dazu geführt haben, dass LastSharp höchstens noch mithilfe der &#8220;Modifizierten Verbindung&#8221; (unter &#8220;Erweiterte Einstellungen&#8221; &gt;&gt; &#8220;Verschiedenes&#8221;) funktioniert, habe ich mir mal das <a href="http://www.lastfm.de/api/radio">Radio-API</a> angesehen, das ja anscheinend die Zukunft des Radiostreamings bei Last.FM ist. Was mich daran allerdings stört, ist die umständliche Anmeldeprozedur: Token holen, Browser öffnen, auf Authentifizierung warten, Session holen.</p>
<p>Der Umweg über den Browser ist einfach unschön, weshalb ich nach einer Lösung gesucht habe, die <strong>den Login sowie die anschließende Bestätigung eines Users im Hintergrund simuliert</strong>, sodass er das eigentliche Programm nicht verlassen muss.</p>
<p><span id="more-774"></span></p>
<p><em>Funktionen, die Authentication-Token und -Session holen, werden im folgenden als gegeben betrachetet, z.B. durch LastFmLib.Net. </em></p>
<p>Wir brauchen nun also zwei Funktionen, die das Abschicken des Login-Formulars und das Klicken auf den &#8220;Zulassen&#8221;-Button vortäuschen. Beide Operationen produzieren eine HTTP-POST-Anfrage, funktionieren aber nur, wenn auch die richtigen Cookies vorhanden sind. (Das hat mich einige Zeit gekostet und dabei war die Lösung so einfach&#8230;) Es bietet sich an, die POST-Anfrage in eine Hilfsfunktion <em>ExecutePOST</em> zu packen, hier z.B. in C#:<br />
(Namespace <em>System.Net</em> muss eingebunden sein)</p>
<pre name="code" class="c-sharp">// Löst das Cookie Problem: Immer denselben Container verwenden!
private static CookieContainer GlobalCookies = new CookieContainer();

// Sendet eine POST-Anfrage
public static void ExecutePOST(string uri, string data) {
    try
    {
        // Bereite die Anfrage vor
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        request.Method = "POST";
        request.CookieContainer = GlobalCookies; // !!
        request.ContentType = "application/x-www-form-urlencoded";
        request.ContentLength = data.Length;

        // Schreiben der Daten
        ASCIIEncoding enc = new ASCIIEncoding();
        byte[] bdata = enc.GetBytes(bodyData);
        Stream str = req.GetRequestStream();
        str.Write(bdata, 0, bdata.Length);
        str.Close();

        // Schicke Anfrage ab und verwirf die Antwort
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        response.Close();
    }
    catch { }
}</pre>
<p>Will man überprüfen, ob die gewünschte Operation erfolgreich war, würde es sich vermutlich anbieten, die Antwort auszuwerten, aber darauf verzichte ich jetzt aus Zeitgründen.</p>
<p>Nächster Schritt ist die Untersuchung der Formulare auf der Last.FM-Seite, damit man weiß, welche Daten an welche Adresse gesendet werden müssen:</p>
<ul>
<li>Login: <strong>https://www.last.fm/login/</strong>
<ul>
<li>refererKey (leer)</li>
<li>backto (&#8220;/&#8221;)</li>
<li>username (Benutzername)</li>
<li>password (Passwort)</li>
</ul>
</li>
<li>Zulassen: <strong>http://www.last.fm/api/grantAccess</strong>
<ul>
<li>api_key (Last.FM schickt den sogar zweimal, wir deshalb sicherheitshalber auch&#8230;)</li>
<li>token (Authentication-Token)</li>
<li>referer (&#8220;/&#8221;)</li>
</ul>
</li>
</ul>
<p>Also, die beiden verbleibenden Funktionen in C#:</p>
<pre name="code" class="c-sharp">    // Lasse ein Programm zu
    public static void GrantAccess(string token)
    {
        ExecutePOST(
            "http://www.last.fm/api/grantAccess",
            "api_key=" + apiKey + "&amp;api_key=" + apiKey + "&amp;token=" + token + "&amp;referer=/"
        );
    }

    // Melde den User bei Last.FM an
    public static void Login(string username, string password)
    {
        ExecutePOST(
            "https://www.last.fm/login/",
            "refererKey=&amp;backto=/&amp;username=" + Uri.EscapeDataString(username) +
            "&amp;password=" + Uri.EscapeDataString(password)
        );
    }</pre>
<p>Um jetzt eine gesamte Anmeldung zu simulieren, müsste man nun folgendes machen:</p>
<pre name="code" class="c-sharp">public static void Authenticate(string username, string password)
{
    string token = GetToken(); // irgendeine externe Funktion
    Login(username, password);
    GrantAccess(token);
    string session = GetSession(); // irgendeine externe Funktion
    // Mache etwas...
}</pre>
<p>Das war&#8217;s auch schon. Wie gesagt fehlt hier ein Test, ob die Anmeldung erfolgreich war, aber irgendwie lässt sich das bestimmt einfach überprüfen. (Vllt. liefert Last.FM ja im Hintergrund einen HTTP-Error, der im Browser nicht auffällt?)</p>
<p>Jedenfalls, wenn jemand die <strong>Authentifizierung für das Last.FM-API 2.0 im Hintergrund</strong> ablaufen lassen will, ist das hier der Weg. Ich hoffe, es hilft.</p>
<p>Die Verwendung des Radio-APIs in LastSharp werde ich übrigens so lang wie möglich hinauszögern, da hier wieder einmal diverse Standards (HTTP, XML, &#8230;) unsachgemäß ausgelegt werden&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://dev.xscheme.de/2009/07/inside-lastfm-silent-authentication/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>[Release] LastSharp 0.4.2</title>
		<link>http://dev.xscheme.de/2009/05/release-lastsharp-042/</link>
		<comments>http://dev.xscheme.de/2009/05/release-lastsharp-042/#comments</comments>
		<pubDate>Mon, 04 May 2009 14:46:24 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[LastSharp]]></category>
		<category><![CDATA[last.fm lider speichern]]></category>
		<category><![CDATA[last.fm lieder herunterladen]]></category>
		<category><![CDATA[last.fm rippen]]></category>
		<category><![CDATA[lastfm]]></category>
		<category><![CDATA[mp3 kostenlos]]></category>
		<category><![CDATA[musik gratis]]></category>
		<category><![CDATA[musik kostenlos]]></category>
		<category><![CDATA[release]]></category>

		<guid isPermaLink="false">http://dev.xscheme.de/?p=734</guid>
		<description><![CDATA[Ich hatte einiges, zu tun, was diese Version angeht. Zum einen viele Bugfixes (unendliches Playlist-Nachladen, Abspielfehler im AudioPlayer, Scrobbling funktionierte nicht, &#8230;), zum anderen Neuerungen, was die Playlist angeht, sowie Optionen zur Erhöhung der Anpassbarkeit von LastSharp.
Ich habe mich schon mehrmals damit auseinandersetzen müssen, dass der Download der eigenen Playlist erheblich eingeschränkt ist (siehe hier) [...]]]></description>
			<content:encoded><![CDATA[<p>Ich hatte einiges, zu tun, was diese Version angeht. Zum einen viele <strong>Bugfixes</strong> (unendliches Playlist-Nachladen, Abspielfehler im AudioPlayer, Scrobbling funktionierte nicht, &#8230;), zum anderen Neuerungen, was die Playlist angeht, sowie Optionen zur Erhöhung der Anpassbarkeit von LastSharp.</p>
<p>Ich habe mich schon mehrmals damit auseinandersetzen müssen, dass der Download der eigenen Playlist erheblich eingeschränkt ist (siehe <a href="http://dev.xscheme.de/2009/04/inside-lastfm-lastsharp-und-die-eigene-playlist/">hier</a>) &#8211; und der Grund dafür ist schlicht und einfach, dass Last.FM das Anhören (und somit die Möglichkeit des Herunterladens) der eigenen Playlist nur noch Abonnenten zugesteht. Um dies zu umgehen, gibt es in LastSharp nun den <strong>direkten Playlist-Download</strong>. Zu beachten ist hierbei, dass unter &#8220;Erweiterte Einstellungen&#8221; &gt;&gt; &#8220;TuneIn&#8221; die <strong>Playlistlänge für diese Funktion bis zu 60 Titel </strong>betragen darf, sowie folgendes:</p>
<ul>
<li>Die Methode funktioniert nur mit Titeln, die &#8220;in voller Länge&#8221; anhörbar sind.</li>
<li>Es muss die gesamte Playlist abgerufen und untersucht werden (im normalen Modus werden immer bloß fünf Titel übertragen), sodass bei der Playlisterstellung kürzere Listen zu bevorzugen sein sollten.</li>
<li>Bei der Methode wird automatisch die &#8220;modifizierte Verbindung&#8221; verwendet, da sie ansonsten nicht funktioniert. D.h., dass es sein kann, dass nicht alle den direkten Playlist-Download nutzen können. Des weiteren kommt eine kurze Verzögerung vor dem Downloadstart hinzu.</li>
</ul>
<p><span id="more-734"></span>Weitere Änderungen:</p>
<ul>
<li>Halbtiefe Ordnerstruktur einstellbar (&#8220;Interpret &#8211; Album/Titel.mp3&#8243;)</li>
<li>Update-Prüfung nur noch optional</li>
<li>Playliste wird nur noch optional nachgeladen, wenn leer.</li>
<li>DLLs sind jetzt im Unterverzeichnis &#8220;inc&#8221; statt &#8220;.inc&#8221;, da der Punkt bei manchen Packprogrammen Probleme zu machen scheint.</li>
</ul>
<p><strong>Download: <a href="https://sourceforge.net/project/showfiles.php?group_id=255668&amp;package_id=313182">https://sourceforge.net/project/showfiles.php?group_id=255668&amp;package_id=313182</a></strong></p>
]]></content:encoded>
			<wfw:commentRss>http://dev.xscheme.de/2009/05/release-lastsharp-042/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Wie baue ich mir ein Script mit Lapicon?</title>
		<link>http://dev.xscheme.de/2009/04/kurzanleitung-zu-lapicon/</link>
		<comments>http://dev.xscheme.de/2009/04/kurzanleitung-zu-lapicon/#comments</comments>
		<pubDate>Sat, 25 Apr 2009 00:06:07 +0000</pubDate>
		<dc:creator>xsc</dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[Lapicon]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[lastfm]]></category>
		<category><![CDATA[rest]]></category>
		<category><![CDATA[webservice]]></category>

		<guid isPermaLink="false">http://dev.xscheme.de/?p=635</guid>
		<description><![CDATA[Lapicon (Loose API Connection Language) ist ein Interpreter für eine Scriptsprache, die auf den ersten Blick Ähnlichkeiten mit dem Windows-Eigenen Batch hat und dafür gedacht ist, mit Webservern in Verbindung zu treten, die ihre Daten als XML bereitstellen und REST-Anfragen über HTTP-GET unterstützen. Soweit die Theorie.
Praktisch gesehen ist Lapicon eine einfache Möglichkeit, Webservices zu verwenden. [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://dev.xscheme.de/lapicon"><strong>Lapicon</strong></a> (Loose API Connection Language) ist ein Interpreter für eine Scriptsprache, die auf den ersten Blick Ähnlichkeiten mit dem Windows-Eigenen <em>Batch</em> hat und dafür gedacht ist, mit Webservern in Verbindung zu treten, die ihre Daten als XML bereitstellen und REST-Anfragen über HTTP-GET unterstützen. Soweit die Theorie.</p>
<p>Praktisch gesehen ist Lapicon eine einfache Möglichkeit, <strong>Webservices zu verwenden</strong>. Und geeignete gibt es (wie die Google-Suche nach &#8220;REST-API&#8221; schon bald zeigt) einige:</p>
<ul>
<li>Last.FM (http://www.last.fm/api)</li>
<li>Flickr (http://www.flickr.com/services/api/)</li>
<li>Twitter (http://apiwiki.twitter.com/Twitter-API-Documentation)</li>
<li>Wuala (http://www.wuala.com/de/api)</li>
<li>&#8230;</li>
</ul>
<p>Aber wie geht man nun vor, wenn man Lapicon für irgendeines dieser APIs verwenden will? Diese Frage will ich in diesem Artikel beantworten, anhand zweier Beispiele: den Skripts DownloadAlbumAfterSearch.lpc und Lyrics.lpc aus dem Paket <strong>DarkSharpScripts</strong>, das ab Version 1.0.9 von Lapicon im Verzeichnis &#8220;packages&#8221; zu finden ist. Das erste Script sucht anhand einer Eingabe alle in Frage kommenden Alben und lässt den User eines auswählen, das anschließend komplett von Last.FM heruntergeladen wird (wenn möglich). Und das zweite findet mithilfe der <a href="http://lyricwiki.org/Main_Page">LyricWiki</a> (noch eine Seite, die REST-Anfragen unterstützt!) den Songtext zu einem vom User eingegebenen Musiktitel.</p>
<p><span id="more-635"></span><strong></strong></p>
<p><strong>Schritt 0: Den einfachen Scriptstart ermöglichen</strong></p>
<p>Damit man möglichst einfach Lapicon-Scripts starten kann, sollte man eine Datei mit der Endung &#8220;.lpc&#8221; anlegen und unter Windows darauf doppelklicken und anschließend über &#8220;Programm aus Liste auswählen&#8221; &gt;&gt; &#8220;Durchsuchen&#8230;&#8221; die Lapicon.exe als Standardprogramm festlegen. Dann lassen sich Scripts per Doppelklick starten.</p>
<p><strong>Schritt 1: Grundeinstellungen</strong></p>
<p>Zuallererst muss Lapicon mitgeteilt werden, wohin es Anfragen schicken soll, wie diese Anfragen aussehen sollen und wie es mit den Antworten umzugehen hat. Man stelle sich folgende Fragen:</p>
<ol>
<li>Wie lautet die URL, an die die Anfragen gehen sollen? (Findet man meistens auf der API-Seite.)</li>
<li>Brauche ich irgendwelche Daten für die Anfrage, die immer vorhanden sein müssen? (Beispielsweise bei Last.FM: ein API-Schlüssel.)</li>
<li>Wie ist das Format der Anfrage-URL? &#8220;URL?Parameter&#8221; oder &#8220;URL/Parameter1/Parameter2&#8243;?</li>
<li>Wie sieht die Antwort aus? Welcher XML-Knoten ist die Wurzel? Sind irgendwelche Namespaces enthalten?</li>
<li>Werden mir Informationen bereitgestellt, ob Fehler aufgetreten sind, und wo finde ich sie?</li>
</ol>
<p>Jetzt geht es ans Konfigurieren von Lapicon, hier am Beispiel Last.FM:</p>
<pre>// s.o. Punkt 1: die Basis-URL
#base http://ws.audioscrobbler.com/2.0/

// s.o. Punkt 2: API-Key muss immer mitgesendet werden
#static api_key ce59c153ff5e3003c74df5b618aeccac

// s.o. Punkt 3: Die Anfrage hat das Format "URL?Parameter" (ansonsten: #noquery)
#query

// s.o. Punkt 4: Wurzel ist der "lfm"-Knoten; wir brauchen die Namespaces "xspf" und "lastfm"
#rootnode
#namespace xspf http://xspf.org/ns/0/
#namespace lastfm http://www.audioscrobbler.net/dtd/xspf-lastfm

// s.o. Punkt 5: wenn kein Fehler aufgetreten ist, steht im Attribut "status" des "lfm"-Knotens "ok"
#errornode lfm/@status
#noerrorvalue ok</pre>
<p>Für &#8220;#rootnode&#8221; und &#8220;#errornode&#8221;, sowie später für Anfragen, wird Kenntnis von XPath benötigt. Es sei an dieser Stelle auf den <a href="http://de.wikipedia.org/wiki/XPATH">Wikipedia-Artikel zum Thema</a> verwiesen.</p>
<p>Da Last.FM als eines der Hauptanwendungsfelder für Lapicon gedacht war, lässt sich das oben geschriebene abkürzen:</p>
<pre>#lastfm</pre>
<p>Und schon ist Lapicon bereit dafür, zu arbeiten.</p>
<p><strong>Schritt 2: Wie soll das Programm ablaufen?</strong></p>
<p>Es ist immer wichtig, sich klarzumachen, was man jetzt eigentlich vorhat. Was also soll unser Script machen? Welche Daten brauchen wir dafür? Welche Anfragen müssen wir senden und welche Teile der Antworten verwenden?</p>
<p>Wir schreiben ein Script, das Alben sucht und anschließend komplett herunterlädt, also soll es folgende Schritte ausführen:</p>
<ol>
<li>Eingabe des Suchstrings</li>
<li>Verwendung der Funktion <a href="http://www.last.fm/api/show/?service=357">album.search</a> des Last.FM-APIs um eine Suche durchzuführen. Aus der Antwort lesen wir die Titel, Interpreten und IDs der einzelnen Alben aus.</li>
<li>Ausgabe einer nummerierten Liste mit Interpreten und Albumtitel</li>
<li>Eingabe der Nummer des gewünschten Albums</li>
<li>Verwendung der Funktion <a href="http://www.last.fm/api/show/?service=271">playlist.fetch</a>, um an die Tracks des Albums zu kommen. (Mit zwei nicht dokumentierten Parametern.) Wir verwenden die URLs und die Titel der Tracks.</li>
<li>Erstellen des Ausgabeverzeichnisses nach dem Muster &#8220;Interpret/Album&#8221;</li>
<li>Schleife, die einen Track nach dem anderen herunterlädt.</li>
<li>Ende.</li>
</ol>
<p><strong>Schritt 3: Implementierung</strong></p>
<p>Das Originalscript setzt zuerst die Last.FM-Einstellungen fest, dann lädt es die DarkSharpScripts-Konfigurationsdatei, die im selben Verzeichnis liegt:</p>
<pre>// Use Last.FM
#lastfm

// Include
inc(&lt;script directory&gt;/Config.DarkSharpScripts.lpc)</pre>
<p>Anschließend folgt die Eingabe des Suchstrings und eine Prüfung, ob auch wirklich etwas eingegeben wurde:</p>
<pre>// Search
def query &lt;&lt; Suchen
echo

[if &lt;query&gt; is not empty]</pre>
<p>Die Zeile &#8220;def query &lt;&lt; Suchen&#8221; bewirkt, dass der User eine Eingabezeile mit der Beschriftung &#8220;[in] Suchen:&#8221; vor sich hat. Drückt er die Enter-Taste wird alles eingegebene in der Variable &#8220;query&#8221; gespeichert. Um auf den Wert dieses Variable wiederum zuzugreifen, muss man sie in spitze Klammern setzen: &#8220;&lt;query&gt;&#8221;. Alles, was jetzt folgt, wird demnach nur ausgeführt, wenn der Wert von &#8220;query&#8221; nicht leer ist:</p>
<pre>   [album.search : album=&lt;query&gt;&amp;limit=&lt;DS_SearchLimit&gt;]*
   store* results/albummatches/album/name in albumTitles
   store* results/albummatches/album/artist in albumCreators
   store* results/albummatches/album/id in albumIDs

   // Choose
   f:displayAlbum n
      echo [&lt;n&gt;] &lt;albumCreators[&lt;n&gt;]&gt; - &lt;albumTitles[&lt;n&gt;]&gt;
   f:end
   forall* albumTitles index t: displayAlbum(&lt;t&gt;)
   echo</pre>
<p>Das Konstrukt in eckigen Klammern ist die Anfrage an Last.FM. Vor dem Doppelpunkt steht die Funktion, die aufgerufen werden soll, dahinter ihre Argumente &#8211; in diesem Fall &#8220;album&#8221; und &#8220;limit&#8221; mit den Werten der Variablen &#8220;query&#8221; und &#8220;DS_SearchLimit&#8221; (letztere aus der Konfigurationsdatei; sie legt fest, wie viele Suchergebnisse maximal angezeigt werden). Die Sternchen signalisieren, dass nun nicht ein einzelner Wert gespeichert werden soll, sondern eine Reihe von Werten in einer Liste. &#8220;store* results/albummatches/album/name in albumTitles&#8221; beispielsweise speichert alle Werte, die über diesen XPath (&#8220;results/&#8230;&#8221;) zu erreichen sind, in der Liste &#8220;albumTitles&#8221;.</p>
<p>Was dann folgt, ist eine Funktion, die genau einen Wert (die Variable &#8220;n&#8221;) als Argument erhält. Sie gibt anhand der beiden Listen &#8220;albumTitles&#8221; und &#8220;alb