banshee r4202 - in trunk/banshee: . src/Core/Banshee.Core/Banshee.Streaming src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.ServiceStack src/Core/Banshee.Services/Banshee.Sources src/Core/Banshee.Services/Banshee.Streaming src/Core/Banshee.ThickClient/Banshee.Gui.Widgets src/Extensions/Banshee.Daap/Banshee.Daap src/Extensions/Banshee.Daap/Daap src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio



Author: gburt
Date: Wed Jun 25 21:17:57 2008
New Revision: 4202
URL: http://svn.gnome.org/viewvc/banshee?rev=4202&view=rev

Log:
2008-06-25  Gabriel Burt  <gabriel burt gmail com>

	This commit has a few goodies.  The track header info for radio stations
	is much improved, showing song information (if we have it) plus the
	station name.  Transferring of DAAP tracks to the library (by DnD and via
	Importing) works now (with smooth progress updates for the transfer).  The
	AddTrack to library job is now cancellable.

	* src/Extensions/Banshee.Daap/Daap/Database.cs: Rework the Fetch/Stream
	methods to not require a Track object, but only the track_id and type,
	which is all they really need.  This lets us avoid the O(n) track lookup.
	Add yield return progress updates to the DownloadTrack method.

	* src/Extensions/Banshee.Daap/Banshee.Daap/DaapSource.cs: Implement
	CopyTrackTo and Import so that dragging and importing tracks from a DAAP
	share into the library works.

	* src/Extensions/Banshee.Daap/Banshee.Daap/DaapTrackInfo.cs: Tweak the
	mimetype to save the DAAP.Track's Format without modification.

	* src/Extensions/Banshee.Daap/Banshee.Daap/DaapPlaylistSource.cs: Get a
	more accurate count if some (or all) tracks are null.

	* src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs:
	Set the track's Album and Artist from the station editor.

	* src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/StationEditor.cs:
	Factor out the table-row entry code, and add a Station Creator row (aka
	artist).

	* src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTileHost.cs:
	Ensure no user job is displayed for less than 1 second, to avoid things
	appearing and disappearing so fast some times.

	* src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TrackInfoDisplay.cs:
	Refactor a lot of code, and special case RadioTrackInfo objects, tacking
	on "on {radio station name}" to the second line if it has an artist or
	album, or just using the station name as the second line if it doesn't.

	* src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTile.cs: When
	the stop button is pressed and confirmed, set the job title to
	"Stopping..." and make the button not sensitive.

	* src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs: Not sure
	what SourceMergeType.All is really for, but treat it the same as
	SourceMergeType.Source.

	* src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs: Override
	AddAllTracks the same as AddSelectedTracks is overriden.  Allow cancelling
	the add job.

	* src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs: Expose
	IsCancelRequested property.

	* src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs: Default
	the title/artist/album for the track to its parent's values.

	* src/Core/Banshee.Services/Banshee.Collection.Database/CachedList.cs: Add
	another static helper for creating a cached list from an entire model.

	* src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs: Bring code over
	from stable, that when given a new track title splits it on " - " if the
	track.IsLive.


Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/CachedList.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TrackInfoDisplay.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTile.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTileHost.cs
   trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapPlaylistSource.cs
   trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapSource.cs
   trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapTrackInfo.cs
   trunk/banshee/src/Extensions/Banshee.Daap/Daap/Database.cs
   trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs
   trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/StationEditor.cs

Modified: trunk/banshee/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs	Wed Jun 25 21:17:57 2008
@@ -27,6 +27,7 @@
 //
 
 using System;
+using System.Text.RegularExpressions;
 
 using Banshee.Base;
 using Banshee.Collection;
@@ -145,7 +146,28 @@
                         track.ArtistName = Choose ((string)tag.Value, track.ArtistName);
                         break;
                     case CommonTags.Title:
-                        track.TrackTitle = Choose ((string)tag.Value, track.TrackTitle);
+                        //track.TrackTitle = Choose ((string)tag.Value, track.TrackTitle);
+                        string title = Choose ((string)tag.Value, track.TrackTitle);
+
+                        // Try our best to figure out common patterns in poor radio metadata.
+                        // Often only one tag is sent on track changes inside the stream, 
+                        // which is title, and usually contains artist and track title, separated
+                        // with a " - " string.
+                        if (track.IsLive && title.Contains (" - ")) {
+                            string [] parts = Regex.Split (title, " - ");
+                            track.TrackTitle = parts[1].Trim ();
+                            track.ArtistName = parts[0].Trim ();
+
+                            // Often, the title portion contains a postfix with more information
+                            // that will confuse lookups, such as "Talk [Studio Version]".
+                            // Strip out the [] part.
+                            Match match = Regex.Match (track.TrackTitle, "^(.+)[ ]+\\[.*\\]$");
+                            if (match.Groups.Count == 2) {
+                                track.TrackTitle = match.Groups[1].Value;
+                            }
+                        } else {
+                            track.TrackTitle = title;
+                        }
                         break;
                     case CommonTags.Album:
                         track.AlbumTitle = Choose ((string)tag.Value, track.AlbumTitle);

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/CachedList.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/CachedList.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/CachedList.cs	Wed Jun 25 21:17:57 2008
@@ -53,9 +53,22 @@
         }
 
         private BansheeModelCache<T> cache;
-
+        
+        public static CachedList<DatabaseTrackInfo> CreateFromModel (DatabaseTrackListModel model)
+        {
+            Selection selection = new Selection ();
+            selection.MaxIndex = model.Count;
+            selection.SelectAll ();
+            return CreateFromModelAndSelection (model, selection);
+        }
+        
         public static CachedList<DatabaseTrackInfo> CreateFromModelSelection (DatabaseTrackListModel model)
         {
+            return CreateFromModelAndSelection (model, model.Selection);
+        }
+
+        public static CachedList<DatabaseTrackInfo> CreateFromModelAndSelection (DatabaseTrackListModel model, Selection selection)
+        {
             CachedList<DatabaseTrackInfo> list = new CachedList<DatabaseTrackInfo> (DatabaseTrackInfo.Provider);
 
             HyenaSqliteCommand add_range_command = new HyenaSqliteCommand (String.Format (@"
@@ -64,7 +77,7 @@
             ));
 
             lock (model) {
-                foreach (RangeCollection.Range range in model.Selection.Ranges) {
+                foreach (RangeCollection.Range range in selection.Ranges) {
                     ServiceManager.DbConnection.Execute (add_range_command, list.CacheId, model.CacheId, range.Start, range.Count);
                 }
             }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs	Wed Jun 25 21:17:57 2008
@@ -48,6 +48,7 @@
         bool CanCancel { get; }
         
         bool IsFinished { get; }
+        bool IsCancelRequested { get; }
         bool DelayShow { get; }
     }
 }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs	Wed Jun 25 21:17:57 2008
@@ -374,7 +374,7 @@
 
         public override void MergeSourceInput (Source source, SourceMergeType mergeType)
         {
-            if (mergeType == SourceMergeType.Source) {
+            if (mergeType == SourceMergeType.Source || mergeType == SourceMergeType.All) {
                 AddAllTracks (source);
             } else if (mergeType == SourceMergeType.ModelSelection) {
                 AddSelectedTracks (source);

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs	Wed Jun 25 21:17:57 2008
@@ -494,9 +494,18 @@
 
             // Store a snapshot of the current selection
             CachedList<DatabaseTrackInfo> cached_list = CachedList<DatabaseTrackInfo>.CreateFromModelSelection (model);
-
             System.Threading.ThreadPool.QueueUserWorkItem (AddTrackList, cached_list);
-
+            return true;
+        }
+        
+        public override bool AddAllTracks (Source source)
+        {
+            if (!AcceptsInputFromSource (source) || source.Count == 0)
+                return false;
+            
+            DatabaseTrackListModel model = (source as ITrackModelSource).TrackModel as DatabaseTrackListModel;
+            CachedList<DatabaseTrackInfo> cached_list = CachedList<DatabaseTrackInfo>.CreateFromModel (model);
+            System.Threading.ThreadPool.QueueUserWorkItem (AddTrackList, cached_list);
             return true;
         }
 
@@ -524,6 +533,12 @@
             AddTrackJob.Total += (int) list.Count;
 
             foreach (DatabaseTrackInfo track in list) {
+                if (AddTrackJob.IsCancelRequested) {
+                    AddTrackJob.Finish ();
+                    IncrementAddedTracks ();
+                    break;
+                }
+                
                 if (track == null) {
                     IncrementAddedTracks ();
                     continue;
@@ -586,6 +601,7 @@
                             "Adding {0} of {1} to {2}"), "{0}", "{1}", Name), 
                             Properties.GetStringList ("Icon.Name"));
                         add_track_job.DelayShow = DelayAddJob;
+                        add_track_job.CanCancel = true;
                         add_track_job.Register ();
                     }
                 }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs	Wed Jun 25 21:17:57 2008
