banshee r3201 - in branches/banshee/stable: . src/Plugins/Banshee.Plugins.Audioscrobbler



Author: rubenv
Date: Sun Feb 10 14:59:45 2008
New Revision: 3201
URL: http://svn.gnome.org/viewvc/banshee?rev=3201&view=rev

Log:
2008-02-10  Ruben Vermeersch  <ruben savanne be>

	* src/Plugins/Banshee.Plugins.Audioscrobbler/AudioscrobblerPlugin.cs:
	* src/Plugins/Banshee.Plugins.Audioscrobbler/Engine.cs:
	* src/Plugins/Banshee.Plugins.Audioscrobbler/Queue.cs: Updated to the 
	new audioscrobbler protocol (1.2) and connection failure guidelines. 
	(BGO: #404965, #501405). Add connection time-out. (BGO: #469490).
	Patch by Pepijn van de Geer (pvandegeer gmail com).


Modified:
   branches/banshee/stable/ChangeLog
   branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/AudioscrobblerPlugin.cs
   branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/Engine.cs
   branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/Queue.cs

Modified: branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/AudioscrobblerPlugin.cs
==============================================================================
--- branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/AudioscrobblerPlugin.cs	(original)
+++ branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/AudioscrobblerPlugin.cs	Sun Feb 10 14:59:45 2008
@@ -75,7 +75,8 @@
             get {
                 return new string [] { 
                     "Chris Toshok",
-                    "Aaron Bockover"
+                    "Aaron Bockover",
+                    "Pepijn van de Geer"
                 };
             }
         }

Modified: branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/Engine.cs
==============================================================================
--- branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/Engine.cs	(original)
+++ branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/Engine.cs	Sun Feb 10 14:59:45 2008
@@ -56,15 +56,18 @@
         const int TICK_INTERVAL = 2000; /* 2 seconds */
         const int FAILURE_LOG_MINUTES = 5; /* 5 minute delay on logging failure to upload information */
         const int RETRY_SECONDS = 60; /* 60 second delay for transmission retries */
+        const int MAX_RETRY_SECONDS = 7200; /* 2 hours, as defined in the last.fm protocol */
+        const int TIME_OUT = 5000; /* 5 seconds timeout for webrequests */
         const string CLIENT_ID = "bsh";
         const string CLIENT_VERSION = "0.1";
         const string SCROBBLER_URL = "http://post.audioscrobbler.com/";;
-        const string SCROBBLER_VERSION = "1.1";
+        const string SCROBBLER_VERSION = "1.2";
 
         string username;
         string md5_pass;
+		string session_id = null;
         string post_url;
-        string security_token;
+		string now_playing_url;
 
         uint timeout_id;
         DateTime next_interval;
@@ -72,10 +75,17 @@
 
         Queue queue;
 
+        int hard_failures = 0;
+        int hard_failure_retry_sec = 60;
+ 
+        bool now_playing_submitted;
         bool song_started; /* if we were watching the current song from the beginning */
         bool queued; /* if current_track has been queued */
-        bool sought; /* if the user has sought in the current playing song */
 
+        DateTime song_start_time;
+        TrackInfo last_track;
+        
+        WebRequest now_playing_post;
         WebRequest current_web_req;
         IAsyncResult current_async_result;
         State state;
