Re: [Banshee-List] Banshee and the Nokia 770



Hi Andrew,

On 5/20/06, Andrew Barr <andrew james barr gmail com> wrote:
I'm trying to use the Banshee.Dap.MassStorage plugin with my Nokia 770,
and to that end I've upgraded my installation to Banshee CVS and added
the following FDI entry for my Nokia 770 to HAL:

You might like to try the attached patch (against current CVS) too.

Regards,
James.
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/banshee/ChangeLog,v
retrieving revision 1.469
diff -u -r1.469 ChangeLog
--- ChangeLog	19 May 2006 03:47:17 -0000	1.469
+++ ChangeLog	22 May 2006 16:47:11 -0000
@@ -1,3 +1,37 @@
+2006-05-22  James Stembridge  <jstembridge gmail com>
+
+	* src/Banshee.Base/Dap/DapCore.cs: Include all volumes when performing
+	initial scan for DAP devices, not just those that are children of
+	devices tagged by hal with portable_audio_player
+	
+	* 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; don't initialize Gnome VFS if it's 
+	already been initialized; accept devices with a file .is_audio_player
+	in the root
+
+2006-05-22  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-05-18  Aaron Bockover  <aaron abock org>
 
 	* src/BansheeImport.cs: a new standalone program that can import music
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	22 May 2006 16:47:12 -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	22 May 2006 16:47: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.Base/Dap/DapCore.cs
===================================================================
RCS file: /cvs/gnome/banshee/src/Banshee.Base/Dap/DapCore.cs,v
retrieving revision 1.12
diff -u -r1.12 DapCore.cs
--- src/Banshee.Base/Dap/DapCore.cs	1 May 2006 17:32:00 -0000	1.12
+++ src/Banshee.Base/Dap/DapCore.cs	22 May 2006 16:47:13 -0000
@@ -149,23 +149,17 @@
         
         private static void BuildDeviceTable()
         {
+            // All volume devices, should cover all storage based players
             foreach(Device device in Device.FindByStringMatch(HalCore.Context, 
+                "info.category", "volume")) {
+                AddDevice(device);
+            }
+             
+            // None storage based players
+            foreach(Device device in Device.FindByStringMatch(HalCore.Context,
                 "info.category", "portable_audio_player")) {
-                // Find the actual storage device that is mountable;
-                // this should probably just be possible by accessing
-                // portable_audio_player.storage_device, but for me
-                // as of HAL 0.5.6, this property just points to its own UDI
-                if(device["portable_audio_player.access_method"] == "storage" &&
-                    !device.GetPropertyBool("block.is_volume")) {
-                    foreach(Device storage_device in Hal.Device.FindByStringMatch(device.Context, 
-                        "info.parent", device.Udi)) {
-                        if(AddDevice(storage_device) && device_waiting_table[storage_device.Udi] == null) {
-                            break;
-                        }
-                    }
-                } else {
+                if(device["portable_audio_player.access_method"] != "storage")
                     AddDevice(device);
-                }
             }
         }
         
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	22 May 2006 16:47:13 -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,46 +65,60 @@
                 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;
-            }
-
-            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") ||
-                !volume_device.PropertyExists("block.device")) {
                 return InitializeResult.Invalid;
             }
 
-            if(!volume_device.PropertyExists ("volume.is_mounted") ||
-                    !volume_device.GetPropertyBool("volume.is_mounted"))
+            if (!volume_device.PropertyExists("block.device"))
+                return InitializeResult.Invalid;
+            
+            if (!volume_device.PropertyExists ("volume.is_mounted") ||
+                !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;
-                }
-            }
-
-            if (volume == null)
+            // Detect player via HAL property or presence of .is_audo_player in root            
+            if (player_device["portable_audio_player.access_method"] != "storage" &&
+                !File.Exists(Path.Combine(MountPoint, ".is_audio_player"))) {                
                 return InitializeResult.Invalid;
-
-            is_read_only = volume.IsReadOnly;
+            }
+	    
+            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;
+            }
 
             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 +161,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 +180,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 +255,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 +281,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 +304,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	22 May 2006 16:47:14 -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-05-16 18:49:49.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]