[Banshee-List] Updated mass storage patch



Couple more minor changes. I've added to check to see if Gnome VFS has
already been initialized before initializing it, and (more
importantly) tried to prevent the mass storage plugin accepting
non-generic devices like iPods.

Cheers,
James.
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/banshee/ChangeLog,v
retrieving revision 1.453
diff -u -r1.453 ChangeLog
--- ChangeLog	20 Apr 2006 18:05:14 -0000	1.453
+++ ChangeLog	28 Apr 2006 18:29:04 -0000
@@ -1,3 +1,33 @@
+2006-04-28  James Stembridge  <jstembridge gmail com>
+
+	* src/Banshee.Dap/MassStorage/MassStorageDap.cs: Check for duplicates 
+	when adding a song to the device; don't use the volumel label for the 
+	device name if it's blank; if the volume for a new device isn't yet 
+	available from VFS, wait for it; check that the device is a generic 
+	audio player and not something with a defined access method such as 
+	an iPod; don't initialize Gnome VFS if it's already been initialized
+
+2006-04-28  Gabriel Burt  <gabriel burt gmail com>
+
+	* src/Banshee.Base/Makefile.am: Add QueuedOperationManager.cs
+
+	* src/Banshee.Base/QueuedOperationManager.cs: New file, allows for generic
+	asynchronous events like copying files to a USB DAP, etc.
+
+	* src/Banshee.Base/Dap/Dap.cs: Add EmitTrackAdded method and allow
+	sub-classes to override AddTrack
+
+	* src/Banshee.Dap/MassStorage/MassStorageDap.cs: Fix the bug where the DAP
+	couldn't be plugged in when Banshee started; handle copying files to the
+	device asynchronously using a QueuedOperationManager that pops up a
+	ActiveUserEvent if the transfer is slow; escape artist/album/title when
+	creating the destination filename and catch exceptions in the copy
+
+	* src/Banshee.Widets/ActiveUserEvent.cs: Add an option for having the user
+	event not register itself (and therefore not show itself) until at least a
+	second has passed, at which time if the operation is less than 33% done,
+	it is registered and shown (and otherwise it is not)
+
 2006-04-20  Aaron Bockover  <aaron abock org>
 
 	* src/Banshee.MediaEngine/Helix/HelixRemotePlayer.cs: Added GetIsLive
Index: src/Banshee.Base/Makefile.am
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Base/Makefile.am,v
retrieving revision 1.29
diff -u -r1.29 Makefile.am
--- src/Banshee.Base/Makefile.am	18 Apr 2006 03:25:40 -0000	1.29
+++ src/Banshee.Base/Makefile.am	28 Apr 2006 18:29:05 -0000
@@ -100,6 +100,7 @@
 	$(srcdir)/LibraryTrackInfo.cs \
 	$(srcdir)/FileTrackInfo.cs \
 	$(srcdir)/DBusRemote.cs \
+	$(srcdir)/QueuedOperationManager.cs \
 	$(srcdir)/QueuedSqliteDatabase.cs \
 	$(srcdir)/AudioCdRipper.cs \
 	$(srcdir)/IImportSource.cs \
Index: src/Banshee.Base/Dap/Dap.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Base/Dap/Dap.cs,v
retrieving revision 1.16
diff -u -r1.16 Dap.cs
--- src/Banshee.Base/Dap/Dap.cs	17 Apr 2006 17:44:10 -0000	1.16
+++ src/Banshee.Base/Dap/Dap.cs	28 Apr 2006 18:29:06 -0000
@@ -123,7 +123,7 @@
         
         private uint uid;
         private PropertyTable properties = new PropertyTable();
-        private ArrayList tracks = new ArrayList(); 
+        protected ArrayList tracks = new ArrayList(); 
         private ActiveUserEvent save_report_event;
         private bool is_syncing = false;
         private bool can_cancel_save = true;
@@ -188,7 +188,7 @@
             OnPropertiesChanged();
         }
         