@@ -76,6 +76,9 @@
         
         public RadioTrackInfo (TrackInfo parentTrack) : this (parentTrack.Uri)
         {
+            ArtistName = parentTrack.ArtistName;
+            TrackTitle = parentTrack.TrackTitle;
+            AlbumTitle = parentTrack.AlbumTitle;
             ParentTrack = parentTrack;
         }
 

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TrackInfoDisplay.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TrackInfoDisplay.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/TrackInfoDisplay.cs	Wed Jun 25 21:17:57 2008
@@ -356,40 +356,80 @@
         
         protected virtual string GetSecondLineText (TrackInfo track)
         {
-            string markup_begin = String.Format ("<span color=\"{0}\" size=\"small\">", 
-                CairoExtensions.ColorGetHex (text_light_color, false));
-            string markup_end = "</span>";
             string markup = null;
+            Banshee.Streaming.RadioTrackInfo radio_track = track as Banshee.Streaming.RadioTrackInfo;
 
             if ((track.MediaAttributes & TrackMediaAttributes.Podcast) != 0) {
                 // Translators: {0} and {1} are for markup, {2} and {3}
                 // are Podcast Name and Published Date, respectively;
                 // e.g. 'from BBtv published 7/26/2007'
-                markup = String.Format (Catalog.GetString ("{0}from{1} {2} {0}published{1} {3}"), markup_begin, markup_end, 
-                    GLib.Markup.EscapeText (track.DisplayAlbumTitle), 
-                    GLib.Markup.EscapeText (track.ReleaseDate.ToShortDateString ()));
-            } else if (!String.IsNullOrEmpty (track.ArtistName) && !String.IsNullOrEmpty (track.AlbumTitle)) {
+                markup = MarkupFormat (Catalog.GetString ("{0}from{1} {2} {0}published{1} {3}"), 
+                    track.DisplayAlbumTitle, track.ReleaseDate.ToShortDateString ());
+            } else if (radio_track != null && radio_track.ParentTrack != null) {
+                // This is complicated because some radio streams send tags when the song changes, and we
+                // want to display them if they do.  But if they don't, we want it to look good too, so we just
+                // display the station name for the second line.
+                string by_from = GetByFrom (
+                    track.ArtistName == radio_track.ParentTrack.ArtistName ? null : track.ArtistName, track.DisplayArtistName,
+                    track.AlbumTitle == radio_track.ParentTrack.AlbumTitle ? null : track.AlbumTitle, track.DisplayAlbumTitle, false
+                );
+                
+                if (String.IsNullOrEmpty (by_from)) {
+                    // simply: "Chicago Public Radio" or whatever the artist name is
+                    markup = GLib.Markup.EscapeText (radio_track.ParentTrack.ArtistName ?? Catalog.GetString ("Unknown Stream"));
+                } else {
+                    // Translators: {0} and {1} are markup, {2} is the name of the radio station
+                    string on = MarkupFormat (Catalog.GetString ("{0}on{1} {2}"), radio_track.ParentTrack.TrackTitle);
+                    
+                    // Translators: {0} is the "from {album} by {artist}" type string, and {1} is the "on {radio station name}" string
+                    markup = String.Format (Catalog.GetString ("{0} {1}"), by_from, on);
+                }
+            } else {
+                markup = GetByFrom (track.ArtistName, track.DisplayArtistName, track.AlbumTitle, track.DisplayAlbumTitle, true);
+            }
+                 
+            
+            return String.Format ("<span color=\"{0}\">{1}</span>",  
+                CairoExtensions.ColorGetHex (text_color, false),
+                markup);
+        }
+        
+        private string MarkupFormat (string fmt, params string [] args)
+        {
+            string [] new_args = new string [args.Length + 2];
+            new_args[0] = String.Format ("<span color=\"{0}\" size=\"small\">", 
+                CairoExtensions.ColorGetHex (text_light_color, false));
+            new_args[1] = "</span>";
+            
+            for (int i = 0; i < args.Length; i++) {
+                new_args[i + 2] = GLib.Markup.EscapeText (args[i]);
+            }
+            
+            return String.Format (fmt, new_args);
+        }
+        
+        private string GetByFrom (string artist, string display_artist, string album, string display_album, bool unknown_ok)
+        {
+            
+            bool has_artist = !String.IsNullOrEmpty (artist);
+            bool has_album = !String.IsNullOrEmpty (album);
+
+            string markup = null;
+            if (has_artist && has_album) {
                 // Translators: {0} and {1} are for markup, {2} and {3}
                 // are Artist Name and Album Title, respectively;
                 // e.g. 'by Parkway Drive from Killing with a Smile'
-                markup = String.Format (Catalog.GetString ("{0}by{1} {2} {0}from{1} {3}"), markup_begin, markup_end, 
-                    GLib.Markup.EscapeText (track.DisplayArtistName), 
-                    GLib.Markup.EscapeText (track.DisplayAlbumTitle));
-            } else if (!String.IsNullOrEmpty (track.AlbumTitle)) {
+                markup = MarkupFormat (Catalog.GetString ("{0}by{1} {2} {0}from{1} {3}"), display_artist, display_album);
+            } else if (has_album) {
                 // Translators: {0} and {1} are for markup, {2} is for Album Title;
                 // e.g. 'from Killing with a Smile'
-                markup = String.Format (Catalog.GetString ("{0}from{1} {2}"), markup_begin, markup_end,
-                    GLib.Markup.EscapeText (track.DisplayAlbumTitle));
-            } else {
+                markup = MarkupFormat (Catalog.GetString ("{0}from{1} {2}"), display_album);
+            } else if (has_artist || unknown_ok) {
                 // Translators: {0} and {1} are for markup, {2} is for Artist Name;
                 // e.g. 'by Parkway Drive'
-                markup = String.Format (Catalog.GetString ("{0}by{1} {2}"), markup_begin, markup_end,
-                    GLib.Markup.EscapeText (track.DisplayArtistName));
+                markup = MarkupFormat (Catalog.GetString ("{0}by{1} {2}"), display_artist);
             }
-            
-            return String.Format ("<span color=\"{0}\">{1}</span>",  
-                CairoExtensions.ColorGetHex (text_color, false),
-                markup);
+            return markup;
         }
     }
 }

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTile.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTile.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTile.cs	Wed Jun 25 21:17:57 2008
@@ -158,21 +158,27 @@
             cancel_dialog = null;
         }
         
