[rhythmbox] rhythmdb: implement plugin installation in RhythmDBImportJob



commit 665226c59f908db1f899d2012070eeaab5d69732
Author: Jonathan Matthew <jonathan d14n org>
Date:   Fri Jun 18 22:15:38 2010 +1000

    rhythmdb: implement plugin installation in RhythmDBImportJob
    
    At the end of an import job, we check if any of the import error entries
    created included plugin installer detail strings, and if so, we emit
    a missing-plugins signal for the plugin install code.  When the plugin
    install finishes successfully, we retry the imports that resulted in
    missing plugin errors.
    
    This approach means that we don't process missing plugins for files imported
    outside of a RhythmDBImportJob, which currently includes files picked up by
    the library crawl on startup or by library monitoring.  This is probably
    a good thing, on the whole.

 plugins/generic-player/rb-generic-player-source.c |    9 ++-
 rhythmdb/rhythmdb-import-job.c                    |  134 ++++++++++++++++++++-
 shell/rb-missing-plugins.c                        |    9 ++
 shell/rb-missing-plugins.h                        |    3 +
 sources/rb-library-source.c                       |    8 ++
 5 files changed, 159 insertions(+), 4 deletions(-)
---
diff --git a/plugins/generic-player/rb-generic-player-source.c b/plugins/generic-player/rb-generic-player-source.c
index 6f8447d..323abc0 100644
--- a/plugins/generic-player/rb-generic-player-source.c
+++ b/plugins/generic-player/rb-generic-player-source.c
@@ -53,6 +53,7 @@
 #include "rb-import-errors-source.h"
 #include "rb-builder-helpers.h"
 #include "rb-sync-settings.h"
+#include "rb-missing-plugins.h"
 
 static void impl_constructed (GObject *object);
 static void impl_dispose (GObject *object);
@@ -466,14 +467,20 @@ load_songs (RBGenericPlayerSource *source)
 	RhythmDBEntryType entry_type;
 	char **audio_folders;
 	char *mount_path;
+	RBShell *shell;
 
 	mount_path = rb_generic_player_source_get_mount_path (source);
-	g_object_get (source, "entry-type", &entry_type, NULL);
+	g_object_get (source,
+		      "entry-type", &entry_type,
+		      "shell", &shell,
+		      NULL);
 
 	/* if we have a set of folders on the device containing audio files,
 	 * load only those folders, otherwise add the whole volume.
 	 */
 	priv->import_job = rhythmdb_import_job_new (priv->db, entry_type, priv->ignore_type, priv->error_type);
+	rb_missing_plugins_init_import_job (shell, priv->import_job);
+	g_object_unref (shell);
 
 	g_signal_connect_object (priv->import_job, "complete", G_CALLBACK (import_complete_cb), source, 0);
 	g_signal_connect_object (priv->import_job, "status-changed", G_CALLBACK (import_status_changed_cb), source, 0);
diff --git a/rhythmdb/rhythmdb-import-job.c b/rhythmdb/rhythmdb-import-job.c
index 7619461..ec9bf20 100644
--- a/rhythmdb/rhythmdb-import-job.c
+++ b/rhythmdb/rhythmdb-import-job.c
@@ -49,6 +49,7 @@ enum
 	STATUS_CHANGED,
 	SCAN_COMPLETE,
 	COMPLETE,
+	MISSING_PLUGINS,
 	LAST_SIGNAL
 };
 
@@ -71,6 +72,9 @@ struct _RhythmDBImportJobPrivate
 	gboolean	started;
 	GCancellable    *cancel;
 
+	GSList		*retry_entries;
+	gboolean	retried;
+
 	int		status_changed_id;
 	gboolean	scan_complete;
 	gboolean	complete;
@@ -139,6 +143,54 @@ rhythmdb_import_job_add_uri (RhythmDBImportJob *job, const char *uri)
 	g_static_mutex_unlock (&job->priv->lock);
 }
 