@@ -91,6 +101,7 @@
         {
             song_started = false;
             PlayerEngineCore.EventChanged += OnPlayerEngineEventChanged;
+            PlayerEngineCore.StateChanged += OnPlayerEngineStateChanged;
             queue.TrackAdded += delegate(object o, EventArgs args) {
                 StartTransitionHandler ();
             };
@@ -106,8 +117,12 @@
 
         public void Stop ()
         {
+            // Queue the current track for later submission
+            Queue(PlayerEngineCore.CurrentTrack);
+            
             PlayerEngineCore.EventChanged -= OnPlayerEngineEventChanged;
-
+            PlayerEngineCore.StateChanged -= OnPlayerEngineStateChanged;
+            
             StopTransitionHandler ();
 
             if (current_web_req != null) {
@@ -133,8 +148,8 @@
             this.username = username;
             this.md5_pass = MD5Encode (pass);
 
-            if (security_token != null) {
-                security_token = null;
+            if (session_id != null) {
+                session_id = null;
                 state = State.NEED_HANDSHAKE;
             }
         }
@@ -151,38 +166,59 @@
             return CryptoConvert.ToHex (hash).ToLower ();
         }
 
+        void OnPlayerEngineStateChanged(object o, PlayerEngineStateArgs args)
+        {
+            if (PlayerEngineCore.CurrentState == PlayerEngineState.Paused && 
+                    PlayerEngineCore.LastState == PlayerEngineState.Playing) {
+                st.Stop();
+            } 
+            else if (PlayerEngineCore.CurrentState == PlayerEngineState.Playing &&
+                PlayerEngineCore.LastState == PlayerEngineState.Paused) {
+                st.Start();
+            }
+        }
+        
+        // We need to time how long the song has played
+        class SongTimer
+        {
+            private DateTime start_time;
+            public int PlayTime = 0;
+            public void Start() { start_time = DateTime.Now; }
+            public void Stop() { PlayTime += (int) (DateTime.Now - start_time).TotalSeconds;}
+            public void Reset() { PlayTime = 0; }
+        }
+        
+        SongTimer st = new SongTimer();
+        
+        void Queue (TrackInfo track) {
+            if (song_started && !queued && track.Duration.TotalSeconds > 30 && 
+                track.Artist != "" && track.Title != "" &&
+               (st.PlayTime >  track.Duration.TotalSeconds / 2 || st.PlayTime > 240)) {
+                  queue.Add (track, song_start_time);
+                  queued = true;
+            }
+        }
+        
         void OnPlayerEngineEventChanged(object o, PlayerEngineEventArgs args)
         {
             switch (args.Event) {
-                /* Queue if we're watching this song from the beginning,
-                 * it isn't queued yet and the user didn't seek until now,
-                 * we're actually playing, song position and length are greater than 0
-                 * and we already played half of the song or 240 seconds */
-                case PlayerEngineEvent.Iterate:
-                    if (song_started && !queued && !sought && PlayerEngineCore.CurrentState == PlayerEngineState.Playing &&
-                        PlayerEngineCore.Length > 0 && PlayerEngineCore.Position > 0 &&
-                        (PlayerEngineCore.Position > PlayerEngineCore.Length / 2 || PlayerEngineCore.Position > 240)) {
-                            TrackInfo track = PlayerEngineCore.CurrentTrack;
-                            if (track == null) {
-                                queued = sought = false;
-                            } else {
-                                queue.Add (track, DateTime.Now - TimeSpan.FromSeconds (PlayerEngineCore.Position));
-                                queued = true;
-                            }
-                    }
-                    break;
-                /* Start of Stream: new song started */
                 case PlayerEngineEvent.StartOfStream:
-                    queued = sought = false;
+                    // Queue the previous track in case of a skip
+                    st.Stop();
+                    Queue(last_track);
+                
+                    st.Reset(); st.Start();
+                    song_start_time = DateTime.Now;
+                    last_track = PlayerEngineCore.CurrentTrack;
+                    now_playing_submitted = queued = false;
                     song_started = true;
+
+                    StartTransitionHandler();
                     break;
-                /* End of Stream: song finished */
                 case PlayerEngineEvent.EndOfStream:
-                    song_started = queued = sought = false;
-                    break;
-                /* Did the user seek? */
-                case PlayerEngineEvent.Seek:
-                    sought = true;
+                    st.Stop();               
+                    Queue(PlayerEngineCore.CurrentTrack);
+                    queued = true;
                     break;
             }
         }
@@ -193,17 +229,22 @@
              * involving the network. */
             if (!Globals.Network.Connected)
                 return true;
-
+            
+            if ((state == State.IDLE || state == State.NEED_TRANSMIT) && hard_failures > 2) {
+                state = State.NEED_HANDSHAKE;
+                hard_failures = 0;
+            }
+            
             /* and address changes in our engine state */
             switch (state) {
             case State.IDLE:
-                if (queue.Count > 0) {
-                    if (username != null && md5_pass != null && security_token == null)
-                        state = State.NEED_HANDSHAKE;
-                    else 
-                        state = State.NEED_TRANSMIT;
+                if (username != null && md5_pass != null && session_id == null) {
+                    state = State.NEED_HANDSHAKE;
                 } else {
-                    StopTransitionHandler ();
+                    if (queue.Count > 0)
+                        state = State.NEED_TRANSMIT;
+                    else if (now_playing_submitted)
+                        StopTransitionHandler ();
                 }
                 break;
             case State.NEED_HANDSHAKE:
@@ -222,6 +263,12 @@
                 /* nothing here */
                 break;
             }
+            
+            // Only submit if queue is empty, otherwise the submission
+            // gets overruled by the queue submission by Last.fm
+            if (queue.Count == 0 && !now_playing_submitted && state == State.IDLE 
+                    && PlayerEngineCore.CurrentState == PlayerEngineState.Playing)
+                NowPlaying(PlayerEngineCore.CurrentTrack);
 
             return true;
         }
@@ -251,7 +298,7 @@
 
             StringBuilder sb = new StringBuilder ();
 
-            sb.AppendFormat ("u={0}&s={1}", HttpUtility.UrlEncode (username), security_token);
+            sb.AppendFormat ("s={0}", session_id);
 
             sb.Append (queue.GetTransmitInfo (out num_tracks_transmitted));
 
@@ -259,29 +306,33 @@
             current_web_req.Method = "POST";
             current_web_req.ContentType = "application/x-www-form-urlencoded";
             current_web_req.ContentLength = sb.Length;
-
+            
             TransmitState ts = new TransmitState ();
             ts.Count = num_tracks_transmitted;
             ts.StringBuilder = sb;
 
             state = State.WAITING_FOR_REQ_STREAM;
             current_async_result = current_web_req.BeginGetRequestStream (TransmitGetRequestStream, ts);
-            if (current_async_result == null) {
+            if (!(current_async_result.AsyncWaitHandle.WaitOne (TIME_OUT, false))) {
+		        LogCore.Instance.PushWarning("Audioscrobbler upload failed", 
+                                             "The request timed out and was aborted", false);
                 next_interval = DateTime.Now + new TimeSpan (0, 0, RETRY_SECONDS);
+                hard_failures++;
                 state = State.IDLE;
-            }
+                current_web_req.Abort();
+
+			}
         }
 
         void TransmitGetRequestStream (IAsyncResult ar)
         {
             Stream stream;
-
             try {
                 stream = current_web_req.EndGetRequestStream (ar);
             }
             catch (Exception e) {
-                Console.WriteLine ("Failed to get the request stream: {0}", e);
-
+                LogCore.Instance.PushError ("Audioscrobbler upload failed", 
+                              String.Format("Failed to get the request stream: {0}", e));
                 state = State.IDLE;
                 next_interval = DateTime.Now + new TimeSpan (0, 0, RETRY_SECONDS);
                 return;
@@ -298,6 +349,7 @@
             current_async_result = current_web_req.BeginGetResponse (TransmitGetResponse, ts);
             if (current_async_result == null) {
                 next_interval = DateTime.Now + new TimeSpan (0, 0, RETRY_SECONDS);
+                hard_failures++;
                 state = State.IDLE;
             }
         }
@@ -310,17 +362,15 @@
                 resp = current_web_req.EndGetResponse (ar);
             }
             catch (Exception e) {
-                Console.WriteLine ("Failed to get the response: {0}", e);
-
+                LogCore.Instance.PushError ("Audioscrobbler upload failed", 
+                              String.Format("Failed to get the response: {0}", e));
                 state = State.IDLE;
                 next_interval = DateTime.Now + new TimeSpan (0, 0, RETRY_SECONDS);
                 return;
             }
 
             TransmitState ts = (TransmitState) ar.AsyncState;
-
             Stream s = resp.GetResponseStream ();
-
             StreamReader sr = new StreamReader (s, Encoding.UTF8);
 
             string line;
@@ -333,18 +383,18 @@
                     last_upload_failed_logged = now;
                 }
                 /* retransmit the queue on the next interval */
