[rhythmbox] Use the new transfer queue for transfers to library and devices



commit 0c2876b4f85fec9bb4ef8d5c5105b069da48524f
Author: Jonathan Matthew <jonathan d14n org>
Date:   Sat May 22 20:31:14 2010 +1000

    Use the new transfer queue for transfers to library and devices
    
    Notable things here are that we don't have to figure out the extension
    for the destination file name, and we can cancel the rest of the batch
    if the destination file system runs out of space or is read-only.

 sources/rb-library-source.c         |  129 ++++++++++++++-----------
 sources/rb-removable-media-source.c |  178 ++++++++++++++++-------------------
 2 files changed, 152 insertions(+), 155 deletions(-)
---
diff --git a/sources/rb-library-source.c b/sources/rb-library-source.c
index 881c311..8960930 100644
--- a/sources/rb-library-source.c
+++ b/sources/rb-library-source.c
@@ -52,6 +52,8 @@
 #include <glib/gi18n.h>
 #include <glib-object.h>
 
+#include "rb-track-transfer-batch.h"
+#include "rb-track-transfer-queue.h"
 #include <profiles/gnome-media-profiles.h>
 #include <profiles/audio-profile-choose.h>
 
@@ -63,8 +65,8 @@
 #include "rb-util.h"
 #include "eel-gconf-extensions.h"
 #include "rb-library-source.h"
-#include "rb-removable-media-manager.h"
 #include "rb-auto-playlist-source.h"
+#include "rb-encoder.h"
 
 static void rb_library_source_class_init (RBLibrarySourceClass *klass);
 static void rb_library_source_init (RBLibrarySource *source);
@@ -1140,7 +1142,7 @@ rb_library_source_layout_filename_changed (GConfClient *client,
  * Stolen from Sound-Juicer
  */
 static char*
-build_filename (RBLibrarySource *source, RhythmDBEntry *entry)
+build_filename (RBLibrarySource *source, RhythmDBEntry *entry, const char *extension)
 {
 	GFile *library_location;
 	GFile *dir;
@@ -1149,19 +1151,16 @@ build_filename (RBLibrarySource *source, RhythmDBEntry *entry)
 	char *realpath;
 	char *filename;
 	char *string = NULL;
-	char *extension = NULL;
 	char *tmp;
 	GSList *list;
 	char *layout_path;
 	char *layout_filename;
-	char *preferred_format;
 
 	list = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
 	layout_path = eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH);
 	layout_filename = eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME);
-	preferred_format = eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT);
 
-	if (list == NULL || layout_path == NULL || layout_filename == NULL || preferred_format == NULL) {
+	if (list == NULL || layout_path == NULL || layout_filename == NULL) {
 		/* emit warning */
 		rb_debug ("Could not retrieve settings from GConf");
 		goto out;
@@ -1178,33 +1177,6 @@ build_filename (RBLibrarySource *source, RhythmDBEntry *entry)
 	g_object_unref (library_location);
 	g_free (realpath);
 
-	if (g_str_has_prefix (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MIMETYPE), "audio/x-raw")) {
-		GMAudioProfile *profile;
-		profile = gm_audio_profile_lookup (preferred_format);
-		if (profile)
-			extension = g_strdup (gm_audio_profile_get_extension (profile));
-	}
-
-	if (extension == NULL) {
-		const char *uri;
-		const char *loc;
-		char *tmp;
-
-		/* use the old extension. strip anything after a '?' for http/daap/etc */
-		uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
-		loc = g_utf8_strrchr (uri, -1, '.');
-		if (loc == NULL)
-			loc = g_utf8_strrchr (uri, -1, '/');
-		if (loc == NULL)
-			loc = uri;
-
-		extension = g_strdup (loc + 1);
-
-		tmp = g_utf8_strchr (extension, -1, '?');
-		if (tmp)
-			*tmp = '\0';
-	}
-
 	realfile = filepath_parse_pattern (source->priv->db, layout_filename, entry);
 	if (extension) {
 		filename = g_strdup_printf ("%s.%s", realfile, extension);
@@ -1215,7 +1187,6 @@ build_filename (RBLibrarySource *source, RhythmDBEntry *entry)
 
 	dest = g_file_resolve_relative_path (dir, filename);
 	g_object_unref (dir);
-	g_free (extension);
 	g_free (filename);
 
 	string = g_file_get_uri (dest);
@@ -1224,7 +1195,6 @@ build_filename (RBLibrarySource *source, RhythmDBEntry *entry)
 	rb_slist_deep_free (list);
 	g_free (layout_path);
 	g_free (layout_filename);
-	g_free (preferred_format);
 
 	return string;
 }
@@ -1254,17 +1224,62 @@ impl_can_paste (RBSource *asource)
 	return can_paste;
 }
 