+static void
+missing_plugins_retry_cb (gpointer instance, gboolean installed, RhythmDBImportJob *job)
+{
+	GSList *retry = NULL;
+	GSList *i;
+
+	g_static_mutex_lock (&job->priv->lock);
+	g_assert (job->priv->retried == FALSE);
+	if (installed == FALSE) {
+		rb_debug ("plugin installation was not successful; job complete");
+		g_signal_emit (job, signals[COMPLETE], 0, job->priv->total);
+	} else {
+		job->priv->retried = TRUE;
+
+		/* reset the job state to just show the retry information */
+		job->priv->total = g_slist_length (job->priv->retry_entries);
+		rb_debug ("plugin installation was successful, retrying %d entries", job->priv->total);
+		job->priv->imported = 0;
+
+		/* remove the import error entries and build the list of URIs to retry */
+		for (i = job->priv->retry_entries; i != NULL; i = i->next) {
+			RhythmDBEntry *entry = (RhythmDBEntry *)i->data;
+			char *uri;
+
+			uri = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_LOCATION);
+			rhythmdb_entry_delete (job->priv->db, entry);
+
+			g_hash_table_insert (job->priv->outstanding, g_strdup (uri), GINT_TO_POINTER (1));
+			retry = g_slist_prepend (retry, uri);
+		}
+		rhythmdb_commit (job->priv->db);
+		retry = g_slist_reverse (retry);
+	}
+	g_static_mutex_unlock (&job->priv->lock);
+
+	for (i = retry; i != NULL; i = i->next) {
+		char *uri = (char *)i->data;
+
+		rhythmdb_add_uri_with_types (job->priv->db,
+					     uri,
+					     job->priv->entry_type,
+					     job->priv->ignore_type,
+					     job->priv->error_type);
+	}
+
+	rb_slist_deep_free (retry);
+}
+
 static gboolean
 emit_status_changed (RhythmDBImportJob *job)
 {
@@ -153,8 +205,52 @@ emit_status_changed (RhythmDBImportJob *job)
 	 */
 	g_object_ref (job);
 	if (job->priv->scan_complete && job->priv->imported >= job->priv->total) {
-		rb_debug ("emitting job complete");
-		g_signal_emit (job, signals[COMPLETE], 0, job->priv->total);
+
+		if (job->priv->retry_entries != NULL && job->priv->retried == FALSE) {
+			gboolean processing = FALSE;
+			char **details = NULL;
+			GClosure *retry;
+			GSList *l;
+			int i;
+
+			/* gather missing plugin details etc. */
+			i = 0;
+			for (l = job->priv->retry_entries; l != NULL; l = l->next) {
+				RhythmDBEntry *entry;
+				char **bits;
+				int j;
+
+				entry = (RhythmDBEntry *)l->data;
+				bits = g_strsplit (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_COMMENT), "\n", 0);
+				for (j = 0; bits[j] != NULL; j++) {
+					if (rb_str_in_strv (bits[j], details) == FALSE) {
+						details = g_realloc (details, sizeof (char *) * (i+2));
+						details[i++] = g_strdup (bits[j]);
+						details[i] = NULL;
+					}
+				}
+				g_strfreev (bits);
+			}
+
+			retry = g_cclosure_new ((GCallback) missing_plugins_retry_cb,
+						g_object_ref (job),
+						(GClosureNotify)g_object_unref);
+			g_closure_set_marshal (retry, g_cclosure_marshal_VOID__BOOLEAN);
+
+			rb_debug ("emitting missing-plugins");
+			g_signal_emit (job, signals[MISSING_PLUGINS], 0, details, retry, &processing);
+			g_strfreev (details);
+			if (processing) {
+				rb_debug ("plugin installation is in progress");
+			} else {
+				rb_debug ("no plugin installation attempted; job complete");
+				g_signal_emit (job, signals[COMPLETE], 0, job->priv->total);
+			}
+			g_closure_sink (retry);
+		} else {
+			rb_debug ("emitting job complete");
+			g_signal_emit (job, signals[COMPLETE], 0, job->priv->total);
+		}
 	}
 	g_static_mutex_unlock (&job->priv->lock);
 	g_object_unref (job);
@@ -339,10 +435,20 @@ entry_added_cb (RhythmDB *db,
 	ours = g_hash_table_remove (job->priv->outstanding, uri);
 
 	if (ours) {
+		const char *details;
+
 		job->priv->imported++;
 		rb_debug ("got entry %s; %d now imported", uri, job->priv->imported);
 		g_signal_emit (job, signals[ENTRY_ADDED], 0, entry);
 
+		/* if it's an import error with missing plugins, add it to the retry list */
+		details = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_COMMENT);
+		if (rhythmdb_entry_get_entry_type (entry) == job->priv->error_type &&
+		    (details != NULL || details[0] != '\0')) {
+			rb_debug ("entry %s is an import error with missing plugin details: %s", uri, details);
+			job->priv->retry_entries = g_slist_prepend (job->priv->retry_entries, rhythmdb_entry_ref (entry));
+		}
+
 		if (job->priv->status_changed_id == 0) {
 			job->priv->status_changed_id = g_idle_add ((GSourceFunc) emit_status_changed, job);
 		}
@@ -562,7 +668,29 @@ rhythmdb_import_job_class_init (RhythmDBImportJobClass *klass)
 			      g_cclosure_marshal_VOID__INT,
 			      G_TYPE_NONE,
 			      1, G_TYPE_INT);