+                hard_failures++;
                 state = State.NEED_TRANSMIT;
             }
-            else if (line.StartsWith ("BADUSER")
-                     || line.StartsWith ("BADAUTH")) {
+            else if (line.StartsWith ("BADSESSION")) {
                 if (now - last_upload_failed_logged > TimeSpan.FromMinutes(FAILURE_LOG_MINUTES)) {
-                    LogCore.Instance.PushWarning ("Audioscrobbler upload failed", "invalid authentication", false);
+                    LogCore.Instance.PushWarning ("Audioscrobbler upload failed", "Session ID sent was invalid", false);
                     last_upload_failed_logged = now;
                 }
                 /* attempt to re-handshake (and retransmit) on the next interval */
-                security_token = null;
+                session_id = null;
                 next_interval = DateTime.Now + new TimeSpan (0, 0, RETRY_SECONDS);
-                state = State.IDLE;
+                state = State.NEED_HANDSHAKE;
                 return;
             }
             else if (line.StartsWith ("OK")) {
@@ -356,58 +406,64 @@
                 /* we succeeded, pop the elements off our queue */
                 queue.RemoveRange (0, ts.Count);
                 queue.Save ();
+                hard_failures = 0;
                 state = State.IDLE;
             }
             else {
                 if (now - last_upload_failed_logged > TimeSpan.FromMinutes(FAILURE_LOG_MINUTES)) {
-                    LogCore.Instance.PushDebug ("Audioscrobbler upload failed", String.Format ("Unrecognized response: {0}", line), false);
+                    LogCore.Instance.PushWarning("Audioscrobbler upload failed", String.Format ("Unrecognized response: {0}", line), false);
                     last_upload_failed_logged = now;
                 }
+                hard_failures++;
                 state = State.IDLE;
             }
-
-            /* now get the next interval */
-            line = sr.ReadLine ();
-            if (line.StartsWith ("INTERVAL")) {
-                int interval_seconds = Int32.Parse (line.Substring ("INTERVAL".Length));
-                next_interval = DateTime.Now + new TimeSpan (0, 0, interval_seconds);
-            }
-            else {
-                Console.WriteLine ("expected INTERVAL..");
-            }
         }
 
         //
         // Async code for handshaking
         //