+static char *
+get_dest_uri_cb (RBTrackTransferBatch *batch,
+		 RhythmDBEntry *entry,
+		 const char *mediatype,
+		 const char *extension,
+		 RBLibrarySource *source)
+{
+	char *dest;
+	char *sane_dest;
+
+	dest = build_filename (source, entry, extension);
+	if (dest == NULL) {
+		rb_debug ("could not create destination path for entry");
+		return NULL;
+	}
+
+	sane_dest = rb_sanitize_uri_for_filesystem (dest);
+	g_free (dest);
+	rb_debug ("destination URI for %s is %s",
+		  rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION),
+		  sane_dest);
+	return sane_dest;
+}
+
 static void
-completed_cb (RhythmDBEntry *entry, const char *dest, guint64 dest_size, GError *error, RBLibrarySource *source)
+track_done_cb (RBTrackTransferBatch *batch,
+	       RhythmDBEntry *entry,
+	       const char *dest,
+	       guint64 dest_size,
+	       const char *dest_mediatype,
+	       GError *error,
+	       RBLibrarySource *source)
 {
-	if (error == NULL) {
-		rhythmdb_add_uri (source->priv->db, dest);
-	} else {
-		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
+	if (error != NULL) {
+		/* probably want to cancel the batch on some errors:
+		 * - out of disk space / read only
+		 * - source has vanished (hmm, how would we know?)
+		 *
+		 * and we probably want to do something intelligent about some other errors:
+		 * - encoder pipeline errors?  hmm.
+		 */
+		if (g_error_matches (error, RB_ENCODER_ERROR, RB_ENCODER_ERROR_OUT_OF_SPACE) ||
+		    g_error_matches (error, RB_ENCODER_ERROR, RB_ENCODER_ERROR_DEST_READ_ONLY)) {
+			rb_debug ("fatal transfer error: %s", error->message);
+			rb_track_transfer_batch_cancel (batch);
+			rb_error_dialog (NULL, _("Error transferring track"), "%s", error->message);
+		} else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
 			rb_debug ("not displaying 'file exists' error for %s", dest);
 		} else {
 			rb_error_dialog (NULL, _("Error transferring track"), "%s", error->message);
 		}
+	} else {
+		/* could probably do something smarter here to avoid
+		 * re-reading tags etc.
+		 */
+		rhythmdb_add_uri (source->priv->db, dest);
 	}
 }
 
