LastFM.XMLProcessor
Die Klasse LastFM.XMLProcessor kann auf Basis des Last.FM-Protokolls 1.2 eine Verbindung zum Server herstellen, sowie Playlists herunterladen. (Scrobbling unterstütz sie nicht.) Eine leicht modifizierte Version dieser Klasse kommt auch in LastSharp zum Einsatz.
using System;
using System.Xml;
using System.Net;
using System.IO;
using System.Text;
using System.Collections;
namespace LastFM
{
// ====================================================
// Klasse mit Hilfsfunktionen
// ====================================================
class Utilities
{
// MD5 erstellen
public static string MD5(string password)
{
byte[] textBytes = System.Text.Encoding.Default.GetBytes(password);
try
{
System.Security.Cryptography.MD5CryptoServiceProvider cryptHandler;
cryptHandler = new System.Security.Cryptography.MD5CryptoServiceProvider();
byte[] hash = cryptHandler.ComputeHash(textBytes);
string ret = "";
foreach (byte a in hash)
{
if (a < 16)
ret += "0" + a.ToString("x");
else
ret += a.ToString("x");
}
return ret;
}
catch
{
throw;
}
}
// Ruft eine URI auf und liefert sie als String zurück
public static string ExecuteURI(string uri)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
StreamReader sr = new StreamReader(resp.GetResponseStream());
string result = sr.ReadToEnd();
sr.Close();
return result;
}
}
// ====================================================
// Datenkapselung für Playlist und Tracks
// ====================================================
public class Playlist : ArrayList
{
public string PlaylistName = "";
public DateTime Timestamp = new DateTime();
}
public class Track
{
public string Title="";
public string Artist = "";
public string Location = "";
public string Album = "";
public string Image = "";
public int Duration = 0;
// Gibt die Dauer des Tracks im Format MM:SS aus
public string DurationString()
{
int sec = (this.Duration / 1000) % 60;
int min = ((this.Duration / 1000) - sec) / 60;
return ((min < 10) ? "0" : "") + min + ":" + ((sec < 10) ? "0" : "") + sec;
}
}
// ====================================================
// Handshake-Klasse für Bereitstellung der Handshake-Daten
// ====================================================
public class Handshake : Hashtable
{
/// Verarbeitet die Handshake-Rückgabe
public Handshake(string HandshakeString)
{
string[] lines = HandshakeString.Split('\n');
foreach (string line in lines)
{
string[] p = line.Split('=');
this.Add(p[0], p[1]);
}
if (!this.ContainsKey("session") || !this.ContainsKey("base_path") ||
!this.ContainsKey("base_url"))
{
throw new Exception("Ungültiger Handshake! (Passwort falsch?)");
}
}
// Daten (nur die wichtigsten)
public string SessionID { get { return (string) this["session"]; } }
public string BasePath { get { return (string) this["base_path"]; } }
public string BaseURL { get { return (string) this["base_url"]; } }
public string RequestURL { get { return "http://" + this.BaseURL + this.BasePath + "/"; } }
// Der Hashtable ist nicht veränderbar
public override bool IsReadOnly
{
get
{
return true;
}
}
}
// ====================================================
// XMLProcessor-Klasse
// ====================================================
public class XMLProcessor
{
// ========================================
// based on: http://code.google.com/p/thelastripper/wiki/LastFM12UnofficialDocumentation
// ========================================
// Login-Daten
private string user;
private string password;
//aktuelle Radiostationsdaten
private bool tuned_in = false;
private string to_station = null;
//
public bool TunedIn { get { return tuned_in; } }
public string Station { get { return to_station; } }
public string Username { get { return user; } }
// ========================================
public XMLProcessor(string username, string password)
{
this.user = username;
this.password = Utilities.MD5(password);
}
///
/// Führt einen Handshake mit LastFM aus.
///
public Handshake DoHandshake()
{
// Request ausführen
string uri =
"http://ws.audioscrobbler.com/radio/handshake.php?version=1.3.1.1" +
"&platform=win32&username=" + this.username + "&passwordmd5="
+ this.password + "&language=de&player=lastsharp";
string resp = Utilities.ExecuteURI(uri);
// Daten verarbeiten (in Handshake-Klasse)
return new Handshake(resp);
}
///
/// Setzt die Radiostation
///
public bool TuneIn(Handshake hs, string station)
{
// Operation ausführen
string uri = hs.RequestURL + "adjust.php?session=" + hs.SessionID + "&url=" + station + "〈=de";
string resp = Utilities.ExecuteURI(uri);
// Überprüfen + Status setzen
if (resp.StartsWith("response=OK"))
{
this.tuned_in = true;
this.to_station = station;
return true;
}
else
{
this.tuned_in = false;
this.to_station = null;
return false;
}
}
///
/// Ruft die Playlist ab
///
public Playlist GetPlaylist(Handshake hs)
{
// Radiostation ausgewählt?
if (!this.tuned_in) throw new Exception("Keine Radiostation angewählt.");
// Playlist abrufen
string uri =
hs.RequestURL + "xspf.php?sk=" + hs.SessionID +
"&discovery=0&desktop=1.3.1.1";
string resp = Utilities.ExecuteURI(uri);
XmlDocument doc = new XmlDocument();
doc.LoadXml(resp);
// =========================================
// Verarbeiten
// =========================================
Playlist pl = new Playlist();
XmlNode root = doc.FirstChild;
if (root.Name != "playlist")
throw new Exception("Ungültiges XML-Format der Playlist");
// Titel der Playlist + Timestamp
XmlNode pl_title = root.SelectSingleNode("title");
if (pl_title != null) pl.PlaylistName = pl_title.InnerText;
pl.Timestamp = DateTime.Now;
// Tracklist
XmlNode pl_tracks = root.SelectSingleNode("trackList");
if (pl_tracks != null) for (int i = 0; i < pl_tracks.ChildNodes.Count; i++)
{
{
XmlNode trn = pl_tracks.ChildNodes[i];
Track tr = new Track();
tr.Artist = trn.SelectSingleNode("creator").InnerText;
tr.Album = trn.SelectSingleNode("album").InnerText;
tr.Title = trn.SelectSingleNode("title").InnerText;
tr.Location = trn.SelectSingleNode("location").InnerText;
tr.Image = trn.SelectSingleNode("image").InnerText;
tr.Duration = Convert.ToInt32(trn.SelectSingleNode("duration").InnerText);
pl.Add(tr);
}
}
return pl;
}
}
}