-        void Handshake ()
+		private string UnixTime ()
         {
-            string uri = String.Format ("{0}?hs=true&p={1}&c={2}&v={3}&u={4}",
+            return ((int) (DateTime.UtcNow - new DateTime (1970, 1, 1)).TotalSeconds).ToString ();
+        }
+		
+		void Handshake ()
+        {
+            string timestamp = UnixTime();
+            string security_token = MD5Encode (md5_pass + timestamp);
+
+            string uri = String.Format ("{0}?hs=true&p={1}&c={2}&v={3}&u={4}&t={5}&a={6}",
                                         SCROBBLER_URL,
                                         SCROBBLER_VERSION,
                                         CLIENT_ID, CLIENT_VERSION,
-                                        HttpUtility.UrlEncode (username));
-
+                                        HttpUtility.UrlEncode (username),
+                                        timestamp,
+                                        security_token);
             current_web_req = WebRequest.Create (uri);
 
             state = State.WAITING_FOR_HANDSHAKE_RESP;
             current_async_result = current_web_req.BeginGetResponse (HandshakeGetResponse, null);
             if (current_async_result == null) {
-                next_interval = DateTime.Now + new TimeSpan (0, 0, RETRY_SECONDS);
-                state = State.IDLE;
+                next_interval = DateTime.Now + new TimeSpan (0, 0, hard_failure_retry_sec);
+                hard_failures++;
+                if (hard_failure_retry_sec < MAX_RETRY_SECONDS)
+                    hard_failure_retry_sec *= 2;
+                state = State.NEED_HANDSHAKE;
             }
         }
 
         void HandshakeGetResponse (IAsyncResult ar)
         {
             bool success = false;
+            bool hard_failure = false;
             WebResponse resp;
 
             try {
                 resp = current_web_req.EndGetResponse (ar);
             }
             catch (Exception e) {
-                Console.WriteLine ("failed to handshake: {0}", e);
+                LogCore.Instance.PushError ("Audioscrobbler init failed", 
+                              String.Format("Failed to handshake: {0}", e));
 
                 /* back off for a time before trying again */
                 state = State.IDLE;
@@ -422,45 +478,113 @@
             string line;
 
             line = sr.ReadLine ();
-            if (line.StartsWith ("FAILED")) {
-                LogCore.Instance.PushWarning ("Audioscrobbler sign-on failed", line.Substring ("FAILED".Length).Trim(), false);
+            if (line.StartsWith ("BANNED")) {
+                LogCore.Instance.PushWarning ("Audioscrobbler sign-on failed", "Player is banned", false);
                                    
             }
-            else if (line.StartsWith ("BADUSER")) {
+            else if (line.StartsWith ("BADAUTH")) {
                 LogCore.Instance.PushWarning ("Audioscrobbler sign-on failed", "unrecognized user/password", false);
             }
-            else if (line.StartsWith ("UPDATE")) {
-                LogCore.Instance.PushInformation ("Audioscrobbler plugin needs updating",
-                                            String.Format ("Fetch a newer version at {0}\nor update to a newer version of Banshee",
-                                                           line.Substring ("UPDATE".Length).Trim()), false);
-                success = true;
+            else if (line.StartsWith ("BADTIME")) {
+                LogCore.Instance.PushWarning ("Audioscrobbler sign-on failed", 
+                                                  "timestamp provided was not close enough to the current time", false);
+            }
+            else if (line.StartsWith ("FAILED")) {
+                LogCore.Instance.PushWarning ("Audioscrobbler sign-on failed",
+                                                  String.Format ("Temporary server failure: {0}",
+                                                                  line.Substring ("FAILED".Length).Trim()), false);
+                hard_failure = true;
             }
-            else if (line.StartsWith ("UPTODATE")) {
+            else if (line.StartsWith ("OK")) {
                 success = true;
             }
+            else {
+                LogCore.Instance.PushError ("Audioscrobbler sign-on failed", 
+                                                  String.Format ("Unknown error: {0}",
+                                                                  line.Trim()), false);
+                hard_failure = true;
+           }
             
-            /* read the challenge string and post url, if
-             * this was a successful handshake */
             if (success == true) {
-                string challenge = sr.ReadLine ().Trim ();
+                LogCore.Instance.PushInformation ("Audioscrobbler sign-on succeeded", "Session ID received", false); 
+                session_id = sr.ReadLine ().Trim ();
+                now_playing_url = sr.ReadLine ().Trim ();
                 post_url = sr.ReadLine ().Trim ();
-
-                security_token = MD5Encode (md5_pass + challenge);
-                //Console.WriteLine ("security token = {0}", security_token);
-            }
-
-            /* read the trailing interval */
-            line = sr.ReadLine ();
-            if (line.StartsWith ("INTERVAL")) {
-                int interval_seconds = Int32.Parse (line.Substring ("INTERVAL".Length));
-                next_interval = DateTime.Now + new TimeSpan (0, 0, interval_seconds);
+                hard_failures = 0;
+                hard_failure_retry_sec = 60;
+            
             }
-            else {
-                Console.WriteLine ("expected INTERVAL..");
+            if (hard_failure == true) {
+                next_interval = DateTime.Now + new TimeSpan (0, 0, hard_failure_retry_sec);
+                hard_failures++;
+                if (hard_failure_retry_sec < MAX_RETRY_SECONDS)
+                    hard_failure_retry_sec *= 2;
+ 
             }
 
             /* XXX we shouldn't just try to handshake again for BADUSER */
             state = success ? State.IDLE : State.NEED_HANDSHAKE;
         }
+        
+        //
+        // Async code for now playing
+        
+        void NowPlaying (TrackInfo track)
+        {
+            if (session_id != null && track.Artist != "" && track.Title != "") {
+                
+                string str_track_number = "";
+                if (track.TrackNumber != 0)
+                    str_track_number = track.TrackNumber.ToString();
+                
+                string uri = String.Format ("{0}?s={1}&a={2}&t={3}&b={4}&l={5}&n={6}&m={7}",
+                                            now_playing_url,
+                                            session_id,
+                                            HttpUtility.UrlEncode(track.Artist),
+                                            HttpUtility.UrlEncode(track.Title),
+    			                            HttpUtility.UrlEncode(track.Album),
+                                            track.Duration.TotalSeconds.ToString(),
+                                            str_track_number,
+    			                            "" /* musicbrainz id */);
+
+                now_playing_post = WebRequest.Create (uri);
+                now_playing_post.Method = "POST";
+                now_playing_post.ContentType = "application/x-www-form-urlencoded";
+                now_playing_post.ContentLength = uri.Length;
+                now_playing_post.BeginGetResponse (NowPlayingGetResponse, null);
+                now_playing_submitted = true;
+            }
+        }
+
+        void NowPlayingGetResponse (IAsyncResult ar)
+        {
+            try {
+
+                WebResponse my_resp = now_playing_post.EndGetResponse (ar);
+
+                Stream s = my_resp.GetResponseStream ();
+                StreamReader sr = new StreamReader (s, Encoding.UTF8);
+
+                string line = sr.ReadLine ();
+                if (line.StartsWith ("BADSESSION")) {
+                    LogCore.Instance.PushWarning ("Audioscrobbler NowPlaying failed", "Session ID sent was invalid", false);
+                    /* attempt to re-handshake on the next interval */
+                    session_id = null;
+                    next_interval = DateTime.Now + new TimeSpan (0, 0, RETRY_SECONDS);
+                    state = State.NEED_HANDSHAKE;
+                    return;
+                }
+                else if (line.StartsWith ("OK")) {
+                    // NowPlaying submitted  
+                }
+                else {
+                    LogCore.Instance.PushWarning ("Audioscrobbler NowPlaying failed", "Unexpected or no response", false);       
+                }
+            }
+            catch (Exception e) {
+                LogCore.Instance.PushError ("Audioscrobbler NowPlaying failed", 
+                              String.Format("Failed to post NowPlaying: {0}", e));
+            }
+        }
     }
 }

Modified: branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/Queue.cs
==============================================================================
--- branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/Queue.cs	(original)
+++ branches/banshee/stable/src/Plugins/Banshee.Plugins.Audioscrobbler/Queue.cs	Sun Feb 10 14:59:45 2008
@@ -50,21 +50,23 @@
                 this.artist = track.Artist;
                 this.album = track.Album;
                 this.title = track.Title;
-                this.duration = (int)track.Duration.TotalSeconds;
-                this.start_time = start_time.ToUniversalTime ();
+                this.track_number = (int) track.TrackNumber;
+                this.duration = (int) track.Duration.TotalSeconds;
+                this.start_time = DateTimeUtil.ToTimeT(start_time.ToUniversalTime ());
             }
 
             public QueuedTrack (string artist, string album,
-                                string title, int duration, DateTime start_time)
+                                string title, int track_number, int duration, long start_time)
             {
                 this.artist = artist;
                 this.album = album;
                 this.title = title;
+                this.track_number = track_number;
                 this.duration = duration;
                 this.start_time = start_time;
             }
 
-            public DateTime StartTime {
+            public long StartTime {
                 get { return start_time; }
             }
             public string Artist {
@@ -73,6 +75,9 @@
             public string Album {
                 get { return album; }
             }
+            public int TrackNumber {
+                get { return track_number; }
+            }
             public string Title {
                 get { return title; }
             }
@@ -83,8 +88,9 @@
             string artist;
             string album;
             string title;
+            int track_number;
             int duration;
-            DateTime start_time;
+            long start_time;
         }
 
         ArrayList queue;
@@ -120,8 +126,9 @@
                 writer.WriteElementString ("Artist", track.Artist);
                 writer.WriteElementString ("Album", track.Album);
                 writer.WriteElementString ("Title", track.Title);
+                writer.WriteElementString ("TrackNumber", track.TrackNumber.ToString());
                 writer.WriteElementString ("Duration", track.Duration.ToString());
-                writer.WriteElementString ("StartTime", DateTimeUtil.ToTimeT(track.StartTime).ToString());
+                writer.WriteElementString ("StartTime", track.StartTime.ToString());
                 writer.WriteEndElement (); // Track
             }
             writer.WriteEndElement (); // AudioscrobblerQueue
@@ -144,8 +151,9 @@
                     string artist = "";    
                     string album = "";
                     string title = "";
+                    int track_number = 0;
                     int duration = 0;
-                    DateTime start_time = new DateTime (0);
+                    long start_time = 0;
 
                     foreach (XmlNode child in node.ChildNodes) {
                         if (child.Name == "Artist" && child.ChildNodes.Count != 0) {
@@ -154,15 +162,16 @@
                             album = child.ChildNodes [0].Value;
                         } else if (child.Name == "Title" && child.ChildNodes.Count != 0) {
                             title = child.ChildNodes [0].Value;
+                        } else if (child.Name == "TrackNumber" && child.ChildNodes.Count != 0) {
+                            track_number = Convert.ToInt32 (child.ChildNodes [0].Value);
                         } else if (child.Name == "Duration" && child.ChildNodes.Count != 0) {
                             duration = Convert.ToInt32 (child.ChildNodes [0].Value);
                         } else if (child.Name == "StartTime" && child.ChildNodes.Count != 0) {
-                            long time = Convert.ToInt64 (child.ChildNodes [0].Value);
-                            start_time = DateTimeUtil.FromTimeT (time);
+                            start_time = Convert.ToInt64 (child.ChildNodes [0].Value);
                         }
                     }
 
-                    queue.Add (new QueuedTrack (artist, album, title, duration, start_time));
+                    queue.Add (new QueuedTrack (artist, album, title, track_number, duration, start_time));
                 }
             } catch { 
             }