@@ -1272,11 +1287,13 @@ static void
 impl_paste (RBSource *asource, GList *entries)
 {
 	RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
-	RBRemovableMediaManager *rm_mgr;
+	RBTrackTransferQueue *xferq;
 	GList *l;
 	GSList *sl;
 	RBShell *shell;
 	RhythmDBEntryType source_entry_type;
+	RBTrackTransferBatch *batch;
+	gboolean start_batch = FALSE;
 
 	if (impl_can_paste (asource) == FALSE) {
 		g_warning ("RBLibrarySource impl_paste called when gconf keys unset");
@@ -1289,15 +1306,17 @@ impl_paste (RBSource *asource, GList *entries)
 		      "shell", &shell,
 		      "entry-type", &source_entry_type,
 		      NULL);
-	g_object_get (shell, "removable-media-manager", &rm_mgr, NULL);
+	g_object_get (shell, "track-transfer-queue", &xferq, NULL);
 	g_object_unref (shell);
 
+	batch = rb_track_transfer_batch_new (NULL, NULL, NULL, RB_SOURCE (source));
+	g_signal_connect_object (batch, "get-dest-uri", G_CALLBACK (get_dest_uri_cb), source, 0);
+	g_signal_connect_object (batch, "track-done", G_CALLBACK (track_done_cb), source, 0);
+
 	for (l = entries; l != NULL; l = g_list_next (l)) {
 		RhythmDBEntry *entry = (RhythmDBEntry *)l->data;
 		RhythmDBEntryType entry_type;
 		RBSource *source_source;
-		char *dest;
-		char *sane_dest;
 
 		rb_debug ("pasting entry %s", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
 
@@ -1314,22 +1333,18 @@ impl_paste (RBSource *asource, GList *entries)
 			continue;
 		}
 
-		dest = build_filename (source, entry);
-		if (dest == NULL) {
-			rb_debug ("could not create destination path for entry");
-			continue;
-		}
-
-		sane_dest = rb_sanitize_uri_for_filesystem (dest);
-		g_free (dest);
-
-		rb_removable_media_manager_queue_transfer (rm_mgr, entry,
-							  sane_dest, NULL,
-							  (RBTransferCompleteCallback)completed_cb, source);
+		rb_track_transfer_batch_add (batch, entry);
+		start_batch = TRUE;
 	}
 	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, source_entry_type);
 
-	g_object_unref (rm_mgr);
+	if (start_batch) {
+		rb_track_transfer_queue_start_batch (xferq, batch);
+	} else {
+		g_object_unref (batch);
+	}
+
+	g_object_unref (xferq);
 }
 
 static guint
diff --git a/sources/rb-removable-media-source.c b/sources/rb-removable-media-source.c
index d56f8cf..77f1604 100644
--- a/sources/rb-removable-media-source.c
+++ b/sources/rb-removable-media-source.c
@@ -54,6 +54,8 @@
 #include "rb-dialog.h"
 #include "rb-util.h"
 #include "rb-file-helpers.h"
+#include "rb-track-transfer-batch.h"
+#include "rb-track-transfer-queue.h"
 
 #if !GLIB_CHECK_VERSION(2,22,0)
 #define g_mount_unmount_with_operation_finish g_mount_unmount_finish
@@ -339,134 +341,109 @@ impl_delete_thyself (RBSource *source)
 	g_object_unref (db);
 }
 
-struct _TrackAddedData {
-	RBRemovableMediaSource *source;
-	char *mimetype;
-};
+static char *
+get_dest_uri_cb (RBTrackTransferBatch *batch,
+		 RhythmDBEntry *entry,
+		 const char *mediatype,
+		 const char *extension,
+		 RBRemovableMediaSource *source)
+{
+	char *free_ext = NULL;
+	char *uri;
+
+	/* make sure the extension isn't ludicrously long */
+	if (extension == NULL) {
+		extension = "";
+	} else if (strlen (extension) > EXTENSION_LENGTH_LIMIT) {
+		free_ext = g_strdup (extension);
+		free_ext[EXTENSION_LENGTH_LIMIT] = '\0';
+		extension = free_ext;
+	}
+	uri = rb_removable_media_source_build_dest_uri (source, entry, mediatype, extension);
+	g_free (free_ext);
+	return uri;
+}
 
 static void
