[Rhythmbox-devel] xine metadata, and iPod HAL support



Heya,

Here's my free time's worth. First is a xine-lib metadata backend (which
I sent some time ago). It's not finished quite yet, but it's disabled by
default.

The rest is HAL support for the iPod source, with a bit of refactoring
for the existing code.
http://www.hadess.net/files/shots/21-06-2004.1.jpg

Enjoy.

---
Bastien Nocera <hadess hadess net> 
She walked into my office like a centipede with 98 missing legs. 
Index: configure.ac
===================================================================
RCS file: /cvs/gnome/rhythmbox/configure.ac,v
retrieving revision 1.113
diff -u -r1.113 configure.ac
--- configure.ac	4 Jun 2004 18:50:03 -0000	1.113
+++ configure.ac	21 Jun 2004 01:16:59 -0000
@@ -15,6 +15,7 @@
 AM_PROG_LIBTOOL
 AC_C_BIGENDIAN
 AC_CHECK_SIZEOF(long)
+AC_SUBST(mkdir_p)
 
 ACLOCAL="$ACLOCAL -I macros"
 
@@ -32,6 +33,23 @@
 	AC_MSG_RESULT([no])
 fi	
 
+dnl iPod support
+dnl enable_ipod = "no"
+
+HAL_REQUIRED=0.2.92
+
+AC_ARG_ENABLE(ipod,
+	      AC_HELP_STRING([--enable-ipod],
+			     [Enable iPod support in Rhythmbox]))
+AM_CONDITIONAL(USE_IPOD, test x"$enable_ipod" = xyes)
+if test x"$enable_ipod" = xyes; then
+	AC_DEFINE(WITH_IPOD_SUPPORT, 1, [Define if iPod support is enabled])
+	USE_HAL=""
+	PKG_CHECK_MODULES(HAL, hal,
+		[ AC_DEFINE([HAVE_HAL], 1, [Use the HAL library]) ]
+		[ USE_HAL="hal >= $HAL_REQUIRED" ], :)
+fi
+
 PKG_CHECK_MODULES(RHYTHMBOX,                    \
                   gtk+-2.0 >= 2.2.2             \
                   libgnomeui-2.0                \
@@ -39,7 +57,8 @@
 		  gnome-vfs-2.0                 \
 		  gnome-vfs-module-2.0		\
 		  libbonobo-2.0			\
-		  bonobo-activation-2.0)
+		  bonobo-activation-2.0		\
+		  $USE_HAL)
 
 RHYTHMBOX_CFLAGS="$CFLAGS $RHYTHMBOX_CFLAGS"
 PKG_CHECK_MODULES(UNUSED, gtk+-2.0 >= 2.4.0, [have_gtk_23=yes], [have_gtk_23=no])
@@ -48,16 +67,6 @@
 fi
 AM_CONDITIONAL(WITH_GTK22, test x"$have_gtk_23" != xyes)
 
