diff -u rhythmbox-0.12.0/debian/changelog rhythmbox-0.12.0/debian/changelog --- rhythmbox-0.12.0/debian/changelog +++ rhythmbox-0.12.0/debian/changelog @@ -1,3 +1,10 @@ +rhythmbox (0.12.0-0ubuntu5) jaunty; urgency=low + + * debian/patches/05_metadata_extend_media_type_checking.patch: + - Only run the codec installer for audio-only files (LP: #343707) + + -- Martin Mai Sat, 11 Jul 2009 14:46:56 +0200 + rhythmbox (0.12.0-0ubuntu4) jaunty; urgency=low * debian/patches/94_svn_change_fix_magnatune_loading.patch: only in patch2: unchanged: --- rhythmbox-0.12.0.orig/debian/patches/05_metadata_extend_media_type_checking.patch +++ rhythmbox-0.12.0/debian/patches/05_metadata_extend_media_type_checking.patch @@ -0,0 +1,203 @@ +diff -Nur -x '*.orig' -x '*~' rhythmbox-0.12.0/rhythmdb/rhythmdb.c rhythmbox-0.12.0.new/rhythmdb/rhythmdb.c +--- rhythmbox-0.12.0/rhythmdb/rhythmdb.c 2009-03-12 03:26:18.000000000 +0100 ++++ rhythmbox-0.12.0.new/rhythmdb/rhythmdb.c 2009-07-11 14:46:40.589004105 +0200 +@@ -80,6 +80,33 @@ + RHYTHMDB_FILE_INFO_ATTRIBUTES "," \ + G_FILE_ATTRIBUTE_STANDARD_NAME + ++/* ++ * Filters for MIME/media types to ignore. ++ * The only complication here is that there are some application/ types that ++ * are used for audio/video files. Otherwise, we'd ignore everything except ++ * audio/ and video/. ++ */ ++struct media_type_filter { ++ const char *prefix; ++ gboolean ignore; ++} media_type_filters[] = { ++ { "image/", TRUE }, ++ { "text/", TRUE }, ++ { "application/ogg", FALSE }, ++ { "application/x-id3", FALSE }, ++ { "application/x-apetag", FALSE }, ++ { "application/x-3gp", FALSE }, ++ { "application/", TRUE }, ++}; ++ ++/* ++ * File size below which we will simply ignore files that can't be identified. ++ * This is mostly here so we ignore the various text files that are packaged ++ * with many netlabel releases and other downloads. ++ */ ++#define REALLY_SMALL_FILE_SIZE (4096) ++ ++ + typedef struct + { + RhythmDB *db; +@@ -727,6 +754,19 @@ + return error; + } + ++static gboolean ++rhythmdb_ignore_media_type (const char *media_type) ++{ ++ int i; ++ ++ for (i = 0; i < G_N_ELEMENTS (media_type_filters); i++) { ++ if (g_str_has_prefix (media_type, media_type_filters[i].prefix)) { ++ return media_type_filters[i].ignore; ++ } ++ } ++ return FALSE; ++} ++ + typedef struct { + RhythmDBEvent *event; + GMutex *mutex; +@@ -2139,20 +2179,14 @@ + + static void + rhythmdb_add_import_error_entry (RhythmDB *db, +- RhythmDBEvent *event) ++ RhythmDBEvent *event, ++ RhythmDBEntryType error_entry_type) + { + RhythmDBEntry *entry; + GValue value = {0,}; +- RhythmDBEntryType error_entry_type = event->error_type; +- +- rb_debug ("adding import error for %s: %s", rb_refstring_get (event->real_uri), event->error->message); +- if (g_error_matches (event->error, RB_METADATA_ERROR, RB_METADATA_ERROR_NOT_AUDIO_IGNORE)) { +- /* only add an ignore entry if we have an entry type for it */ +- if (event->ignore_type == RHYTHMDB_ENTRY_TYPE_INVALID) +- return; + +- error_entry_type = event->ignore_type; +- } else if (event->error_type == RHYTHMDB_ENTRY_TYPE_INVALID) { ++ rb_debug ("adding import error for %s: %s", rb_refstring_get (event->real_uri), event->error ? event->error->message : ""); ++ if (error_entry_type == RHYTHMDB_ENTRY_TYPE_INVALID) { + /* we don't have an error entry type, so we can't add an import error */ + return; + } +@@ -2232,18 +2266,51 @@ + { + RhythmDBEntry *entry; + GValue value = {0,}; +- const char *mime; + GTimeVal time; ++ const char *media_type; ++ ++ /* ++ * always ignore anything with video in it, or anything ++ * matching one of the media types we don't care about. ++ * if we can identify it that much, we know it's not interesting. ++ * otherwise, add an import error entry if there was an error, ++ * or just ignore it if it doesn't contain audio. ++ */ ++ ++ media_type = rb_metadata_get_mime (event->metadata); ++ if (rb_metadata_has_video (event->metadata) || ++ (media_type != NULL && rhythmdb_ignore_media_type (media_type))) { ++ rhythmdb_add_import_error_entry (event->db, event, event->ignore_type); ++ return TRUE; ++ } ++ ++ /* also ignore really small files we can't identify */ ++ if (event->error && event->error->code == RB_METADATA_ERROR_UNRECOGNIZED) { ++ guint64 file_size; ++ ++ file_size = g_file_info_get_attribute_uint64 (event->file_info, ++ G_FILE_ATTRIBUTE_STANDARD_SIZE); ++ if (file_size == 0) { ++ /* except for empty files */ ++ g_clear_error (&event->error); ++ g_set_error (&event->error, ++ RB_METADATA_ERROR, ++ RB_METADATA_ERROR_EMPTY_FILE, ++ _("Empty file")); ++ } else if (file_size < REALLY_SMALL_FILE_SIZE) { ++ rhythmdb_add_import_error_entry (event->db, event, event->ignore_type); ++ return TRUE; ++ } ++ } + + if (event->error) { +- rhythmdb_add_import_error_entry (event->db, event); ++ rhythmdb_add_import_error_entry (event->db, event, event->error_type); + return TRUE; + } + +- /* do we really need to do this? */ +- mime = rb_metadata_get_mime (event->metadata); +- if (!mime) { +- rb_debug ("unsupported file"); ++ /* check if this is something we want in the library */ ++ if (rb_metadata_has_audio (event->metadata) == FALSE) { ++ rhythmdb_add_import_error_entry (event->db, event, event->ignore_type); + return TRUE; + } + +@@ -2374,22 +2441,19 @@ + rhythmdb_process_metadata_load (RhythmDB *db, + RhythmDBEvent *event) + { +- char **missing_plugins; +- char **plugin_descriptions; +- +- /* don't process missing plugin messages for files we're ignoring */ +- if (g_error_matches (event->error, +- RB_METADATA_ERROR, +- RB_METADATA_ERROR_NOT_AUDIO_IGNORE)) { +- return rhythmdb_process_metadata_load_real (event); +- } else if (event->metadata != NULL && +- rb_metadata_get_missing_plugins (event->metadata, +- &missing_plugins, +- &plugin_descriptions)) { ++ /* only process missing plugins for audio files */ ++ if (event->metadata != NULL && ++ rb_metadata_has_audio (event->metadata) == TRUE && ++ rb_metadata_has_video (event->metadata) == FALSE && ++ rb_metadata_has_missing_plugins (event->metadata) == TRUE) { ++ char **missing_plugins; ++ char **plugin_descriptions; + GClosure *closure; + gboolean processing; ++ ++ rb_metadata_get_missing_plugins (event->metadata, &missing_plugins, &plugin_descriptions); + +- rb_debug ("missing plugins during metadata load for %s (event = %p)", rb_refstring_get (event->real_uri), event); ++ rb_debug ("missing plugins during metadata load for %s", rb_refstring_get (event->real_uri)); + + g_mutex_lock (event->db->priv->metadata_lock); + +@@ -2406,6 +2470,17 @@ + + g_closure_sink (closure); + return FALSE; ++ } else if (rb_metadata_has_missing_plugins (event->metadata)) { ++ rb_debug ("ignoring missing plugins for %s; not audio (%d %d %d)", ++ rb_refstring_get (event->real_uri), ++ rb_metadata_has_audio (event->metadata), ++ rb_metadata_has_video (event->metadata), ++ rb_metadata_has_other_data (event->metadata)); ++ ++ g_mutex_lock (db->priv->metadata_lock); ++ db->priv->metadata_blocked = FALSE; ++ g_cond_signal (db->priv->metadata_cond); ++ g_mutex_unlock (db->priv->metadata_lock); + } + + return rhythmdb_process_metadata_load_real (event); +@@ -2678,10 +2753,7 @@ + /* if we're missing some plugins, block further attempts to + * read metadata until we've processed them. + */ +- if (!g_error_matches (event->error, +- RB_METADATA_ERROR, +- RB_METADATA_ERROR_NOT_AUDIO_IGNORE) && +- rb_metadata_has_missing_plugins (event->metadata)) { ++ if (rb_metadata_has_missing_plugins (event->metadata)) { + event->db->priv->metadata_blocked = TRUE; + } + only in patch2: unchanged: --- rhythmbox-0.12.0.orig/metadata/test-metadata.c +++ rhythmbox-0.12.0/metadata/test-metadata.c @@ -86,6 +86,7 @@ char **missing_plugins; char **plugin_descriptions; GError *error = NULL; + RBMetaDataField f; if (strncmp (uri, "file://", 7)) { if (uri[0] == '/') { @@ -106,22 +107,18 @@ rb_metadata_load (md, (const char *)uri, &error); if (error) { - switch (error->code) { - case RB_METADATA_ERROR_NOT_AUDIO_IGNORE: - printf ("file ignored: %s\n", error->message); - break; - default: - printf ("error: %s\n", error->message); - break; - } + printf ("error: %s\n", error->message); g_clear_error (&error); - } else { - RBMetaDataField f; - - printf ("type: %s\n", rb_metadata_get_mime (md)); - for (f =(RBMetaDataField)0; f < RB_METADATA_FIELD_LAST; f++) - print_metadata_string (md, f, rb_metadata_get_field_name (f)); } + + printf ("type: %s\n", rb_metadata_get_mime (md)); + for (f =(RBMetaDataField)0; f < RB_METADATA_FIELD_LAST; f++) + print_metadata_string (md, f, rb_metadata_get_field_name (f)); + + printf ("has audio: %d\n", rb_metadata_has_audio (md)); + printf ("has video: %d\n", rb_metadata_has_video (md)); + printf ("has other data: %d\n", rb_metadata_has_other_data (md)); + if (rb_metadata_get_missing_plugins (md, &missing_plugins, &plugin_descriptions)) { int i = 0; g_print ("missing plugins:\n"); @@ -168,14 +165,15 @@ rb_debug_init (TRUE); } - if (can_save) { - g_idle_add (check_can_save_cb, argv[2]); - } loop = g_main_loop_new (NULL, FALSE); md = rb_metadata_new (); while (argv[1] != NULL) { - g_idle_add (load_metadata_cb, argv[1]); + if (can_save) { + g_idle_add (check_can_save_cb, argv[1]); + } else { + g_idle_add (load_metadata_cb, argv[1]); + } argv++; filecount++; } @@ -183,7 +181,11 @@ g_main_loop_run (loop); - printf ("%d file(s) read\n", filecount); + if (can_save) { + printf ("%d file type(s) checked\n", filecount); + } else { + printf ("%d file(s) read\n", filecount); + } g_object_unref (G_OBJECT (md)); return 0; } only in patch2: unchanged: --- rhythmbox-0.12.0.orig/metadata/rb-metadata-gst.c +++ rhythmbox-0.12.0/metadata/rb-metadata-gst.c @@ -40,48 +40,16 @@ #include #include "rb-metadata.h" +#include "rb-metadata-gst-common.h" #include "rb-debug.h" #include "rb-util.h" #include "rb-file-helpers.h" G_DEFINE_TYPE(RBMetaData, rb_metadata, G_TYPE_OBJECT) -static void rb_metadata_finalize (GObject *object); - -typedef GstElement *(*RBAddTaggerElem) (RBMetaData *, GstElement *); - -/* - * The list of mine type prefixes for files that shouldn't display errors about being non-audio. - * Useful for people who have cover art, et cetera, in their music directories - */ -const char * ignore_mime_types[] = { - "image/", - "text/", - "application/xml", - "application/zip", - "application/x-executable", - "application/x-ms-dos-executable", - "application/x-bzip", - "application/x-gzip", - "application/pdf", - "application/x-rar", - "application/msword", - "application/octet-stream" -}; +typedef GstElement *(*RBAddTaggerElem) (GstElement *pipeline, GstElement *source, GstTagList *tags); -/* - * File size below which we will simply ignore files that can't be identified. - * This is mostly here so we ignore the various text files that are packaged - * with many netlabel releases and other downloads. - */ -#define REALLY_SMALL_FILE_SIZE (4096) - -struct RBMetadataGstType -{ - char *mimetype; - RBAddTaggerElem tag_func; - char *human_name; -}; +static void rb_metadata_finalize (GObject *object); struct RBMetaDataPrivate { @@ -94,11 +62,9 @@ gulong typefind_cb_id; GstTagList *tags; - /* Array of RBMetadataGstType */ - GPtrArray *supported_types; + GHashTable *taggers; char *type; - gboolean handoff; gboolean eos; gboolean has_audio; gboolean has_non_audio; @@ -109,49 +75,20 @@ #define RB_METADATA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_METADATA, RBMetaDataPrivate)) -static void -gst_date_gulong_transform (const GValue *src, GValue *dest) -{ - const GDate *date = gst_value_get_date (src); - - g_value_set_ulong (dest, (date) ? g_date_get_julian (date) : 0); -} - -static void -gulong_gst_date_transform (const GValue *src, GValue *dest) -{ - gulong day = g_value_get_ulong (src); - GDate *date = g_date_new_julian (day); - - gst_value_set_date (dest, date); - g_date_free (date); -} - -static void -rb_metadata_class_init (RBMetaDataClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = rb_metadata_finalize; - - g_type_class_add_private (klass, sizeof (RBMetaDataPrivate)); - g_value_register_transform_func (GST_TYPE_DATE, G_TYPE_ULONG, gst_date_gulong_transform); - g_value_register_transform_func (G_TYPE_ULONG, GST_TYPE_DATE, gulong_gst_date_transform); -} static GstElement * -rb_add_flac_tagger (RBMetaData *md, GstElement *element) +flac_tagger (GstElement *pipeline, GstElement *link_to, GstTagList *tags) { GstElement *tagger = NULL; - if (!(tagger = gst_element_factory_make ("flactag", "flactag"))) + tagger = gst_element_factory_make ("flactag", NULL); + if (tagger == NULL) return NULL; - gst_bin_add (GST_BIN (md->priv->pipeline), tagger); - gst_element_link_many (element, tagger, NULL); - - gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), md->priv->tags, GST_TAG_MERGE_REPLACE_ALL); + gst_bin_add (GST_BIN (pipeline), tagger); + gst_element_link_many (link_to, tagger, NULL); + gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), tags, GST_TAG_MERGE_REPLACE_ALL); return tagger; } @@ -162,66 +99,30 @@ mux_pad = gst_element_get_compatible_pad (mux, pad, NULL); if (gst_pad_link (pad, mux_pad) != GST_PAD_LINK_OK) - rb_debug ("unable to link pad from id3demux to id3mux"); + rb_debug ("unable to link pad from id3demux to id3v2mux"); else - rb_debug ("linked pad from id3de to id3mux"); -} - -static gboolean -rb_gst_plugin_greater (const char *plugin, const char *element, gint major, gint minor, gint micro) -{ - const char *version; - GstPlugin *p; - guint i; - guint count; - - if (gst_default_registry_check_feature_version (element, major, minor, micro + 1)) - return TRUE; - - if (!gst_default_registry_check_feature_version (element, major, minor, micro)) - return FALSE; - - p = gst_default_registry_find_plugin (plugin); - if (p == NULL) - return FALSE; - - version = gst_plugin_get_version (p); - - /* check if it's not a release */ - count = sscanf (version, "%u.%u.%u.%u", &i, &i, &i, &i); - return (count > 3); + rb_debug ("linked pad from id3demux to id3v2mux"); } static GstElement * -rb_add_id3_tagger (RBMetaData *md, GstElement *element) +id3_tagger (GstElement *pipeline, GstElement *link_to, GstTagList *tags) { GstElement *demux = NULL; GstElement *mux = NULL; + /* TODO use new id3tag element here; not sure what name it'll end up with though */ demux = gst_element_factory_make ("id3demux", NULL); - mux = gst_element_factory_make ("id3v2mux", NULL); - if (mux != NULL) { - /* check for backwards id3v2mux merge-mode */ - if (!rb_gst_plugin_greater ("taglib", "id3v2mux", 0, 10, 3)) { - rb_debug ("using id3v2mux with backwards merge mode"); - gst_tag_setter_set_tag_merge_mode (GST_TAG_SETTER (mux), GST_TAG_MERGE_REPLACE); - } else { - rb_debug ("using id3v2mux"); - } - } - if (demux == NULL || mux == NULL) goto error; - gst_bin_add_many (GST_BIN (md->priv->pipeline), demux, mux, NULL); - if (!gst_element_link (element, demux)) + gst_bin_add_many (GST_BIN (pipeline), demux, mux, NULL); + if (!gst_element_link (link_to, demux)) goto error; g_signal_connect (demux, "pad-added", (GCallback)id3_pad_added_cb, mux); - gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), md->priv->tags, GST_TAG_MERGE_REPLACE_ALL); - + gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), tags, GST_TAG_MERGE_REPLACE_ALL); return mux; error: @@ -231,7 +132,7 @@ } static void -ogg_pad_added_cb (GstElement *demux, GstPad *pad, RBMetaData *md) +ogg_pad_added_cb (GstElement *demux, GstPad *pad, GstTagList *tags) { GstCaps *caps; GstStructure *structure; @@ -278,7 +179,7 @@ conn_pad = gst_element_get_compatible_pad (tagger, pad, NULL); gst_pad_link (pad, conn_pad); - gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), md->priv->tags, GST_TAG_MERGE_REPLACE_ALL); + gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), tags, GST_TAG_MERGE_REPLACE_ALL); } else { conn_pad = gst_element_get_compatible_pad (mux, pad, NULL); gst_pad_link (pad, conn_pad); @@ -290,7 +191,7 @@ } static GstElement * -rb_add_ogg_tagger (RBMetaData *md, GstElement *element) +vorbis_tagger (GstElement *pipeline, GstElement *link_to, GstTagList *tags) { GstElement *demux = NULL; GstElement *mux = NULL; @@ -301,13 +202,12 @@ if (demux == NULL || mux == NULL) goto error; - gst_bin_add_many (GST_BIN (md->priv->pipeline), demux, mux, NULL); - if (!gst_element_link (element, demux)) + gst_bin_add_many (GST_BIN (pipeline), demux, mux, NULL); + if (!gst_element_link (link_to, demux)) goto error; g_object_set_data (G_OBJECT (demux), "mux", mux); - g_signal_connect (demux, "pad-added", (GCallback)ogg_pad_added_cb, md); - + g_signal_connect (demux, "pad-added", (GCallback)ogg_pad_added_cb, tags); return mux; error: @@ -317,7 +217,7 @@ } static void -qt_pad_added_cb (GstElement *demux, GstPad *demuxpad, GstPad *muxpad) +mp4_pad_added_cb (GstElement *demux, GstPad *demuxpad, GstPad *muxpad) { if (gst_pad_link (demuxpad, muxpad) != GST_PAD_LINK_OK) rb_debug ("unable to link pad from qtdemux to mp4mux"); @@ -327,7 +227,7 @@ static GstElement * -rb_add_qt_tagger (RBMetaData *md, GstElement *element) +mp4_tagger (GstElement *pipeline, GstElement *link_to, GstTagList *tags) { GstElement *demux; GstElement *mux; @@ -338,14 +238,14 @@ if (demux == NULL || mux == NULL) goto error; - gst_bin_add_many (GST_BIN (md->priv->pipeline), demux, mux, NULL); - if (!gst_element_link (element, demux)) + gst_bin_add_many (GST_BIN (pipeline), demux, mux, NULL); + if (!gst_element_link (link_to, demux)) goto error; muxpad = gst_element_get_request_pad (mux, "audio_%d"); - g_signal_connect (demux, "pad-added", G_CALLBACK (qt_pad_added_cb), muxpad); - - gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), md->priv->tags, GST_TAG_MERGE_REPLACE_ALL); + g_signal_connect (demux, "pad-added", G_CALLBACK (mp4_pad_added_cb), muxpad); + + gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), tags, GST_TAG_MERGE_REPLACE_ALL); return mux; @@ -357,92 +257,74 @@ return NULL; } + + static void -add_supported_type (RBMetaData *md, - const char *mime, - RBAddTaggerElem add_tagger_func, - const char *human_name) -{ - struct RBMetadataGstType *type = g_new0 (struct RBMetadataGstType, 1); - type->mimetype = g_strdup (mime); - type->tag_func = add_tagger_func; - type->human_name = g_strdup (human_name); - g_ptr_array_add (md->priv->supported_types, type); +rb_metadata_class_init (RBMetaDataClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = rb_metadata_finalize; + + g_type_class_add_private (klass, sizeof (RBMetaDataPrivate)); + rb_metadata_gst_register_transforms (); } static void rb_metadata_init (RBMetaData *md) { - RBAddTaggerElem tagger; - gboolean has_giosink = FALSE; - gboolean has_id3 = FALSE; - md->priv = RB_METADATA_GET_PRIVATE (md); - md->priv->supported_types = g_ptr_array_new (); - - /* the list of supported types serves two purposes: - * - it knows how to construct elements for tag writing - * - it knows human-readable names for MIME types so we can say - * "There is no plugin available to play WMV files" - * rather than " .. play video/x-ms-asf files". - * - * only registering types we have plugins for defeats the second - * purpose. - */ - has_giosink = (gst_element_factory_find ("giostreamsink") != NULL); - has_id3 = (gst_element_factory_find ("id3v2mux") != NULL); - tagger = (has_giosink && has_id3) ? rb_add_id3_tagger : NULL; - add_supported_type (md, "application/x-id3", tagger, "MP3"); - add_supported_type (md, "audio/mpeg", tagger, "MP3"); - - { - gboolean has_vorbis; - - has_vorbis = ((gst_element_factory_find ("vorbistag") != NULL) && - gst_default_registry_check_feature_version ("vorbisparse", 0, 10, 6) && - gst_default_registry_check_feature_version ("oggmux", 0, 10, 6) && - gst_default_registry_check_feature_version ("oggdemux", 0, 10, 6)); - tagger = (has_giosink && has_vorbis) ? rb_add_ogg_tagger : NULL; - } - add_supported_type (md, "application/ogg", tagger, "Ogg Vorbis"); - add_supported_type (md, "audio/x-vorbis", tagger, "Ogg Vorbis"); - - add_supported_type (md, "audio/x-mod", NULL, "MOD"); - add_supported_type (md, "audio/x-wav", NULL, "WAV"); - add_supported_type (md, "video/x-ms-asf", NULL, "ASF"); - - tagger = (has_giosink && gst_element_factory_find ("flactag")) ? rb_add_flac_tagger : NULL; - add_supported_type (md, "audio/x-flac", tagger, "FLAC"); - - tagger = (has_giosink && gst_element_factory_find ("qtdemux") && gst_element_factory_find ("mp4mux")) ? rb_add_qt_tagger : NULL; - add_supported_type (md, "audio/x-m4a", tagger, "M4A"); - add_supported_type (md, "video/quicktime", tagger, "M4A"); /* hmm. */ + md->priv->taggers = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + if (gst_element_factory_find ("giostreamsink") == FALSE) { + rb_debug ("giostreamsink not found, can't tag anything"); + } else { + if (gst_element_factory_find ("vorbistag") && + gst_element_factory_find ("vorbisparse") && + gst_element_factory_find ("oggdemux") && + gst_element_factory_find ("oggmux")) { + rb_debug ("ogg vorbis tagging available"); + g_hash_table_insert (md->priv->taggers, "application/ogg", vorbis_tagger); + g_hash_table_insert (md->priv->taggers, "audio/x-vorbis", vorbis_tagger); + } + + if (gst_element_factory_find ("flactag")) { + rb_debug ("flac tagging available"); + g_hash_table_insert (md->priv->taggers, "audio/x-flac", flac_tagger); + } + + /* TODO check for new id3 tag element too */ + if (gst_element_factory_find ("id3v2mux") && gst_element_factory_find ("id3demux")) { + rb_debug ("id3 tagging available"); + g_hash_table_insert (md->priv->taggers, "application/x-id3", id3_tagger); + g_hash_table_insert (md->priv->taggers, "audio/mpeg", id3_tagger); + } + + if (gst_element_factory_find ("qtdemux") && gst_element_factory_find ("mp4mux")) { + rb_debug ("mp4 tagging available"); + g_hash_table_insert (md->priv->taggers, "audio/x-m4a", mp4_tagger); + g_hash_table_insert (md->priv->taggers, "video/quicktime", mp4_tagger); + } + } } static void rb_metadata_finalize (GObject *object) { - int i; RBMetaData *md; md = RB_METADATA (object); - for (i = 0; i < md->priv->supported_types->len; i++) { - struct RBMetadataGstType *type = g_ptr_array_index (md->priv->supported_types, i); - g_free (type->mimetype); - g_free (type->human_name); - g_free (type); - } - g_ptr_array_free (md->priv->supported_types, TRUE); - if (md->priv->metadata) g_hash_table_destroy (md->priv->metadata); if (md->priv->pipeline) gst_object_unref (GST_OBJECT (md->priv->pipeline)); + if (md->priv->taggers) + g_hash_table_destroy (md->priv->taggers); + g_free (md->priv->type); g_free (md->priv->uri); g_clear_error (&md->priv->error); @@ -457,202 +339,6 @@ } static void -free_gvalue (GValue *val) -{ - g_value_unset (val); - g_free (val); -} - -static int -rb_metadata_gst_tag_to_field (const char *tag) -{ - if (!strcmp (tag, GST_TAG_TITLE)) - return RB_METADATA_FIELD_TITLE; - else if (!strcmp (tag, GST_TAG_ARTIST)) - return RB_METADATA_FIELD_ARTIST; - else if (!strcmp (tag, GST_TAG_ALBUM)) - return RB_METADATA_FIELD_ALBUM; - else if (!strcmp (tag, GST_TAG_DATE)) - return RB_METADATA_FIELD_DATE; - else if (!strcmp (tag, GST_TAG_GENRE)) - return RB_METADATA_FIELD_GENRE; - else if (!strcmp (tag, GST_TAG_COMMENT)) - return RB_METADATA_FIELD_COMMENT; - else if (!strcmp (tag, GST_TAG_TRACK_NUMBER)) - return RB_METADATA_FIELD_TRACK_NUMBER; - else if (!strcmp (tag, GST_TAG_TRACK_COUNT)) - return RB_METADATA_FIELD_MAX_TRACK_NUMBER; - else if (!strcmp (tag, GST_TAG_ALBUM_VOLUME_NUMBER)) - return RB_METADATA_FIELD_DISC_NUMBER; - else if (!strcmp (tag, GST_TAG_ALBUM_VOLUME_COUNT)) - return RB_METADATA_FIELD_MAX_TRACK_NUMBER; - else if (!strcmp (tag, GST_TAG_DESCRIPTION)) - return RB_METADATA_FIELD_DESCRIPTION; - else if (!strcmp (tag, GST_TAG_VERSION)) - return RB_METADATA_FIELD_VERSION; - else if (!strcmp (tag, GST_TAG_ISRC)) - return RB_METADATA_FIELD_ISRC; - else if (!strcmp (tag, GST_TAG_ORGANIZATION)) - return RB_METADATA_FIELD_ORGANIZATION; - else if (!strcmp (tag, GST_TAG_COPYRIGHT)) - return RB_METADATA_FIELD_COPYRIGHT; - else if (!strcmp (tag, GST_TAG_CONTACT)) - return RB_METADATA_FIELD_CONTACT; - else if (!strcmp (tag, GST_TAG_LICENSE)) - return RB_METADATA_FIELD_LICENSE; - else if (!strcmp (tag, GST_TAG_PERFORMER)) - return RB_METADATA_FIELD_PERFORMER; - else if (!strcmp (tag, GST_TAG_DURATION)) - return RB_METADATA_FIELD_DURATION; - else if (!strcmp (tag, GST_TAG_CODEC)) - return RB_METADATA_FIELD_CODEC; - else if (!strcmp (tag, GST_TAG_BITRATE)) - return RB_METADATA_FIELD_BITRATE; - else if (!strcmp (tag, GST_TAG_TRACK_GAIN)) - return RB_METADATA_FIELD_TRACK_GAIN; - else if (!strcmp (tag, GST_TAG_TRACK_PEAK)) - return RB_METADATA_FIELD_TRACK_PEAK; - else if (!strcmp (tag, GST_TAG_ALBUM_GAIN)) - return RB_METADATA_FIELD_ALBUM_GAIN; - else if (!strcmp (tag, GST_TAG_ALBUM_PEAK)) - return RB_METADATA_FIELD_ALBUM_PEAK; -#ifdef GST_TAG_MUSICBRAINZ_TRACKID - else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_TRACKID)) - return RB_METADATA_FIELD_MUSICBRAINZ_TRACKID; - else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_ARTISTID)) - return RB_METADATA_FIELD_MUSICBRAINZ_ARTISTID; - else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_ALBUMID)) - return RB_METADATA_FIELD_MUSICBRAINZ_ALBUMID; - else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_ALBUMARTISTID)) - return RB_METADATA_FIELD_MUSICBRAINZ_ALBUMARTISTID; - else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_SORTNAME)) - return RB_METADATA_FIELD_ARTIST_SORTNAME; - -#endif - else - return -1; -} - -static const char * -rb_metadata_gst_field_to_gst_tag (RBMetaDataField field) -{ - switch (field) - { - case RB_METADATA_FIELD_TITLE: - return GST_TAG_TITLE; - case RB_METADATA_FIELD_ARTIST: - return GST_TAG_ARTIST; - case RB_METADATA_FIELD_ALBUM: - return GST_TAG_ALBUM; - case RB_METADATA_FIELD_DATE: - return GST_TAG_DATE; - case RB_METADATA_FIELD_GENRE: - return GST_TAG_GENRE; - case RB_METADATA_FIELD_COMMENT: - return GST_TAG_COMMENT; - case RB_METADATA_FIELD_TRACK_NUMBER: - return GST_TAG_TRACK_NUMBER; - case RB_METADATA_FIELD_MAX_TRACK_NUMBER: - return GST_TAG_TRACK_COUNT; - case RB_METADATA_FIELD_DISC_NUMBER: - return GST_TAG_ALBUM_VOLUME_NUMBER; - case RB_METADATA_FIELD_MAX_DISC_NUMBER: - return GST_TAG_ALBUM_VOLUME_COUNT; - case RB_METADATA_FIELD_DESCRIPTION: - return GST_TAG_DESCRIPTION; - case RB_METADATA_FIELD_VERSION: - return GST_TAG_VERSION; - case RB_METADATA_FIELD_ISRC: - return GST_TAG_ISRC; - case RB_METADATA_FIELD_ORGANIZATION: - return GST_TAG_ORGANIZATION; - case RB_METADATA_FIELD_COPYRIGHT: - return GST_TAG_COPYRIGHT; - case RB_METADATA_FIELD_CONTACT: - return GST_TAG_CONTACT; - case RB_METADATA_FIELD_LICENSE: - return GST_TAG_LICENSE; - case RB_METADATA_FIELD_PERFORMER: - return GST_TAG_PERFORMER; - case RB_METADATA_FIELD_DURATION: - return GST_TAG_DURATION; - case RB_METADATA_FIELD_CODEC: - return GST_TAG_CODEC; - case RB_METADATA_FIELD_BITRATE: - return GST_TAG_BITRATE; - case RB_METADATA_FIELD_TRACK_GAIN: - return GST_TAG_TRACK_GAIN; - case RB_METADATA_FIELD_TRACK_PEAK: - return GST_TAG_TRACK_PEAK; - case RB_METADATA_FIELD_ALBUM_GAIN: - return GST_TAG_ALBUM_GAIN; - case RB_METADATA_FIELD_ALBUM_PEAK: - return GST_TAG_ALBUM_PEAK; -#ifdef GST_TAG_MUSICBRAINZ_TRACKID - case RB_METADATA_FIELD_MUSICBRAINZ_TRACKID: - return GST_TAG_MUSICBRAINZ_TRACKID; - case RB_METADATA_FIELD_MUSICBRAINZ_ARTISTID: - return GST_TAG_MUSICBRAINZ_ARTISTID; - case RB_METADATA_FIELD_MUSICBRAINZ_ALBUMID: - return GST_TAG_MUSICBRAINZ_ALBUMID; - case RB_METADATA_FIELD_MUSICBRAINZ_ALBUMARTISTID: - return GST_TAG_MUSICBRAINZ_ALBUMARTISTID; - case RB_METADATA_FIELD_ARTIST_SORTNAME: - return GST_TAG_MUSICBRAINZ_SORTNAME; -#endif - default: - return NULL; - } -} - -static const char * -rb_metadata_gst_type_to_name (RBMetaData *md, const char *mimetype) -{ - int i; - for (i = 0; i < md->priv->supported_types->len; i++) { - struct RBMetadataGstType *type = g_ptr_array_index (md->priv->supported_types, i); - if (!strcmp (type->mimetype, mimetype)) - return type->human_name; - } - return NULL; -} - -static RBAddTaggerElem -rb_metadata_gst_type_to_tag_function (RBMetaData *md, const char *mimetype) -{ - int i; - for (i = 0; i < md->priv->supported_types->len; i++) { - struct RBMetadataGstType *type = g_ptr_array_index (md->priv->supported_types, i); - if (!strcmp (type->mimetype, mimetype)) - return type->tag_func; - } - return NULL; -} - -static char * -make_undecodable_error (RBMetaData *md) -{ - const char *human_name; - char *free_name = NULL; - - human_name = rb_metadata_gst_type_to_name (md, md->priv->type); - if (human_name == NULL) { - free_name = rb_mime_get_friendly_name (md->priv->type); - human_name = free_name; - } - - if (human_name) { - return g_strdup_printf (_("The GStreamer plugins to decode \"%s\" files cannot be found"), - human_name); - } else { - return g_strdup_printf (_("The file contains a stream of type %s, which is not decodable"), - md->priv->type); - } - - g_free (free_name); -} - -static void rb_metadata_gst_load_tag (const GstTagList *list, const gchar *tag, RBMetaData *md) { int count, tem, type; @@ -660,20 +346,20 @@ GValue *newval; const GValue *val; - rb_debug ("uri: %s tag: %s ", md->priv->uri, tag); - count = gst_tag_list_get_tag_size (list, tag); if (count < 1) return; tem = rb_metadata_gst_tag_to_field (tag); - if (tem < 0) + if (tem < 0) { + rb_debug ("no metadata field for tag \"%s\"", tag); return; + } field = (RBMetaDataField) tem; type = rb_metadata_get_field_type (field); val = gst_tag_list_get_value_index (list, tag, 0); - newval = g_new0 (GValue, 1); + newval = g_slice_new0 (GValue); g_value_init (newval, type); if (!g_value_transform (val, newval)) { @@ -721,6 +407,9 @@ } } } + + rb_debug ("processed string tag \"%s\": \"%s\"", tag, str); + g_value_take_string (newval, str); break; } @@ -734,6 +423,7 @@ gulong bitrate; bitrate = g_value_get_ulong (newval); g_value_set_ulong (newval, bitrate/1000); + rb_debug ("processed bitrate value: %lu", g_value_get_ulong (newval)); break; } @@ -744,6 +434,7 @@ guint64 duration; duration = g_value_get_uint64 (val); g_value_set_ulong (newval, duration/(1000*1000*1000)); + rb_debug ("processed duration value: %lu", g_value_get_ulong (newval)); break; } @@ -786,8 +477,9 @@ } else { GstPad *sink_pad; - sink_pad = gst_element_get_pad (md->priv->sink, "sink"); + sink_pad = gst_element_get_static_pad (md->priv->sink, "sink"); gst_pad_link (pad, sink_pad); + gst_object_unref (sink_pad); /* is this pad audio? */ structure = gst_caps_get_structure (caps, 0); @@ -798,12 +490,8 @@ md->priv->has_audio = TRUE; } else if (g_str_has_prefix (mimetype, "video/")) { rb_debug ("got decoded video pad of type %s", mimetype); - md->priv->has_non_audio = TRUE; md->priv->has_video = TRUE; } else { - /* assume anything we can get a video or text stream out of is - * something that should be fed to totem rather than rhythmbox. - */ rb_debug ("got decoded pad of non-audio type %s", mimetype); md->priv->has_non_audio = TRUE; } @@ -819,27 +507,6 @@ gst_element_set_state (md->priv->pipeline, GST_STATE_NULL); } -static void -rb_metadata_gst_unknown_type_cb (GstElement *decodebin, GstPad *pad, GstCaps *caps, RBMetaData *md) -{ - if (!gst_caps_is_empty (caps) && !gst_caps_is_any (caps)) { - GstStructure *structure; - const gchar *mimetype; - - structure = gst_caps_get_structure (caps, 0); - mimetype = gst_structure_get_name (structure); - - g_free (md->priv->type); - md->priv->type = g_strdup (mimetype); - - rb_debug ("decodebin emitted unknown type signal for %s", mimetype); - } else { - rb_debug ("decodebin emitted unknown type signal"); - } - - md->priv->has_non_audio = TRUE; -} - static GstElement *make_pipeline_element (GstElement *pipeline, const char *element, GError **error) { GstElement *elem = gst_element_factory_make (element, element); @@ -859,10 +526,39 @@ static void rb_metadata_handle_missing_plugin_message (RBMetaData *md, GstMessage *message) { + char *detail; + + detail = gst_missing_plugin_message_get_installer_detail (message); rb_debug ("got missing-plugin message from %s: %s", GST_OBJECT_NAME (GST_MESSAGE_SRC (message)), - gst_missing_plugin_message_get_installer_detail (message)); + detail); + g_free (detail); + md->priv->missing_plugins = g_slist_prepend (md->priv->missing_plugins, gst_message_ref (message)); + + /* update our information on what's in the stream based on + * what we're missing. + */ + switch (rb_metadata_gst_get_missing_plugin_type (message)) { + case MEDIA_TYPE_NONE: + break; + case MEDIA_TYPE_CONTAINER: + /* hm, maybe we need a way to say 'we don't even know what's in here'. + * but for now, the things we actually identify as containers are mostly + * used for audio, so pretending they actually are is good enough. + */ + case MEDIA_TYPE_AUDIO: + md->priv->has_audio = TRUE; + break; + case MEDIA_TYPE_VIDEO: + md->priv->has_video = TRUE; + break; + case MEDIA_TYPE_OTHER: + md->priv->has_non_audio = TRUE; + break; + default: + g_assert_not_reached (); + } } static gboolean @@ -965,7 +661,7 @@ GstMessage *message; if (block) - message = gst_bus_poll (bus, GST_MESSAGE_ANY, -1); + message = gst_bus_timed_pop (bus, GST_CLOCK_TIME_NONE); else message = gst_bus_pop (bus); @@ -1001,7 +697,6 @@ md->priv->type = NULL; md->priv->error = NULL; md->priv->eos = FALSE; - md->priv->handoff = FALSE; md->priv->has_audio = FALSE; md->priv->has_non_audio = FALSE; md->priv->has_video = FALSE; @@ -1021,7 +716,7 @@ if (md->priv->metadata) g_hash_table_destroy (md->priv->metadata); md->priv->metadata = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, (GDestroyNotify) free_gvalue); + NULL, (GDestroyNotify) rb_value_free); /* The main tagfinding pipeline looks like this: * ! decodebin ! fakesink @@ -1051,7 +746,6 @@ } g_signal_connect_object (decodebin, "new-decoded-pad", G_CALLBACK (rb_metadata_gst_new_decoded_pad_cb), md, 0); - g_signal_connect_object (decodebin, "unknown-type", G_CALLBACK (rb_metadata_gst_unknown_type_cb), md, 0); /* locate the decodebin's typefind, so we can get the have_type signal too. * this is kind of nasty, since it relies on an essentially arbitrary string @@ -1079,7 +773,7 @@ change_timeout < 5) { GstMessage *msg; - msg = gst_bus_poll (bus, GST_MESSAGE_ANY, 1 * GST_SECOND); + msg = gst_bus_timed_pop (bus, 1 * GST_SECOND); if (msg) { rb_metadata_bus_handler (bus, msg, md); gst_message_unref (msg); @@ -1117,7 +811,7 @@ if (gst_element_query_duration (md->priv->sink, &format, &length)) { g_assert (format == GST_FORMAT_TIME); - newval = g_new0 (GValue, 1); + newval = g_slice_new0 (GValue); rb_debug ("duration query succeeded"); @@ -1142,81 +836,19 @@ if (state_ret == GST_STATE_CHANGE_ASYNC) { g_warning ("Failed to return metadata reader to NULL state"); } - md->priv->handoff = (state_ret == GST_STATE_CHANGE_SUCCESS); - - /* report errors for various failure cases. - * these don't include the URI as the import errors source - * already displays it. - */ - if ((md->priv->has_video || !md->priv->has_audio) && - (md->priv->has_non_audio || !md->priv->handoff)) { - gboolean ignore = FALSE; - int i; - if (md->priv->has_video) { - ignore = TRUE; - } else { - for (i = 0; i < G_N_ELEMENTS (ignore_mime_types); i++) { - if (g_str_has_prefix (md->priv->type, ignore_mime_types[i])) { - ignore = TRUE; - break; - } - } - } - - if (!ignore) { - char *msg = make_undecodable_error (md); - g_set_error (error, - RB_METADATA_ERROR, - RB_METADATA_ERROR_NOT_AUDIO, - "%s", msg); - g_free (msg); - } else { - /* we don't need an error message here (it'll never be - * displayed). using NULL causes crashes with some C - * libraries, and gcc doesn't like zero-length format - * strings, so we use a single space instead. - */ - g_set_error (error, - RB_METADATA_ERROR, - RB_METADATA_ERROR_NOT_AUDIO_IGNORE, - " "); - } - } else if (md->priv->error != NULL) { + if (md->priv->error != NULL) { g_propagate_error (error, md->priv->error); md->priv->error = NULL; } else if (!md->priv->type) { - /* ignore really small files that can't be identified */ - gint error_code = RB_METADATA_ERROR_UNRECOGNIZED; - if (file_size > 0 && file_size < REALLY_SMALL_FILE_SIZE) { - rb_debug ("ignoring %s because it's too small to care about", md->priv->uri); - error_code = RB_METADATA_ERROR_NOT_AUDIO_IGNORE; - } else if (file_size == 0) { - g_clear_error (error); - g_set_error (error, - RB_METADATA_ERROR, - RB_METADATA_ERROR_EMPTY_FILE, - _("Empty file")); - goto out; - } - g_clear_error (error); g_set_error (error, RB_METADATA_ERROR, - error_code, + RB_METADATA_ERROR_UNRECOGNIZED, _("The MIME type of the file could not be identified")); } else { /* yay, it worked */ rb_debug ("successfully read metadata for %s", uri); - - /* it doesn't matter if we don't recognise the format, - * as long as gstreamer can play it, we can put it in - * the library. - */ - if (!rb_metadata_gst_type_to_name (md, md->priv->type)) { - rb_debug ("we don't know what type %s (from file %s) is, but we'll play it anyway", - md->priv->type, uri); - } } out: @@ -1228,7 +860,26 @@ gboolean rb_metadata_can_save (RBMetaData *md, const char *mimetype) { - return rb_metadata_gst_type_to_tag_function (md, mimetype) != NULL; + return g_hash_table_lookup (md->priv->taggers, mimetype) != NULL; +} + +char ** +rb_metadata_get_saveable_types (RBMetaData *md) +{ + GHashTableIter iter; + gpointer key; + gpointer value; + char **types; + int i; + + types = g_new0 (char *, g_hash_table_size (md->priv->taggers) + 1); + i = 0; + g_hash_table_iter_init (&iter, md->priv->taggers); + while (g_hash_table_iter_next (&iter, &key, &value)) { + types[i++] = g_strdup ((const char *) key); + } + + return types; } static void @@ -1332,8 +983,7 @@ md); /* Tagger element(s) */ - add_tagger_func = rb_metadata_gst_type_to_tag_function (md, md->priv->type); - + add_tagger_func = g_hash_table_lookup (md->priv->taggers, md->priv->type); if (!add_tagger_func) { g_set_error (error, RB_METADATA_ERROR, @@ -1342,7 +992,7 @@ goto out_error; } - retag_end = add_tagger_func (md, source); + retag_end = add_tagger_func (md->priv->pipeline, source, md->priv->tags); if (!retag_end) { g_set_error (error, RB_METADATA_ERROR, @@ -1469,7 +1119,7 @@ type = rb_metadata_get_field_type (field); g_return_val_if_fail (type == G_VALUE_TYPE (val), FALSE); - newval = g_new0 (GValue, 1); + newval = g_slice_new0 (GValue); g_value_init (newval, type); g_value_copy (val, newval); @@ -1525,3 +1175,21 @@ return TRUE; } +gboolean +rb_metadata_has_audio (RBMetaData *md) +{ + return md->priv->has_audio; +} + +gboolean +rb_metadata_has_video (RBMetaData *md) +{ + return md->priv->has_video; +} + +gboolean +rb_metadata_has_other_data (RBMetaData *md) +{ + return md->priv->has_non_audio; /* kinda */ +} + only in patch2: unchanged: --- rhythmbox-0.12.0.orig/metadata/rb-metadata-gst-common.c +++ rhythmbox-0.12.0/metadata/rb-metadata-gst-common.c @@ -0,0 +1,260 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2003,2004 Colin Walters + * + * 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 + +#include + +#include +#include +#include + +#include "rb-metadata-gst-common.h" +#include "rb-debug.h" + +static void +gst_date_gulong_transform (const GValue *src, GValue *dest) +{ + const GDate *date = gst_value_get_date (src); + + g_value_set_ulong (dest, (date) ? g_date_get_julian (date) : 0); +} + +static void +gulong_gst_date_transform (const GValue *src, GValue *dest) +{ + gulong day = g_value_get_ulong (src); + GDate *date = g_date_new_julian (day); + + gst_value_set_date (dest, date); + g_date_free (date); +} + +void +rb_metadata_gst_register_transforms () +{ + g_value_register_transform_func (GST_TYPE_DATE, G_TYPE_ULONG, gst_date_gulong_transform); + g_value_register_transform_func (G_TYPE_ULONG, GST_TYPE_DATE, gulong_gst_date_transform); +} + +RBMetaDataField +rb_metadata_gst_tag_to_field (const char *tag) +{ + if (!strcmp (tag, GST_TAG_TITLE)) + return RB_METADATA_FIELD_TITLE; + else if (!strcmp (tag, GST_TAG_ARTIST)) + return RB_METADATA_FIELD_ARTIST; + else if (!strcmp (tag, GST_TAG_ALBUM)) + return RB_METADATA_FIELD_ALBUM; + else if (!strcmp (tag, GST_TAG_DATE)) + return RB_METADATA_FIELD_DATE; + else if (!strcmp (tag, GST_TAG_GENRE)) + return RB_METADATA_FIELD_GENRE; + else if (!strcmp (tag, GST_TAG_COMMENT)) + return RB_METADATA_FIELD_COMMENT; + else if (!strcmp (tag, GST_TAG_TRACK_NUMBER)) + return RB_METADATA_FIELD_TRACK_NUMBER; + else if (!strcmp (tag, GST_TAG_TRACK_COUNT)) + return RB_METADATA_FIELD_MAX_TRACK_NUMBER; + else if (!strcmp (tag, GST_TAG_ALBUM_VOLUME_NUMBER)) + return RB_METADATA_FIELD_DISC_NUMBER; + else if (!strcmp (tag, GST_TAG_ALBUM_VOLUME_COUNT)) + return RB_METADATA_FIELD_MAX_TRACK_NUMBER; + else if (!strcmp (tag, GST_TAG_DESCRIPTION)) + return RB_METADATA_FIELD_DESCRIPTION; + else if (!strcmp (tag, GST_TAG_VERSION)) + return RB_METADATA_FIELD_VERSION; + else if (!strcmp (tag, GST_TAG_ISRC)) + return RB_METADATA_FIELD_ISRC; + else if (!strcmp (tag, GST_TAG_ORGANIZATION)) + return RB_METADATA_FIELD_ORGANIZATION; + else if (!strcmp (tag, GST_TAG_COPYRIGHT)) + return RB_METADATA_FIELD_COPYRIGHT; + else if (!strcmp (tag, GST_TAG_CONTACT)) + return RB_METADATA_FIELD_CONTACT; + else if (!strcmp (tag, GST_TAG_LICENSE)) + return RB_METADATA_FIELD_LICENSE; + else if (!strcmp (tag, GST_TAG_PERFORMER)) + return RB_METADATA_FIELD_PERFORMER; + else if (!strcmp (tag, GST_TAG_DURATION)) + return RB_METADATA_FIELD_DURATION; + else if (!strcmp (tag, GST_TAG_CODEC)) + return RB_METADATA_FIELD_CODEC; + else if (!strcmp (tag, GST_TAG_BITRATE)) + return RB_METADATA_FIELD_BITRATE; + else if (!strcmp (tag, GST_TAG_TRACK_GAIN)) + return RB_METADATA_FIELD_TRACK_GAIN; + else if (!strcmp (tag, GST_TAG_TRACK_PEAK)) + return RB_METADATA_FIELD_TRACK_PEAK; + else if (!strcmp (tag, GST_TAG_ALBUM_GAIN)) + return RB_METADATA_FIELD_ALBUM_GAIN; + else if (!strcmp (tag, GST_TAG_ALBUM_PEAK)) + return RB_METADATA_FIELD_ALBUM_PEAK; + else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_TRACKID)) + return RB_METADATA_FIELD_MUSICBRAINZ_TRACKID; + else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_ARTISTID)) + return RB_METADATA_FIELD_MUSICBRAINZ_ARTISTID; + else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_ALBUMID)) + return RB_METADATA_FIELD_MUSICBRAINZ_ALBUMID; + else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_ALBUMARTISTID)) + return RB_METADATA_FIELD_MUSICBRAINZ_ALBUMARTISTID; + else if (!strcmp (tag, GST_TAG_ARTIST_SORTNAME)) + return RB_METADATA_FIELD_ARTIST_SORTNAME; + else if (!strcmp (tag, GST_TAG_ALBUM_SORTNAME)) + return RB_METADATA_FIELD_ALBUM_SORTNAME; + else + return -1; +} + +const char * +rb_metadata_gst_field_to_gst_tag (RBMetaDataField field) +{ + switch (field) + { + case RB_METADATA_FIELD_TITLE: + return GST_TAG_TITLE; + case RB_METADATA_FIELD_ARTIST: + return GST_TAG_ARTIST; + case RB_METADATA_FIELD_ALBUM: + return GST_TAG_ALBUM; + case RB_METADATA_FIELD_DATE: + return GST_TAG_DATE; + case RB_METADATA_FIELD_GENRE: + return GST_TAG_GENRE; + case RB_METADATA_FIELD_COMMENT: + return GST_TAG_COMMENT; + case RB_METADATA_FIELD_TRACK_NUMBER: + return GST_TAG_TRACK_NUMBER; + case RB_METADATA_FIELD_MAX_TRACK_NUMBER: + return GST_TAG_TRACK_COUNT; + case RB_METADATA_FIELD_DISC_NUMBER: + return GST_TAG_ALBUM_VOLUME_NUMBER; + case RB_METADATA_FIELD_MAX_DISC_NUMBER: + return GST_TAG_ALBUM_VOLUME_COUNT; + case RB_METADATA_FIELD_DESCRIPTION: + return GST_TAG_DESCRIPTION; + case RB_METADATA_FIELD_VERSION: + return GST_TAG_VERSION; + case RB_METADATA_FIELD_ISRC: + return GST_TAG_ISRC; + case RB_METADATA_FIELD_ORGANIZATION: + return GST_TAG_ORGANIZATION; + case RB_METADATA_FIELD_COPYRIGHT: + return GST_TAG_COPYRIGHT; + case RB_METADATA_FIELD_CONTACT: + return GST_TAG_CONTACT; + case RB_METADATA_FIELD_LICENSE: + return GST_TAG_LICENSE; + case RB_METADATA_FIELD_PERFORMER: + return GST_TAG_PERFORMER; + case RB_METADATA_FIELD_DURATION: + return GST_TAG_DURATION; + case RB_METADATA_FIELD_CODEC: + return GST_TAG_CODEC; + case RB_METADATA_FIELD_BITRATE: + return GST_TAG_BITRATE; + case RB_METADATA_FIELD_TRACK_GAIN: + return GST_TAG_TRACK_GAIN; + case RB_METADATA_FIELD_TRACK_PEAK: + return GST_TAG_TRACK_PEAK; + case RB_METADATA_FIELD_ALBUM_GAIN: + return GST_TAG_ALBUM_GAIN; + case RB_METADATA_FIELD_ALBUM_PEAK: + return GST_TAG_ALBUM_PEAK; + case RB_METADATA_FIELD_MUSICBRAINZ_TRACKID: + return GST_TAG_MUSICBRAINZ_TRACKID; + case RB_METADATA_FIELD_MUSICBRAINZ_ARTISTID: + return GST_TAG_MUSICBRAINZ_ARTISTID; + case RB_METADATA_FIELD_MUSICBRAINZ_ALBUMID: + return GST_TAG_MUSICBRAINZ_ALBUMID; + case RB_METADATA_FIELD_MUSICBRAINZ_ALBUMARTISTID: + return GST_TAG_MUSICBRAINZ_ALBUMARTISTID; + case RB_METADATA_FIELD_ARTIST_SORTNAME: + return GST_TAG_ARTIST_SORTNAME; + case RB_METADATA_FIELD_ALBUM_SORTNAME: + return GST_TAG_ALBUM_SORTNAME; + default: + return NULL; + } +} + + +/* don't like this much, but it's all we can do for now. + * these media types are copied from gst-plugins-base/gst-libs/gst/pbutils/descriptions.c. + * these are only the media types that don't start with 'audio/' or 'video/', which are + * identified fairly accurately by the filters for those prefixes. + */ +static const char *container_formats[] = { + "application/ogg", + "application/vnd.rn-realmedia", + "application/x-id3", + "application/x-ape", + "application/x-icy" +}; + + +RBGstMediaType +rb_metadata_gst_get_missing_plugin_type (GstMessage *message) +{ + const char *media_type; + const char *missing_type; + const GstStructure *structure; + const GstCaps *caps; + const GValue *val; + int i; + + structure = gst_message_get_structure (message); + missing_type = gst_structure_get_string (structure, "type"); + if (missing_type == NULL || strcmp (missing_type, "decoder") != 0) { + rb_debug ("missing plugin is not a decoder"); + return MEDIA_TYPE_NONE; + } + + val = gst_structure_get_value (structure, "detail"); + caps = gst_value_get_caps (val); + + media_type = gst_structure_get_name (gst_caps_get_structure (caps, 0)); + for (i = 0; i < G_N_ELEMENTS (container_formats); i++) { + if (strcmp (media_type, container_formats[i]) == 0) { + rb_debug ("missing plugin is a container demuxer"); + return MEDIA_TYPE_CONTAINER; + } + } + + if (g_str_has_prefix (media_type, "audio/")) { + rb_debug ("missing plugin is an audio decoder"); + return MEDIA_TYPE_AUDIO; + } else if (g_str_has_prefix (media_type, "video/")) { + rb_debug ("missing plugin is (probably) a video decoder"); + return MEDIA_TYPE_VIDEO; + } else { + rb_debug ("missing plugin is neither a video nor audio decoder"); + return MEDIA_TYPE_OTHER; + } +} + only in patch2: unchanged: --- rhythmbox-0.12.0.orig/metadata/rb-metadata-gst-common.h +++ rhythmbox-0.12.0/metadata/rb-metadata-gst-common.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2003,2004 Colin Walters + * + * 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_METADATA_GST_COMMON_H +#define RB_METADATA_GST_COMMON_H + +G_BEGIN_DECLS + +#include + +#include "rb-metadata.h" + +typedef enum { + MEDIA_TYPE_NONE = 0, + MEDIA_TYPE_CONTAINER, + MEDIA_TYPE_AUDIO, + MEDIA_TYPE_VIDEO, + MEDIA_TYPE_OTHER +} RBGstMediaType; + +const char * rb_metadata_gst_field_to_gst_tag (RBMetaDataField field); +RBMetaDataField rb_metadata_gst_tag_to_field (const char *tag); + +void rb_metadata_gst_register_transforms (void); + +RBGstMediaType rb_metadata_gst_get_missing_plugin_type (GstMessage *msg); + +G_END_DECLS + +#endif /* RB_METADATA_GST_COMMON_H */ only in patch2: unchanged: --- rhythmbox-0.12.0.orig/metadata/Makefile.in +++ rhythmbox-0.12.0/metadata/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.10.1 from Makefile.am. +# Makefile.in generated by automake 1.10.2 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -40,7 +40,12 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/macros/gnome-doc-utils.m4 \ $(top_srcdir)/macros/gtk-doc.m4 \ - $(top_srcdir)/macros/intltool.m4 $(top_srcdir)/configure.ac + $(top_srcdir)/macros/intltool.m4 \ + $(top_srcdir)/macros/libtool.m4 \ + $(top_srcdir)/macros/ltoptions.m4 \ + $(top_srcdir)/macros/ltsugar.m4 \ + $(top_srcdir)/macros/ltversion.m4 \ + $(top_srcdir)/macros/lt~obsolete.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs @@ -58,7 +63,8 @@ $(librbmetadata_la_LDFLAGS) $(LDFLAGS) -o $@ librbmetadatasvc_la_LIBADD = am_librbmetadatasvc_la_OBJECTS = rb-metadata-common.lo \ - rb-metadata-dbus.lo rb-metadata-gst.lo + rb-metadata-dbus.lo rb-metadata-gst.lo \ + rb-metadata-gst-common.lo librbmetadatasvc_la_OBJECTS = $(am_librbmetadatasvc_la_OBJECTS) am__installdirs = "$(DESTDIR)$(libexecdir)" libexecPROGRAMS_INSTALL = $(INSTALL_PROGRAM) @@ -137,12 +143,13 @@ DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@ DOC_USER_FORMATS = @DOC_USER_FORMATS@ DSYMUTIL = @DSYMUTIL@ -ECHO = @ECHO@ +DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ +FGREP = @FGREP@ GCONFTOOL = @GCONFTOOL@ GCONF_SCHEMA_CONFIG_SOURCE = @GCONF_SCHEMA_CONFIG_SOURCE@ GCONF_SCHEMA_FILE_DIR = @GCONF_SCHEMA_FILE_DIR@ @@ -173,31 +180,13 @@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INSTOBJEXT = @INSTOBJEXT@ INTLLIBS = @INTLLIBS@ -INTLTOOL_CAVES_RULE = @INTLTOOL_CAVES_RULE@ -INTLTOOL_DESKTOP_RULE = @INTLTOOL_DESKTOP_RULE@ -INTLTOOL_DIRECTORY_RULE = @INTLTOOL_DIRECTORY_RULE@ INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ -INTLTOOL_KBD_RULE = @INTLTOOL_KBD_RULE@ -INTLTOOL_KEYS_RULE = @INTLTOOL_KEYS_RULE@ INTLTOOL_MERGE = @INTLTOOL_MERGE@ -INTLTOOL_OAF_RULE = @INTLTOOL_OAF_RULE@ INTLTOOL_PERL = @INTLTOOL_PERL@ -INTLTOOL_POLICY_RULE = @INTLTOOL_POLICY_RULE@ -INTLTOOL_PONG_RULE = @INTLTOOL_PONG_RULE@ -INTLTOOL_PROP_RULE = @INTLTOOL_PROP_RULE@ -INTLTOOL_SCHEMAS_RULE = @INTLTOOL_SCHEMAS_RULE@ -INTLTOOL_SERVER_RULE = @INTLTOOL_SERVER_RULE@ -INTLTOOL_SERVICE_RULE = @INTLTOOL_SERVICE_RULE@ -INTLTOOL_SHEET_RULE = @INTLTOOL_SHEET_RULE@ -INTLTOOL_SOUNDLIST_RULE = @INTLTOOL_SOUNDLIST_RULE@ -INTLTOOL_THEME_RULE = @INTLTOOL_THEME_RULE@ -INTLTOOL_UI_RULE = @INTLTOOL_UI_RULE@ INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ -INTLTOOL_XAM_RULE = @INTLTOOL_XAM_RULE@ -INTLTOOL_XML_NOMERGE_RULE = @INTLTOOL_XML_NOMERGE_RULE@ -INTLTOOL_XML_RULE = @INTLTOOL_XML_RULE@ IPOD_CFLAGS = @IPOD_CFLAGS@ IPOD_LIBS = @IPOD_LIBS@ +LD = @LD@ LDFLAGS = @LDFLAGS@ LIBBRASERO_MEDIA_CFLAGS = @LIBBRASERO_MEDIA_CFLAGS@ LIBBRASERO_MEDIA_LIBS = @LIBBRASERO_MEDIA_LIBS@ @@ -208,6 +197,7 @@ LIBSEXY_CFLAGS = @LIBSEXY_CFLAGS@ LIBSEXY_LIBS = @LIBSEXY_LIBS@ LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ @@ -226,12 +216,16 @@ MUSICBRAINZ3_LIBS = @MUSICBRAINZ3_LIBS@ MUSICBRAINZ_CFLAGS = @MUSICBRAINZ_CFLAGS@ MUSICBRAINZ_LIBS = @MUSICBRAINZ_LIBS@ +NM = @NM@ NMEDIT = @NMEDIT@ NOTIFY_CFLAGS = @NOTIFY_CFLAGS@ NOTIFY_LIBS = @NOTIFY_LIBS@ NO_STRICT_ALIASING_CFLAGS = @NO_STRICT_ALIASING_CFLAGS@ +OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OMF_DIR = @OMF_DIR@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ @@ -291,6 +285,7 @@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ @@ -321,6 +316,7 @@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ @@ -369,7 +365,9 @@ rb-metadata-common.c \ rb-metadata-dbus.h \ rb-metadata-dbus.c \ - rb-metadata-gst.c + rb-metadata-gst.c \ + rb-metadata-gst-common.h \ + rb-metadata-gst-common.c rhythmbox_metadata_SOURCES = \ rb-metadata-dbus-service.c @@ -400,8 +398,8 @@ @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ - && exit 0; \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ @@ -490,6 +488,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rb-metadata-dbus-client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rb-metadata-dbus-service.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rb-metadata-dbus.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rb-metadata-gst-common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rb-metadata-gst.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-metadata.Po@am__quote@ @@ -595,7 +594,7 @@ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ - $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS only in patch2: unchanged: --- rhythmbox-0.12.0.orig/metadata/rb-metadata-common.c +++ rhythmbox-0.12.0/metadata/rb-metadata-common.c @@ -74,6 +74,7 @@ /* RB_METADATA_FIELD_MUSICBRAINZ_ALBUMID */ { G_TYPE_STRING, "musicbrainz-albumid" }, /* RB_METADATA_FIELD_MUSICBRAINZ_ALBUMARTISTID */ { G_TYPE_STRING, "musicbrainz-albumartistid" }, /* RB_METADATA_FIELD_ARTIST_SORTNAME */ { G_TYPE_STRING, "musicbrainz-sortname" }, + /* RB_METADATA_FIELD_ALBUM_SORTNAME */ { G_TYPE_STRING, "album-sortname" }, }; only in patch2: unchanged: --- rhythmbox-0.12.0.orig/metadata/rb-metadata-dbus-service.c +++ rhythmbox-0.12.0/metadata/rb-metadata-dbus-service.c @@ -58,19 +58,25 @@ gboolean external; } ServiceData; -enum { - ERROR_FLAG = 1, - MISSING_PLUGINS = 2 -}; +static gboolean +append_error (DBusMessageIter *iter, + gint error_type, + const char *message) +{ + if (!dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &error_type) || + !dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &message)) { + rb_debug ("couldn't append error data"); + return FALSE; + } + + return TRUE; +} static DBusHandlerResult -_send_error (DBusConnection *connection, - DBusMessage *request, - int details, - char **missing_plugins, - char **plugin_descriptions, - gint error_type, - const char *message) +send_error (DBusConnection *connection, + DBusMessage *request, + gint error_type, + const char *message) { DBusMessage *reply = dbus_message_new_method_return (request); DBusMessageIter iter; @@ -83,29 +89,7 @@ } dbus_message_iter_init_append (reply, &iter); - - if (details & MISSING_PLUGINS) { - if (!rb_metadata_dbus_add_strv (&iter, missing_plugins)) { - rb_debug ("couldn't append missing plugins data"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - if (!rb_metadata_dbus_add_strv (&iter, plugin_descriptions)) { - rb_debug ("couldn't append missing plugin descriptions"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } - - if (details & ERROR_FLAG) { - gboolean ok = FALSE; - if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &ok)) { - rb_debug ("couldn't append error flag"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } - - if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &error_type) || - !dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message)) { - rb_debug ("couldn't append error data"); + if (append_error (&iter, error_type, message) == FALSE) { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -123,54 +107,37 @@ DBusMessageIter iter; DBusMessage *reply; GError *error = NULL; - gboolean ok = TRUE; + gboolean ok; const char *mimetype = NULL; char **missing_plugins = NULL; char **plugin_descriptions = NULL; + gboolean has_audio; + gboolean has_video; + gboolean has_other_data; if (!dbus_message_iter_init (message, &iter)) { return DBUS_HANDLER_RESULT_NEED_MEMORY; } if (!rb_metadata_dbus_get_string (&iter, &uri)) { - /* make translatable? */ - return _send_error (connection, message, - ERROR_FLAG | MISSING_PLUGINS, - NULL, NULL, - RB_METADATA_ERROR_INTERNAL, - "Unable to read URI from request"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } rb_debug ("loading metadata from %s", uri); rb_metadata_load (svc->metadata, uri, &error); + rb_debug ("metadata load finished (type %s)", rb_metadata_get_mime (svc->metadata)); g_free (uri); - rb_metadata_get_missing_plugins (svc->metadata, &missing_plugins, &plugin_descriptions); - - if (error != NULL) { - DBusHandlerResult r; - rb_debug ("metadata error: %s", error->message); - - r = _send_error (connection, message, - ERROR_FLAG | MISSING_PLUGINS, - missing_plugins, plugin_descriptions, - error->code, error->message); - g_clear_error (&error); - g_strfreev (missing_plugins); - return r; - } - rb_debug ("metadata load finished; mimetype = %s", rb_metadata_get_mime (svc->metadata)); - /* construct reply */ reply = dbus_message_new_method_return (message); if (!reply) { rb_debug ("out of memory creating return message"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - - mimetype = rb_metadata_get_mime (svc->metadata); - dbus_message_iter_init_append (reply, &iter); + dbus_message_iter_init_append (reply, &iter); + + rb_metadata_get_missing_plugins (svc->metadata, &missing_plugins, &plugin_descriptions); if (!rb_metadata_dbus_add_strv (&iter, missing_plugins)) { rb_debug ("out of memory adding data to return message"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -180,19 +147,39 @@ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &ok) || + mimetype = rb_metadata_get_mime (svc->metadata); + if (mimetype == NULL) { + mimetype = ""; + } + has_audio = rb_metadata_has_audio (svc->metadata); + has_video = rb_metadata_has_video (svc->metadata); + has_other_data = rb_metadata_has_other_data (svc->metadata); + + if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &has_audio) || + !dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &has_video) || + !dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &has_other_data) || !dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mimetype)) { rb_debug ("out of memory adding data to return message"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } + ok = (error == NULL); + if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &ok)) { + rb_debug ("out of memory adding error flag to return message"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (error != NULL) { + rb_debug ("metadata error: %s", error->message); + if (append_error (&iter, error->code, error->message) == FALSE) { + rb_debug ("out of memory adding error details to return message"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + if (!rb_metadata_dbus_add_to_message (svc->metadata, &iter)) { - /* make translatable? */ - return _send_error (connection, message, - ERROR_FLAG | MISSING_PLUGINS, - missing_plugins, plugin_descriptions, - RB_METADATA_ERROR_INTERNAL, - "Unable to add metadata to return message"); + rb_debug ("unable to add metadata to return message"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } if (!dbus_connection_send (connection, reply, NULL)) { @@ -201,34 +188,17 @@ } dbus_message_unref (reply); - return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult -rb_metadata_dbus_can_save (DBusConnection *connection, - DBusMessage *message, - ServiceData *svc) +rb_metadata_dbus_get_saveable_types (DBusConnection *connection, + DBusMessage *message, + ServiceData *svc) { - char *mimetype; DBusMessageIter iter; DBusMessage *reply; - gboolean can_save; - - if (!dbus_message_iter_init (message, &iter)) { - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - if (!rb_metadata_dbus_get_string (&iter, &mimetype)) { - /* make translatable? */ - return _send_error (connection, message, ERROR_FLAG, - NULL, NULL, - RB_METADATA_ERROR_INTERNAL, - "Unable to read MIME type from request"); - } - - can_save = rb_metadata_can_save (svc->metadata, mimetype); - g_free (mimetype); + char **saveable_types; /* construct reply */ reply = dbus_message_new_method_return (message); @@ -238,10 +208,15 @@ } dbus_message_iter_init_append (reply, &iter); - if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &can_save)) { - rb_debug ("out of memory adding data to return message"); + + saveable_types = rb_metadata_get_saveable_types (svc->metadata); + + if (!rb_metadata_dbus_add_strv (&iter, saveable_types)) { + rb_debug ("out of memory adding saveable types to return message"); + g_strfreev (saveable_types); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } + g_strfreev (saveable_types); if (!dbus_connection_send (connection, reply, NULL)) { rb_debug ("failed to send return message"); @@ -280,9 +255,9 @@ data, &iter)) { /* make translatable? */ - return _send_error (connection, message, 0, NULL, NULL, - RB_METADATA_ERROR_INTERNAL, - "Unable to read metadata from message"); + return send_error (connection, message, + RB_METADATA_ERROR_INTERNAL, + "Unable to read metadata from message"); } /* pass to real metadata instance, and save it */ @@ -295,7 +270,7 @@ DBusHandlerResult r; rb_debug ("metadata error: %s", error->message); - r = _send_error (connection, message, 0, NULL, NULL, error->code, error->message); + r = send_error (connection, message, error->code, error->message); g_clear_error (&error); return r; } @@ -366,20 +341,23 @@ _handle_message (DBusConnection *connection, DBusMessage *message, void *data) { ServiceData *svc = (ServiceData *)data; - rb_debug ("handling metadata service message"); + DBusHandlerResult result; + rb_debug ("handling metadata service message: %s", dbus_message_get_member (message)); - svc->last_active = time (NULL); if (dbus_message_is_method_call (message, RB_METADATA_DBUS_INTERFACE, "load")) { - return rb_metadata_dbus_load (connection, message, svc); - } else if (dbus_message_is_method_call (message, RB_METADATA_DBUS_INTERFACE, "canSave")) { - return rb_metadata_dbus_can_save (connection, message, svc); + result = rb_metadata_dbus_load (connection, message, svc); + } else if (dbus_message_is_method_call (message, RB_METADATA_DBUS_INTERFACE, "getSaveableTypes")) { + result = rb_metadata_dbus_get_saveable_types (connection, message, svc); } else if (dbus_message_is_method_call (message, RB_METADATA_DBUS_INTERFACE, "save")) { - return rb_metadata_dbus_save (connection, message, svc); + result = rb_metadata_dbus_save (connection, message, svc); } else if (dbus_message_is_method_call (message, RB_METADATA_DBUS_INTERFACE, "ping")) { - return rb_metadata_dbus_ping (connection, message, svc); + result = rb_metadata_dbus_ping (connection, message, svc); } else { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } + + svc->last_active = time (NULL); + return result; } static void @@ -414,15 +392,21 @@ } static int -test_can_save (const char *mimetype) +test_saveable_types () { RBMetaData *md; - gboolean can_save; + char **saveable; + int i; md = rb_metadata_new (); - can_save = rb_metadata_can_save (md, mimetype); - g_print ("%s save %s\n", can_save ? "Can" : "Can't", mimetype); - g_object_unref (G_OBJECT (md)); + saveable = rb_metadata_get_saveable_types (md); + g_object_unref (md); + + for (i = 0; saveable[i] != NULL; i++) { + g_print ("%s\n", saveable[i]); + } + g_strfreev (saveable); + return 0; } @@ -438,13 +422,7 @@ md = rb_metadata_new (); rb_metadata_load (md, uri, &error); if (error) { - if (error->code == RB_METADATA_ERROR_NOT_AUDIO_IGNORE) { - g_print ("%s is not an audio stream (ignored)\n", uri); - } else if (error->code == RB_METADATA_ERROR_NOT_AUDIO) { - g_print ("%s is not an audio stream\n", uri); - } else { - g_print ("Error loading metadata from %s: %s\n", uri, error->message); - } + g_print ("Error loading metadata from %s: %s\n", uri, error->message); g_clear_error (&error); rv = -1; } else { @@ -465,6 +443,10 @@ } } + g_print ("has audio: %d\n", rb_metadata_has_audio (md)); + g_print ("has video: %d\n", rb_metadata_has_video (md)); + g_print ("has other: %d\n", rb_metadata_has_other_data (md)); + if (rb_metadata_get_missing_plugins (md, &missing_plugins, &plugin_descriptions)) { int i = 0; g_print ("missing plugins:\n"); @@ -510,8 +492,8 @@ if (argv[1] != NULL && strcmp(argv[1], "--load") == 0) { return test_load (argv[2]); } - if (argv[1] != NULL && strcmp(argv[1], "--can-save") == 0) { - return test_can_save (argv[2]); + if (argv[1] != NULL && strcmp(argv[1], "--saveable-types") == 0) { + return test_saveable_types (); } if (argv[1] != NULL && strcmp (argv[1], "--external") == 0) { only in patch2: unchanged: --- rhythmbox-0.12.0.orig/metadata/rb-metadata.h +++ rhythmbox-0.12.0/metadata/rb-metadata.h @@ -67,6 +67,7 @@ RB_METADATA_FIELD_MUSICBRAINZ_ALBUMID, /* string */ RB_METADATA_FIELD_MUSICBRAINZ_ALBUMARTISTID, /* string */ RB_METADATA_FIELD_ARTIST_SORTNAME, /* string */ + RB_METADATA_FIELD_ALBUM_SORTNAME, /* string */ RB_METADATA_FIELD_LAST /* nothing */ } RBMetaDataField; @@ -79,8 +80,6 @@ RB_METADATA_ERROR_UNSUPPORTED, RB_METADATA_ERROR_GENERAL, RB_METADATA_ERROR_INTERNAL, - RB_METADATA_ERROR_NOT_AUDIO, - RB_METADATA_ERROR_NOT_AUDIO_IGNORE, RB_METADATA_ERROR_EMPTY_FILE } RBMetaDataError; @@ -121,6 +120,7 @@ RBMetaData * rb_metadata_new (void); gboolean rb_metadata_can_save (RBMetaData *md, const char *mimetype); +char ** rb_metadata_get_saveable_types (RBMetaData *md); void rb_metadata_load (RBMetaData *md, const char *uri, @@ -136,6 +136,9 @@ gboolean rb_metadata_get_missing_plugins (RBMetaData *md, char ***missing_plugins, char ***plugin_descriptions); +gboolean rb_metadata_has_audio (RBMetaData *md); +gboolean rb_metadata_has_video (RBMetaData *md); +gboolean rb_metadata_has_other_data (RBMetaData *md); gboolean rb_metadata_get (RBMetaData *md, RBMetaDataField field, GValue *val); only in patch2: unchanged: --- rhythmbox-0.12.0.orig/metadata/Makefile.am +++ rhythmbox-0.12.0/metadata/Makefile.am @@ -41,7 +41,9 @@ rb-metadata-common.c \ rb-metadata-dbus.h \ rb-metadata-dbus.c \ - rb-metadata-gst.c + rb-metadata-gst.c \ + rb-metadata-gst-common.h \ + rb-metadata-gst-common.c libexec_PROGRAMS = rhythmbox-metadata rhythmbox_metadata_SOURCES = \ only in patch2: unchanged: --- rhythmbox-0.12.0.orig/metadata/rb-metadata-dbus-client.c +++ rhythmbox-0.12.0/metadata/rb-metadata-dbus-client.c @@ -83,6 +83,7 @@ static int metadata_stdout = -1; static GMainContext *main_context = NULL; static GStaticMutex conn_mutex = G_STATIC_MUTEX_INIT; +static char **saveable_types = NULL; struct RBMetaDataPrivate { @@ -90,6 +91,9 @@ char *mimetype; char **missing_plugins; char **plugin_descriptions; + gboolean has_audio; + gboolean has_video; + gboolean has_other_data; GHashTable *metadata; }; @@ -204,9 +208,13 @@ start_metadata_service (GError **error) { DBusError dbus_error = {0,}; + DBusMessage *message; + DBusMessage *response; + DBusMessageIter iter; GIOChannel *stdout_channel; GIOStatus status; gchar *dbus_address = NULL; + char *saveable_type_list; if (dbus_connection) { if (ping_metadata_service (error)) @@ -304,6 +312,51 @@ dbus_connection_setup_with_g_main (dbus_connection, main_context); rb_debug ("Metadata process %d started", metadata_child); + + /* now ask it what types it can re-tag */ + if (saveable_types != NULL) { + g_strfreev (saveable_types); + } + + message = dbus_message_new_method_call (RB_METADATA_DBUS_NAME, + RB_METADATA_DBUS_OBJECT_PATH, + RB_METADATA_DBUS_INTERFACE, + "getSaveableTypes"); + if (!message) { + /* what now? */ + rb_debug ("unable to query metadata helper for saveable types"); + return FALSE; + } + + rb_debug ("sending metadata saveable types query"); + response = dbus_connection_send_with_reply_and_block (dbus_connection, + message, + RB_METADATA_DBUS_TIMEOUT, + &dbus_error); + + if (!response) { + rb_debug ("saveable type query failed"); + return FALSE; + } + + if (!dbus_message_iter_init (response, &iter)) { + rb_debug ("couldn't read saveable type query response"); + return FALSE; + } + + if (!rb_metadata_dbus_get_strv (&iter, &saveable_types)) { + rb_debug ("couldn't get saveable type data from response message"); + return FALSE; + } + + saveable_type_list = g_strjoinv (", ", saveable_types); + rb_debug ("saveable types from metadata helper: %s", saveable_type_list); + g_free (saveable_type_list); + + if (message) + dbus_message_unref (message); + if (response) + dbus_message_unref (response); return TRUE; } @@ -371,6 +424,11 @@ DBusError dbus_error = {0,}; gboolean ok; GError *fake_error = NULL; + GError *dbus_gerror; + + dbus_gerror = g_error_new (RB_METADATA_ERROR, + RB_METADATA_ERROR_INTERNAL, + _("D-BUS communication error")); if (error == NULL) error = &fake_error; @@ -397,15 +455,9 @@ RB_METADATA_DBUS_INTERFACE, "load"); if (!message) { - g_set_error (error, - RB_METADATA_ERROR, - RB_METADATA_ERROR_INTERNAL, - _("D-BUS communication error")); + g_propagate_error (error, dbus_gerror); } else if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &uri, DBUS_TYPE_INVALID)) { - g_set_error (error, - RB_METADATA_ERROR, - RB_METADATA_ERROR_INTERNAL, - _("D-BUS communication error")); + g_propagate_error (error, dbus_gerror); } } @@ -422,30 +474,21 @@ if (*error == NULL) { if (!dbus_message_iter_init (response, &iter)) { - g_set_error (error, - RB_METADATA_ERROR, - RB_METADATA_ERROR_INTERNAL, - _("D-BUS communication error")); + g_propagate_error (error, dbus_gerror); rb_debug ("couldn't read response message"); } } if (*error == NULL) { if (!rb_metadata_dbus_get_strv (&iter, &md->priv->missing_plugins)) { - g_set_error (error, - RB_METADATA_ERROR, - RB_METADATA_ERROR_INTERNAL, - _("D-BUS communication error")); + g_propagate_error (error, dbus_gerror); rb_debug ("couldn't get missing plugin data from response message"); } } if (*error == NULL) { if (!rb_metadata_dbus_get_strv (&iter, &md->priv->plugin_descriptions)) { - g_set_error (error, - RB_METADATA_ERROR, - RB_METADATA_ERROR_INTERNAL, - _("D-BUS communication error")); + g_propagate_error (error, dbus_gerror); rb_debug ("couldn't get missing plugin descriptions from response message"); } } @@ -460,29 +503,50 @@ } if (*error == NULL) { - if (!rb_metadata_dbus_get_boolean (&iter, &ok)) { - g_set_error (error, - RB_METADATA_ERROR, - RB_METADATA_ERROR_INTERNAL, - _("D-BUS communication error")); - rb_debug ("couldn't get success flag from response message"); - } else if (ok == FALSE) { - read_error_from_message (md, &iter, error); + if (!rb_metadata_dbus_get_boolean (&iter, &md->priv->has_audio)) { + g_propagate_error (error, dbus_gerror); + rb_debug ("couldn't get has-audio flag from response message"); + } else { + rb_debug ("has audio: %d", md->priv->has_audio); } } - if (ok && *error == NULL) { + if (*error == NULL) { + if (!rb_metadata_dbus_get_boolean (&iter, &md->priv->has_video)) { + g_propagate_error (error, dbus_gerror); + rb_debug ("couldn't get has-video flag from response message"); + } else { + rb_debug ("has video: %d", md->priv->has_video); + } + } + + if (*error == NULL) { + if (!rb_metadata_dbus_get_boolean (&iter, &md->priv->has_other_data)) { + g_propagate_error (error, dbus_gerror); + rb_debug ("couldn't get has-other-data flag from response message"); + } else { + rb_debug ("has other data: %d", md->priv->has_other_data); + } + } + + if (*error == NULL) { if (!rb_metadata_dbus_get_string (&iter, &md->priv->mimetype)) { - g_set_error (error, - RB_METADATA_ERROR, - RB_METADATA_ERROR_INTERNAL, - _("D-BUS communication error")); + g_propagate_error (error, dbus_gerror); } else { rb_debug ("got mimetype: %s", md->priv->mimetype); } } - if (ok && *error == NULL) { + if (*error == NULL) { + if (!rb_metadata_dbus_get_boolean (&iter, &ok)) { + g_propagate_error (error, dbus_gerror); + rb_debug ("couldn't get success flag from response message"); + } else if (ok == FALSE) { + read_error_from_message (md, &iter, error); + } + } + + if (*error == NULL) { rb_metadata_dbus_read_from_message (md, md->priv->metadata, &iter); } @@ -490,6 +554,8 @@ dbus_message_unref (message); if (response) dbus_message_unref (response); + if (*error != dbus_gerror) + g_error_free (dbus_gerror); if (fake_error) g_error_free (fake_error); @@ -626,52 +692,43 @@ rb_metadata_can_save (RBMetaData *md, const char *mimetype) { GError *error = NULL; - DBusMessage *message = NULL; - DBusMessage *response = NULL; - gboolean can_save = FALSE; - DBusError dbus_error = {0,}; - DBusMessageIter iter; - gboolean ok = TRUE; + gboolean result = FALSE; + int i = 0; g_static_mutex_lock (&conn_mutex); - if (start_metadata_service (&error) == FALSE) { - g_error_free (error); - ok = FALSE; - } - - if (ok) { - message = dbus_message_new_method_call (RB_METADATA_DBUS_NAME, - RB_METADATA_DBUS_OBJECT_PATH, - RB_METADATA_DBUS_INTERFACE, - "canSave"); - if (!message) { - ok = FALSE; - } else if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &mimetype, DBUS_TYPE_INVALID)) { - ok = FALSE; + if (saveable_types == NULL) { + if (start_metadata_service (&error) == FALSE) { + g_error_free (error); + return FALSE; } } - if (ok) { - response = dbus_connection_send_with_reply_and_block (dbus_connection, - message, - RB_METADATA_DBUS_TIMEOUT, - &dbus_error); - if (!response) { - dbus_error_free (&dbus_error); - ok = FALSE; - } else if (dbus_message_iter_init (response, &iter)) { - rb_metadata_dbus_get_boolean (&iter, &can_save); + for (i = 0; saveable_types[i] != NULL; i++) { + if (g_str_equal (mimetype, saveable_types[i])) { + result = TRUE; + break; } } - if (message) - dbus_message_unref (message); - if (response) - dbus_message_unref (response); g_static_mutex_unlock (&conn_mutex); + return result; +} - return can_save; +/** + * rb_metadata_get_saveable_types: + * @md: a #RBMetaData + * + * Constructs a list of the media types for which the metadata backend + * implements tag saving. + * + * Return value: a NULL-terminated array of media type strings. Use g_strfreev + * to free it. + */ +char ** +rb_metadata_get_saveable_types (RBMetaData *md) +{ + return g_strdupv (saveable_types); } /** @@ -724,7 +781,7 @@ if (*error == NULL) { response = dbus_connection_send_with_reply_and_block (dbus_connection, message, - RB_METADATA_DBUS_TIMEOUT, + RB_METADATA_SAVE_DBUS_TIMEOUT, &dbus_error); if (!response) { handle_dbus_error (md, &dbus_error, error); @@ -743,3 +800,21 @@ g_static_mutex_unlock (&conn_mutex); } + +gboolean +rb_metadata_has_audio (RBMetaData *md) +{ + return md->priv->has_audio; +} + +gboolean +rb_metadata_has_video (RBMetaData *md) +{ + return md->priv->has_video; +} + +gboolean +rb_metadata_has_other_data (RBMetaData *md) +{ + return md->priv->has_other_data; +} only in patch2: unchanged: --- rhythmbox-0.12.0.orig/metadata/rb-metadata-dbus.h +++ rhythmbox-0.12.0/metadata/rb-metadata-dbus.h @@ -40,10 +40,13 @@ #define RB_METADATA_DBUS_OBJECT_PATH "/org/gnome/rhythmbox/MetadataService" #define RB_METADATA_DBUS_INTERFACE "org.gnome.rhythmbox.Metadata" -/* Timeout in milliseconds. If a metadata operation takes longer than this, - * the metadata process will be killed and the operation will fail. +/* Timeouts in milliseconds. If a metadata operation takes longer than this, + * the metadata process will be killed and the operation will fail. We use a + * longer timeout for save operations because they involve reading and writing + * the entire file being modified, which can take some time on slow devices. */ #define RB_METADATA_DBUS_TIMEOUT (15000) +#define RB_METADATA_SAVE_DBUS_TIMEOUT (120000) gboolean rb_metadata_dbus_get_boolean (DBusMessageIter *iter, gboolean *value);