-_track_added_cb (RhythmDBEntry *entry, const char *uri, guint64 dest_size, GError *error, struct _TrackAddedData *data)
+track_done_cb (RBTrackTransferBatch *batch,
+	       RhythmDBEntry *entry,
+	       const char *dest,
+	       guint64 dest_size,
+	       const char *dest_mediatype,
+	       GError *error,
+	       RBRemovableMediaSource *source)
 {
 	if (error == NULL) {
-		rb_removable_media_source_track_added (data->source, entry, uri, dest_size, data->mimetype);
+		rb_removable_media_source_track_added (source, entry, dest, dest_size, dest_mediatype);
 	} else {
-		rb_removable_media_source_track_add_error (data->source, entry, uri, error);
+		if (g_error_matches (error, RB_ENCODER_ERROR, RB_ENCODER_ERROR_OUT_OF_SPACE) ||
+		    g_error_matches (error, RB_ENCODER_ERROR, RB_ENCODER_ERROR_DEST_READ_ONLY)) {
+			rb_debug ("fatal transfer error: %s", error->message);
+			rb_track_transfer_batch_cancel (batch);
+		}
+		rb_removable_media_source_track_add_error (source, entry, dest, error);
 	}
-	g_free (data->mimetype);
-	g_free (data);
 }
 
 static void