-        public void AddTrack(TrackInfo track)
+        public virtual void AddTrack(TrackInfo track)
         {
             TrackInfo dap_track = OnTrackAdded(track);
             
@@ -537,6 +537,12 @@
             
             return new SafeUri(dir + Path.DirectorySeparatorChar 
                 + ".banshee-dap-" + file + "." + newext);
+        }
+
+        protected void EmitTrackAdded (TrackInfo track)
+        {
+            if(TrackAdded != null)
+                TrackAdded(this, new DapTrackListUpdatedArgs(track));
         }
          
         public virtual Gdk.Pixbuf GetIcon(int size)
Index: src/Banshee.Dap/MassStorage/MassStorageDap.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Dap/MassStorage/MassStorageDap.cs,v
retrieving revision 1.6
diff -u -r1.6 MassStorageDap.cs
--- src/Banshee.Dap/MassStorage/MassStorageDap.cs	9 Apr 2006 21:30:26 -0000	1.6
+++ src/Banshee.Dap/MassStorage/MassStorageDap.cs	28 Apr 2006 18:29:06 -0000
@@ -45,7 +45,9 @@
         private static Gnome.Vfs.VolumeMonitor monitor;
 
         static MassStorageDap () {
-            Gnome.Vfs.Vfs.Initialize ();
+            if (!Gnome.Vfs.Vfs.Initialized)
+                Gnome.Vfs.Vfs.Initialize ();
+
             monitor = Gnome.Vfs.VolumeMonitor.Get ();
         }
 
@@ -63,13 +65,12 @@
                 player_device = Hal.Device.UdisToDevices (volume_device.Context, new string [] {volume_device ["info.parent"]}) [0];
                 usb_device = Hal.Device.UdisToDevices (player_device.Context, new string [] {player_device ["storage.physical_device"]}) [0];
             } catch (Exception e) {
-                    return InitializeResult.Invalid;
+                return InitializeResult.Invalid;
             }
 
             if (!player_device.PropertyExists ("portable_audio_player.access_method") ||
                 player_device ["portable_audio_player.access_method"] != "storage" ||
-                !usb_device.PropertyExists("usb.vendor_id") ||
-                !usb_device.PropertyExists("usb.product_id") ||
+                player_device ["portable_audio_player.type"] != "generic" ||
                 !volume_device.PropertyExists("block.device")) {
                 return InitializeResult.Invalid;
             }
@@ -78,31 +79,44 @@
                     !volume_device.GetPropertyBool("volume.is_mounted"))
                 return InitializeResult.WaitForPropertyChange;
 	    
-
-            string block_device = volume_device ["block_device"];
-            foreach (Gnome.Vfs.Volume vol in monitor.MountedVolumes) {
-                if (vol.DevicePath == block_device) {
-                    this.volume = vol;
-                    break;
-                }
+            volume = monitor.GetVolumeForPath(MountPoint);
+            if(volume == null) {
+                // Gnome VFS doesn't know volume is mounted yet
+                monitor.VolumeMounted += OnVolumeMounted;
+                is_read_only = true;
+            } else {
+                is_read_only = volume.IsReadOnly;
             }
 
-            if (volume == null)
-                return InitializeResult.Invalid;
-
-            is_read_only = volume.IsReadOnly;
-
             base.Initialize (usb_device);
  
             InstallProperty("Vendor", usb_device["usb.vendor"]);
 
-            ReloadDatabase();
+            if(!Globals.UIManager.IsInitialized) {
+                Globals.UIManager.Initialized += OnUIManagerInitialized;
+            } else {
+                ReloadDatabase();
+            }
             
             // FIXME probably should be able to cancel at some point when you can actually sync
             CanCancelSave = false;
             return InitializeResult.Valid;
         }
 
+        private void OnVolumeMounted(object o, Gnome.Vfs.VolumeMountedArgs args) {
+            if(args.Volume.DevicePath == volume_device["block.device"]) {
+                monitor.VolumeMounted -= OnVolumeMounted;
+            
+                volume = args.Volume;
+                is_read_only = volume.IsReadOnly;
+            }
+        }
+
+        private void OnUIManagerInitialized(object o, EventArgs args)
+        {
+            ReloadDatabase ();
+        }
+ 
         public override void Dispose()
         {
             // FIXME anything else to do here?
@@ -145,7 +159,8 @@
 
         public override void Eject ()
         {
-            volume.Unmount (UnmountCallback);
+            if(volume != null)
+                volume.Unmount (UnmountCallback);
         }
 
         private void UnmountCallback (bool succeeded, string error, string detailed_error)
@@ -163,17 +178,42 @@
             else
                 Console.WriteLine ("Failed to eject.  {1} {2}", error, detailed_error);
         }