+        private void SetTitle (string new_title)
+        {
+            if (String.IsNullOrEmpty (new_title)) {
+                title_label.Hide ();
+            } else {
+                title_label.Markup = String.Format ("<small><b>{0}</b></small>", GLib.Markup.EscapeText (new_title));
+                title_label.Show ();
+            }
+            title = new_title;
+        }
+        
         private void UpdateFromJob ()
         {
             if (cancel_dialog != null && !job.CanCancel) {
                 cancel_dialog.Respond (Gtk.ResponseType.Cancel);
             }
             
-            if (title != job.Title) {
-                if (String.IsNullOrEmpty (job.Title)) {
-                    title_label.Hide ();
-                } else {
-                    title_label.Markup = String.Format ("<small><b>{0}</b></small>", 
-                        GLib.Markup.EscapeText (job.Title));
-                    title_label.Show ();
-                }
-                title = job.Title;
+            if (job.IsCancelRequested) {
+                SetTitle (Catalog.GetString ("Stopping..."));
+            } else if (title != job.Title) {
+                SetTitle (job.Title);
             }
             
             if (status != job.Status) {
@@ -197,7 +203,7 @@
                 }
             }
             
-            cancel_button.Sensitive = job.CanCancel;
+            cancel_button.Sensitive = job.CanCancel && !job.IsCancelRequested;
             
             if (job.Progress == 0 && progress_bounce_id > 0) {
                 return;

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTileHost.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTileHost.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTileHost.cs	Wed Jun 25 21:17:57 2008
@@ -41,6 +41,7 @@
     {
         private AnimatedVBox box;
         private Dictionary<IUserJob, UserJobTile> job_tiles = new Dictionary<IUserJob, UserJobTile> ();
+        private Dictionary<IUserJob, DateTime> job_start_times = new Dictionary<IUserJob, DateTime> ();
         
         public UserJobTileHost () : base (0.0f, 0.0f, 1.0f, 1.0f)
         {
@@ -69,45 +70,66 @@
         }
 
         private void AddJob (IUserJob job)
-        {                    
-            if (job == null || job.IsFinished) {
-                return;
-            }
-            
-            if ((job.DelayShow && job.Progress < 0.33) || !job.DelayShow) {
-                UserJobTile tile = new UserJobTile (job);
-                job_tiles.Add (job, tile);
-                box.PackEnd (tile, Easing.QuadraticOut);
-                tile.Show ();
+        {                
+            lock (this) {    
+                if (job == null || job.IsFinished) {
+                    return;
+                }
+                
+                if ((job.DelayShow && job.Progress < 0.33) || !job.DelayShow) {
+                    UserJobTile tile = new UserJobTile (job);
+                    job_tiles.Add (job, tile);
+                    job_start_times.Add (job, DateTime.Now);
+                    box.PackEnd (tile, Easing.QuadraticOut);
+                    tile.Show ();
+                }
             }
         }
         
         private void OnJobAdded (object o, UserJobEventArgs args)
         {
             ThreadAssist.ProxyToMain (delegate {
-                lock (this) {
-                    if (args.Job.DelayShow) {
-                        // Give the Job 1 second to become more than 33% complete
-                        Banshee.ServiceStack.Application.RunTimeout (1000, delegate {
-                            AddJob (args.Job);
-                            return false;
-                        });
-                    } else {
+                if (args.Job.DelayShow) {
+                    // Give the Job 1 second to become more than 33% complete
+                    Banshee.ServiceStack.Application.RunTimeout (1000, delegate {
                         AddJob (args.Job);
-                    }
+                        return false;
+                    });
+                } else {
+                    AddJob (args.Job);
                 }
             });
         }
         
+        private void RemoveJob (IUserJob job)
+        {
+            lock (this) {
+                if (job_tiles.ContainsKey (job)) {
+                    UserJobTile tile = job_tiles[job];
+                    box.Remove (tile);
+                    job_tiles.Remove (job);
+                    job_start_times.Remove (job);
+                }
+            }
+        }
+        
         private void OnJobRemoved (object o, UserJobEventArgs args)
         {
             ThreadAssist.ProxyToMain (delegate {
                 lock (this) {
-                    if (job_tiles.ContainsKey (args.Job)) {
-                        UserJobTile tile = job_tiles[args.Job];
-                        box.Remove (tile);
-                        job_tiles.Remove (args.Job);
+                    if (job_start_times.ContainsKey (args.Job)) {
+                        double ms_since_added = (DateTime.Now - job_start_times[args.Job]).TotalMilliseconds;
+                        if (ms_since_added < 1000) {
+                            // To avoid user jobs flasing up and out, don't let any job be visible for less than 1 second
+                            Banshee.ServiceStack.Application.RunTimeout ((uint) (1000 - ms_since_added), delegate {
+                                RemoveJob (args.Job);
+                                return false;
+                            });
+                            return;
+                        }
                     }
+                    
+                    RemoveJob (args.Job);
                 }
             });
         }

Modified: trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapPlaylistSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapPlaylistSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapPlaylistSource.cs	Wed Jun 25 21:17:57 2008
@@ -60,17 +60,23 @@
             this.parent = parent;
             Save ();
             
+            int count = 0;
             if (playlist.Tracks.Count > 0) {
                 //IList<DAAP.Track> tracks = playlist.Tracks;
                 int [] external_ids = new int [playlist.Tracks.Count];
                 int i = 0;
                 foreach (DAAP.Track track in playlist.Tracks) {
                     external_ids[i++] = track == null ? -1 : track.Id;
+                    if (track != null) {
+                        count++;
+                    }
                 }
 
-                ServiceManager.DbConnection.Execute (insert_track_command, DbId, parent.DbId, external_ids);
+                if (count > 0) {
+                    ServiceManager.DbConnection.Execute (insert_track_command, DbId, parent.DbId, external_ids);
+                }
             }
-            SavedCount = playlist.Tracks.Count;
+            SavedCount = count;
             OnUpdated ();
         }
         

Modified: trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapSource.cs	Wed Jun 25 21:17:57 2008
@@ -78,6 +78,16 @@
             }
         }
         
+        public override void CopyTrackTo (DatabaseTrackInfo track, SafeUri uri, BatchUserJob job)
+        {
+            if (track.PrimarySource == this && track.Uri.Scheme.StartsWith ("http")) {
+                foreach (double percent in database.DownloadTrack ((int)track.ExternalId, track.MimeType, uri.AbsolutePath)) {
+                    job.DetailedProgress = percent;
+                }
+            }
+        }
+
+        
         public override void Activate ()
         {
             if (client != null || is_activating) {
@@ -244,7 +254,11 @@
         {
             foreach (DAAP.Playlist pl in database.Playlists) {
                 DaapPlaylistSource source = new DaapPlaylistSource (pl, this);
-                AddChildSource (source);
+                if (source.Count == 0) {
+                    source.Unmap ();
+                } else {
+                    AddChildSource (source);
+                }
             }
         }
         
@@ -289,12 +303,7 @@
         
         public void Import ()
         {
-            Log.Debug ("Starting import...");
-            DateTime start = DateTime.Now;
-            ServiceManager.SourceManager.MusicLibrary.AddAllTracks (this);
-            DateTime finish = DateTime.Now;
-            TimeSpan time = finish - start;
-            Log.DebugFormat ("Import completed. Took {0} seconds.", time.TotalSeconds);
+            ServiceManager.SourceManager.MusicLibrary.MergeSourceInput (this, SourceMergeType.All);
         }
         
         public bool CanImport {

Modified: trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapTrackInfo.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapTrackInfo.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Daap/Banshee.Daap/DaapTrackInfo.cs	Wed Jun 25 21:17:57 2008
@@ -53,7 +53,7 @@
             TrackNumber = track.TrackNumber;
             Year = track.Year;
             Duration = track.Duration;
-            MimeType = String.Format ("taglib/{0}", track.Format);
+            MimeType = track.Format;
             ExternalId = track.Id;
             
             PrimarySource = source;

Modified: trunk/banshee/src/Extensions/Banshee.Daap/Daap/Database.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Daap/Daap/Database.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Daap/Daap/Database.cs	Wed Jun 25 21:17:57 2008
@@ -396,34 +396,51 @@
             RefreshPlaylists (revquery);
         }
 
-        private HttpWebResponse FetchTrack (Track track, long offset) {
-            return client.Fetcher.FetchFile (String.Format ("/databases/{0}/items/{1}.{2}", id, track.Id, track.Format),
-                                             offset);
+        private HttpWebResponse FetchTrack (int track_id, string format, long offset)
+        {
+            return client.Fetcher.FetchFile (String.Format ("/databases/{0}/items/{1}.{2}", id, track_id, format), offset);
         }
 
-        public Stream StreamTrack (Track track, out long length) {
+        public Stream StreamTrack (Track track, out long length)
+        {
             return StreamTrack (track, -1, out length);
         }
         
-        public Stream StreamTrack (Track track, long offset, out long length) {
-            HttpWebResponse response = FetchTrack (track, offset);
+        public Stream StreamTrack (Track track, long offset, out long length)
+        {
+            return StreamTrack (track.Id, track.Format, offset, out length);
+        }
+        
+        public Stream StreamTrack (int track_id, string track_format, out long length)
+        {
+            return StreamTrack (track_id, track_format, -1, out length);
+        }
+        
+        public Stream StreamTrack (int track_id, string track_format, long offset, out long length) 
+        {
+            HttpWebResponse response = FetchTrack (track_id, track_format, offset);
             length = response.ContentLength;
             return response.GetResponseStream ();
         }
 
-        public void DownloadTrack (Track track, string dest) {
-
+        public IEnumerable<double> DownloadTrack (int track_id, string track_format, string dest) {
             BinaryWriter writer = new BinaryWriter (File.Open (dest, FileMode.Create));
 
             try {
-                long len;
-                using (BinaryReader reader = new BinaryReader (StreamTrack (track, out len))) {
+                long len, pos = 0, i = 0;
+                using (BinaryReader reader = new BinaryReader (StreamTrack (track_id, track_format, out len))) {
                     int count = 0;
-                    byte[] buf = new byte[ChunkLength];
+                    byte [] buf = new byte[ChunkLength];
                     
                     do {
                         count = reader.Read (buf, 0, ChunkLength);
+                        pos += count;
                         writer.Write (buf, 0, count);
+                        
+                        // Roughly every 40KB yield an updated percent-done double
+                        if (i++ % 5 == 0) {
+                            yield return (double)pos / (double)len;
+                        }
                     } while (count != 0);
                 }
             } finally {

Modified: trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs	Wed Jun 25 21:17:57 2008
@@ -197,6 +197,10 @@
                         editor.ErrorMessage = Catalog.GetString ("Please provide a valid station URI");
                     }
                     
+                    if (!String.IsNullOrEmpty (editor.StationCreator)) {
+                        track.ArtistName = editor.StationCreator;
+                    }    
+                    
                     track.Comment = editor.Description;
                     
                     if (!String.IsNullOrEmpty (editor.Genre)) {
@@ -208,6 +212,7 @@
                     
                     if (!String.IsNullOrEmpty (editor.StationTitle)) {
                         track.TrackTitle = editor.StationTitle;
+                        track.AlbumTitle = editor.StationTitle;
                     } else {
                         destroy = false;
                         editor.ErrorMessage = Catalog.GetString ("Please provide a station title");

Modified: trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/StationEditor.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/StationEditor.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/StationEditor.cs	Wed Jun 25 21:17:57 2008
@@ -43,12 +43,15 @@
         private Entry name_entry;
         private Entry description_entry;
         private Entry stream_entry;
+        private Entry creator_entry;
         private ComboBoxEntry genre_entry;
         private RatingEntry rating_entry;
         private Alignment error_container;
         private Label error;
         private DatabaseTrackInfo track;
         
+        Table table;
+        
         public StationEditor (DatabaseTrackInfo track) : base()
         {
             AccelGroup accel_group = new AccelGroup ();
@@ -96,19 +99,13 @@
             message.Wrap = true;
             message.Show ();
             
-            Table table = new Table (5, 2, false);
+            table = new Table (5, 2, false);
             table.RowSpacing = 6;
             table.ColumnSpacing = 6;
-            
-            Label label = new Label (Catalog.GetString ("Station Genre:"));
-            label.Xalign = 0.0f;
-            
+                        
             genre_entry = ComboBoxEntry.NewText ();
-            
-            System.Data.IDataReader reader = ServiceManager.DbConnection.Query (
-                "SELECT DISTINCT Genre FROM CoreTracks ORDER BY Genre");
-            while (reader != null && reader.Read ()) {
-                string genre = reader[0] as string;
+
+            foreach (string genre in ServiceManager.DbConnection.QueryEnumerable<string> ("SELECT DISTINCT Genre FROM CoreTracks ORDER BY Genre")) {
                 if (!String.IsNullOrEmpty (genre)) {
                     genre_entry.AppendText (genre);
                 }
@@ -118,42 +115,17 @@
                 genre_entry.Entry.Text = track.Genre;
             }
             
-            table.Attach (label, 0, 1, 0, 1, AttachOptions.Fill, AttachOptions.Fill | AttachOptions.Expand, 0, 0);
-            table.Attach (genre_entry, 1, 2, 0, 1, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Shrink, 0, 0);
-            
-            label = new Label (Catalog.GetString ("Station Title:"));
-            label.Xalign = 0.0f;
-            
-            name_entry = new Entry ();
-            
-            table.Attach (label, 0, 1, 1, 2, AttachOptions.Fill, AttachOptions.Fill | AttachOptions.Expand, 0, 0);
-            table.Attach (name_entry, 1, 2, 1, 2, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Shrink, 0, 0);
-
-            label = new Label (Catalog.GetString ("Stream URL:"));
-            label.Xalign = 0.0f;
-            
-            stream_entry = new Entry ();
-            
-            table.Attach (label, 0, 1, 2, 3, AttachOptions.Fill, AttachOptions.Fill | AttachOptions.Expand, 0, 0);
-            table.Attach (stream_entry, 1, 2, 2, 3, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Shrink, 0, 0);
-            
-            label = new Label (Catalog.GetString ("Description:"));
-            label.Xalign = 0.0f;
-            
-            description_entry = new Entry ();
-            
-            table.Attach (label, 0, 1, 3, 4, AttachOptions.Fill, AttachOptions.Fill | AttachOptions.Expand, 0, 0);
-            table.Attach (description_entry, 1, 2, 3, 4, AttachOptions.Expand | AttachOptions.Fill, AttachOptions.Shrink, 0, 0);
+            AddRow (Catalog.GetString ("Station Genre:"), genre_entry);
             
-            label = new Label (Catalog.GetString ("Rating:"));
-            label.Xalign = 0.0f;
+            name_entry        = AddEntryRow (Catalog.GetString ("Station Name:"));
+            stream_entry      = AddEntryRow (Catalog.GetString ("Stream URL:"));
+            creator_entry     = AddEntryRow (Catalog.GetString ("Station Creator:"));
+            description_entry = AddEntryRow (Catalog.GetString ("Description:"));
             
             rating_entry = new RatingEntry ();
-            
-            table.Attach (label, 0, 1, 4, 5, AttachOptions.Fill, AttachOptions.Fill | AttachOptions.Expand, 0, 0);
             HBox rating_box = new HBox ();
             rating_box.PackStart (rating_entry, false, false, 0);
-            table.Attach (rating_box, 1, 2, 4, 5, AttachOptions.Expand | AttachOptions.Fill, AttachOptions.Shrink, 0, 0);
+            AddRow (Catalog.GetString ("Rating:"), rating_box);
             
             table.ShowAll ();
             
@@ -202,6 +174,10 @@
                     description_entry.Text = track.Comment;
                 }
                 
+                if (!String.IsNullOrEmpty (track.ArtistName)) {
+                    creator_entry.Text = track.ArtistName;
+                }
+                
                 rating_entry.Value = track.Rating;
             }
             
@@ -234,6 +210,24 @@
             OnFieldsChanged (this, EventArgs.Empty);
         }
         
+        private Entry AddEntryRow (string title)
+        {
+            Entry entry = new Entry ();
+            AddRow (title, entry);
+            return entry;
+        }
+        
+        private uint row = 0;
+        private void AddRow (string title, Widget entry)
+        {
+            Label label = new Label (title);
+            label.Xalign = 0.0f;
+            
+            table.Attach (label, 0, 1, row, row + 1, AttachOptions.Fill, AttachOptions.Fill | AttachOptions.Expand, 0, 0);
+            table.Attach (entry, 1, 2, row, row + 1, AttachOptions.Fill | AttachOptions.Expand, AttachOptions.Shrink, 0, 0);
+            row++;
+        }
+        
         private void OnFieldsChanged (object o, EventArgs args)
         {
             save_button.Sensitive = genre_entry.Entry.Text.Trim ().Length > 0 && 
@@ -254,6 +248,10 @@
             get { return genre_entry.Entry.Text.Trim (); }
         }
         
+        public string StationCreator {
+            get { return creator_entry.Text.Trim (); }
+        }
+        
         public string StationTitle {
             get { return name_entry.Text.Trim (); }
         }



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