-impl_paste (RBSource *source, GList *entries)
+impl_paste (RBSource *bsource, GList *entries)
 {
-	RBRemovableMediaManager *rm_mgr;
+	RBRemovableMediaSource *source = RB_REMOVABLE_MEDIA_SOURCE (bsource);
+	RBTrackTransferQueue *xferq;
 	RBShell *shell;
+	GList *mime_types;
 	GList *l;
 	RhythmDBEntryType our_entry_type;
-	RBEncoder *encoder;
-
-	g_object_get (source, "shell", &shell, NULL);
-	g_object_get (shell,
-		      "removable-media-manager", &rm_mgr,
-		      NULL);
-	g_object_unref (shell);
+	RBTrackTransferBatch *batch;
+	gboolean start_batch = FALSE;
 
 	g_object_get (source,
+		      "shell", &shell,
 		      "entry-type", &our_entry_type,
 		      NULL);
+	g_object_get (shell, "track-transfer-queue", &xferq, NULL);
+	g_object_unref (shell);
 
-	encoder = rb_encoder_new ();
+	mime_types = rb_removable_media_source_get_mime_types (source);
+	batch = rb_track_transfer_batch_new (mime_types, NULL, NULL, RB_SOURCE (source));
+	rb_list_deep_free (mime_types);
+
+	g_signal_connect_object (batch, "get-dest-uri", G_CALLBACK (get_dest_uri_cb), source, 0);
+	g_signal_connect_object (batch, "track-done", G_CALLBACK (track_done_cb), source, 0);
 
 	for (l = entries; l != NULL; l = l->next) {
 		RhythmDBEntry *entry;
 		RhythmDBEntryType entry_type;
-		GList *mime_types;
-		const char *entry_mime;
-		char *mimetype;
-		char *extension;
-		char *dest;
-		struct _TrackAddedData *added_data;
-
-		dest = NULL;
-		mimetype = NULL;
-		extension = NULL;
-		mime_types = NULL;
+		const char *location;
+
 		entry = (RhythmDBEntry *)l->data;
+		location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
 		entry_type = rhythmdb_entry_get_entry_type (entry);
 
-		if (entry_type == our_entry_type ||
-		    !rb_removable_media_source_should_paste (RB_REMOVABLE_MEDIA_SOURCE (source), entry)) {
-			goto impl_paste_end;
-		}
-
-		entry_mime = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MIMETYPE);
-		/* hackish mapping of gstreamer media types to mime types; this
-		 * should be easier when we do proper (deep) typefinding.
-		 */
-		if (strcmp (entry_mime, "audio/x-wav") == 0) {
-			/* if it has a bitrate, assume it's mp3-in-wav */
-			if (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_BITRATE) != 0)
-				entry_mime = "audio/mpeg";
-		} else if (strcmp (entry_mime, "audio/x-m4a") == 0) {
-			entry_mime = "audio/aac";
-		} else if (strcmp (entry_mime, "application/x-id3") == 0) {
-			entry_mime = "audio/mpeg";
-		} else if (strcmp (entry_mime, "audio/x-flac") == 0) {
-			entry_mime = "audio/flac";
-		}
-
-		mime_types = rb_removable_media_source_get_mime_types (RB_REMOVABLE_MEDIA_SOURCE (source));
-		if (mime_types != NULL && !rb_string_list_contains (mime_types, entry_mime)) {
-			if (!rb_encoder_get_media_type (encoder, entry, mime_types, &mimetype, &extension)) {
-				rb_debug ("failed to find acceptable mime type for %s", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
-				goto impl_paste_end;
+		if (entry_type != our_entry_type) {
+			if (rb_removable_media_source_should_paste (source, entry)) {
+				rb_debug ("pasting entry %s", location);
+				rb_track_transfer_batch_add (batch, entry);
+				start_batch = TRUE;
+			} else {
+				rb_debug ("device doesn't want entry %s", location);
 			}
 		} else {
-			const char *s;
-			char       *path;
-
-			rb_debug ("copying using existing format");
-			path = rb_uri_get_short_path_name (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
-			s = g_strrstr (path, ".");
-			extension = (s != NULL) ? g_strdup (s + 1) : NULL;
-			g_free (path);
-		}
-
-		/* make sure the extension isn't ludicrously long */
-		if (extension != NULL && strlen (extension) > EXTENSION_LENGTH_LIMIT) {
-			extension[EXTENSION_LENGTH_LIMIT] = '\0';
-		}
-
-		dest = rb_removable_media_source_build_dest_uri (RB_REMOVABLE_MEDIA_SOURCE (source), entry, mimetype, extension);
-		if (dest == NULL) {
-			rb_debug ("could not create destination path for entry");
-			goto impl_paste_end;
+			rb_debug ("can't copy entry %s from the device to itself", location);
 		}
 
-		rb_list_deep_free (mime_types);
-		if (mimetype != NULL)
-			mime_types = g_list_prepend (NULL, g_strdup (mimetype));
-		else
-			mime_types = NULL;
-		added_data = g_new0 (struct _TrackAddedData, 1);
-		added_data->source = RB_REMOVABLE_MEDIA_SOURCE (source);
-		added_data->mimetype = g_strdup (mimetype);
-		rb_removable_media_manager_queue_transfer (rm_mgr, entry,
-							   dest, mime_types,
-							   (RBTransferCompleteCallback)_track_added_cb, added_data);
-impl_paste_end:
-		g_free (dest);
-		g_free (mimetype);
-		g_free (extension);
-		if (mime_types)
-			rb_list_deep_free (mime_types);
-		if (entry_type)
+		if (entry_type) {
 			g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+		}
 	}
-
 	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, our_entry_type);
-	g_object_unref (rm_mgr);
-	g_object_unref (encoder);
+
+	if (start_batch) {
+		rb_track_transfer_queue_start_batch (xferq, batch);
+	} else {
+		g_object_unref (batch);
+	}
+	g_object_unref (xferq);
 }
 
 static guint
@@ -663,8 +640,8 @@ rb_removable_media_source_build_dest_uri (RBRemovableMediaSource *source,
 		uri = NULL;
 	}
 
-	sane_uri = rb_sanitize_uri_for_filesystem(uri);
-	g_return_val_if_fail(sane_uri != NULL, NULL);
+	sane_uri = rb_sanitize_uri_for_filesystem (uri);
+	g_return_val_if_fail (sane_uri != NULL, NULL);
 	g_free(uri);
 	uri = sane_uri;
 
@@ -900,6 +877,11 @@ rb_removable_media_source_track_add_error (RBRemovableMediaSource *source,
 	RBRemovableMediaSourceClass *klass = RB_REMOVABLE_MEDIA_SOURCE_GET_CLASS (source);
 	gboolean show_dialog = TRUE;
 
+	/* hrm, want the subclass to decide whether to display the error and
+	 * whether to cancel the batch (may have some device-specific errors?)
+	 *
+	 * for now we'll just cancel on the most common things..
+	 */
 	if (klass->impl_track_add_error)
 		show_dialog = klass->impl_track_add_error (source, entry, uri, error);
 



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