+
+        public override void AddTrack(TrackInfo track)
+        {
+            if (track == null || TrackExistsInList(track, tracks) || IsReadOnly)
+                return;
+
+            // If we're "adding" it when it's already on the device, then
+            // we don't need to copy it
+            if (track is MassStorageTrackInfo) {
+                tracks.Add(track);
+                EmitTrackAdded(track);
+            } else {
+                Copier.Enqueue (track);
+            }
+        }
         
-        protected override TrackInfo OnTrackAdded(TrackInfo track)
+        private void HandleCopyRequested (object o, QueuedOperationArgs args)
         {
-            if (track is MassStorageTrackInfo || IsReadOnly)
-                return track;
+            TrackInfo track = args.Object as TrackInfo;
 
-            string new_path = GetTrackPath (track);
-            Directory.CreateDirectory (Path.GetDirectoryName (new_path));
-            File.Copy (track.Uri.LocalPath, new_path);
+            if (track == null)
+                return;
+            
+            try {
+                string new_path = GetTrackPath (track);
+                Directory.CreateDirectory (Path.GetDirectoryName (new_path));
+                File.Copy (track.Uri.LocalPath, new_path);
+
+                TrackInfo new_track = new MassStorageTrackInfo (new SafeUri (new_path));
+                tracks.Add(new_track);
+                EmitTrackAdded(track);
 
-            return new MassStorageTrackInfo (new SafeUri (new_path));
+                args.ReturnMessage = String.Format("{0} - {1}", track.Artist, track.Title);
+            } catch (Exception e) {
+                args.ReturnMessage = String.Format("Skipping Song", track.Artist, track.Title);
+            }
         }
         
         protected override void OnTrackRemoved(TrackInfo track)
@@ -213,19 +253,24 @@
         private string GetTrackPath (TrackInfo track)
         {
             string file_path = "";
+
+            string artist = FileNamePattern.Escape (track.Artist);
+            string album = FileNamePattern.Escape (track.Album);
+            string number_title = FileNamePattern.Escape (track.TrackNumberTitle);
+
             if (player_device.PropertyExists ("portable_audio_player.filepath_format")) {
                 file_path = player_device.GetPropertyString ("portable_audio_player.filepath_format");
-                file_path = file_path.Replace ("%Artist", track.Artist);
-                file_path = file_path.Replace ("%Album", track.Album);
+                file_path = file_path.Replace ("%Artist", artist);
+                file_path = file_path.Replace ("%Album", album);
 
                 if (file_path.IndexOf ("%Track") == -1) {
-                    file_path = System.IO.Path.Combine (file_path, track.TrackNumberTitle);
+                    file_path = System.IO.Path.Combine (file_path, number_title);
                 } else {
-                    file_path = file_path.Replace ("%Track", track.TrackNumberTitle);
+                    file_path = file_path.Replace ("%Track", number_title);
                 }
             } else {
-                file_path = System.IO.Path.Combine (track.Artist, track.Album);
-                file_path = System.IO.Path.Combine (file_path, track.TrackNumberTitle);
+                file_path = System.IO.Path.Combine (artist, album);
+                file_path = System.IO.Path.Combine (file_path, number_title);
             }
 
             file_path += Path.GetExtension (track.Uri.LocalPath);
@@ -234,6 +279,21 @@
             return System.IO.Path.Combine (MountPoint, file_path);
         }
 
