Re: [Banshee-List] Re: Mass Storage Support
- From: "James Stembridge" <jstembridge gmail com>
- To: banshee-list gnome org
- Subject: Re: [Banshee-List] Re: Mass Storage Support
- Date: Tue, 18 Apr 2006 13:11:09 +0100
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]