@@ -170,24 +179,31 @@
 
         public string GetTransmitInfo (out int num_tracks)
         {
+            string str_track_number = "";
             StringBuilder sb = new StringBuilder ();
-
+            
             int i;
             for (i = 0; i < queue.Count; i ++) {
-                /* we queue a maximum of 10 tracks per request */
-                if (i == 9) break;
+                /* Last.FM 1.2 can handle up to 50 songs in one request */
+                if (i == 49) break;
 
                 QueuedTrack track = (QueuedTrack)queue[i];
 
+                if (track.TrackNumber != 0)
+                    str_track_number = track.TrackNumber.ToString();
+                    
                 sb.AppendFormat (
-                         "&a[{6}]={0}&t[{6}]={1}&b[{6}]={2}&m[{6}]={3}&l[{6}]={4}&i[{6}]={5}",
-                         HttpUtility.UrlEncode (track.Artist),
-                         HttpUtility.UrlEncode (track.Title),
-                         HttpUtility.UrlEncode (track.Album),
-                         "" /* musicbrainz id */,
-                         track.Duration.ToString (),
-                         HttpUtility.UrlEncode (track.StartTime.ToString ("yyyy-MM-dd HH:mm:ss")),
-                         i);
+                    "&a[{9}]={0}&t[{9}]={1}&i[{9}]={2}&o[{9}]={3}&r[{9}]={4}&l[{9}]={5}&b[{9}]={6}&n[{9}]={7}&m[{9}]={8}",
+                    HttpUtility.UrlEncode (track.Artist),
+                    HttpUtility.UrlEncode (track.Title),
+                    track.StartTime.ToString (),
+                    "P" /* source: chosen by user */, 
+                    ""  /* rating: L/B/S */, 
+                    track.Duration.ToString (),
+                    HttpUtility.UrlEncode (track.Album),
+                    str_track_number,
+                    "" /* musicbrainz id */,
+                     i);
             }
 
             num_tracks = i;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]