xscDevBlog – LastSharp & Co.

Der xscheme-DevelopmentBlog

LastFM.XMLProcessor

without comments

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

Written by xsc

August 9th, 2008 at 10:00 pm

Posted in C#

Tagged with , ,

Leave a Reply