+        private QueuedOperationManager copier;
+        public QueuedOperationManager Copier {
+            get {
+                if (copier == null) {
+                    copier = new QueuedOperationManager ();
+                    copier.ActionMessage = Catalog.GetString ("Copying Songs");
+                    copier.ProgressMessage = Catalog.GetString ("Copying {0} of {1}");
+                    copier.OperationRequested += HandleCopyRequested;
+                }
+
+                return copier;
+            }
+            set { copier = value; }
+        }
+
         public virtual string IconId {
             get {
                 return null;
@@ -242,7 +302,8 @@
  
         public override string Name {
             get {
-                if (volume_device.PropertyExists("volume.label"))
+                if (volume_device.PropertyExists("volume.label") &&
+                    volume_device["volume.label"].Length > 0)
                     return volume_device["volume.label"];
 
                 if (player_device.PropertyExists("info.product"))
Index: src/Banshee.Widgets/ActiveUserEvent.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Widgets/ActiveUserEvent.cs,v
retrieving revision 1.7
diff -u -r1.7 ActiveUserEvent.cs
--- src/Banshee.Widgets/ActiveUserEvent.cs	13 Feb 2006 22:18:14 -0000	1.7
+++ src/Banshee.Widgets/ActiveUserEvent.cs	28 Apr 2006 18:29:07 -0000
@@ -49,6 +49,7 @@
         private string header;
         
         private uint timeout_id = 0;
+        private uint slow_timeout_id = 0;
         private bool disposed = false;
         
         public event EventHandler Disposed;
@@ -56,8 +57,10 @@
         
         private bool cancel_requested;
         private bool can_cancel;
-     
-        public ActiveUserEvent(string name) 
+
+        public ActiveUserEvent(string name) : this (name, false) {}
+
+        public ActiveUserEvent(string name, bool delay_show)
         {
             tips = new Tooltips();
             
@@ -117,7 +120,11 @@
             
             table.ShowAll();
             
-            ActiveUserEventsManager.Instance.Register(this);
+            Console.WriteLine ("Delay show = {0}", delay_show);
+            if (delay_show)
+                slow_timeout_id = GLib.Timeout.Add(1000, OnCheckForDisplay);
+            else
+                ActiveUserEventsManager.Instance.Register(this);
         }
         
         public void Cancel()
@@ -138,10 +145,34 @@
                 GLib.Source.Remove(timeout_id);
                 timeout_id = 0;
             }
+
+            if(slow_timeout_id > 0) {
+                GLib.Source.Remove(slow_timeout_id);
+                slow_timeout_id = 0;
+            }
             
             if(Disposed != null) {
                 Disposed(this, new EventArgs());
             }
+        }
+
+        private bool OnCheckForDisplay()
+        {
+            Console.WriteLine ("OnCheckForDisplay called..");
+            if (disposed)
+                return false;
+
+            Console.WriteLine ("...and not disposed..");
+
+            // If the event has not made enough progress, show this event
+            if (Progress < 0.33) {
+                Console.WriteLine ("Didn't make enough progress, showing action event");
+                ActiveUserEventsManager.Instance.Register(this);
+            } else {
+                Console.WriteLine ("Made enough progress, not showing action event");
+            }
+
+            return false;
         }
         
         private bool OnTimeout()
--- /dev/null	2006-04-24 16:37:02.000000000 +0100
+++ src/Banshee.Base/QueuedOperationManager.cs	2006-04-17 20:03:08.000000000 +0100
@@ -0,0 +1,196 @@
+/***************************************************************************
+ *  QueuedOperationManager.cs
+ *
+ *  Copyright (C) 2006 Novell, Inc.
+ *  Written by Aaron Bockover <aaron abock org>
+ ****************************************************************************/
+
+/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
+ *
+ *  Permission is hereby granted, free of charge, to any person obtaining a
+ *  copy of this software and associated documentation files (the "Software"),  
+ *  to deal in the Software without restriction, including without limitation  
+ *  the rights to use, copy, modify, merge, publish, distribute, sublicense,  
+ *  and/or sell copies of the Software, and to permit persons to whom the  
+ *  Software is furnished to do so, subject to the following conditions:
+ *
+ *  The above copyright notice and this permission notice shall be included in 
+ *  all copies or substantial portions of the Software.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ *  DEALINGS IN THE SOFTWARE.
+ */
+ 
+using System;
+using System.Collections;
+using System.IO;
+using System.Threading;
+using Mono.Unix;
+using Mono.Unix.Native;
+using Gtk;
+
+using Banshee.Widgets;
+
+namespace Banshee.Base
+{
+    public delegate void QueuedOperationHandler(object o, QueuedOperationArgs args);
+    
+    public class QueuedOperationArgs : EventArgs
+    {
+        public object Object;
+        public string ReturnMessage;
+    }
+
+    public class QueuedOperationManager
+    {
+        private class OperationCanceledException : ApplicationException
+        {
+        }
+        
+        private Queue object_queue;
+        private int total_count;
+        private int processed_count;
+        private bool processing_queue = false;
+
+        private ActiveUserEvent user_event;
+        public ActiveUserEvent UserEvent {
+            get {
+                CreateUserEvent();
+                return user_event;
+            }
+        }
+
+        public event QueuedOperationHandler OperationRequested;
+        
+        public QueuedOperationManager()
+        {
+            object_queue = new Queue();
+        }
+        
+        private void CreateUserEvent()
+        {
+            if(user_event == null) {
+                user_event = new ActiveUserEvent(ActionMessage, true);
+                user_event.Icon = IconThemeUtils.LoadIcon(22, "system-search", Stock.Find);
+                lock(user_event) {
+                    total_count = 0;
+                    processed_count = 0;
+                }
+            }
+        }
+        
+        private void DestroyUserEvent()
+        {
+            if(user_event != null) {
+                lock(user_event) {
+                    user_event.Dispose();
+                    user_event = null;
+                    total_count = 0;
+                    processed_count = 0;
+                }
+            }
+        }
+        
+        private void UpdateCount(string message)
+        {
+            CreateUserEvent();
+            processed_count++;
+
+            double new_progress = (double)processed_count / (double)total_count;
+            double old_progress = user_event.Progress;
+            
+            if(new_progress >= 0.0 && new_progress <= 1.0 && Math.Abs(new_progress - old_progress) > 0.001) {
+                string disp_progress = String.Format(ProgressMessage, processed_count, total_count);
+                
+                user_event.Header = disp_progress;
+                user_event.Message = message;
+                user_event.Progress = new_progress;
+            }
+        }
+        
+        private void CheckForCanceled()
+        {
+            if(user_event != null && user_event.IsCancelRequested) {
+                throw new OperationCanceledException();  
+            }
+        }
+        
+        private void FinalizeOperation()
+        {
+            object_queue.Clear();
+            processing_queue = false;
+            DestroyUserEvent();
+        }
+        
+        public void Enqueue(object obj)
+        {
+            CreateUserEvent();
+            ThreadAssist.Spawn(delegate {
+                try {
+                    lock(object_queue.SyncRoot) {
+                        if(object_queue.Contains(obj)) {
+                            return;
+                        }
+                        
+                        total_count++;
+                        object_queue.Enqueue(obj);
+                    }
+
+                    ProcessQueue();
+                } catch(OperationCanceledException) {
+                    FinalizeOperation();
+                }
+            });
+        }
+        
+        private void ProcessQueue()
+        {
+            lock (object_queue.SyncRoot) {
+                if(processing_queue)
+                    return;
+                else
+                    processing_queue = true;
+            }
+
+            CreateUserEvent();
+            
+            while(object_queue.Count > 0) {
+                CheckForCanceled();
+                
+                object obj = object_queue.Dequeue();
+                    
+                QueuedOperationHandler handler = OperationRequested;
+                if(handler != null && obj != null) {
+                    QueuedOperationArgs args = new QueuedOperationArgs();
+                    args.Object = obj;
+                    handler(this, args);
+                    UpdateCount(args.ReturnMessage);
+                } else {
+                    UpdateCount(null);
+                }
+            }
+
+            object_queue.Clear();
+            processing_queue = false;
+            
+            DestroyUserEvent();
+        }
+
+        private string action_message;
+        public string ActionMessage {
+            get { return action_message; }
+            set { action_message = value; }
+        }
+        private string progress_message;
+        public string ProgressMessage {
+            get { return progress_message; }
+            set { progress_message = value; }
+        }
+    }
+}
+


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