Re: [Banshee-List] Re: Mass Storage Support



Another update, duplicate detection done via tags rather than filename
as it's more reliable.

Cheers,
James.
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/banshee/ChangeLog,v
retrieving revision 1.452
diff -u -r1.452 ChangeLog
--- ChangeLog	18 Apr 2006 03:25:33 -0000	1.452
+++ ChangeLog	18 Apr 2006 12:07:10 -0000
@@ -1,3 +1,31 @@
+2006-04-17  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 
+	Name if it's blank.  If the volume for a new device isn't yet available 
+	from VFS, wait for it.
+
+2006-04-17  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.  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-17  Aaron Bockover  <aaron abock org>
 
 	* banshee.mdp: Updated MonoDevelop project
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	18 Apr 2006 12:07:11 -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	18 Apr 2006 12:07:12 -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	18 Apr 2006 12:07:12 -0000
@@ -63,7 +63,7 @@
                 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") ||
@@ -78,31 +78,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 +158,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 +177,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 +252,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 +278,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 +301,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	18 Apr 2006 12:07:13 -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-03 18:40:38.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]