using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net; using System.IO; using System.Xml; using System.Web; namespace OrbScrobbler { public partial class OrbScrobbler : Form { private class Item { private String _artist; private String _album; private String _title; private DateTime _lastAccessed; private long _length; public DateTime LastAccessed { get { return _lastAccessed; } set { _lastAccessed = value; } } public String Title { get { return _title; } set { _title = value; } } public String Album { get { return _album; } set { _album = value; } } public String Artist { get { return _artist; } set { _artist = value; } } public long LengthMS { get { return _length; } set { _length = value; } } public double LengtbSeconds { get { return (double)_length / 1000.0; } set { _length = (long)(1000 * value); } } public Item URLEncodedItem { get { Item i = new Item(); i.Artist = HttpUtility.UrlEncode(Artist, Encoding.UTF8); i.Album = HttpUtility.UrlEncode(Album, Encoding.UTF8); i.Title = HttpUtility.UrlEncode(Title, Encoding.UTF8); i.LastAccessed = LastAccessed; i.LengthMS = LengthMS; return i; } } } private DateTime _lastUpdateTime; private Queue _scrobbleQueue; private DateTime _lastAccessTime; private Timer _scrobbleTimer; private static String _orbQueryUrlBase = "http://api.orb.com/orb/xml/media.search?q=mediaType%3Daudio&fields=title,artist,album,lastAccessTime,duration&groupBy=orbMediumId&sortBy=lastAccessTime:desc&count="; private static String _orbLoginUrlBase = "https://api.orb.com/orb/xml/session.login?"; private static String _orbLogoutUrl = "http://api.orb.com/orb/xml/session.logout?"; private static String _orbKeepAliveUrl = "http://api.orb.com/orb/xml/session.keepAlive?"; private String _orbSessionId; private String _orbApiKey = "10sz7kavcnoif"; private String _orbUserName = "andrewjmatheny"; private String _orbPass = "magicmissile"; private static String _lfHandShakeUrlBase = "http://post.audioscrobbler.com/?hs=true&p=1.2.1"; private String _lfClientId = "tst"; private String _lfClientVersion = "1.0"; private String _lfUserId = "andywillsaveus"; private String _lfUserPassMD5 = "ba31560ee49b238c02ff7318b98b86f3"; private String _lfSessionId; private String _lfNowPlayingUrl; private String _lfSubmitUrl; public OrbScrobbler() { InitializeComponent(); InitializeVariables(); InitializeWebServices(); } public void Start() { InitialScrobble(); } private void InitializeWebServices() { //Initialize Orb HttpWebRequest.Create(_orbLogoutUrl).GetResponse(); HttpWebRequest orbHwr = (HttpWebRequest)HttpWebRequest.Create(_orbLoginUrlBase + "apiKey=" + _orbApiKey + "&l=" + _orbUserName + "&password=" + _orbPass + "&speed=240"); XmlReader xr = XmlReader.Create(orbHwr.GetResponse().GetResponseStream()); while (xr.Read()) { if (xr.Name.Trim().Equals("orbSessionId", StringComparison.InvariantCultureIgnoreCase)) _orbSessionId = xr.ReadElementString(); } _debugTextBox.Text += "\n" + orbHwr.RequestUri.OriginalString + "\n"; //Initialize last.fm LastFmHandshake(); } private void LastFmHandshake() { long ts = GetUnixTime(); String auth = HashString(_lfUserPassMD5 + ts); String requestStr = _lfHandShakeUrlBase + "&c=" + _lfClientId + "&v=" + _lfClientVersion + "&u=" + _lfUserId + "&t=" + ts + "&a=" + auth; HttpWebRequest lfHwr = (HttpWebRequest)HttpWebRequest.Create(requestStr); TextReader sr = new StreamReader(lfHwr.GetResponse().GetResponseStream()); if (sr.ReadLine().Trim().Equals("OK")) { _lfSessionId = sr.ReadLine().Trim(); _lfNowPlayingUrl = sr.ReadLine().Trim(); _lfSubmitUrl = sr.ReadLine().Trim(); } else { throw new Exception("Error connecting to last.fm"); } } private string HashString(string Value) { System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider(); byte[] data = System.Text.Encoding.ASCII.GetBytes(Value); data = x.ComputeHash(data); string ret = ""; for (int i = 0; i < data.Length; i++) ret += data[i].ToString("x2").ToLower(); return ret; } private void InitializeVariables() { _scrobbleTimer = new Timer(); _scrobbleTimer.Interval = 1000 * 60 * 5; //ms per s * s in a min * 5 min _scrobbleTimer.Tick += new EventHandler(CheckForUpdate); //load config file } private void CheckForUpdate(object sender, EventArgs eArgs) { } private List GetRecentlyPlayedItems(int howMany) { List items = new List(); HttpWebRequest orbQuery = (HttpWebRequest)HttpWebRequest.Create(_orbQueryUrlBase + howMany); orbQuery.CookieContainer = new CookieContainer(); orbQuery.CookieContainer.Add(orbQuery.RequestUri, new Cookie("JSESSIONID", _orbSessionId)); WebResponse wr = orbQuery.GetResponse(); _debugTextBox.Text += "\n" + orbQuery.RequestUri.OriginalString + "\n"; XmlReader xr = XmlReader.Create(wr.GetResponseStream()); while (xr.Read()) { if (xr.Name.Equals("item", StringComparison.InvariantCultureIgnoreCase)) { Item newItem = new Item(); XmlReaderSettings xrs = new XmlReaderSettings(); xrs.ConformanceLevel = ConformanceLevel.Fragment; XmlReader itemXr = XmlReader.Create(new StringReader(xr.ReadInnerXml()), xrs); while (itemXr.Read()) { String name = itemXr.GetAttribute("name"); if(String.IsNullOrEmpty(name)) continue; if (name.Equals("artist")) newItem.Artist = itemXr.ReadInnerXml(); else if (name.Equals("album")) newItem.Album = itemXr.ReadInnerXml(); else if (name.Equals("title")) newItem.Title = itemXr.ReadInnerXml(); else if (name.Equals("lastAccessTime")) newItem.LastAccessed = DateTime.Parse(itemXr.ReadInnerXml()).ToUniversalTime(); else if (name.Equals("duration")) newItem.LengthMS = long.Parse(itemXr.ReadInnerXml()); } items.Add(newItem); } } return items; } private void InitialScrobble() { //Initially scan for last 50 songs played List items = GetRecentlyPlayedItems(50); Queue queue = new Queue(); //SetNowPlaying(items[0]); queue.Enqueue(items[0]); ScrobbleQueue(queue); } private long GetUnixTime() { TimeSpan span = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)); return (long)Math.Floor(span.TotalSeconds); } private long GetUnixTime(DateTime time) { TimeSpan span = (time - new DateTime(1970, 1, 1, 0, 0, 0)); return (long)Math.Floor(span.TotalSeconds); } private String ScrobbleItem(Item item) { //Build the string with the post data Item i = item.URLEncodedItem; String postString = "s=" + _lfSessionId + "&a[0]=" + i.Artist + "&t[0]=" + i.Title + "&i[0]=" + GetUnixTime(i.LastAccessed) + "&o[0]=P&l[0]=" + i.LengtbSeconds + "&b[0]="; if (!String.IsNullOrEmpty(i.Album)) postString += i.Album; postString += "&r[0]=&n[0]=&m[0]="; //build the request HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create(_lfSubmitUrl); hwr.Method = "POST"; byte[] postData = Encoding.UTF8.GetBytes(postString); hwr.ContentLength = postData.Length; hwr.ContentType = "application/x-www-form-urlencoded"; // Write the request Stream s = hwr.GetRequestStream(); s.Write(postData, 0, postData.Length); s.Close(); // Get response TextReader tr = new StreamReader(hwr.GetResponse().GetResponseStream()); String result = tr.ReadLine(); _debugTextBox.Text += "\n" + postString + "\n"; _debugTextBox.Text += result + "\n"; return result; } private String SetNowPlaying(Item item) { item = item.URLEncodedItem; String postString = "s=" + _lfSessionId + "&a=" + item.Artist + "&t=" + item.Title + "&l=" + item.LengtbSeconds + "&b="; if (!String.IsNullOrEmpty(item.Album)) postString += item.Album; postString += "&n=&m="; HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create(_lfNowPlayingUrl); hwr.Method = "POST"; byte[] postData = Encoding.UTF8.GetBytes(postString); hwr.ContentLength = postData.Length; hwr.ContentType = "application/x-www-form-urlencoded"; // Write the request Stream s = hwr.GetRequestStream(); s.Write(postData, 0, postData.Length); s.Close(); TextReader tr = new StreamReader(hwr.GetResponse().GetResponseStream()); _debugTextBox.Text += "\n" + postString + "\n"; String result = tr.ReadLine(); _debugTextBox.Text += result + "\n"; return result; } private void ScrobbleQueue(Queue itemQueue) { while (itemQueue.Count > 0) { Item i = itemQueue.Peek(); SetNowPlaying(i); System.Threading.Thread.Sleep((int)(i.LengthMS / 2)); String result = ScrobbleItem(i); if (result.Equals("BADSESSION")) { LastFmHandshake(); } else { itemQueue.Dequeue(); } } } private void _buttonStart_Click(object sender, EventArgs e) { Start(); } } }