[rhythmbox] audiocd: replace sj-metadata code
- From: Jonathan Matthew <jmatthew src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rhythmbox] audiocd: replace sj-metadata code
- Date: Mon, 3 Sep 2012 11:39:41 +0000 (UTC)
commit 19528b5a05f0f89c04b060130133f6242fb24bbb
Author: Jonathan Matthew <jonathan d14n org>
Date: Mon Sep 3 21:24:04 2012 +1000
audiocd: replace sj-metadata code
Rather than using libmusicbrainz, we now read disc information
using GStreamer and talk to the musicbrainz web service using
libsoup.
We now use a combo box in the info bar rather than a separate dialog
for choosing between multiple album matches.
.gitignore | 2 +
configure.ac | 50 --
doc/reference/Makefile.am | 8 -
plugins/audiocd/Makefile.am | 89 +--
plugins/audiocd/multiple-album.ui | 119 ----
plugins/audiocd/rb-audiocd-info.c | 281 ++++++++
plugins/audiocd/rb-audiocd-info.h | 68 ++
plugins/audiocd/rb-audiocd-source.c | 1044 ++++++++++++++--------------
plugins/audiocd/rb-musicbrainz-lookup.c | 524 ++++++++++++++
plugins/audiocd/rb-musicbrainz-lookup.h | 110 +++
plugins/audiocd/sj-error.c | 36 -
plugins/audiocd/sj-error.h | 41 --
plugins/audiocd/sj-metadata-getter.c | 261 -------
plugins/audiocd/sj-metadata-getter.h | 62 --
plugins/audiocd/sj-metadata-gvfs.c | 264 -------
plugins/audiocd/sj-metadata-gvfs.h | 57 --
plugins/audiocd/sj-metadata-marshal.list | 1 -
plugins/audiocd/sj-metadata-musicbrainz3.c | 460 ------------
plugins/audiocd/sj-metadata-musicbrainz3.h | 56 --
plugins/audiocd/sj-metadata-musicbrainz4.c | 615 ----------------
plugins/audiocd/sj-metadata-musicbrainz4.h | 56 --
plugins/audiocd/sj-metadata.c | 225 ------
plugins/audiocd/sj-metadata.h | 59 --
plugins/audiocd/sj-structures.c | 81 ---
plugins/audiocd/sj-structures.h | 99 ---
plugins/audiocd/test-cd.c | 234 +++++++
plugins/audiocd/update-from-egg.sh | 25 -
po/POTFILES.in | 8 +-
28 files changed, 1744 insertions(+), 3191 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 73c80e0..95f3244 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,3 +126,5 @@ doc/reference/rhythmbox.signals
#
widgets/test-rb-segmented-bar
widgets/test-uri-dialog
+
+plugins/audiocd/test-cd
diff --git a/configure.ac b/configure.ac
index 76f2a67..b9ec80f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -49,8 +49,6 @@ GST_0_10_REQS=0.10.32
GDK_PIXBUF_REQS=2.18.0
GLIB_REQS=2.32.0
LIBGPOD_REQS=0.6
-MUSICBRAINZ3_REQS=3.0.2
-MUSICBRAINZ4_REQS=4.0.0
TOTEM_PLPARSER_REQS=3.2.0
VALA_REQS=0.9.4
AVAHI_REQS=0.6
@@ -346,46 +344,6 @@ PKG_CHECK_MODULES(GSTCDDA, gstreamer-cdda-0.10)
AC_SUBST(GSTCDDA_LIBS)
AC_SUBST(GSTCDDA_CFLAGS)
-dnl check for MusicBrainz
-have_sj_metadata_getter=no
-AC_ARG_ENABLE(musicbrainz, AC_HELP_STRING([--disable-musicbrainz],
- [build without MusicBrainz support]))
-if test x"$enable_musicbrainz" != "xno"; then
- PKG_CHECK_MODULES(MUSICBRAINZ3, libmusicbrainz3 >= $MUSICBRAINZ3_REQS gconf-2.0, [have_musicbrainz3=yes], [have_musicbrainz3=no])
- AC_SUBST(MUSICBRAINZ3_CFLAGS)
- AC_SUBST(MUSICBRAINZ3_LIBS)
-
- PKG_CHECK_MODULES(MUSICBRAINZ4, libmusicbrainz4 >= $MUSICBRAINZ4_REQS gconf-2.0, [have_musicbrainz4=yes], [have_musicbrainz4=no])
- AC_SUBST(MUSICBRAINZ4_CFLAGS)
- AC_SUBST(MUSICBRAINZ4_LIBS)
-
- if test x"$have_musicbrainz3" = xyes; then
- oldlibs=$LIBS
- LIBS="$LIBS $MUSICBRAINZ3_LIBS"
- AC_CHECK_FUNCS(mb_extract_uuid)
- LIBS="$oldlibs"
-
- AC_DEFINE([HAVE_MUSICBRAINZ3], 1, [Whether libmusicbrainz3 is available])
- have_sj_metadata_getter=yes
- fi
-
- if test x"$have_musicbrainz4" = xyes; then
- AC_DEFINE([HAVE_MUSICBRAINZ4], 1, [Whether libmusicbrainz4 is available])
- have_sj_metadata_getter=yes
- fi
-
- if test x"$have_sj_metadata_getter" = xyes; then
- AC_DEFINE([HAVE_SJ_METADATA_GETTER], 1, [Whether to use the sound-juicer metadata getter code])
- else
- if test x"$enable_musicbrainz" = xyes; then
- AC_MSG_ERROR([MusicBrainz requested, but neither libmusicbrainz3 nor libmusicbrainz4 are available])
- fi
- fi
-fi
-AM_CONDITIONAL([HAVE_MUSICBRAINZ3], [test "x$have_musicbrainz3" = "xyes"])
-AM_CONDITIONAL([HAVE_MUSICBRAINZ4], [test "x$have_musicbrainz4" = "xyes"])
-AM_CONDITIONAL([HAVE_SJ_METADATA_GETTER], [test "x$have_sj_metadata_getter" = "xyes"])
-
AC_PATH_XTRA
CFLAGS="$CFLAGS $X_CFLAGS"
#LIBS=$X_LIBS
@@ -481,8 +439,6 @@ AC_SUBST(mkdir_p) if test x"$mkdir_p" = "x"; then
fi
AC_SUBST(MKINSTALLDIRS)
-AM_GCONF_SOURCE_2
-
dnl LIRC
AC_ARG_ENABLE(lirc,
AC_HELP_STRING([--enable-lirc],[enable lirc support]))
@@ -902,12 +858,6 @@ else
AC_MSG_NOTICE([** Multimedia keys support is enabled])
fi
-if test x"$have_musicbrainz3" = "xyes"; then
- AC_MSG_NOTICE([** MusicBrainz support is enabled])
-else
- AC_MSG_NOTICE([ MusicBrainz support is disabled])
-fi
-
if test x"$use_ipod" = xyes; then
AC_MSG_NOTICE([** iPod integration enabled])
else
diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am
index 4a4cf5c..665d3c2 100644
--- a/doc/reference/Makefile.am
+++ b/doc/reference/Makefile.am
@@ -74,14 +74,6 @@ IGNORE_HFILES= \
rb-sync-state-ui.h \
\
rb-audiocd-source.h \
- sj-error.h \
- sj-metadata-getter.h \
- sj-metadata-gvfs.h \
- sj-metadata-marshal.h \
- sj-metadata-musicbrainz.h \
- sj-metadata-musicbrainz3.h \
- sj-metadata.h \
- sj-structures.h \
rb-audioscrobbler-entry.h \
rb-lastfm-play-order.h \
rb-audioscrobbler.h \
diff --git a/plugins/audiocd/Makefile.am b/plugins/audiocd/Makefile.am
index e757d2d..7ad773e 100644
--- a/plugins/audiocd/Makefile.am
+++ b/plugins/audiocd/Makefile.am
@@ -3,9 +3,13 @@ plugindatadir = $(PLUGINDATADIR)/audiocd
plugin_LTLIBRARIES = libaudiocd.la
libaudiocd_la_SOURCES = \
+ rb-audiocd-info.c \
+ rb-audiocd-info.h \
rb-audiocd-plugin.c \
rb-audiocd-source.c \
- rb-audiocd-source.h
+ rb-audiocd-source.h \
+ rb-musicbrainz-lookup.c \
+ rb-musicbrainz-lookup.h
libaudiocd_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
libaudiocd_la_LIBTOOLFLAGS = --tag=disable-static
@@ -22,12 +26,9 @@ INCLUDES = \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/lib/libmediaplayerid \
-I$(top_srcdir)/metadata \
- -I$(top_srcdir)/player \
-I$(top_srcdir)/rhythmdb \
-I$(top_srcdir)/widgets \
-I$(top_srcdir)/sources \
- -I$(top_srcdir)/iradio \
- -I$(top_srcdir)/podcast \
-I$(top_srcdir)/remote \
-I$(top_builddir)/remote \
-I$(top_srcdir)/plugins \
@@ -42,86 +43,34 @@ INCLUDES = \
-DUSE_TOTEM_PL_PARSER \
-D_BSD_SOURCE
-if HAVE_MUSICBRAINZ3
-libaudiocd_la_LIBADD +=$(MUSICBRAINZ3_LIBS)
-INCLUDES += $(MUSICBRAINZ3_CFLAGS)
-endif
-if HAVE_MUSICBRAINZ4
-libaudiocd_la_LIBADD +=$(MUSICBRAINZ4_LIBS)
-INCLUDES += $(MUSICBRAINZ4_CFLAGS)
-endif
-
libaudiocd_la_LIBADD += $(NULL)
gtkbuilderdir = $(plugindatadir)
gtkbuilder_DATA = \
- album-info.ui \
- multiple-album.ui
+ album-info.ui
uixmldir = $(plugindatadir)
uixml_DATA = audiocd-ui.xml
+noinst_PROGRAMS = test-cd
+
+test_cd_SOURCES = \
+ test-cd.c \
+ rb-audiocd-info.c \
+ rb-audiocd-info.h \
+ rb-musicbrainz-lookup.c \
+ rb-musicbrainz-lookup.h
+test_cd_LDADD = $(RHYTHMBOX_LIBS) $(GSTCDDA_LIBS)
+
plugin_in_files = audiocd.plugin.in
%.plugin: %.plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
plugin_DATA = $(plugin_in_files:.plugin.in=.plugin)
-EXTRA_DIST = $(gtkbuilder_DATA) $(uixml_DATA) $(plugin_in_files) sj-metadata-marshal.list
-
-SJ_FILES = \
- sj-error.c sj-error.h \
- sj-metadata.c sj-metadata.h \
- sj-metadata-gvfs.c sj-metadata-gvfs.h \
- sj-metadata-getter.c sj-metadata-getter.h \
- sj-metadata-musicbrainz3.c sj-metadata-musicbrainz3.h \
- sj-metadata-musicbrainz4.c sj-metadata-musicbrainz4.h \
- sj-structures.c sj-structures.h \
- sj-metadata-marshal.list
-
-EGGDIR=$(srcdir)/../../../sound-juicer/libjuicer
-regenerate-built-sources:
- EGGFILES="$(SJ_FILES)" EGGDIR="$(EGGDIR)" $(srcdir)/update-from-egg.sh || true
-
-MARSHALFILES =
-
-if HAVE_SJ_METADATA_GETTER
-libaudiocd_la_SOURCES += \
- sj-metadata.h sj-metadata.c \
- sj-metadata-getter.c sj-metadata-getter.h \
- sj-metadata-gvfs.c sj-metadata-gvfs.h \
- sj-structures.h sj-structures.c \
- sj-error.h sj-error.c \
- $(MARSHALFILES)
-
-if HAVE_MUSICBRAINZ3
-libaudiocd_la_SOURCES += sj-metadata-musicbrainz3.c sj-metadata-musicbrainz3.h
-endif
-if HAVE_MUSICBRAINZ4
-libaudiocd_la_SOURCES += sj-metadata-musicbrainz4.c sj-metadata-musicbrainz4.h
-endif
-
-MARSHALFILES += sj-metadata-marshal.h sj-metadata-marshal.c
-
-sj-metadata-marshal.h: sj-metadata-marshal.list
- ( $(GLIB_GENMARSHAL) --prefix=metadata_marshal $< \
- --header > marshal-header.tmp \
- && mv marshal-header.tmp $@ ) \
- || ( rm -f marshal-header.tmp && exit 1 )
-
-sj-metadata-marshal.c: sj-metadata-marshal.list
- ( $(GLIB_GENMARSHAL) --prefix=metadata_marshal $< \
- --body > marshal-source.tmp \
- && echo "#include \"sj-metadata-marshal.h\"" > $@ \
- && cat marshal-source.tmp >> $@ \
- && rm -f marshal-source.tmp ) \
- || ( rm -f marshal-source.tmp && exit 1 )
-
-endif
-
-BUILT_SOURCES = $(MARSHALFILES)
-
-CLEANFILES = $(plugin_DATA) $(BUILT_SOURCES)
+EXTRA_DIST = $(gtkbuilder_DATA) $(uixml_DATA) $(plugin_in_files)
+
+CLEANFILES = $(plugin_DATA)
DISTCLEANFILES = $(plugin_DATA)
diff --git a/plugins/audiocd/rb-audiocd-info.c b/plugins/audiocd/rb-audiocd-info.c
new file mode 100644
index 0000000..46c5203
--- /dev/null
+++ b/plugins/audiocd/rb-audiocd-info.c
@@ -0,0 +1,281 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2012 Jonathan Matthew
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * The Rhythmbox authors hereby grant permission for non-GPL compatible
+ * GStreamer plugins to be used and distributed together with GStreamer
+ * and Rhythmbox. This permission is above and beyond the permissions granted
+ * by the GPL license by which Rhythmbox is covered. If you modify this code
+ * you may extend this exception to your version of the code, but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gst/gst.h>
+#include <gst/cdda/gstcddabasesrc.h>
+#include <gio/gio.h>
+
+#include "rb-audiocd-info.h"
+
+static gboolean
+read_gst_disc_info (RBAudioCDInfo *info, GError **error)
+{
+ GstElement *source;
+ GstElement *sink;
+ GstElement *pipeline;
+ GstFormat format;
+ GstFormat out_format;
+ GstBus *bus;
+ gint64 num_tracks;
+ gboolean done;
+ int i;
+
+ source = gst_element_make_from_uri (GST_URI_SRC, "cdda://", NULL);
+ if (source == NULL) {
+ /* if cdparanoiasrc wasn't in base and installed by default
+ * everywhere, plugin install might be worth trying here.
+ */
+ g_set_error_literal (error,
+ GST_CORE_ERROR,
+ GST_CORE_ERROR_MISSING_PLUGIN,
+ _("Could not find a GStreamer CD source plugin"));
+ return FALSE;
+ }
+
+ g_object_set (source, "device", info->device, NULL);
+ pipeline = gst_pipeline_new (NULL);
+ sink = gst_element_factory_make ("fakesink", NULL);
+ gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
+ gst_element_link (source, sink);
+
+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "paranoia-mode"))
+ g_object_set (source, "paranoia-mode", 0, NULL);
+
+ gst_element_set_state (pipeline, GST_STATE_PAUSED);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ done = FALSE;
+ while (done == FALSE) {
+ GstMessage *msg;
+ GstTagList *tags;
+
+ msg = gst_bus_timed_pop (bus, 3 * GST_SECOND);
+ if (msg == NULL)
+ break;
+
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_TAG:
+ gst_message_parse_tag (msg, &tags);
+
+ gst_tag_list_get_string (tags,
+ GST_TAG_CDDA_MUSICBRAINZ_DISCID,
+ &info->musicbrainz_disc_id);
+ gst_tag_list_get_string (tags,
+ GST_TAG_CDDA_MUSICBRAINZ_DISCID_FULL,
+ &info->musicbrainz_full_disc_id);
+
+ gst_tag_list_free (tags);
+ break;
+
+ case GST_MESSAGE_STATE_CHANGED:
+ if (GST_MESSAGE_SRC (msg) == GST_OBJECT (pipeline)) {
+ GstState oldstate;
+ GstState newstate;
+ GstState pending;
+
+ gst_message_parse_state_changed (msg, &oldstate, &newstate, &pending);
+ if (newstate == GST_STATE_PAUSED && pending == GST_STATE_VOID_PENDING)
+ done = TRUE;
+ }
+ break;
+ case GST_MESSAGE_ERROR:
+ gst_message_parse_error (msg, error, NULL);
+ done = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ gst_message_unref (msg);
+ }
+
+ if (*error == NULL) {
+ format = gst_format_get_by_nick ("track");
+ out_format = format;
+ gst_element_query_duration (source, &out_format, &num_tracks);
+ info->num_tracks = num_tracks;
+
+ info->tracks = g_new0 (RBAudioCDTrack, num_tracks);
+ for (i = 0; i < num_tracks; i++) {
+ RBAudioCDTrack *track = &info->tracks[i];
+ GstCddaBaseSrcTrack *gst_track = &GST_CDDA_BASE_SRC (source)->tracks[i];
+ guint64 duration = 0;
+
+ track->is_audio = gst_track->is_audio;
+ track->track_num = gst_track->num;
+
+ gst_tag_list_get_uint64 (gst_track->tags, GST_TAG_DURATION, &duration);
+ track->duration = (duration / GST_MSECOND);
+ }
+ }
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (bus);
+ gst_object_unref (pipeline);
+ return (*error == NULL);
+}
+
+static void
+read_gvfs_disc_info (RBAudioCDInfo *info)
+{
+ GFile *cdda;
+ GFileInfo *fileinfo;
+ GFileEnumerator *tracks;
+ const char *attr;
+ char *uri;
+ char *dev;
+
+ dev = g_path_get_basename (info->device);
+ uri = g_strdup_printf ("cdda://%s", dev);
+ g_free (dev);
+
+ cdda = g_file_new_for_uri (uri);
+ g_free (uri);
+
+ fileinfo = g_file_query_info (cdda, "xattr::*", G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ if (fileinfo == NULL) {
+ g_object_unref (cdda);
+ return;
+ }
+
+ attr = g_file_info_get_attribute_string (fileinfo, "xattr::org.gnome.audio.title");
+ if (attr != NULL) {
+ info->album = g_strdup (attr);
+ }
+ attr = g_file_info_get_attribute_string (fileinfo, "xattr::org.gnome.audio.artist");
+ if (attr != NULL) {
+ info->album_artist = g_strdup (attr);
+ }
+ attr = g_file_info_get_attribute_string (fileinfo, "xattr::org.gnome.audio.genre");
+ if (attr != NULL) {
+ info->genre = g_strdup (attr);
+ }
+
+ tracks = g_file_enumerate_children (cdda, G_FILE_ATTRIBUTE_STANDARD_NAME ",xattr::*", G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ if (tracks != NULL) {
+ for (fileinfo = g_file_enumerator_next_file (tracks, NULL, NULL);
+ fileinfo != NULL;
+ fileinfo = g_file_enumerator_next_file (tracks, NULL, NULL)) {
+ const char *name;
+ const char *attr;
+ int track_num;
+
+ name = g_file_info_get_name (fileinfo);
+ if (name == NULL || sscanf (name, "Track %d.wav", &track_num) != 1) {
+ continue;
+ }
+
+ if (track_num < 1 || track_num > info->num_tracks) {
+ continue;
+ }
+ g_assert (track_num == info->tracks[track_num-1].track_num);
+
+ attr = g_file_info_get_attribute_string (fileinfo, "xattr::org.gnome.audio.title");
+ if (attr != NULL) {
+ info->tracks[track_num - 1].title = g_strdup (attr);
+ }
+ attr = g_file_info_get_attribute_string (fileinfo, "xattr::org.gnome.audio.artist");
+ if (attr != NULL) {
+ info->tracks[track_num - 1].artist = g_strdup (attr);
+ }
+ }
+ }
+ g_object_unref (tracks);
+
+ g_object_unref (cdda);
+}
+
+static void
+audiocd_info_thread (GSimpleAsyncResult *result, GObject *object, GCancellable *cancellable)
+{
+ RBAudioCDInfo *info;
+ GError *error = NULL;
+
+ info = g_simple_async_result_get_op_res_gpointer (result);
+
+ if (read_gst_disc_info (info, &error)) {
+ read_gvfs_disc_info (info);
+ } else {
+ rb_audiocd_info_free (info);
+ g_simple_async_result_set_op_res_gpointer (result, NULL, NULL);
+ g_simple_async_result_take_error (result, error);
+ }
+}
+
+void
+rb_audiocd_info_free (RBAudioCDInfo *info)
+{
+ int i;
+
+ g_free (info->device);
+ g_free (info->musicbrainz_disc_id);
+ g_free (info->musicbrainz_full_disc_id);
+ g_free (info->album);
+ g_free (info->genre);
+ g_free (info->album_artist);
+
+ for (i = 0; i < info->num_tracks; i++) {
+ g_free (info->tracks[i].artist);
+ g_free (info->tracks[i].title);
+ }
+ g_free (info->tracks);
+ g_free (info);
+}
+
+void
+rb_audiocd_info_get (const char *device,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ RBAudioCDInfo *info;
+
+ result = g_simple_async_result_new (NULL, callback, user_data, rb_audiocd_info_get);
+ g_simple_async_result_set_check_cancellable (result, cancellable);
+
+ info = g_new0 (RBAudioCDInfo, 1);
+ info->device = g_strdup (device);
+ g_simple_async_result_set_op_res_gpointer (result, info, NULL);
+
+ g_simple_async_result_run_in_thread (result, audiocd_info_thread, G_PRIORITY_DEFAULT, cancellable);
+}
+
+RBAudioCDInfo *
+rb_audiocd_info_finish (GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, rb_audiocd_info_get),
+ NULL);
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return NULL;
+
+ return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+}
diff --git a/plugins/audiocd/rb-audiocd-info.h b/plugins/audiocd/rb-audiocd-info.h
new file mode 100644
index 0000000..8e63534
--- /dev/null
+++ b/plugins/audiocd/rb-audiocd-info.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 Jonathan Matthew <jonathan d14n org>
+ *
+ * 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.
+ *
+ * The Rhythmbox authors hereby grant permission for non-GPL compatible
+ * GStreamer plugins to be used and distributed together with GStreamer
+ * and Rhythmbox. This permission is above and beyond the permissions granted
+ * by the GPL license by which Rhythmbox is covered. If you modify this code
+ * you may extend this exception to your version of the code, but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef RB_AUDIOCD_INFO_H
+#define RB_AUDIOCD_INFO_H
+
+#include <glib.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ gboolean is_audio;
+ int track_num;
+ int duration; /* milliseconds? */
+ char *artist;
+ char *title;
+} RBAudioCDTrack;
+
+typedef struct {
+ char *device;
+
+ char *musicbrainz_disc_id;
+ char *musicbrainz_full_disc_id;
+
+ char *album;
+ char *genre;
+ char *album_artist;
+
+ int num_tracks;
+ RBAudioCDTrack *tracks;
+} RBAudioCDInfo;
+
+RBAudioCDInfo * rb_audiocd_info_finish (GAsyncResult *result,
+ GError **error);
+
+void rb_audiocd_info_get (const char *device,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void rb_audiocd_info_free (RBAudioCDInfo *info);
+
+#endif /* RB_AUDIOCD_INFO_H */
diff --git a/plugins/audiocd/rb-audiocd-source.c b/plugins/audiocd/rb-audiocd-source.c
index 66c3bc5..68894c3 100644
--- a/plugins/audiocd/rb-audiocd-source.c
+++ b/plugins/audiocd/rb-audiocd-source.c
@@ -26,18 +26,13 @@
*
*/
-/*
- * TODO
- * * save user-edited metadata somewhere (use S-J stuff?)
- */
-
#include "config.h"
#include <string.h>
+
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <gst/gst.h>
-#include <gst/cdda/gstcddabasesrc.h>
#include "rhythmdb.h"
#include "rb-shell.h"
@@ -50,11 +45,8 @@
#include "rb-file-helpers.h"
#include "rb-source-toolbar.h"
#include "rb-shell-player.h"
-
-#ifdef HAVE_SJ_METADATA_GETTER
-#include "sj-metadata-getter.h"
-#include "sj-structures.h"
-#endif
+#include "rb-audiocd-info.h"
+#include "rb-musicbrainz-lookup.h"
enum
{
@@ -76,20 +68,15 @@ static guint impl_want_uri (RBSource *source, const char *uri);
static gboolean impl_uri_is_source (RBSource *source, const char *uri);
static RBEntryView *impl_get_entry_view (RBSource *source);
-static gpointer rb_audiocd_load_songs (RBAudioCdSource *source);
-static void rb_audiocd_load_metadata (RBAudioCdSource *source, RhythmDB *db);
-static void rb_audiocd_load_metadata_cancel (RBAudioCdSource *source);
-
static gboolean update_artist_cb (GtkWidget *widget, GdkEventFocus *event, RBAudioCdSource *source);
static gboolean update_artist_sort_cb (GtkWidget *widget, GdkEventFocus *event, RBAudioCdSource *source);
static gboolean update_album_cb (GtkWidget *widget, GdkEventFocus *event, RBAudioCdSource *source);
static gboolean update_genre_cb (GtkWidget *widget, GdkEventFocus *event, RBAudioCdSource *source);
static gboolean update_year_cb (GtkWidget *widget, GdkEventFocus *event, RBAudioCdSource *source);
static gboolean update_disc_number_cb (GtkWidget *widget, GdkEventFocus *event, RBAudioCdSource *source);
-#if defined(HAVE_SJ_METADATA_GETTER)
-static void info_bar_response_cb (GtkInfoBar *info_bar, gint response_id, RBAudioCdSource *source);
-#endif
+static void rb_audiocd_source_load_disc_info (RBAudioCdSource *source);
+static gboolean rb_audiocd_source_load_metadata (RBAudioCdSource *source);
static void reload_metadata_cmd (GtkAction *action, RBAudioCdSource *source);
static void copy_tracks_cmd (GtkAction *action, RBAudioCdSource *source);
@@ -114,11 +101,14 @@ struct _RBAudioCdSourcePrivate
GVolume *volume;
gchar *device_path;
+ RBAudioCDInfo *disc_info;
+ RBMusicBrainzData *mb_data;
+ GList *releases;
GList *tracks;
- GstElement *pipeline;
- GstElement *cdda;
- GstElement *fakesink;
+ GCancellable *cancel_disc_info;
+ GtkWidget *infogrid;
+ GtkWidget *info_bar;
RBEntryView *entry_view;
GtkWidget *artist_entry;
@@ -128,19 +118,6 @@ struct _RBAudioCdSourcePrivate
GtkWidget *genre_entry;
GtkWidget *disc_number_entry;
-#ifdef HAVE_SJ_METADATA_GETTER
- SjMetadataGetter *metadata;
- GtkWidget *multiple_album_dialog;
- GtkWidget *albums_listview;
- GtkListStore *albums_store;
- GList *albums;
-
- GtkWidget *info_bar;
- GtkWidget *info_bar_label;
-
- char *submit_url;
-#endif
-
GtkActionGroup *action_group;
};
@@ -161,10 +138,6 @@ GType rb_audiocd_entry_type_get_type (void);
G_DEFINE_DYNAMIC_TYPE (RBAudioCdEntryType, rb_audiocd_entry_type, RHYTHMDB_TYPE_ENTRY_TYPE);
-#ifdef HAVE_SJ_METADATA_GETTER
-static void multiple_album_dialog (RBAudioCdSource *source, GList *albums);
-#endif
-
static GtkActionEntry rb_audiocd_source_actions[] = {
{ "AudioCdCopyTracks", GTK_STOCK_CDROM, N_("_Extract to Library"), NULL,
N_("Copy tracks to the library"),
@@ -265,13 +238,16 @@ rb_audiocd_source_finalize (GObject *object)
RBAudioCdSource *source = RB_AUDIOCD_SOURCE (object);
g_free (source->priv->device_path);
-#ifdef HAVE_SJ_METADATA_GETTER
- g_free (source->priv->submit_url);
-#endif
if (source->priv->tracks) {
g_list_free (source->priv->tracks);
- source->priv->tracks = NULL;
+ }
+
+ if (source->priv->disc_info) {
+ rb_audiocd_info_free (source->priv->disc_info);
+ }
+ if (source->priv->mb_data) {
+ rb_musicbrainz_data_free (source->priv->mb_data);
}
G_OBJECT_CLASS (rb_audiocd_source_parent_class)->finalize (object);
@@ -287,11 +263,6 @@ rb_audiocd_source_dispose (GObject *object)
source->priv->action_group = NULL;
}
- if (source->priv->pipeline) {
- gst_object_unref (GST_OBJECT (source->priv->pipeline));
- source->priv->pipeline = NULL;
- }
-
G_OBJECT_CLASS (rb_audiocd_source_parent_class)->dispose (object);
}
@@ -332,7 +303,6 @@ rb_audiocd_source_constructed (GObject *object)
GtkBuilder *builder;
GtkWidget *grid;
GtkWidget *widget;
- GtkWidget *infogrid;
GtkAction *action;
GObject *plugin;
RBShell *shell;
@@ -344,15 +314,12 @@ rb_audiocd_source_constructed (GObject *object)
RBSourceToolbar *toolbar;
char *ui_file;
int toggle_width;
-#if defined(HAVE_SJ_METADATA_GETTER)
- GtkWidget *box;
- char *message;
-#endif
RB_CHAIN_GOBJECT_METHOD (rb_audiocd_source_parent_class, constructed, object);
source = RB_AUDIOCD_SOURCE (object);
rb_device_source_set_display_details (RB_DEVICE_SOURCE (source));
+ source->priv->device_path = g_volume_get_identifier (source->priv->volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
g_object_get (source, "shell", &shell, NULL);
g_object_get (shell,
@@ -377,10 +344,6 @@ rb_audiocd_source_constructed (GObject *object)
for Copy to Library action. */
g_object_set (action, "short-label", _("Extract"), NULL);
-#if !defined(HAVE_SJ_METADATA_GETTER)
- action = gtk_action_group_get_action (source->priv->action_group, "AudioCdSourceReloadMetadata");
- g_object_set (action, "visible", FALSE, NULL);
-#endif
/* source toolbar */
toolbar = rb_source_toolbar_new (RB_SOURCE (source), ui_manager);
g_object_unref (ui_manager);
@@ -458,28 +421,8 @@ rb_audiocd_source_constructed (GObject *object)
builder = rb_builder_load (ui_file, NULL);
g_free (ui_file);
- infogrid = GTK_WIDGET (gtk_builder_get_object (builder, "album_info"));
- g_assert (infogrid != NULL);
-
-#if defined(HAVE_SJ_METADATA_GETTER)
- /* Info bar for non-Musicbrainz data */
- source->priv->info_bar = gtk_info_bar_new_with_buttons (_("S_ubmit Album"), GTK_RESPONSE_OK,
- _("Hide"), GTK_RESPONSE_CANCEL,
- NULL);
- message = g_strdup_printf ("<b>%s</b>\n%s", _("Could not find this album on MusicBrainz."),
- _("You can improve the MusicBrainz database by adding this album."));
- source->priv->info_bar_label = gtk_label_new (NULL);
- gtk_label_set_markup (GTK_LABEL (source->priv->info_bar_label), message);
- gtk_label_set_justify (GTK_LABEL (source->priv->info_bar_label), GTK_JUSTIFY_LEFT);
- g_free (message);
- box = gtk_info_bar_get_content_area (GTK_INFO_BAR (source->priv->info_bar));
- gtk_container_add (GTK_CONTAINER (box), source->priv->info_bar_label);
- gtk_widget_show_all (box);
- gtk_widget_set_no_show_all (source->priv->info_bar, TRUE);
- g_signal_connect (G_OBJECT (source->priv->info_bar), "response",
- G_CALLBACK (info_bar_response_cb), source);
- gtk_grid_attach (GTK_GRID (infogrid), source->priv->info_bar, 0, 0, 2, 1);
-#endif
+ source->priv->infogrid = GTK_WIDGET (gtk_builder_get_object (builder, "album_info"));
+ g_assert (source->priv->infogrid != NULL);
source->priv->artist_entry = GTK_WIDGET (gtk_builder_get_object (builder, "artist_entry"));
source->priv->artist_sort_entry = GTK_WIDGET (gtk_builder_get_object (builder, "artist_sort_entry"));
@@ -498,7 +441,7 @@ rb_audiocd_source_constructed (GObject *object)
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET (toolbar), 0, 0, 1, 1);
- gtk_grid_attach (GTK_GRID (grid), infogrid, 0, 1, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), source->priv->infogrid, 0, 1, 1, 1);
gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET (source->priv->entry_view), 0, 2, 1, 1);
gtk_widget_set_margin_top (GTK_WIDGET (grid), 6);
g_object_unref (builder);
@@ -508,7 +451,9 @@ rb_audiocd_source_constructed (GObject *object)
gtk_widget_show_all (grid);
gtk_container_add (GTK_CONTAINER (source), grid);
- g_thread_new ("audiocd-scan", (GThreadFunc)rb_audiocd_load_songs, g_object_ref (source));
+ source->priv->cancel_disc_info = g_cancellable_new ();
+ rb_audiocd_source_load_disc_info (source);
+
g_object_unref (db);
g_object_unref (shell_player);
}
@@ -617,558 +562,601 @@ entry_set_string_prop (RhythmDB *db,
g_value_unset (&value);
}
-static RhythmDBEntry *
-rb_audiocd_create_track_entry (RBAudioCdSource *source,
- RhythmDB *db,
- guint track_number)
+static void
+clear_info_bar (RBAudioCdSource *source)
{
- RhythmDBEntry *entry;
- char *audio_path;
- guint64 duration;
- GValue value = {0, };
- gchar *str;
- RhythmDBEntryType *entry_type;
- RBAudioCDEntryData *extra_data;
+ if (source->priv->info_bar != NULL) {
+ gtk_widget_hide (source->priv->info_bar);
+ gtk_container_remove (GTK_CONTAINER (source->priv->infogrid), source->priv->info_bar);
+ source->priv->info_bar = NULL;
+ }
+}
- audio_path = g_strdup_printf ("cdda://%d#%s", track_number, source->priv->device_path);
+static void
+show_info_bar (RBAudioCdSource *source, GtkWidget *info_bar)
+{
+ clear_info_bar (source);
- g_object_get (source, "entry-type", &entry_type, NULL);
- rb_debug ("Audio CD - create entry for track %d from %s", track_number, audio_path);
- entry = rhythmdb_entry_new (db, entry_type, audio_path);
- g_object_unref (entry_type);
- if (entry == NULL) {
- g_free (audio_path);
- return NULL;
- }
+ gtk_widget_show_all (info_bar);
+ gtk_grid_attach (GTK_GRID (source->priv->infogrid), info_bar, 0, 0, 2, 1);
+ source->priv->info_bar = info_bar;
+}
- /* generate track # */
- g_value_init (&value, G_TYPE_ULONG);
- g_value_set_ulong (&value, track_number);
- rhythmdb_entry_set (db, entry,
- RHYTHMDB_PROP_TRACK_NUMBER,
- &value);
- g_value_unset (&value);
- /* generate track name */
- g_value_init (&value, G_TYPE_STRING);
- str = g_strdup_printf (_("Track %u"), track_number);
- g_value_take_string (&value, str);
- rhythmdb_entry_set (db, entry,
- RHYTHMDB_PROP_TITLE,
- &value);
- g_value_unset (&value);
+static void
+submit_info_bar_response_cb (GtkInfoBar *info_bar, gint response_id, RBAudioCdSource *source)
+{
+ GError *error = NULL;
- /* determine the duration
- * FIXME: http://bugzilla.gnome.org/show_bug.cgi?id=551011 */
- if (gst_tag_list_get_uint64 (GST_CDDA_BASE_SRC(source->priv->cdda)->tracks[track_number - 1].tags, GST_TAG_DURATION, &duration)) {
- g_value_init (&value, G_TYPE_ULONG);
- g_value_set_ulong (&value, (gulong)(duration / GST_SECOND));
- rhythmdb_entry_set (db, entry,
- RHYTHMDB_PROP_DURATION,
- &value);
- g_value_unset (&value);
- } else {
- g_warning ("Failed to query cd track duration");
+ if (response_id == GTK_RESPONSE_OK) {
+ char *submit_url;
+
+ submit_url = rb_musicbrainz_create_submit_url (
+ source->priv->disc_info->musicbrainz_disc_id,
+ source->priv->disc_info->musicbrainz_full_disc_id);
+
+ if (!gtk_show_uri (NULL, submit_url, GDK_CURRENT_TIME, &error)) {
+ rb_debug ("Could not launch submit URL %s: %s", submit_url, error->message);
+ g_error_free (error);
+ }
+ g_free (submit_url);
}
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_ARTIST, FALSE, NULL);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_ALBUM, FALSE, NULL);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_GENRE, FALSE, NULL);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_MEDIA_TYPE, TRUE, "audio/x-raw-int");
+ clear_info_bar (source);
+}
- extra_data = RHYTHMDB_ENTRY_GET_TYPE_DATA (entry, RBAudioCDEntryData);
- extra_data->extract = TRUE;
+static void
+show_submit_info_bar (RBAudioCdSource *source)
+{
+ GtkWidget *info_bar;
+ GtkWidget *label;
+ GtkWidget *box;
+ char *message;
- rhythmdb_commit (db);
- g_free (audio_path);
+ rb_debug ("showing musicbrainz submit info bar");
+
+ info_bar = gtk_info_bar_new_with_buttons (_("S_ubmit Album"), GTK_RESPONSE_OK,
+ _("Hide"), GTK_RESPONSE_CANCEL,
+ NULL);
+
+ message = g_strdup_printf ("<b>%s</b>\n%s", _("Could not find this album on MusicBrainz."),
+ _("You can improve the MusicBrainz database by adding this album."));
+ label = gtk_label_new (NULL);
+ gtk_label_set_markup (GTK_LABEL (label), message);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ g_free (message);
- return entry;
+ box = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
+ gtk_container_add (GTK_CONTAINER (box), label);
+
+ g_signal_connect (G_OBJECT (info_bar), "response",
+ G_CALLBACK (submit_info_bar_response_cb), source);
+ show_info_bar (source, info_bar);
}
-static gboolean
-rb_audiocd_get_cd_info (RBAudioCdSource *source,
- gint64 *num_tracks)
+static void
+mb_error_info_bar_response_cb (GtkInfoBar *info_bar, gint response_id, RBAudioCdSource *source)
{
- GstFormat fmt = gst_format_get_by_nick ("track");
- GstFormat out_fmt = fmt;
- if (!gst_element_query_duration (source->priv->cdda, &out_fmt, num_tracks) || out_fmt != fmt) {
- return FALSE;
+ if (response_id == GTK_RESPONSE_OK) {
+ rb_audiocd_source_load_metadata (source);
}
-
- return TRUE;
+ clear_info_bar (source);
}
-static gboolean
-rb_audiocd_scan_songs (RBAudioCdSource *source,
- RhythmDB *db)
+static void
+show_lookup_error_info_bar (RBAudioCdSource *source, GError *error)
{
- gint64 i, num_tracks;
- GstStateChangeReturn ret;
- gboolean ok = TRUE;
-
- ret = gst_element_set_state (source->priv->pipeline, GST_STATE_PAUSED);
- if (ret == GST_STATE_CHANGE_ASYNC) {
- ret = gst_element_get_state (source->priv->pipeline, NULL, NULL, 3 * GST_SECOND);
- }
- if (ret == GST_STATE_CHANGE_FAILURE) {
- gdk_threads_enter ();
- rb_error_dialog (NULL, _("Couldn't load Audio CD"),
- _("Rhythmbox couldn't access the CD."));
- gdk_threads_leave ();
- ok = FALSE;
- }
+ GtkWidget *info_bar;
+ GtkWidget *label;
+ GtkWidget *box;
+ char *message;
- if (ok && !rb_audiocd_get_cd_info (source, &num_tracks)) {
- gdk_threads_enter ();
- rb_error_dialog (NULL, _("Couldn't load Audio CD"),
- _("Rhythmbox couldn't read the CD information."));
- gdk_threads_leave ();
- ok = FALSE;
- }
+ rb_debug ("showing musicbrainz error info bar");
- if (ok) {
- rb_debug ("importing Audio Cd %s - %d tracks", source->priv->device_path, (int)num_tracks);
- for (i = 1; i <= num_tracks; i++) {
- RhythmDBEntry* entry = rb_audiocd_create_track_entry (source, db, i);
+ info_bar = gtk_info_bar_new_with_buttons (_("_Retry"), GTK_RESPONSE_OK,
+ _("_Hide"), GTK_RESPONSE_CANCEL,
+ NULL);
- if (entry)
- source->priv->tracks = g_list_prepend (source->priv->tracks, entry);
- else
- g_warning ("Could not create audio cd track entry");
- }
- source->priv->tracks = g_list_reverse (source->priv->tracks);
- }
+ message = g_strdup_printf ("<b>%s</b>\n%s", _("Could not search MusicBrainz for album details."),
+ error->message);
+ label = gtk_label_new (NULL);
+ gtk_label_set_markup (GTK_LABEL (label), message);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ g_free (message);
- if (gst_element_set_state (source->priv->pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) {
- rb_debug ("failed to set cd state");
- }
+ box = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
+ gtk_container_add (GTK_CONTAINER (box), label);
- return ok;
+ g_signal_connect (G_OBJECT (info_bar), "response",
+ G_CALLBACK (mb_error_info_bar_response_cb), source);
+ show_info_bar (source, info_bar);
}
+static void
+cd_error_info_bar_response_cb (GtkInfoBar *info_bar, gint response_id, RBAudioCdSource *source)
+{
+ if (response_id == GTK_RESPONSE_OK) {
+ rb_audiocd_source_load_disc_info (source);
+ }
+ clear_info_bar (source);
+}
static void
-reload_metadata_cmd (GtkAction *action, RBAudioCdSource *source)
+show_cd_error_info_bar (RBAudioCdSource *source, GError *error)
{
-#ifdef HAVE_SJ_METADATA_GETTER
- RhythmDB *db;
+ GtkWidget *info_bar;
+ GtkWidget *label;
+ GtkWidget *box;
+ char *message;
- g_return_if_fail (RB_IS_AUDIOCD_SOURCE (source));
+ rb_debug ("showing cd read error info bar");
- db = get_db_for_source (RB_AUDIOCD_SOURCE (source));
- rb_audiocd_load_metadata (RB_AUDIOCD_SOURCE (source), db);
- g_object_unref (db);
-#endif
+ info_bar = gtk_info_bar_new_with_buttons (_("_Retry"), GTK_RESPONSE_OK,
+ _("_Hide"), GTK_RESPONSE_CANCEL,
+ NULL);
+
+ message = g_strdup_printf ("<b>%s</b>\n%s", _("Could not read the CD device."),
+ error->message);
+ label = gtk_label_new (NULL);
+ gtk_label_set_markup (GTK_LABEL (label), message);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ g_free (message);
+
+ box = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
+ gtk_container_add (GTK_CONTAINER (box), label);
+
+ g_signal_connect (G_OBJECT (info_bar), "response",
+ G_CALLBACK (cd_error_info_bar_response_cb), source);
+ show_info_bar (source, info_bar);
}
-#ifdef HAVE_SJ_METADATA_GETTER
static void
-apply_album_metadata (RBAudioCdSource *source, AlbumDetails *album)
+apply_musicbrainz_release (RBAudioCdSource *source, RBMusicBrainzData *release)
{
+ RBMusicBrainzData *medium;
RhythmDB *db;
- GValue true_value = {0,};
- GList *cd_track;
-
- db = get_db_for_source (source);
-
- g_value_init (&true_value, G_TYPE_BOOLEAN);
- g_value_set_boolean (&true_value, TRUE);
-
- if (album->metadata_source != SOURCE_MUSICBRAINZ) {
- source->priv->submit_url = sj_metadata_getter_get_submit_url (source->priv->metadata);
- if (source->priv->submit_url != NULL)
- gtk_widget_show (source->priv->info_bar);
+ GList *l;
+ const char *value;
+ int disc_num = 0;
+ gulong release_date = 0;
+ const char *album;
+ const char *album_id;
+ const char *album_artist;
+ const char *album_artist_id;
+ const char *album_artist_sortname;
+
+ medium = rb_musicbrainz_data_find_child (release,
+ "disc-id",
+ source->priv->disc_info->musicbrainz_disc_id);
+ g_assert (medium != NULL);
+
+ /* set album stuff in widgets */
+ album = rb_musicbrainz_data_get_attr_value (release, RB_MUSICBRAINZ_ATTR_ALBUM);
+ if (album != NULL) {
+ rb_debug ("album title: %s", album);
+ gtk_entry_set_text (GTK_ENTRY (source->priv->album_entry), album);
+ g_object_set (source, "name", album, NULL);
}
- if (album->metadata_source == SOURCE_FALLBACK) {
- rb_debug ("ignoring CD metadata from fallback source");
- g_object_unref (source->priv->metadata);
- source->priv->metadata = NULL;
- g_object_unref (db);
- g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
- return;
+ album_artist = rb_musicbrainz_data_get_attr_value (release, RB_MUSICBRAINZ_ATTR_ALBUM_ARTIST);
+ if (album_artist != NULL) {
+ rb_debug ("album artist: %s", album_artist);
+ gtk_entry_set_text (GTK_ENTRY (source->priv->artist_entry), album_artist);
}
- if (album->artist != NULL) {
- gtk_entry_set_text (GTK_ENTRY (source->priv->artist_entry), album->artist);
+ album_artist_sortname = rb_musicbrainz_data_get_attr_value (release, RB_MUSICBRAINZ_ATTR_ALBUM_ARTIST_SORTNAME);
+ if (album_artist_sortname != NULL) {
+ rb_debug ("album artist sortname: %s", album_artist_sortname);
+ gtk_entry_set_text (GTK_ENTRY (source->priv->artist_sort_entry), album_artist_sortname);
}
- if (album->artist_sortname != NULL) {
- gtk_entry_set_text (GTK_ENTRY (source->priv->artist_sort_entry), album->artist_sortname);
- }
- if (album->title != NULL) {
- gtk_entry_set_text (GTK_ENTRY (source->priv->album_entry), album->title);
- }
- if (album->release_date != NULL) {
- char *year;
- year = g_strdup_printf ("%d", g_date_get_year (album->release_date));
- gtk_entry_set_text (GTK_ENTRY (source->priv->year_entry), year);
- g_free (year);
- }
- if (album->disc_number != 0) {
- char *num;
- num = g_strdup_printf ("%d", album->disc_number);
- gtk_entry_set_text (GTK_ENTRY (source->priv->disc_number_entry), num);
- g_free (num);
+
+ value = rb_musicbrainz_data_get_attr_value (release, RB_MUSICBRAINZ_ATTR_DATE);
+ if (value != NULL) {
+ int year = 1;
+ int month = 1;
+ int day = 1;
+
+ if (sscanf (value, "%u-%u-%u", &year, &month, &day) > 0) {
+ GDate date;
+ char *year_text;
+
+ year_text = g_strdup_printf ("%d", year);
+ gtk_entry_set_text (GTK_ENTRY (source->priv->year_entry), year_text);
+ g_free (year_text);
+
+ g_date_set_dmy (&date,
+ (day == 0) ? 1 : day,
+ (month == 0) ? 1 : month,
+ year);
+ release_date = g_date_get_julian (&date);
+ } else {
+ rb_debug ("unable to parse release date: %s", value);
+ }
}
- if (album->genre != NULL) {
- gtk_entry_set_text (GTK_ENTRY (source->priv->genre_entry), album->genre);
+
+ value = rb_musicbrainz_data_get_attr_value (medium, RB_MUSICBRAINZ_ATTR_DISC_NUMBER);
+ if (value != NULL) {
+ disc_num = strtol (value, NULL, 10); /* 0 is ok if this fails */
+ gtk_entry_set_text (GTK_ENTRY (source->priv->disc_number_entry), value);
+ rb_debug ("disc number %d", disc_num);
}
- g_object_set (G_OBJECT (source), "name", album->title, NULL);
- rb_debug ("musicbrainz_albumid: %s", album->album_id);
- rb_debug ("musicbrainz_albumartistid: %s", album->artist_id);
- rb_debug ("album artist: %s", album->artist);
- rb_debug ("album artist sortname: %s", album->artist_sortname);
- rb_debug ("disc number: %d", album->disc_number);
- rb_debug ("genre: %s", album->genre);
-
- cd_track = source->priv->tracks;
- while (album->tracks && cd_track) {
- TrackDetails *track = (TrackDetails*)album->tracks->data;
- RhythmDBEntry *entry = cd_track->data;
- GValue value = {0, };
+ album_id = rb_musicbrainz_data_get_attr_value (release, RB_MUSICBRAINZ_ATTR_ALBUM_ID);
+ rb_debug ("musicbrainz_albumid: %s", album_id);
- rb_debug ("storing metadata for %s - %s - %s", track->artist, album->title, track->title);
+ album_artist_id = rb_musicbrainz_data_get_attr_value (release, RB_MUSICBRAINZ_ATTR_ALBUM_ARTIST_ID);
+ rb_debug ("musicbrainz_albumartistid: %s", album_artist_id);
- rb_debug ("musicbrainz_trackid: %s", track->track_id);
- rb_debug ("musicbrainz_artistid: %s", track->artist_id);
- rb_debug ("artist sortname: %s", track->artist_sortname);
+ db = get_db_for_source (source);
- /* record track info in entry*/
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_TITLE, FALSE, track->title);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_ARTIST, FALSE, track->artist);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_ALBUM, FALSE, album->title);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_GENRE, FALSE, album->genre);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_MUSICBRAINZ_TRACKID, TRUE, track->track_id);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_MUSICBRAINZ_ARTISTID, TRUE, track->artist_id);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_MUSICBRAINZ_ALBUMID, TRUE, album->album_id);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_MUSICBRAINZ_ALBUMARTISTID, TRUE, album->artist_id);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_ARTIST_SORTNAME, TRUE, track->artist_sortname);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_ALBUM_ARTIST, TRUE, album->artist);
- entry_set_string_prop (db, entry, RHYTHMDB_PROP_ALBUM_ARTIST_SORTNAME, TRUE, album->artist_sortname);
+ /* apply things to tracks */
+ l = rb_musicbrainz_data_get_children (medium);
+ for (l = rb_musicbrainz_data_get_children (medium); l != NULL; l = l->next) {
+ RhythmDBEntry *entry;
+ RBMusicBrainzData *mb_track = l->data;
+ GList *tl;
+ GValue value = {0, };
+ const char *attr;
+ int mb_track_num;
+
+ attr = rb_musicbrainz_data_get_attr_value (mb_track, RB_MUSICBRAINZ_ATTR_TRACK_NUMBER);
+ rb_debug ("processing musicbrainz track %s", attr);
+ mb_track_num = strtol (attr, 0, 10);
+
+ /* find matching track */
+ entry = NULL;
+ for (tl = source->priv->tracks; tl != NULL; tl = tl->next) {
+ gulong tn;
+ tn = rhythmdb_entry_get_ulong (tl->data, RHYTHMDB_PROP_TRACK_NUMBER);
+ if (tn == mb_track_num) {
+ entry = tl->data;
+ break;
+ }
+ }
- g_value_init (&value, G_TYPE_ULONG);
- g_value_set_ulong (&value, track->duration);
- rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_DURATION, &value);
- g_value_unset (&value);
+ if (entry == NULL) {
+ g_warning ("couldn't find track entry for musicbrainz track %d",
+ mb_track_num);
+ continue;
+ }
- if (album->disc_number != 0) {
+ /* apply release stuff */
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_ALBUM, FALSE, album);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_MUSICBRAINZ_ALBUMID, TRUE, album_id);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_MUSICBRAINZ_ALBUMARTISTID, TRUE, album_artist_id);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_ALBUM_ARTIST, TRUE, album_artist);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_ALBUM_ARTIST_SORTNAME, TRUE, album_artist_sortname);
+
+ if (release_date != 0) {
g_value_init (&value, G_TYPE_ULONG);
- g_value_set_ulong (&value, album->disc_number);
- rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_DISC_NUMBER, &value);
+ g_value_set_ulong (&value, release_date);
+ rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_DATE, &value);
g_value_unset (&value);
}
- /*album->release_date (could potentially have multiple values)*/
- /* in current sj-structures.h, however, it does not */
-
- if (album->release_date) {
- GType type = rhythmdb_get_property_type (db, RHYTHMDB_PROP_DATE);
- g_value_init (&value, type);
- g_value_set_ulong (&value, g_date_get_julian (album->release_date));
- rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_DATE, &value);
+ /* apply medium stuff */
+ if (disc_num != 0) {
+ g_value_init (&value, G_TYPE_ULONG);
+ g_value_set_ulong (&value, disc_num);
+ rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_DISC_NUMBER, &value);
g_value_unset (&value);
}
- rhythmdb_commit (db);
+ /* apply track stuff */
+ attr = rb_musicbrainz_data_get_attr_value (mb_track, RB_MUSICBRAINZ_ATTR_TITLE);
+ rb_debug ("title: %s", attr);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_TITLE, FALSE, attr);
- album->tracks = g_list_next (album->tracks);
- cd_track = g_list_next (cd_track);
- }
+ attr = rb_musicbrainz_data_get_attr_value (mb_track, RB_MUSICBRAINZ_ATTR_TRACK_ID);
+ rb_debug ("musicbrainz track id: %s", attr);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_MUSICBRAINZ_TRACKID, TRUE, attr);
- while (cd_track) {
- /* Musicbrainz doesn't report data tracks on multisession CDs.
- * These aren't interesting to us anyway, so they should be hidden.
- */
- RhythmDBEntry *entry = cd_track->data;
- rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_HIDDEN, &true_value);
- rhythmdb_commit (db);
+ attr = rb_musicbrainz_data_get_attr_value (mb_track, RB_MUSICBRAINZ_ATTR_ARTIST);
+ rb_debug ("artist: %s", attr);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_ARTIST, FALSE, attr);
+
+ attr = rb_musicbrainz_data_get_attr_value (mb_track, RB_MUSICBRAINZ_ATTR_ARTIST_SORTNAME);
+ rb_debug ("artist sortname: %s", attr);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_ARTIST_SORTNAME, TRUE, attr);
- cd_track = g_list_next (cd_track);
+ attr = rb_musicbrainz_data_get_attr_value (mb_track, RB_MUSICBRAINZ_ATTR_ARTIST_ID);
+ rb_debug ("musicbrainz_artistid: %s", attr);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_MUSICBRAINZ_ARTISTID, TRUE, attr);
}
- g_object_unref (source->priv->metadata);
- source->priv->metadata = NULL;
+ rhythmdb_commit (db);
g_object_unref (db);
-
- g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
-}
-
-
-/*
- * Called by the Multiple Album dialog when the user hits return in
- * the list view
- */
-static void
-album_row_activated (GtkTreeView *treeview,
- GtkTreePath *arg1,
- GtkTreeViewColumn *arg2,
- gpointer user_data)
-{
- GtkDialog *dialog = GTK_DIALOG (user_data);
- g_assert (dialog != NULL);
- gtk_dialog_response (dialog, GTK_RESPONSE_OK);
}
static void
-multiple_album_response_cb (GtkWidget *dialog, int response, RBAudioCdSource *source)
+album_combo_changed_cb (GtkWidget *combo, RBAudioCdSource *source)
{
- AlbumDetails *album;
- GtkTreeIter iter;
- GtkTreeSelection *selection;
-
- /* maybe assert? */
- if (dialog == source->priv->multiple_album_dialog) {
- source->priv->multiple_album_dialog = NULL;
- }
+ GList *l;
+ int active;
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (source->priv->albums_listview));
+ active = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+ if (active == -1)
+ return;
- if (response == GTK_RESPONSE_DELETE_EVENT) {
- gtk_tree_model_get_iter_first (GTK_TREE_MODEL (source->priv->albums_store), &iter);
- } else {
- gtk_tree_selection_get_selected (selection, NULL, &iter);
+ l = g_list_nth (source->priv->releases, active);
+ if (l != NULL) {
+ apply_musicbrainz_release (source, l->data);
}
-
- gtk_tree_model_get (GTK_TREE_MODEL (source->priv->albums_store), &iter, 2, &album, -1);
- apply_album_metadata (source, album);
-
- gtk_widget_destroy (dialog);
-
- g_list_foreach (source->priv->albums, (GFunc)album_details_free, NULL);
- g_list_free (source->priv->albums);
- source->priv->albums = NULL;
}
-/*
- * Utility function for when there are more than one albums
- * available. Borrowed from Sound Juicer.
- */
static void
-multiple_album_dialog (RBAudioCdSource *source, GList *albums)
+show_multiple_release_info_bar (RBAudioCdSource *source)
{
- GtkTreeSelection *selection;
- GtkTreeIter iter;
- GtkBuilder *builder;
- GtkTreeViewColumn *column;
- GtkCellRenderer *text_renderer;
- GObject *plugin;
- char *builder_file;
+ GtkWidget *info_bar;
+ GtkWidget *label;
+ GtkWidget *box;
+ GtkWidget *combo;
+ GList *l;
- gdk_threads_enter ();
+ rb_debug ("showing musicbrainz multiple release info bar");
- g_object_get (source, "plugin", &plugin, NULL);
- g_assert (plugin != NULL);
+ info_bar = gtk_info_bar_new ();
- /* create dialog */
- builder_file = rb_find_plugin_data_file (plugin, "multiple-album.ui");
- g_object_unref (plugin);
+ label = gtk_label_new (_("This disc matches multiple albums. Select the correct album."));
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ box = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
+ gtk_container_add (GTK_CONTAINER (box), label);
- if (builder_file == NULL) {
- g_warning ("couldn't find multiple-album.ui");
- apply_album_metadata (source, (AlbumDetails *)albums->data);
- g_list_foreach (albums, (GFunc)album_details_free, NULL);
- g_list_free (albums);
- return;
- }
+ combo = gtk_combo_box_text_new ();
+ for (l = source->priv->releases; l != NULL; l = l->next) {
+ const char *artist;
+ const char *album;
+ const char *country;
+ char *text;
- source->priv->albums = albums;
-
- builder = rb_builder_load (builder_file, NULL);
- g_free (builder_file);
-
- source->priv->multiple_album_dialog = GTK_WIDGET (gtk_builder_get_object (builder, "multiple_dialog"));
- g_assert (source->priv->multiple_album_dialog != NULL);
- gtk_window_set_transient_for (GTK_WINDOW (source->priv->multiple_album_dialog),
- GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (source))));
- source->priv->albums_listview = GTK_WIDGET (gtk_builder_get_object (builder, "albums_listview"));
-
- g_signal_connect (source->priv->albums_listview,
- "row-activated",
- G_CALLBACK (album_row_activated),
- source->priv->multiple_album_dialog);
-
- /* add columns */
- text_renderer = gtk_cell_renderer_text_new ();
- column = gtk_tree_view_column_new_with_attributes (_("Title"),
- text_renderer,
- "text", 0,
- NULL);
-
- gtk_tree_view_append_column (GTK_TREE_VIEW (source->priv->albums_listview), column);
-
- column = gtk_tree_view_column_new_with_attributes (_("Artist"),
- text_renderer,
- "text", 1,
- NULL);
- gtk_tree_view_append_column (GTK_TREE_VIEW (source->priv->albums_listview), column);
-
- /* create model for the tree view */
- source->priv->albums_store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
- gtk_tree_view_set_model (GTK_TREE_VIEW (source->priv->albums_listview), GTK_TREE_MODEL (source->priv->albums_store));
-
- for (; albums ; albums = g_list_next (albums)) {
- GtkTreeIter iter;
- AlbumDetails *album = (AlbumDetails*)(albums->data);
- gtk_list_store_append (source->priv->albums_store, &iter);
- gtk_list_store_set (source->priv->albums_store, &iter,
- 0, album->title,
- 1, album->artist,
- 2, album,
- -1);
+ artist = rb_musicbrainz_data_get_attr_value (l->data, RB_MUSICBRAINZ_ATTR_ALBUM_ARTIST);
+ album = rb_musicbrainz_data_get_attr_value (l->data, RB_MUSICBRAINZ_ATTR_ALBUM);
+ country = rb_musicbrainz_data_get_attr_value (l->data, RB_MUSICBRAINZ_ATTR_COUNTRY);
+ text = g_strdup_printf ("%s - %s (%s)", artist, album, country);
+ gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), NULL, text);
+ g_free (text);
}
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (source->priv->albums_listview));
- gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+ g_signal_connect (combo, "changed", G_CALLBACK (album_combo_changed_cb), source);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
- /* select the first row */
- gtk_tree_model_get_iter_first (GTK_TREE_MODEL (source->priv->albums_store), &iter);
- gtk_tree_selection_select_iter (selection, &iter);
+ box = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar));
+ gtk_container_add (GTK_CONTAINER (box), combo);
- g_signal_connect (source->priv->multiple_album_dialog,
- "response",
- G_CALLBACK (multiple_album_response_cb),
- source);
- gtk_widget_grab_focus (source->priv->albums_listview);
- gtk_widget_show_all (source->priv->multiple_album_dialog);
-
- gdk_threads_leave ();
+ show_info_bar (source, info_bar);
}
-
static void
-metadata_cb (SjMetadataGetter *metadata,
- GList *albums,
- GError *error,
- RBAudioCdSource *source)
+musicbrainz_lookup_cb (GObject *obj, GAsyncResult *result, RBAudioCdSource **source_ptr)
{
- g_assert (metadata == source->priv->metadata);
+ RBAudioCdSource *source;
+ GError *error = NULL;
+ GList *l;
- if (error != NULL) {
- rb_debug ("Failed to load cd metadata: %s", error->message);
- /* TODO display error to user? */
- g_object_unref (metadata);
- source->priv->metadata = NULL;
- g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
+ source = *source_ptr;
+ if (source == NULL) {
+ rb_debug ("cd source was destroyed");
+ g_free (source_ptr);
return;
}
- if (albums == NULL) {
- rb_debug ("Musicbrainz didn't return any CD metadata, but didn't give an error");
- g_object_unref (metadata);
- source->priv->metadata = NULL;
- g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
+ g_object_remove_weak_pointer (G_OBJECT (source), (gpointer *)source_ptr);
+ g_free (source_ptr);
+
+ if (source->priv->releases != NULL) {
+ g_list_free (source->priv->releases);
+ source->priv->releases = NULL;
+ }
+ if (source->priv->mb_data != NULL) {
+ rb_musicbrainz_data_free (source->priv->mb_data);
+ }
+
+ g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
+
+ source->priv->mb_data = rb_musicbrainz_lookup_finish (result, &error);
+ if (error != NULL) {
+ if (error->domain == G_IO_ERROR &&
+ error->code == G_IO_ERROR_CANCELLED) {
+ /* do nothing */
+ } else if (error->domain == RB_MUSICBRAINZ_ERROR &&
+ error->code == RB_MUSICBRAINZ_ERROR_NOT_FOUND) {
+ show_submit_info_bar (source);
+ } else {
+ show_lookup_error_info_bar (source, error);
+ }
+ g_clear_error (&error);
return;
}
- if (source->priv->tracks == NULL) {
- /* empty cd? */
- rb_debug ("no tracks on the CD?");
- g_object_unref (metadata);
- source->priv->metadata = NULL;
- g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
+
+ /* find the release and medium matching the disc id */
+ l = rb_musicbrainz_data_get_children (source->priv->mb_data);
+ if (l == NULL) {
+ show_submit_info_bar (source);
return;
}
- g_free (source->priv->submit_url);
- source->priv->submit_url = NULL;
+ for (; l != NULL; l = l->next) {
+ RBMusicBrainzData *m;
- /* if we have multiple results, ask the user to pick one */
- if (g_list_length (albums) > 1) {
- multiple_album_dialog (source, albums);
+ m = rb_musicbrainz_data_find_child (l->data,
+ "disc-id",
+ source->priv->disc_info->musicbrainz_disc_id);
+ if (m == NULL)
+ continue;
+
+ source->priv->releases = g_list_append (source->priv->releases, l->data);
+ }
+
+ if (source->priv->releases == NULL) {
+ show_submit_info_bar (source);
+ } else if (g_list_length (source->priv->releases) > 1) {
+ show_multiple_release_info_bar (source);
} else {
- apply_album_metadata (source, (AlbumDetails *)albums->data);
- g_list_foreach (albums, (GFunc)album_details_free, NULL);
- g_list_free (albums);
+ apply_musicbrainz_release (source, source->priv->releases->data);
}
}
-static void
-metadata_cancelled_cb (SjMetadataGetter *metadata,
- GList *albums,
- GError *error,
- gpointer old_source)
+static gboolean
+rb_audiocd_source_load_metadata (RBAudioCdSource *source)
{
- /* NOTE: the source may have been finalised, and so should NOT be used*/
- g_list_foreach (albums, (GFunc)album_details_free, NULL);
- g_list_free (albums);
- g_object_unref (metadata);
+ RBAudioCdSource **source_ptr;
+ const char *disc_includes[] = { "recordings", "artist-credits", NULL };
+
+ if (source->priv->disc_info->musicbrainz_disc_id == NULL) {
+ rb_debug ("not doing musicbrainz lookup as we don't have a disc id");
+ return FALSE;
+ }
+
+ source_ptr = g_new0 (RBAudioCdSource *, 1);
+ *source_ptr = source;
+ g_object_add_weak_pointer (G_OBJECT (source), (gpointer *)source_ptr);
+ rb_debug ("looking up musicbrainz data for disc %s",
+ source->priv->disc_info->musicbrainz_disc_id);
+ rb_musicbrainz_lookup ("discid",
+ source->priv->disc_info->musicbrainz_disc_id,
+ disc_includes,
+ source->priv->cancel_disc_info,
+ (GAsyncReadyCallback) musicbrainz_lookup_cb,
+ source_ptr);
+ return TRUE;
}
-#endif
static void
-rb_audiocd_load_metadata (RBAudioCdSource *source,
- RhythmDB *db)
+reload_metadata_cmd (GtkAction *action, RBAudioCdSource *source)
{
-#ifdef HAVE_SJ_METADATA_GETTER
- source->priv->metadata = sj_metadata_getter_new ();
- sj_metadata_getter_set_cdrom (source->priv->metadata, source->priv->device_path);
-
- g_signal_connect (G_OBJECT (source->priv->metadata), "metadata",
- G_CALLBACK (metadata_cb), source);
- sj_metadata_getter_list_albums (source->priv->metadata, NULL);
-#else
- g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
-#endif
+ g_return_if_fail (RB_IS_AUDIOCD_SOURCE (source));
+
+ rb_audiocd_source_load_metadata (source);
}
static void
-rb_audiocd_load_metadata_cancel (RBAudioCdSource *source)
+disc_info_cb (GObject *obj, GAsyncResult *result, RBAudioCdSource **source_ptr)
{
-#ifdef HAVE_SJ_METADATA_GETTER
- if (source->priv->metadata) {
- g_signal_handlers_disconnect_by_func (G_OBJECT (source->priv->metadata),
- G_CALLBACK (metadata_cb), source);
- g_signal_connect (G_OBJECT (source->priv->metadata), "metadata",
- G_CALLBACK (metadata_cancelled_cb), source);
+ RBAudioCdSource *source;
+ GError *error = NULL;
+ RhythmDB *db;
+ int i;
+
+ source = *source_ptr;
+ if (source == NULL) {
+ rb_debug ("cd source was destroyed");
+ g_free (source_ptr);
+ return;
}
-#endif
-}
+ g_object_remove_weak_pointer (G_OBJECT (source), (gpointer *)source_ptr);
+ g_free (source_ptr);
-static gpointer
-rb_audiocd_load_songs (RBAudioCdSource *source)
-{
- RhythmDB *db;
- GVolume *volume;
+ source->priv->disc_info = rb_audiocd_info_finish (result, &error);
+ if (error != NULL) {
+ if (error->domain == G_IO_ERROR &&
+ error->code == G_IO_ERROR_CANCELLED) {
+ /* do nothing */
+ } else {
+ show_cd_error_info_bar (source, error);
+ }
+ g_clear_error (&error);
+
+ g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
+ return;
+ }
- g_object_get (source, "volume", &volume, NULL);
- source->priv->device_path = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
- g_object_unref (volume);
+ if (source->priv->disc_info->album_artist != NULL) {
+ gtk_entry_set_text (GTK_ENTRY (source->priv->artist_entry), source->priv->disc_info->album_artist);
+ }
+ if (source->priv->disc_info->album != NULL) {
+ gtk_entry_set_text (GTK_ENTRY (source->priv->album_entry), source->priv->disc_info->album);
+ g_object_set (source, "name", source->priv->disc_info->album, NULL);
+ }
+ if (source->priv->disc_info->genre != NULL) {
+ gtk_entry_set_text (GTK_ENTRY (source->priv->genre_entry), source->priv->disc_info->genre);
+ }
db = get_db_for_source (source);
+ for (i = 0; i < source->priv->disc_info->num_tracks; i++) {
+ RhythmDBEntry *entry;
+ char *audio_path;
+ GValue value = {0, };
+ gchar *str;
+ RhythmDBEntryType *entry_type;
+ RBAudioCDEntryData *extra_data;
+ RBAudioCDTrack *track = &source->priv->disc_info->tracks[i];
+
+ /* ignore data tracks */
+ if (track->is_audio == FALSE) {
+ rb_debug ("ignoring non-audio track %d", track->track_num);
+ continue;
+ }
- rb_debug ("loading Audio CD from %s", source->priv->device_path);
- /* create a cdda gstreamer element, to get cd info from */
- source->priv->cdda = gst_element_make_from_uri (GST_URI_SRC, "cdda://", NULL);
- if (!source->priv->cdda) {
- gdk_threads_enter ();
- rb_error_dialog (NULL, _("Couldn't load Audio CD"),
- _("Rhythmbox could not get access to the CD device."));
- gdk_threads_leave ();
- goto error_out;
- }
+ audio_path = g_strdup_printf ("cdda://%s#%d", source->priv->disc_info->device, track->track_num);
- rb_debug ("cdda longname: %s", gst_element_factory_get_longname (gst_element_get_factory (source->priv->cdda)));
- g_object_set (G_OBJECT (source->priv->cdda), "device", source->priv->device_path, NULL);
- source->priv->pipeline = gst_pipeline_new ("pipeline");
- source->priv->fakesink = gst_element_factory_make ("fakesink", "fakesink");
- gst_bin_add_many (GST_BIN (source->priv->pipeline), source->priv->cdda, source->priv->fakesink, NULL);
- gst_element_link (source->priv->cdda, source->priv->fakesink);
+ g_object_get (source, "entry-type", &entry_type, NULL);
+ rb_debug ("creating entry for track %d from %s", track->track_num, source->priv->disc_info->device);
+ entry = rhythmdb_entry_new (db, entry_type, audio_path);
+ g_object_unref (entry_type);
+ if (entry == NULL) {
+ g_warning ("unable to create entry %s", audio_path);
+ g_free (audio_path);
+ continue;
+ }
- /* disable paranoia (if using cdparanoia) since we're only reading track information here.
- * this reduces cdparanoia's cache size, so the process is much faster.
- */
- if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "paranoia-mode"))
- g_object_set (source, "paranoia-mode", 0, NULL);
+ g_value_init (&value, G_TYPE_ULONG);
+ g_value_set_ulong (&value, track->track_num);
+ rhythmdb_entry_set (db, entry,
+ RHYTHMDB_PROP_TRACK_NUMBER,
+ &value);
+ g_value_unset (&value);
- if (rb_audiocd_scan_songs (source, db))
- rb_audiocd_load_metadata (source, db);
+ g_value_init (&value, G_TYPE_STRING);
+ str = g_strdup_printf (_("Track %u"), track->track_num);
+ g_value_take_string (&value, str);
+ rhythmdb_entry_set (db, entry,
+ RHYTHMDB_PROP_TITLE,
+ &value);
+ g_value_unset (&value);
+
+ g_value_init (&value, G_TYPE_ULONG);
+ g_value_set_ulong (&value, track->duration / 1000);
+ rhythmdb_entry_set (db, entry,
+ RHYTHMDB_PROP_DURATION,
+ &value);
+ g_value_unset (&value);
+
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_ARTIST, FALSE, track->artist);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_TITLE, FALSE, track->title);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_ALBUM, FALSE, source->priv->disc_info->album);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_ALBUM_ARTIST, FALSE, source->priv->disc_info->album_artist);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_GENRE, FALSE, source->priv->disc_info->genre);
+ entry_set_string_prop (db, entry, RHYTHMDB_PROP_MEDIA_TYPE, TRUE, "audio/x-raw-int");
+
+ extra_data = RHYTHMDB_ENTRY_GET_TYPE_DATA (entry, RBAudioCDEntryData);
+ extra_data->extract = TRUE;
+
+ rhythmdb_commit (db);
+ g_free (audio_path);
+
+ source->priv->tracks = g_list_prepend (source->priv->tracks, entry);
+ }
-error_out:
g_object_unref (db);
- g_object_unref (source);
- return NULL;
+ if (rb_audiocd_source_load_metadata (source) == FALSE) {
+ g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
+ }
+}
+
+static void
+rb_audiocd_source_load_disc_info (RBAudioCdSource *source)
+{
+ RBAudioCdSource **source_ptr;
+
+ source_ptr = g_new0 (RBAudioCdSource *, 1);
+ *source_ptr = source;
+ g_object_add_weak_pointer (G_OBJECT (source), (gpointer *)source_ptr);
+ rb_audiocd_info_get (source->priv->device_path,
+ source->priv->cancel_disc_info,
+ (GAsyncReadyCallback) disc_info_cb,
+ source_ptr);
}
static void
@@ -1180,13 +1168,9 @@ impl_delete_thyself (RBDisplayPage *page)
rb_debug ("audio cd ejected");
-#ifdef HAVE_SJ_METADATA_GETTER
- if (source->priv->multiple_album_dialog != NULL) {
- gtk_dialog_response (GTK_DIALOG (source->priv->multiple_album_dialog), GTK_RESPONSE_DELETE_EVENT);
+ if (source->priv->cancel_disc_info) {
+ g_cancellable_cancel (source->priv->cancel_disc_info);
}
-#endif
-
- rb_audiocd_load_metadata_cancel (source);
db = get_db_for_source (source);
@@ -1372,26 +1356,6 @@ update_disc_number_cb (GtkWidget *widget, GdkEventFocus *event, RBAudioCdSource
return FALSE;
}
-#if defined(HAVE_SJ_METADATA_GETTER)
-static void
-info_bar_response_cb (GtkInfoBar *info_bar, gint response_id, RBAudioCdSource *source)
-{
- GError *error = NULL;
-
- g_return_if_fail (source->priv->submit_url != NULL);
-
- if (response_id == GTK_RESPONSE_OK) {
- if (!gtk_show_uri (NULL, source->priv->submit_url, GDK_CURRENT_TIME, &error)) {
- rb_debug ("Could not launch submit URL %s: %s", source->priv->submit_url, error->message);
- g_error_free (error);
- return;
- }
- }
-
- gtk_widget_hide (source->priv->info_bar);
-}
-#endif
-
static void
extract_cell_data_func (GtkTreeViewColumn *column,
GtkCellRenderer *renderer,
diff --git a/plugins/audiocd/rb-musicbrainz-lookup.c b/plugins/audiocd/rb-musicbrainz-lookup.c
new file mode 100644
index 0000000..9bac942
--- /dev/null
+++ b/plugins/audiocd/rb-musicbrainz-lookup.c
@@ -0,0 +1,524 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2012 Jonathan Matthew
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * The Rhythmbox authors hereby grant permission for non-GPL compatible
+ * GStreamer plugins to be used and distributed together with GStreamer
+ * and Rhythmbox. This permission is above and beyond the permissions granted
+ * by the GPL license by which Rhythmbox is covered. If you modify this code
+ * you may extend this exception to your version of the code, but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <libsoup/soup.h>
+#include <libsoup/soup-gnome.h>
+
+#include "rb-musicbrainz-lookup.h"
+
+
+struct ParseAttrMap {
+ const char *path;
+ const char *xml_attr;
+ const char *attr;
+};
+
+struct _RBMusicBrainzData {
+ char *type;
+ GHashTable *attrs;
+ GList *children;
+ RBMusicBrainzData *parent;
+
+ GList *path_start;
+};
+
+typedef struct {
+ RBMusicBrainzData *current;
+ RBMusicBrainzData *root;
+
+ GQueue path;
+ const char *item;
+ GString text;
+
+ struct ParseAttrMap *map;
+} RBMusicBrainzParseContext;
+
+
+static struct ParseAttrMap root_attr_map[] = {
+ { NULL, NULL, NULL }
+};
+
+static struct ParseAttrMap release_attr_map[] = {
+ { "/release", "id", RB_MUSICBRAINZ_ATTR_ALBUM_ID },
+ { "/release/asin", NULL, RB_MUSICBRAINZ_ATTR_ASIN },
+ { "/release/country", NULL, RB_MUSICBRAINZ_ATTR_COUNTRY },
+ { "/release/date", NULL, RB_MUSICBRAINZ_ATTR_DATE },
+ { "/release/title", NULL, RB_MUSICBRAINZ_ATTR_ALBUM },
+ { "/release/artist-credit/name-credit/artist", "id", RB_MUSICBRAINZ_ATTR_ALBUM_ARTIST_ID },
+ { "/release/artist-credit/name-credit/artist/name", NULL, RB_MUSICBRAINZ_ATTR_ALBUM_ARTIST },
+ { "/release/artist-credit/name-credit/artist/sort-name", NULL, RB_MUSICBRAINZ_ATTR_ALBUM_ARTIST_SORTNAME },
+ { NULL, NULL, NULL }
+};
+
+static struct ParseAttrMap medium_attr_map[] = {
+ { "/medium/position", NULL, RB_MUSICBRAINZ_ATTR_DISC_NUMBER },
+ { "/medium/track-list", "count", RB_MUSICBRAINZ_ATTR_TRACK_COUNT },
+ { "/medium/disc-list/disc", "id", RB_MUSICBRAINZ_ATTR_DISC_ID },
+ { NULL, NULL, NULL }
+};
+
+static struct ParseAttrMap track_attr_map[] = {
+ { "/track/number", NULL, RB_MUSICBRAINZ_ATTR_TRACK_NUMBER },
+ { "/track/length", NULL, RB_MUSICBRAINZ_ATTR_DURATION },
+ { "/track/recording", "id", RB_MUSICBRAINZ_ATTR_TRACK_ID },
+ { "/track/recording/title", NULL, RB_MUSICBRAINZ_ATTR_TITLE },
+ { "/track/recording/artist-credit/name-credit/artist", "id", RB_MUSICBRAINZ_ATTR_ARTIST_ID },
+ { "/track/recording/artist-credit/name-credit/artist/name", NULL, RB_MUSICBRAINZ_ATTR_ARTIST },
+ { "/track/recording/artist-credit/name-credit/artist/sort-name", NULL, RB_MUSICBRAINZ_ATTR_ARTIST_SORTNAME },
+ { NULL, NULL, NULL }
+};
+
+static struct ParseAttrMap relation_attr_map[] = {
+ { "/relation", "type", RB_MUSICBRAINZ_ATTR_RELATION_TYPE },
+ { "/relation/target", NULL, RB_MUSICBRAINZ_ATTR_RELATION_TARGET },
+ { "/relation/artist", "id", RB_MUSICBRAINZ_ATTR_ARTIST_ID },
+ { "/relation/artist/name", NULL, RB_MUSICBRAINZ_ATTR_ARTIST },
+ { "/relation/artist/sortname", NULL, RB_MUSICBRAINZ_ATTR_ARTIST_SORTNAME },
+ { "/relation/work", "id", RB_MUSICBRAINZ_ATTR_WORK_ID },
+ { "/relation/work/title", NULL, RB_MUSICBRAINZ_ATTR_WORK_TITLE },
+ { NULL, NULL, NULL }
+};
+
+static struct {
+ const char *name;
+ struct ParseAttrMap *map;
+} object_types[] = {
+ { "root", root_attr_map },
+ { "release", release_attr_map },
+ { "medium", medium_attr_map },
+ { "track", track_attr_map },
+ { "relation", relation_attr_map }
+};
+
+GQuark
+rb_musicbrainz_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("rb_musicbrainz_error");
+
+ return quark;
+}
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+GType
+rb_musicbrainz_error_get_type (void)
+{
+ static GType etype = 0;
+
+ if (etype == 0) {
+ static const GEnumValue values[] = {
+ ENUM_ENTRY (RB_MUSICBRAINZ_ERROR_NOT_FOUND, "not-found"),
+ ENUM_ENTRY (RB_MUSICBRAINZ_ERROR_SERVER, "server-error"),
+ ENUM_ENTRY (RB_MUSICBRAINZ_ERROR_NETWORK, "network-error"),
+ { 0, 0, 0 }
+ };
+
+ etype = g_enum_register_static ("RBMusicBrainzError", values);
+ }
+
+ return etype;
+}
+
+
+static void
+free_values (GQueue *attrs)
+{
+ g_queue_free_full (attrs, (GDestroyNotify) g_free);
+}
+
+void
+rb_musicbrainz_data_free (RBMusicBrainzData *data)
+{
+ g_hash_table_unref (data->attrs);
+ g_list_free_full (data->children, (GDestroyNotify) rb_musicbrainz_data_free);
+ g_free (data->type);
+ g_free (data);
+}
+const char *
+rb_musicbrainz_data_get_attr_value (RBMusicBrainzData *data, const char *attr)
+{
+ GQueue *d;
+ d = g_hash_table_lookup (data->attrs, attr);
+ if (d == NULL) {
+ return NULL;
+ }
+
+ return d->head->data;
+}
+
+GList *
+rb_musicbrainz_data_get_attr_names (RBMusicBrainzData *data)
+{
+ return g_hash_table_get_keys (data->attrs);
+}
+
+RBMusicBrainzData *
+rb_musicbrainz_data_find_child (RBMusicBrainzData *data, const char *attr, const char *value)
+{
+ GList *l;
+ for (l = data->children; l != NULL; l = l->next) {
+ RBMusicBrainzData *child = l->data;
+ GQueue *d;
+ GList *i;
+
+ d = g_hash_table_lookup (child->attrs, attr);
+ if (d == NULL)
+ continue;
+ for (i = d->head; i != NULL; i = i->next) {
+ if (g_strcmp0 (value, i->data) == 0)
+ return child;
+ }
+ }
+
+ return NULL;
+}
+
+GList *
+rb_musicbrainz_data_get_children (RBMusicBrainzData *data)
+{
+ return g_list_copy (data->children);
+}
+
+const char *
+rb_musicbrainz_data_get_data_type (RBMusicBrainzData *data)
+{
+ return data->type;
+}
+
+static RBMusicBrainzData *
+new_data (RBMusicBrainzData *parent, const char *type)
+{
+ RBMusicBrainzData *d = g_new0 (RBMusicBrainzData, 1);
+ d->type = g_strdup (type);
+ d->parent = parent;
+ d->attrs = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)free_values);
+
+ if (parent) {
+ parent->children = g_list_append (parent->children, d);
+ }
+ return d;
+}
+
+static void
+add_attr (RBMusicBrainzData *data, const char *attr, const char *value)
+{
+ GQueue *d;
+
+ d = g_hash_table_lookup (data->attrs, attr);
+ if (d == NULL) {
+ d = g_queue_new ();
+ g_hash_table_insert (data->attrs, (char *)attr, d);
+ }
+
+ g_queue_push_tail (d, g_strdup (value));
+}
+
+static char *
+get_path (RBMusicBrainzParseContext *ctx)
+{
+ GString s = {0,};
+ GList *l;
+
+ for (l = ctx->current->path_start; l != NULL; l = l->next) {
+ g_string_append (&s, "/");
+ g_string_append (&s, l->data);
+ }
+
+ return s.str;
+}
+
+static void
+start_element (GMarkupParseContext *pctx,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ RBMusicBrainzParseContext *ctx = user_data;
+ char *path;
+ int i;
+ int j;
+
+ g_queue_push_tail (&ctx->path, g_strdup (element_name));
+
+ for (i = 0; i < G_N_ELEMENTS (object_types); i++) {
+ if (g_strcmp0 (element_name, object_types[i].name) == 0) {
+ RBMusicBrainzData *d = new_data (ctx->current, element_name);
+ d->path_start = ctx->path.tail;
+ ctx->current = d;
+ ctx->map = object_types[i].map;
+ break;
+ }
+ }
+
+ path = get_path (ctx);
+ for (i = 0; ctx->map[i].path != NULL; i++) {
+ if (g_strcmp0 (path, ctx->map[i].path) == 0) {
+ if (ctx->map[i].xml_attr != NULL) {
+ for (j = 0; attribute_names[j] != NULL; j++) {
+ if (g_strcmp0 (attribute_names[j], ctx->map[i].xml_attr) == 0) {
+ add_attr (ctx->current,
+ (char *)ctx->map[i].attr,
+ attribute_values[j]);
+ }
+ }
+ } else {
+ ctx->item = ctx->map[i].attr;
+ }
+ break;
+ }
+ }
+
+ g_free (path);
+}
+
+static void
+end_element (GMarkupParseContext *pctx,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ RBMusicBrainzParseContext *ctx = user_data;
+
+ if (ctx->item) {
+ add_attr (ctx->current, (char *)ctx->item, ctx->text.str);
+ ctx->item = NULL;
+ }
+
+ if (ctx->path.tail == ctx->current->path_start) {
+ ctx->current->path_start = NULL;
+ ctx->current = ctx->current->parent;
+ }
+
+ g_free (g_queue_pop_tail (&ctx->path));
+
+ g_free (ctx->text.str);
+ ctx->text.str = NULL;
+ ctx->text.len = 0;
+ ctx->text.allocated_len = 0;
+}
+
+static void
+text (GMarkupParseContext *pctx,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ RBMusicBrainzParseContext *ctx = user_data;
+
+ if (ctx->item) {
+ g_string_append (&ctx->text, text);
+ }
+}
+
+
+RBMusicBrainzData *
+rb_musicbrainz_data_parse (const char *data, gssize len, GError **error)
+{
+ RBMusicBrainzParseContext ctx;
+ GMarkupParser parser = {
+ start_element,
+ end_element,
+ text
+ };
+ GMarkupParseContext *pctx;
+
+ ctx.root = new_data (NULL, "root");
+ ctx.current = ctx.root;
+ ctx.text.str = NULL;
+ ctx.text.len = 0;
+ ctx.text.allocated_len = 0;
+ g_queue_init (&ctx.path);
+
+ pctx = g_markup_parse_context_new (&parser, 0, &ctx, NULL);
+ if (g_markup_parse_context_parse (pctx, data, len, error) == FALSE) {
+ rb_musicbrainz_data_free (ctx.root);
+ return NULL;
+ }
+
+ if (g_markup_parse_context_end_parse (pctx, error) == FALSE) {
+ rb_musicbrainz_data_free (ctx.root);
+ return NULL;
+ }
+ g_markup_parse_context_free (pctx);
+
+ return ctx.root;
+}
+
+GList *
+rb_musicbrainz_data_get_attr_values (RBMusicBrainzData *data, const char *attr)
+{
+ GQueue *d;
+ d = g_hash_table_lookup (data->attrs, attr);
+ if (d == NULL) {
+ return NULL;
+ }
+
+ return g_list_copy (d->head);
+}
+
+
+static void
+lookup_cb (SoupSession *session, SoupMessage *msg, GSimpleAsyncResult *result)
+{
+ RBMusicBrainzData *data;
+ int code;
+ GError *error = NULL;
+
+ g_object_get (msg, SOUP_MESSAGE_STATUS_CODE, &code, NULL);
+ if (code == SOUP_STATUS_NOT_FOUND || code == SOUP_STATUS_BAD_REQUEST) {
+ g_simple_async_result_set_error (result,
+ RB_MUSICBRAINZ_ERROR,
+ RB_MUSICBRAINZ_ERROR_NOT_FOUND,
+ _("Not found"));
+ } else if (code < 100) {
+ g_simple_async_result_set_error (result,
+ RB_MUSICBRAINZ_ERROR,
+ RB_MUSICBRAINZ_ERROR_NETWORK,
+ _("Unable to connect to Musicbrainz server"));
+ } else if (code != SOUP_STATUS_OK || msg->response_body->data == NULL) {
+ g_simple_async_result_set_error (result,
+ RB_MUSICBRAINZ_ERROR,
+ RB_MUSICBRAINZ_ERROR_SERVER,
+ _("Musicbrainz server error"));
+ } else {
+ data = rb_musicbrainz_data_parse (msg->response_body->data,
+ msg->response_body->length,
+ &error);
+ if (data == NULL) {
+ g_simple_async_result_set_from_error (result, error);
+ g_clear_error (&error);
+ } else {
+ g_simple_async_result_set_op_res_gpointer (result, data, NULL);
+ }
+ }
+
+ g_simple_async_result_complete (result);
+ g_object_unref (result);
+ g_object_unref (session);
+}
+
+void
+rb_musicbrainz_lookup (const char *entity,
+ const char *entity_id,
+ const char **includes,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ SoupURI *uri;
+ SoupMessage *message;
+ SoupSession *session;
+ char *uri_str;
+ char *inc;
+
+ result = g_simple_async_result_new (NULL,
+ callback,
+ user_data,
+ rb_musicbrainz_lookup);
+ g_simple_async_result_set_check_cancellable (result, cancellable);
+
+ session = soup_session_async_new_with_options (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
+ SOUP_TYPE_GNOME_FEATURES_2_26,
+ SOUP_SESSION_USER_AGENT,
+ "Rhythmbox/" VERSION " ",
+ NULL);
+ uri_str = g_strdup_printf ("http://musicbrainz.org/ws/2/%s/%s", entity, entity_id);
+ uri = soup_uri_new (uri_str);
+ g_free (uri_str);
+
+ if (includes != NULL) {
+ inc = g_strjoinv ("+", (char **)includes);
+ soup_uri_set_query_from_fields (uri, "inc", inc, NULL);
+ g_free (inc);
+ }
+
+ message = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+ soup_uri_free (uri);
+
+ soup_session_queue_message (session,
+ message,
+ (SoupSessionCallback) lookup_cb,
+ result);
+}
+
+RBMusicBrainzData *
+rb_musicbrainz_lookup_finish (GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_simple_async_result_is_valid (result,
+ NULL,
+ rb_musicbrainz_lookup),
+ NULL);
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return NULL;
+
+ return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+}
+
+char *
+rb_musicbrainz_create_submit_url (const char *disc_id, const char *full_disc_id)
+{
+ char **bits;
+ int *intbits;
+ GString *url;
+ int i;
+ int n;
+
+ /* full disc id is a space-delimited list of hex numbers.. */
+ bits = g_strsplit (full_disc_id, " ", 0);
+ n = g_strv_length (bits);
+ intbits = g_new0 (int, n+1);
+ for (i = 0; i < n; i++) {
+ intbits[i] = strtol (bits[i], 0, 16);
+ }
+ g_strfreev (bits);
+
+ url = g_string_new ("http://mm.musicbrainz.org/bare/cdlookup.html?id=");
+
+ g_string_append (url, disc_id); /* urlencode? */
+ g_string_append_printf (url, "&tracks=%d&toc=%d", intbits[1], intbits[0]);
+
+ /* .. that we put in the url in decimal */
+ for (i = 1; i < n; i++) {
+ g_string_append_printf (url, "+%d", intbits[i]);
+ }
+
+ g_free (intbits);
+
+ return g_string_free (url, FALSE);
+}
diff --git a/plugins/audiocd/rb-musicbrainz-lookup.h b/plugins/audiocd/rb-musicbrainz-lookup.h
new file mode 100644
index 0000000..c9e2677
--- /dev/null
+++ b/plugins/audiocd/rb-musicbrainz-lookup.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 Jonathan Matthew <jonathan d14n org>
+ *
+ * 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.
+ *
+ * The Rhythmbox authors hereby grant permission for non-GPL compatible
+ * GStreamer plugins to be used and distributed together with GStreamer
+ * and Rhythmbox. This permission is above and beyond the permissions granted
+ * by the GPL license by which Rhythmbox is covered. If you modify this code
+ * you may extend this exception to your version of the code, but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef RB_MUSICBRAINZ_LOOKUP_H
+#define RB_MUSICBRAINZ_LOOKUP_H
+
+#include <glib.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define RB_MUSICBRAINZ_ATTR_ASIN "asin"
+#define RB_MUSICBRAINZ_ATTR_COUNTRY "country"
+#define RB_MUSICBRAINZ_ATTR_DATE "date"
+#define RB_MUSICBRAINZ_ATTR_TITLE "title"
+#define RB_MUSICBRAINZ_ATTR_ALBUM "album"
+#define RB_MUSICBRAINZ_ATTR_ALBUM_ID "album-id"
+#define RB_MUSICBRAINZ_ATTR_ALBUM_ARTIST "album-artist"
+#define RB_MUSICBRAINZ_ATTR_ALBUM_ARTIST_ID "album-artist-id"
+#define RB_MUSICBRAINZ_ATTR_ALBUM_ARTIST_SORTNAME "album-artist-sortname"
+#define RB_MUSICBRAINZ_ATTR_DISC_ID "disc-id"
+#define RB_MUSICBRAINZ_ATTR_DISC_NUMBER "disc-number"
+#define RB_MUSICBRAINZ_ATTR_TRACK_COUNT "track-count"
+#define RB_MUSICBRAINZ_ATTR_TRACK_NUMBER "track-number"
+#define RB_MUSICBRAINZ_ATTR_DURATION "duration"
+#define RB_MUSICBRAINZ_ATTR_TRACK_ID "track-id"
+#define RB_MUSICBRAINZ_ATTR_TITLE "title"
+#define RB_MUSICBRAINZ_ATTR_ARTIST "artist"
+#define RB_MUSICBRAINZ_ATTR_ARTIST_ID "artist-id"
+#define RB_MUSICBRAINZ_ATTR_ARTIST_SORTNAME "artist-sortname"
+#define RB_MUSICBRAINZ_ATTR_RELATION_TYPE "relation-type"
+#define RB_MUSICBRAINZ_ATTR_RELATION_TARGET "relation-target"
+#define RB_MUSICBRAINZ_ATTR_WORK_ID "work-id"
+#define RB_MUSICBRAINZ_ATTR_WORK_TITLE "work-title"
+
+typedef enum
+{
+ RB_MUSICBRAINZ_ERROR_NOT_FOUND,
+ RB_MUSICBRAINZ_ERROR_NETWORK,
+ RB_MUSICBRAINZ_ERROR_SERVER
+} RBMusicBrainzError;
+
+GType rb_musicbrainz_error_get_type (void);
+GQuark rb_musicbrainz_error_quark (void);
+#define RB_TYPE_MUSICBRAINZ_ERROR (rb_musicbrainz_error_get_type())
+#define RB_MUSICBRAINZ_ERROR (rb_musicbrainz_error_quark())
+
+typedef struct _RBMusicBrainzData RBMusicBrainzData;
+
+void rb_musicbrainz_data_free (RBMusicBrainzData *data);
+const char * rb_musicbrainz_data_get_data_type (RBMusicBrainzData *data);
+
+GList * rb_musicbrainz_data_get_attr_names (RBMusicBrainzData *data);
+GList * rb_musicbrainz_data_get_attr_values (RBMusicBrainzData *data,
+ const char *attr);
+const char * rb_musicbrainz_data_get_attr_value (RBMusicBrainzData *data,
+ const char *attr);
+
+RBMusicBrainzData * rb_musicbrainz_data_find_child (RBMusicBrainzData *data,
+ const char *attr,
+ const char *value);
+
+GList * rb_musicbrainz_data_get_children (RBMusicBrainzData *data);
+
+
+RBMusicBrainzData * rb_musicbrainz_data_parse (const char *data,
+ gssize len,
+ GError **error);
+
+void rb_musicbrainz_lookup (const char *entity,
+ const char *entity_id,
+ const char **includes,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+RBMusicBrainzData * rb_musicbrainz_lookup_finish (GAsyncResult *result,
+ GError **error);
+
+char * rb_musicbrainz_create_submit_url (const char *disc_id,
+ const char *full_disc_id);
+
+
+G_END_DECLS
+
+#endif /* RB_MUSICBRAINZ_LOOKUP_H */
diff --git a/plugins/audiocd/test-cd.c b/plugins/audiocd/test-cd.c
new file mode 100644
index 0000000..38c84c3
--- /dev/null
+++ b/plugins/audiocd/test-cd.c
@@ -0,0 +1,234 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * 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.
+ *
+ * The Rhythmbox authors hereby grant permission for non-GPL compatible
+ * GStreamer plugins to be used and distributed together with GStreamer
+ * and Rhythmbox. This permission is above and beyond the permissions granted
+ * by the GPL license by which Rhythmbox is covered. If you modify this code
+ * you may extend this exception to your version of the code, but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <gst/gst.h>
+
+#include "rb-audiocd-info.h"
+#include "rb-musicbrainz-lookup.h"
+
+GMainLoop *mainloop = NULL;
+
+static void
+indent (int d)
+{
+ int i;
+ for (i = 0; i < d; i++)
+ g_print(" ");
+}
+
+static void
+dump (RBMusicBrainzData *d, int depth, int recurse)
+{
+ GList *l, *i;
+
+ indent (depth);
+ g_print ("%s {\n", rb_musicbrainz_data_get_data_type (d));
+ l = rb_musicbrainz_data_get_attr_names (d);
+ for (i = l; i != NULL; i = i->next) {
+ GList *v, *vi;
+
+ indent (depth+1);
+ g_print ("%s = [", (char *)i->data);
+ v = rb_musicbrainz_data_get_attr_values (d, i->data);
+ for (vi = v; vi != NULL; vi = vi->next) {
+ if (vi != v)
+ g_print (", ");
+ g_print ("%s", (char *)vi->data);
+ }
+ g_list_free (v);
+ g_print ("]\n");
+ }
+ g_list_free (l);
+
+ if (recurse) {
+ l = rb_musicbrainz_data_get_children (d);
+ for (i = l; i != NULL; i = i->next) {
+ dump (i->data, depth+1, recurse);
+ }
+ g_list_free (l);
+ }
+
+ indent (depth);
+ g_print ("}\n");
+}
+
+static void
+release_lookup_cb (GObject *object, GAsyncResult *result, RBAudioCDInfo *info)
+{
+ RBMusicBrainzData *dd;
+ GError *error = NULL;
+ dd = rb_musicbrainz_lookup_finish (result, &error);
+ if (dd != NULL) {
+ RBMusicBrainzData *r;
+ RBMusicBrainzData *m;
+
+ r = rb_musicbrainz_data_get_children (dd)->data;
+ m = rb_musicbrainz_data_find_child (r, "disc-id", info->musicbrainz_disc_id);
+ dump (r, 0, 0);
+ dump (m, 0, 1);
+ rb_musicbrainz_data_free (dd);
+ }
+
+ g_main_loop_quit (mainloop);
+ rb_audiocd_info_free (info);
+}
+
+static void
+lookup_cb (GObject *object, GAsyncResult *result, RBAudioCDInfo *info)
+{
+ RBMusicBrainzData *dd;
+ GError *error = NULL;
+ GList *l;
+ const char *release_includes[] = {
+ "discids",
+ "media",
+ "recordings",
+ "artist-credits",
+ "work-rels",
+ "recording-level-rels",
+ "work-level-rels",
+ "artist-rels",
+ "url-rels",
+ NULL
+ };
+
+ dd = rb_musicbrainz_lookup_finish (result, &error);
+ if (dd != NULL) {
+ g_print ("\n\n");
+ for (l = rb_musicbrainz_data_get_children (dd); l != NULL; l = l->next) {
+ RBMusicBrainzData *r = l->data;
+
+ RBMusicBrainzData *m = rb_musicbrainz_data_find_child (r, "disc-id", info->musicbrainz_disc_id);
+ if (m != NULL) {
+ dump (r, 0, 0);
+ dump (m, 0, 1);
+
+ rb_musicbrainz_lookup ("release",
+ rb_musicbrainz_data_get_attr_value (r, RB_MUSICBRAINZ_ATTR_ALBUM_ID),
+ release_includes,
+ NULL,
+ (GAsyncReadyCallback) release_lookup_cb,
+ info);
+ break;
+ }
+ }
+ rb_musicbrainz_data_free (dd);
+ } else {
+ g_print ("lookup failed: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
+}
+
+
+static void
+audiocd_info_cb (GObject *source, GAsyncResult *result, gpointer data)
+{
+ RBAudioCDInfo *info;
+ GError *error = NULL;
+ int i;
+
+ info = rb_audiocd_info_finish (result, &error);
+
+ if (error != NULL) {
+ g_print ("err: %s\n", error->message);
+ g_clear_error (&error);
+ } else {
+ g_print ("disc id: %s\n", info->musicbrainz_disc_id);
+ g_print ("%d tracks\n", info->num_tracks);
+
+ for (i = 0; i < info->num_tracks; i++) {
+ g_print ("%d: %ds\n", info->tracks[i].track_num, info->tracks[i].duration/1000);
+ }
+ }
+
+ if (info->musicbrainz_disc_id) {
+ const char *includes[] = { "artist-credits" };
+
+ rb_musicbrainz_lookup ("discid",
+ info->musicbrainz_disc_id,
+ includes,
+ NULL,
+ (GAsyncReadyCallback) lookup_cb,
+ info);
+ } else {
+ rb_audiocd_info_free (info);
+ g_main_loop_quit (mainloop);
+ }
+}
+
+static void
+release_cb (GObject *source, GAsyncResult *result, gpointer data)
+{
+ RBMusicBrainzData *dd;
+ GError *error = NULL;
+
+ dd = rb_musicbrainz_lookup_finish (result, &error);
+ if (dd != NULL) {
+ dump (dd, 0, 1);
+ rb_musicbrainz_data_free (dd);
+ } else {
+ g_print ("lookup failed: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
+ g_main_loop_quit (mainloop);
+}
+
+static gboolean
+go (const char *thing)
+{
+ if (thing[0] == '/') {
+ rb_audiocd_info_get (thing, NULL, (GAsyncReadyCallback)audiocd_info_cb, NULL);
+ } else {
+ const char *includes[] = { "artist-credits", NULL };
+ rb_musicbrainz_lookup ("release", thing, includes, NULL, (GAsyncReadyCallback) release_cb, NULL);
+ }
+ return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+ char *thing;
+
+ g_type_init ();
+ gst_init (NULL, NULL);
+
+ if (argv[1] == NULL) {
+ thing = "/dev/cdrom";
+ } else {
+ thing = argv[1];
+ }
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+ g_idle_add ((GSourceFunc) go, thing);
+ g_main_loop_run (mainloop);
+
+ gst_deinit ();
+
+ return 0;
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 01d7638..dbd8d4e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -33,14 +33,10 @@ plugins/artsearch/artsearch.py
plugins/artsearch/lastfm.py
[type: gettext/glade]plugins/audiocd/album-info.ui
[type: gettext/ini]plugins/audiocd/audiocd.plugin.in
-[type: gettext/glade]plugins/audiocd/multiple-album.ui
+plugins/audiocd/rb-audiocd-info.c
plugins/audiocd/rb-audiocd-plugin.c
plugins/audiocd/rb-audiocd-source.c
-plugins/audiocd/sj-error.c
-plugins/audiocd/sj-metadata.c
-plugins/audiocd/sj-metadata-getter.c
-plugins/audiocd/sj-metadata-gvfs.c
-plugins/audiocd/sj-structures.c
+plugins/audiocd/rb-musicbrainz-lookup.c
[type: gettext/ini]plugins/audioscrobbler/audioscrobbler.plugin.in
[type: gettext/glade]plugins/audioscrobbler/audioscrobbler-preferences.ui
[type: gettext/glade]plugins/audioscrobbler/audioscrobbler-profile.ui
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]