+	/**
+	 * RhythmDBImportJob::missing-plugins:
+	 * @job: the #RhythmDBImportJob
+	 * @details: NULL-terminated array of installer detail strings
+	 * @closure: a closure to invoke once the installer has finished
+	 *
+	 * Emitted when the whole import job is complete (but before the
+	 * 'complete' signal) but additional plugins are required to
+	 * import some of the files.
+	 *
+	 * If a handler initiates plugin installation, it should return TRUE
+	 * and invoke the closure when the installation finishes.
+	 * Otherwise it should return FALSE.
+	 */
+	signals[MISSING_PLUGINS] =
+		g_signal_new ("missing-plugins",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0, /* no internal handler */
+			      NULL, NULL,
+			      rb_marshal_BOOLEAN__POINTER_POINTER,
+			      G_TYPE_BOOLEAN,
+			      2, G_TYPE_STRV, G_TYPE_CLOSURE);
 
 	g_type_class_add_private (klass, sizeof (RhythmDBImportJobPrivate));
 }
-
diff --git a/shell/rb-missing-plugins.c b/shell/rb-missing-plugins.c
index 98c03b2..0ec0ff7 100644
--- a/shell/rb-missing-plugins.c
+++ b/shell/rb-missing-plugins.c
@@ -292,3 +292,12 @@ rb_missing_plugins_init (RBShell *shell)
 
 	GST_INFO ("Set up support for automatic missing plugin installation");
 }
+
+void
+rb_missing_plugins_init_import_job (RBShell *shell, RhythmDBImportJob *job)
+{
+	g_signal_connect (job,
+			  "missing-plugins",
+			  G_CALLBACK (missing_plugins_cb),
+			  shell);
+}
diff --git a/shell/rb-missing-plugins.h b/shell/rb-missing-plugins.h
index 8f3c0d8..20aa9e7 100644
--- a/shell/rb-missing-plugins.h
+++ b/shell/rb-missing-plugins.h
@@ -25,11 +25,14 @@
 #define RB_MISSING_PLUGINS_H
 
 #include <shell/rb-shell.h>
+#include <rhythmdb/rhythmdb-import-job.h>
 
 G_BEGIN_DECLS
 
 void rb_missing_plugins_init (RBShell *shell);
 
+void rb_missing_plugins_init_import_job (RBShell *shell, RhythmDBImportJob *job);
+
 G_END_DECLS
 
 #endif /* RB_MISSING_PLUGINS_H */
diff --git a/sources/rb-library-source.c b/sources/rb-library-source.c
index 473fec3..7c942a1 100644
--- a/sources/rb-library-source.c
+++ b/sources/rb-library-source.c
@@ -67,6 +67,7 @@
 #include "rb-library-source.h"
 #include "rb-auto-playlist-source.h"
 #include "rb-encoder.h"
+#include "rb-missing-plugins.h"
 
 static void rb_library_source_class_init (RBLibrarySourceClass *klass);
 static void rb_library_source_init (RBLibrarySource *source);
@@ -1394,11 +1395,18 @@ maybe_create_import_job (RBLibrarySource *source)
 {
 	RhythmDBImportJob *job;
 	if (source->priv->import_jobs == NULL || source->priv->start_import_job_id == 0) {
+		RBShell *shell;
+
 		rb_debug ("creating new import job");
 		job = rhythmdb_import_job_new (source->priv->db,
 					       RHYTHMDB_ENTRY_TYPE_SONG,
 					       RHYTHMDB_ENTRY_TYPE_IGNORE,
 					       RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR);
+
+		g_object_get (source, "shell", &shell, NULL);
+		rb_missing_plugins_init_import_job (shell, job);
+		g_object_unref (shell);
+
 		g_signal_connect_object (job,
 					 "status-changed",
 					 G_CALLBACK (import_job_status_changed_cb),



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