-dnl iPod support
-dnl enable_ipod = "no"
-AC_ARG_ENABLE(ipod,
-              AC_HELP_STRING([--enable-ipod],
-	                     [Enable iPod support in Rhythmbox]))
-AM_CONDITIONAL(USE_IPOD, test x"$enable_ipod" = xyes)
-if test x"$enable_ipod" = xyes; then
-	AC_DEFINE(WITH_IPOD_SUPPORT, 1, [Define if iPod support is enabled])
-fi
-
 dnl Database
 AC_ARG_WITH(database,
               AC_HELP_STRING([--with-database=tree],
@@ -169,23 +178,30 @@
 
 dnl Tagging
 AC_ARG_WITH(metadata,
-              AC_HELP_STRING([--with-metadata=gstreamer|monkeymedia|auto],
+              AC_HELP_STRING([--with-metadata=gstreamer|monkeymedia|xine|auto],
 			     [Select the metadata reader to use]),,
 	      with_metadata=auto)
 if test x"$with_player" = xxine && test x"$with_metadata" = xgstreamer; then
   AC_MSG_ERROR([cannot use xine as player with gstreamer for metadata])
 fi  
+if test x"$with_player" = xgstreamer && test x"$with_metadata" = xxine; then
+  AC_MSG_ERROR([cannot use gstreamer as player with xine for metadata])
+fi
+
 if test x"$with_metadata" = xauto; then
    if test x"$with_player" = xgstreamer; then
       with_metadata=gstreamer
    else
       with_metadata=monkeymedia
    fi
-fi   
+fi
 AM_CONDITIONAL(USE_MONKEYMEDIA, test x"$with_metadata" = xmonkeymedia)
 if test x"$with_metadata" = xmonkeymedia; then
 	AC_DEFINE(WITH_MONKEYMEDIA, 1, [Define if you are using the monkeymedia metadata reader])
-fi	
+fi
+
+AM_CONDITIONAL(WITH_XINE_METADATA, test x"$with_metadata" = "xxine")
+AM_CONDITIONAL(WITH_GST_METADATA, test x"$with_metadata" = "xgstreamer")
 
 dnl Stolen from D-BUS configury
 dnl Atomic integers (checks by Sebastian Wilhelmi for GLib)
Index: metadata/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/metadata/Makefile.am,v
retrieving revision 1.3
diff -u -r1.3 Makefile.am
--- metadata/Makefile.am	19 Dec 2003 03:50:38 -0000	1.3
+++ metadata/Makefile.am	21 Jun 2004 01:17:00 -0000
@@ -24,6 +24,12 @@
 librbmetadata_la_LIBADD += $(top_builddir)/metadata/monkey-media/libmonkey-media.la
 librbmetadata_la_SOURCES += rb-metadata-mm.c
 INCLUDES += -I$(top_srcdir)/metadata/monkey-media
-else
+endif
+
+if WITH_XINE_METADATA
+librbmetadata_la_SOURCES += rb-metadata-xine.c
+endif
+
+if WITH_GST_METADATA
 librbmetadata_la_SOURCES += rb-metadata-gst.c
 endif
Index: metadata/rb-metadata-xine.c
===================================================================
RCS file: metadata/rb-metadata-xine.c
diff -N metadata/rb-metadata-xine.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ metadata/rb-metadata-xine.c	21 Jun 2004 01:17:01 -0000
@@ -0,0 +1,334 @@
+/*
+ *  arch-tag: Implementation of xine-lib metadata loading
+ *
+ *  Copyright (C) 2003-2004 Bastien Nocera <hadess hadess net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <libgnomevfs/gnome-vfs.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <xine.h>
+
+#include "rb-string-helpers.h"
+
+#include "rb-metadata.h"
+
+static void rb_metadata_class_init (RBMetaDataClass *klass);
+static void rb_metadata_init (RBMetaData *md);
+static void rb_metadata_finalize (GObject *object);
+
+struct RBMetaDataPrivate
+{
+	xine_t *xine;
+	xine_stream_t *stream;
+};
+
+static GObjectClass *parent_class = NULL;
+
+GType
+rb_metadata_get_type (void)
+{
+	static GType type = 0;
+
+	if (type == 0)
+	{
+		static const GTypeInfo our_info =
+		{
+			sizeof (RBMetaDataClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) rb_metadata_class_init,
+			NULL,
+			NULL,
+			sizeof (RBMetaData),
+			0,
+			(GInstanceInitFunc) rb_metadata_init
+		};
+
+		type = g_type_register_static (G_TYPE_OBJECT,
+					       "RBMetaData",
+					       &our_info, 0);
+	}
+
+	return type;
+}
+
+static void
+rb_metadata_class_init (RBMetaDataClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->finalize = rb_metadata_finalize;
+}
+
+static void
+rb_metadata_init (RBMetaData *md)
+{
+	char *path;
+
+	md->priv = g_new0 (RBMetaDataPrivate, 1);
+	md->priv->xine = xine_new ();
+	path = g_build_filename (g_get_home_dir(), ".gnome2",
+				 "xine-config", NULL);
+	xine_config_load (md->priv->xine, path);
+
+	xine_init (md->priv->xine);
+	md->priv->stream = xine_stream_new (md->priv->xine, NULL, NULL);
+}
+
+RBMetaData *
+rb_metadata_new (void)
+{
+	return RB_METADATA (g_object_new (RB_TYPE_METADATA, NULL));
+}
+
+static void
+rb_metadata_finalize (GObject *object)
+{
+	RBMetaData *md;
+
+	md = RB_METADATA (object);
+
+	xine_close (md->priv->stream);
+	xine_dispose (md->priv->stream);
+	xine_exit (md->priv->xine);
+
+	g_free (md->priv);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/* FIXME implement error collapsing like in Totem */
+static gboolean
+xine_error (RBMetaData *md, GError **error)
+{
+	int err;
+
+	err = xine_get_error (md->priv->stream);
+	if (err == XINE_ERROR_NONE)
+		return TRUE;
+
+	/* FIXME actualise to the current metadata errors */
+#if 0
+	switch (err)
+	{
+	case XINE_ERROR_NO_INPUT_PLUGIN:
+	case XINE_ERROR_NO_DEMUX_PLUGIN:
+		*error = g_error_new (RB_METADATA_ERROR, RB_METADATA_ERROR_OPEN_FAILED, _("There is no plugin to handle this song"));
+		break;
+	case XINE_ERROR_DEMUX_FAILED:
+		*error = g_error_new (RB_METADATA_ERROR, RB_METADATA_ERROR_OPEN_FAILED, _("This song is broken and can not be played further"));
+		break;
+	case XINE_ERROR_MALFORMED_MRL:
+		*error = g_error_new (RB_METADATA_ERROR, RB_METADATA_ERROR_OPEN_FAILED, _("This location is not a valid one"));
+		break;
+	case XINE_ERROR_INPUT_FAILED:
+		*error = g_error_new (RB_METADATA_ERROR, RB_METADATA_ERROR_OPEN_FAILED, _("This song could not be opened"));                break;
+	default:
+		*error = g_error_new (RB_METADATA_ERROR, RB_METADATA_ERROR_OPEN_FAILED, _("Generic Error"));
+		break;
+	}
+#endif
+	return FALSE;
+}
+
+void
+rb_metadata_load (RBMetaData *md, const char *uri, GError **error)
+{
+	int err;
+
+	xine_close (md->priv->stream);
+	err = xine_open (md->priv->stream, uri);
+
+	if (error == 0)
+	{
+		if (xine_error (md, error) == FALSE)
+		{
+			xine_close (md->priv->stream);
+			g_object_set (G_OBJECT (md), "error", error, NULL);
+			return;
+		}
+	}
+
+	if (xine_get_stream_info (md->priv->stream,
+				XINE_STREAM_INFO_HAS_AUDIO) == FALSE)
+	{
+#if 0
+		error = g_error_new (RB_METADATA_ERROR,
+				     RB_METADATA_ERROR_OPEN_FAILED,
+				     _("File is not an audio file"));
+		g_object_set (G_OBJECT (md), "error", error, NULL);
+#endif
+		return;
+	}
+
+	if (xine_get_stream_info (md->priv->stream,
+				XINE_STREAM_INFO_AUDIO_HANDLED) == FALSE)
+	{
+#if 0
+		error = g_error_new (RB_METADATA_ERROR,
+				     RB_METADATA_ERROR_OPEN_FAILED,
+				     _("Song is not handled"));
+		g_object_set (G_OBJECT (md), "error", error, NULL);
+#endif
+		return;
+	}
+}
+
+gboolean
+rb_metadata_get (RBMetaData *md,
+			        RBMetaDataField field,
+				GValue *value)
+{
+	const char *tmp = NULL;
+
+	switch (field)
+	{
+	/* tags */
+	case RB_METADATA_FIELD_TITLE:
+		g_value_init (value, G_TYPE_STRING);
+		tmp = xine_get_meta_info (md->priv->stream,
+				XINE_META_INFO_TITLE);
+		g_value_set_string (value, tmp);
+		break;
+	case RB_METADATA_FIELD_ARTIST:
+		g_value_init (value, G_TYPE_STRING);
+		tmp = xine_get_meta_info (md->priv->stream,
+				XINE_META_INFO_ARTIST);
+		g_value_set_string (value, tmp);
+		break;
+	case RB_METADATA_FIELD_ALBUM:
+		g_value_init (value, G_TYPE_STRING);
+		tmp = xine_get_meta_info (md->priv->stream,
+				XINE_META_INFO_ALBUM);
+		g_value_set_string (value, tmp);
+		break;
+	case RB_METADATA_FIELD_DATE:
+		g_value_init (value, G_TYPE_STRING);
+		tmp = xine_get_meta_info (md->priv->stream,
+				XINE_META_INFO_YEAR);
+		g_value_set_string (value, tmp);
+		break;
+	case RB_METADATA_FIELD_GENRE:
+		g_value_init (value, G_TYPE_STRING);
+		tmp = xine_get_meta_info (md->priv->stream,
+				XINE_META_INFO_GENRE);
+		g_value_set_string (value, tmp);
+		break;
+	case RB_METADATA_FIELD_COMMENT:
+		g_value_init (value, G_TYPE_STRING);
+		tmp = xine_get_meta_info (md->priv->stream,
+				XINE_META_INFO_COMMENT);
+		g_value_set_string (value, tmp);
+		break;
+	case RB_METADATA_FIELD_TRACK_NUMBER:
+#if 0
+		g_value_init (value, G_TYPE_INT);
+		g_value_set_int (value, xine_get_stream_info (md->priv->stream,
+					XINE_STREAM_INFO_TRACK_NUMBER));
+#endif
+		break;
+	case RB_METADATA_FIELD_MAX_TRACK_NUMBER:
+#if 0
+		g_value_init (value, G_TYPE_INT);
+		g_value_set_int (value, xine_get_stream_info (md->priv->stream,
+					XINE_STREAM_INFO_MAX_TRACK_NUMBER));
+#endif
+		break;
+	case RB_METADATA_FIELD_DESCRIPTION:
+	case RB_METADATA_FIELD_VERSION:
+	case RB_METADATA_FIELD_ISRC:
+	case RB_METADATA_FIELD_ORGANIZATION:
+	case RB_METADATA_FIELD_COPYRIGHT:
+	case RB_METADATA_FIELD_CONTACT:
+	case RB_METADATA_FIELD_LICENSE:
+	case RB_METADATA_FIELD_PERFORMER:
+		g_value_init (value, G_TYPE_STRING);
+		g_value_set_string (value, "");
+		break;
+	case RB_METADATA_FIELD_DURATION:
+		{
+			int length = 0;
+			int pos_stream, pos_time;
+
+			xine_get_pos_length (md->priv->stream,
+					&pos_stream, &pos_time, &length);
+
+			g_value_init (value, G_TYPE_LONG);
+			g_value_set_long (value, length / 1000);
+		}
+		break;
+	case RB_METADATA_FIELD_CODEC:
+		g_value_init (value, G_TYPE_STRING);
+		tmp = xine_get_meta_info (md->priv->stream,
+				XINE_META_INFO_AUDIOCODEC);
+		g_value_set_string (value, tmp);
+		break;
+	case RB_METADATA_FIELD_BITRATE:
+		g_value_init (value, G_TYPE_INT);
+		g_value_set_int (value, xine_get_stream_info (md->priv->stream,
+					XINE_STREAM_INFO_AUDIO_BITRATE) / 1000);
+		break;
+	case RB_METADATA_FIELD_ALBUM_GAIN:
+	case RB_METADATA_FIELD_TRACK_GAIN:
+	case RB_METADATA_FIELD_ALBUM_PEAK:
+	case RB_METADATA_FIELD_TRACK_PEAK:
+		g_value_init (value, G_TYPE_DOUBLE);
+		g_value_set_double (value, 0.0);
+		break;
+
+	/* default */
+	default:
+		g_warning ("Invalid field!");
+		g_value_init (value, G_TYPE_NONE);
+		break;
+	}
+
+	return TRUE;
+}
+
+gboolean
+rb_metadata_set (RBMetaData *md,
+				RBMetaDataField field,
+				const GValue *value)
+{
+	return FALSE;
+}
+
+gboolean
+rb_metadata_can_save (RBMetaData *md, const char *mimetype)
+{
+	return FALSE;
+}
+
+const char *
+rb_metadata_get_mime (RBMetaData *md)
+{
+	return "application/x-fixme";
+}
+
+void
+rb_metadata_save (RBMetaData *md, GError **error)
+{
+	return;
+}
+
Index: sources/rb-ipod-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-ipod-source.c,v
retrieving revision 1.2
diff -u -r1.2 rb-ipod-source.c
--- sources/rb-ipod-source.c	21 Mar 2004 18:50:40 -0000	1.2
+++ sources/rb-ipod-source.c	21 Jun 2004 01:17:09 -0000
@@ -18,8 +18,15 @@
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
  */
+
+#include <config.h>
+
 #include <gtk/gtktreeview.h>
 #include <string.h>
+#ifdef HAVE_HAL
+#include <libhal.h>
+#include <dbus/dbus-glib.h>
+#endif
 #include "itunesdb.h"
 #include "rhythmdb.h"
 #include <libgnome/gnome-i18n.h>
@@ -34,13 +41,28 @@
 static void rb_ipod_source_finalize (GObject *object);
 static void rb_ipod_source_class_init (RBiPodSourceClass *klass);
 
+static void ipod_itunesdb_monitor (RBiPodSource *source, gboolean enable);
 static gboolean ipod_itunesdb_monitor_cb (RBiPodSource *source);
+#ifdef HAVE_HAL
+static gboolean ipod_itunesdb_monitor_hal (RBiPodSource *source,
+		gboolean enable);
+#endif
 
 
+static void ipod_set_present (RBiPodSource *source, gboolean is_present);
+
 
 struct RBiPodSourcePrivate
 {
 	guint ipod_polling_id;
+	gboolean was_present;
+	RhythmDB *db;
+
+#ifdef HAVE_HAL
+	LibHalContext *ctx;
+#endif
+	char *mount_path;
+	char *itunesdb_path;
 };
 
 
@@ -84,8 +106,7 @@
 static void
 rb_ipod_source_init (RBiPodSource *source)
 {
-	source->priv = g_new0 (RBiPodSourcePrivate, 1);	
-	source->priv->ipod_polling_id = g_timeout_add (1000, (GSourceFunc)ipod_itunesdb_monitor_cb, source);
+	source->priv = g_new0 (RBiPodSourcePrivate, 1);
 }
 
 
@@ -94,19 +115,22 @@
 {
 	RBiPodSource *source = RB_IPOD_SOURCE (object);
 
-	if (source->priv->ipod_polling_id) {
-		g_source_remove (source->priv->ipod_polling_id);
-	}
+	ipod_itunesdb_monitor (source, FALSE);
+	g_free (source->priv->itunesdb_path);
+	g_free (source->priv->mount_path);
 }
 
 
 RBSource *
 rb_ipod_source_new (RBShell *shell, RhythmDB *db, BonoboUIComponent *component)
 {
+	RBiPodSource *ipod_source;
 	RBSource *source;
 	GtkWidget *dummy = gtk_tree_view_new ();
 	GdkPixbuf *icon;
 
+	g_message ("new");
+
 	icon = gtk_widget_render_icon (dummy, RB_STOCK_IPOD,
 				       GTK_ICON_SIZE_LARGE_TOOLBAR,
 				       NULL);
@@ -117,14 +141,18 @@
 					  "name", _("iPod"),
 					  "entry-type", RHYTHMDB_ENTRY_TYPE_IPOD,
 					  "internal-name", "<ipod>",
-					  "icon", icon,
 					  "db", db,
+					  "icon", icon,
 					  "component", component,
 					  NULL));
 
+	ipod_source = RB_IPOD_SOURCE (source);
+	ipod_source->priv->db = db;
+
 	rb_shell_register_entry_type_for_source (shell, source, 
 						 RHYTHMDB_ENTRY_TYPE_IPOD);
 
+	ipod_itunesdb_monitor (ipod_source, TRUE);
 
 	return source;
 }
@@ -149,7 +177,7 @@
 	gchar *mount_path = ipod_get_mount_path ();
 
 	result = g_build_filename (mount_path,
-				   "iPod_Control/iTunes/iTunesDB", 
+				   "/iPod_Control/iTunes/iTunesDB", 
 				   NULL);
 	g_free (mount_path);
 	return result;
@@ -302,22 +330,16 @@
 
 /* We need to be locked to use this function */
 static GnomeVFSResult
-add_ipod_songs_to_db (RhythmDB *db)
+add_ipod_songs_to_db (RBiPodSource *source)
 {
-	char *path;
 	iPodParser *parser;
 	RBiPodSongAdderCtxt *ctxt;
 
 	ctxt = g_new0 (RBiPodSongAdderCtxt, 1);
-	if (ctxt == NULL) {
-		return GNOME_VFS_ERROR_NO_MEMORY;
-	}
+	g_message ("mount point: %s", source->priv->mount_path);
+	parser = ipod_parser_new (source->priv->mount_path);
 
-	path = ipod_get_mount_path ();
-	parser = ipod_parser_new (path);
-	g_free (path);
-
-	ctxt->db = db;
+	ctxt->db = source->priv->db;
 	ctxt->parser = parser;
 
 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, 
@@ -327,13 +349,12 @@
 	return GNOME_VFS_OK;
 }
 
-
 static void
-ipod_load_songs (RhythmDB *rdb)
+ipod_load_songs (RBiPodSource *source)
 {
 	GnomeVFSResult res;
 
-	res = add_ipod_songs_to_db (rdb);
+	res = add_ipod_songs_to_db (source);
 
 	if (res != GNOME_VFS_OK) {
 		g_warning ("Error loading iPod database");
@@ -341,11 +362,26 @@
 }
 
 static void
-ipod_unload_songs (RhythmDB *db)
+ipod_unload_songs (RBiPodSource *source)
 {
-	rhythmdb_write_lock (db);
-	rhythmdb_entry_delete_by_type (db, RHYTHMDB_ENTRY_TYPE_IPOD);
-	rhythmdb_write_unlock (db);
+	rhythmdb_write_lock (source->priv->db);
+	rhythmdb_entry_delete_by_type (source->priv->db, RHYTHMDB_ENTRY_TYPE_IPOD);
+	rhythmdb_write_unlock (source->priv->db);
+}
+
+static void
+ipod_itunesdb_monitor (RBiPodSource *source, gboolean enable)
+{
+#ifdef HAVE_HAL
+	if (ipod_itunesdb_monitor_hal (source, enable) != FALSE) {
+		return;
+	}
+#endif
+	if (enable) {
+		source->priv->ipod_polling_id = g_timeout_add (1000, (GSourceFunc)ipod_itunesdb_monitor_cb, source);
+	} else {
+		g_source_remove (source->priv->ipod_polling_id);
+	}
 }
 
 static gboolean
@@ -353,32 +389,201 @@
 {
 	gboolean is_present;
 	gchar *itunesdb_path;
-	static gboolean was_present = FALSE;
 
 	itunesdb_path = ipod_get_itunesdb_path ();
-	g_assert (itunesdb_path != NULL);
-	is_present = g_file_test (itunesdb_path, G_FILE_TEST_EXISTS);
+	g_return_val_if_fail (itunesdb_path != NULL, TRUE);
 
-	if (is_present && !was_present) {
-		RhythmDB *db;
+	is_present = g_file_test (itunesdb_path, G_FILE_TEST_EXISTS);
 
-		g_print ("iPod plugged\n");
-		was_present = TRUE;
-		g_object_get (G_OBJECT (source), "db", &db, NULL);
-		ipod_load_songs (db);
+	if (is_present && !source->priv->was_present) {
 		/* FIXME: should we suspend this monitor until the iPod 
-		 * database has been read and fed to rhythmbox?
-		 */
-	} else if (!is_present && was_present) {
-		RhythmDB *db;
-
-		g_print ("iPod unplugged\n");
-		was_present = FALSE;
-		g_object_get (G_OBJECT (source), "db", &db, NULL);
-		ipod_unload_songs (db);
+		 * database has been read and fed to rhythmbox? */
+		source->priv->itunesdb_path = itunesdb_path;
+		source->priv->mount_path = ipod_get_mount_path ();
+		ipod_set_present (source, TRUE);
+	} else if (!is_present && source->priv->was_present) {
+		ipod_set_present (source, TRUE);
+		g_free (source->priv->itunesdb_path);
+		g_free (source->priv->mount_path);
+		source->priv->mount_path = source->priv->itunesdb_path = NULL;
 	}
+
 	g_free (itunesdb_path);
 	return TRUE;
+}
+#ifdef HAVE_HAL
+static void
+hal_mainloop_integration (LibHalContext *ctx,
+		DBusConnection * dbus_connection)
+{
+	dbus_connection_setup_with_g_main (dbus_connection, NULL);
+}
+
+static void
+hal_check_udi (LibHalContext *ctx, const char *udi)
+{
+	dbus_bool_t val;
+	RBiPodSource *source;
+
+	source = hal_ctx_get_user_data (ctx);
+
+	val = hal_device_get_property_bool (ctx, udi,
+			"volume.is_mounted");
+
+	if (val != FALSE) {
+		g_message ("Mounted: %s\n", udi);
+		source->priv->mount_path = hal_device_get_property_string (ctx,
+				udi, "volume.mount_point");
+		source->priv->itunesdb_path = g_build_filename
+			(source->priv->mount_path,
+			 "/iPod_Control/iTunes/iTunesDB",
+			 NULL);
+	} else {
+		g_message ("Unmounted: %s\n", udi);
+		g_free (source->priv->itunesdb_path);
+		g_free (source->priv->mount_path);
+		source->priv->mount_path = NULL;
+		source->priv->itunesdb_path = NULL;
+	}
+
+	source = hal_ctx_get_user_data (ctx);
+	ipod_set_present (source, val);
+}
+
+static void
+hal_property_modified (LibHalContext *ctx,
+		const char *udi,
+		const char *key,
+		dbus_bool_t is_removed,
+		dbus_bool_t is_added)
+{
+	char *parent_udi, *parent_name;
+
+	if (g_strcasecmp (key, "volume.is_mounted") != 0) {
+		return;
+	}
+
+	parent_udi = hal_device_get_property_string (ctx, udi,
+			"info.parent");
+	parent_name = hal_device_get_property_string (ctx, parent_udi,
+			"storage.model");
+	g_free (parent_udi);
+
+	if (parent_name != NULL && strcmp (parent_name, "iPod") == 0) {
+		hal_check_udi (ctx, udi);
+	}
+
+	g_free (parent_name);
+}
+
+static void
+hal_find_existing (LibHalContext *ctx)
+{
+	int i;
+	int num_devices;
+	char **devices_names;
+	char *parent_udi;
+
+	parent_udi = NULL;
+	devices_names = hal_manager_find_device_string_match (ctx,
+			"info.product", "iPod", &num_devices);
+
+	for (i = 0; i < num_devices; i++)
+	{
+		char *string;
+
+		string = hal_device_get_property_string (ctx,
+				devices_names[i], "storage.drive_type");
+		if (string == NULL || strcmp (string, "disk") != 0) {
+			g_free (string);
+			continue;
+		}
+		g_free (string);
+
+		parent_udi = hal_device_get_property_string (ctx,
+				devices_names[i], "info.udi");
+		break;
+	}
+
+	g_message ("found iPod");
+
+	g_strfreev (devices_names);
+
+	if (parent_udi == NULL) {
+		return;
+	}
+
+	devices_names = hal_manager_find_device_string_match (ctx,
+			"info.parent", parent_udi, &num_devices);
+
+	g_free (parent_udi);
+
+	for (i = 0; i < num_devices; i++)
+	{
+		gboolean mounted;
+
+		mounted = hal_device_get_property_bool (ctx,
+				devices_names[i], "volume.is_mounted");
+		if (mounted != FALSE) {
+			g_message ("found udi %s", devices_names[i]);
+			hal_check_udi (ctx, devices_names[i]);
+			break;
+		}
+	}
+
+	g_strfreev (devices_names);
+}
+
+static gboolean
+ipod_itunesdb_monitor_hal (RBiPodSource *source, gboolean enable)
+{
+	if (enable) {
+		LibHalFunctions hal_functions = {
+			hal_mainloop_integration,
+			NULL, /* hal_device_added */
+			NULL, /* hal_device_removed */
+			NULL, /* hal_device_new_capability */
+			NULL, /* hal_device_lost_capability */
+			hal_property_modified,
+			NULL  /* hal_device_condition */
+		};
+
+		source->priv->ctx = hal_initialize (&hal_functions, FALSE);
+		if (source->priv->ctx == NULL) {
+			return FALSE;
+		}
+
+		if (hal_device_property_watch_all (source->priv->ctx)) {
+			return FALSE;
+		}
+
+		hal_ctx_set_user_data (source->priv->ctx, source);
+
+		hal_find_existing (source->priv->ctx);
+	} else {
+		if (source->priv->ctx == NULL) {
+			return FALSE;
+		}
+		hal_shutdown (source->priv->ctx);
+		source->priv->ctx = NULL;
+	}
+
+	return TRUE;
+}
+#endif /* HAVE_HAL */
+
+static void
+ipod_set_present (RBiPodSource *source, gboolean is_present)
+{
+	if (is_present) {
+		g_print ("iPod plugged\n");
+		source->priv->was_present = TRUE;
+		ipod_load_songs (source);
+	} else {
+		g_print ("iPod unplugged\n");
+		source->priv->was_present = FALSE;
+		ipod_unload_songs (source);
+	}
 }
 
 RhythmDBEntryType rhythmdb_entry_ipod_get_type (void) 


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