diff --git a/rb-use-newer-plparser-7.patch b/rb-use-newer-plparser-7.patch deleted file mode 100644 index ffaf6e4..0000000 --- a/rb-use-newer-plparser-7.patch +++ /dev/null @@ -1,1324 +0,0 @@ -Index: podcast/test-podcast-parse.c -=================================================================== ---- podcast/test-podcast-parse.c (revision 5413) -+++ podcast/test-podcast-parse.c (working copy) -@@ -66,7 +66,6 @@ - g_date_strftime (datebuf, 1024, "%F %T", &date); - - g_print ("Podcast title: %s\n", data->title); -- g_print ("Summary: %s\n", data->summary); - g_print ("Description: %s\n", data->description); - g_print ("Author: %s\n", data->author); - g_print ("Date: %s\n", datebuf); -Index: podcast/rb-podcast-manager.c -=================================================================== ---- podcast/rb-podcast-manager.c (revision 5413) -+++ podcast/rb-podcast-manager.c (working copy) -@@ -816,7 +816,7 @@ - RBPodcastThreadInfo *info; - gchar *valid_url; - -- if (g_str_has_prefix (url, "feed://")) { -+ if (g_str_has_prefix (url, "feed://") || g_str_has_prefix (url, "itpc://")) { - char *tmp; - - tmp = g_strdup_printf ("http://%s", url + strlen ("feed://")); -@@ -898,7 +899,7 @@ - { - RBPodcastChannel *feed = g_new0 (RBPodcastChannel, 1); - -- if (rb_podcast_parse_load_feed (feed, info->url)) { -+ if (rb_podcast_parse_load_feed (feed, info->url) && (feed->is_opml == FALSE)) { - RBPodcastManagerParseResult *result; - - result = g_new0 (RBPodcastManagerParseResult, 1); -@@ -910,6 +911,16 @@ - (GSourceFunc) rb_podcast_manager_parse_complete_cb, - result, - (GDestroyNotify) rb_podcast_manager_free_parse_result); -+ } else if (feed->is_opml) { -+ GList *l; -+ -+ rb_debug ("Loading OPML feeds from %s", info->url); -+ -+ for (l = feed->posts; l != NULL; l = l->next) { -+ RBPodcastItem *item = l->data; -+ rb_podcast_manager_subscribe_feed (info->pd, item->url); -+ } -+ rb_podcast_parse_channel_free (feed); - } - - g_object_unref (info->pd); -@@ -1496,8 +1507,6 @@ - { - GValue description_val = { 0, }; - GValue title_val = { 0, }; -- GValue subtitle_val = { 0, }; -- GValue summary_val = { 0, }; - GValue lang_val = { 0, }; - GValue copyright_val = { 0, }; - GValue image_val = { 0, }; -@@ -1568,13 +1577,6 @@ - rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_ARTIST, &author_val); - g_value_unset (&author_val); - -- if (data->subtitle) { -- g_value_init (&subtitle_val, G_TYPE_STRING); -- g_value_set_string (&subtitle_val, (gchar *) data->subtitle); -- rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_SUBTITLE, &subtitle_val); -- g_value_unset (&subtitle_val); -- } -- - if (data->description) { - g_value_init (&description_val, G_TYPE_STRING); - g_value_set_string (&description_val, (gchar *) data->description); -@@ -1582,13 +1584,6 @@ - g_value_unset (&description_val); - } - -- if (data->summary) { -- g_value_init (&summary_val, G_TYPE_STRING); -- g_value_set_string (&summary_val, (gchar *) data->summary); -- rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_SUMMARY, &summary_val); -- g_value_unset (&summary_val); -- } -- - if (data->lang) { - g_value_init (&lang_val, G_TYPE_STRING); - g_value_set_string (&lang_val, (gchar *) data->lang); -Index: podcast/rb-podcast-parse.c -=================================================================== ---- podcast/rb-podcast-parse.c (revision 5413) -+++ podcast/rb-podcast-parse.c (working copy) -@@ -22,427 +22,116 @@ - - #include "config.h" - --#define _XOPEN_SOURCE --#define __EXTENSIONS__ /* get strptime */ - #include --#include - --#include --#include --#include -+#include - #include - #include - #include - - #include "rb-debug.h" - #include "rb-podcast-parse.h" -+#include "rb-file-helpers.h" - --#define BUFFER_SIZE 256 -- --struct RBPoadcastLoadContext --{ -- guint in_unknown_elt; -- xmlParserCtxtPtr xmlctx; -- GString *prop_value; -- RBPodcastChannel *channel_data; -- RBPodcastItem *item_data; -- -- enum { -- RB_PODCAST_PARSER_STATE_START, -- RB_PODCAST_PARSER_STATE_RSS, -- RB_PODCAST_PARSER_STATE_CHANNEL, -- RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY, -- RB_PODCAST_PARSER_STATE_IMG, -- RB_PODCAST_PARSER_STATE_IMG_PROPERTY, -- RB_PODCAST_PARSER_STATE_ITEM, -- RB_PODCAST_PARSER_STATE_ITEM_PROPERTY, -- RB_PODCAST_PARSER_STATE_END, -- } state; --}; -- --static gboolean rb_validate_channel_propert (const char *name); --static gboolean rb_validate_item_propert (const char *name); --static uintmax_t rb_podcast_parse_date (const char* date_str); --static gulong rb_podcast_parse_time (const char *time_str); --static void rb_podcast_parser_start_element (struct RBPoadcastLoadContext* ctx, const char *name, const char **attrs); --static void rb_podcast_parser_end_element (struct RBPoadcastLoadContext* ctx, const char *name); --static void rb_podcast_parser_characters (struct RBPoadcastLoadContext* ctx, const char *data, guint len); --static void rb_set_channel_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value); --static void rb_set_item_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value); -- --static RBPodcastItem * --rb_podcast_initializa_item () --{ -- RBPodcastItem *data = g_new0 (RBPodcastItem, 1); -- return data; --} -- - static void --rb_set_channel_value (struct RBPoadcastLoadContext *ctx, -- const char *name, -- const char *value) -+playlist_metadata_foreach (const char *key, -+ const char *value, -+ gpointer data) - { -- xmlChar *dvalue; -+ RBPodcastChannel *channel = (RBPodcastChannel *) data; - -- if (value == NULL) -- return; -- -- if (name == NULL) -- return; -- -- dvalue = xmlCharStrdup (value); -- g_strstrip ((char *)dvalue); -- -- if (!strcmp (name, "title")) { -- ctx->channel_data->title = dvalue; -- } else if (!strcmp (name, "language")) { -- ctx->channel_data->lang = dvalue; -- } else if (!strcmp (name, "itunes:subtitle")) { -- ctx->channel_data->subtitle = dvalue; -- } else if (!strcmp (name, "itunes:summary")) { -- ctx->channel_data->summary = dvalue; -- } else if (!strcmp (name, "description")) { -- ctx->channel_data->description = dvalue; -- } else if (!strcmp (name, "generator")) { -- if (ctx->channel_data->author == NULL) -- ctx->channel_data->author = dvalue; -- } else if (!strcmp (name, "itunes:author")) { -- g_free (ctx->channel_data->author); -- ctx->channel_data->author = dvalue; -- } else if (!strcmp (name, "webMaster")) { -- ctx->channel_data->contact = dvalue; -- } else if (!strcmp (name, "pubDate")) { -- ctx->channel_data->pub_date = rb_podcast_parse_date ((char *)dvalue); -- g_free (dvalue); -- } else if (!strcmp (name, "copyright")) { -- ctx->channel_data->copyright = dvalue; -- } else if (!strcmp (name, "img")) { -- ctx->channel_data->img = dvalue; -- } else { -- g_free (dvalue); -+ if (strcmp (key, TOTEM_PL_PARSER_FIELD_TITLE) == 0) { -+ channel->title = g_strdup (value); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_LANGUAGE) == 0) { -+ channel->lang = g_strdup (value); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_DESCRIPTION) == 0) { -+ channel->description = g_strdup (value); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_AUTHOR) == 0) { -+ channel->author = g_strdup (value); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_CONTACT) == 0) { -+ channel->contact = g_strdup (value); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_IMAGE_URL) == 0) { -+ channel->img = g_strdup (value); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_PUB_DATE) == 0) { -+ channel->pub_date = totem_pl_parser_parse_date (value, FALSE); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_COPYRIGHT) == 0) { -+ channel->copyright = g_strdup (value); - } - } - - static void --rb_set_item_value (struct RBPoadcastLoadContext *ctx, -- const char *name, -- const char *value) -+playlist_started (TotemPlParser *parser, -+ const char *uri, -+ GHashTable *metadata, -+ gpointer data) - { -- xmlChar *dvalue; -- -- dvalue = xmlCharStrdup (value); -- g_strstrip ((char *)dvalue); -- -- if (!strcmp (name, "title")) { -- ctx->item_data->title = dvalue; -- } else if (!strcmp (name, "url")) { -- ctx->item_data->url = dvalue; -- } else if (!strcmp (name, "pubDate")) { -- ctx->item_data->pub_date = rb_podcast_parse_date ((char *)dvalue); -- g_free (dvalue); -- } else if (!strcmp (name, "description")) { -- ctx->item_data->description = dvalue; -- } else if (!strcmp (name, "author")) { -- ctx->item_data->author = dvalue; -- } else if (!strcmp (name, "itunes:duration")) { -- ctx->item_data->duration = rb_podcast_parse_time ((char *)dvalue); -- g_free (dvalue); -- } else if (!strcmp (name, "length")) { -- ctx->item_data->filesize = g_ascii_strtoull ((char *)dvalue, NULL, 10); -- } else { -- g_free (dvalue); -- } -+ g_hash_table_foreach (metadata, (GHFunc) playlist_metadata_foreach, data); - } - - static void --rb_insert_item (struct RBPoadcastLoadContext *ctx) -+playlist_ended (TotemPlParser *parser, -+ const char *uri, -+ gpointer data) - { -- RBPodcastItem *data = ctx->item_data; -+ RBPodcastChannel *channel = (RBPodcastChannel *) data; - -- rb_debug ("Inserting item as post"); -- -- if (!data->url) { -- rb_debug ("Item does not have a URL, skipping"); -- return; -- } -- -- ctx->channel_data->posts = g_list_prepend (ctx->channel_data->posts, ctx->item_data); -+ channel->posts = g_list_reverse (channel->posts); - } - --static gboolean --rb_validate_channel_propert (const char *name) --{ -- if (name == NULL) { -- return FALSE; -- } -- -- if (!strcmp (name, "title") || -- !strcmp (name, "language") || -- !strcmp (name, "itunes:subtitle") || -- !strcmp (name, "itunes:summary") || -- !strcmp (name, "description") || -- !strcmp (name, "generator") || -- !strcmp (name, "itunes:author") || -- !strcmp (name, "webMaster") || -- !strcmp (name, "lastBuildDate") || -- !strcmp (name, "pubDate") || -- !strcmp (name, "copyright")) { -- return TRUE; -- } else { -- return FALSE; -- } -- --} -- --static gboolean --rb_validate_item_propert (const char *name) --{ -- if (name == NULL) { -- return FALSE; -- } -- -- if (!strcmp (name, "title") || -- !strcmp (name, "url") || -- !strcmp (name, "pubDate") || -- !strcmp (name, "description") || -- !strcmp (name, "author") || -- !strcmp (name, "itunes:duration") ) { -- -- return TRUE; -- } else { -- return FALSE; -- } --} -- - static void --rb_podcast_parser_start_element (struct RBPoadcastLoadContext *ctx, -- const char *name, -- const char **attrs) -+entry_metadata_foreach (const char *key, -+ const char *value, -+ gpointer data) - { -+ RBPodcastItem *item = (RBPodcastItem *) data; - -- rb_debug ("Start element: %s state: %d", name, ctx->state); -- -- switch (ctx->state) { -- case RB_PODCAST_PARSER_STATE_START: -- { -- if (!strcmp (name, "rss")) { -- ctx->state = RB_PODCAST_PARSER_STATE_RSS; -- } else { -- ctx->in_unknown_elt++; -- } -- -- break; -- } -- -- case RB_PODCAST_PARSER_STATE_RSS: -- { -- if (!strcmp (name, "channel")) { -- ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL; -- } else { -- ctx->in_unknown_elt++; -- } -- -- break; -- } -- -- case RB_PODCAST_PARSER_STATE_CHANNEL: -- { -- if (strcmp (name, "image") == 0) -- { -- ctx->state = RB_PODCAST_PARSER_STATE_IMG; -- } else if (strcmp (name, "itunes:image") == 0) { -- for (; attrs && *attrs; attrs +=2) { -- if (!strcmp (*attrs, "href")) { -- const char *href_value = *(attrs + 1); -- rb_set_channel_value (ctx, "img", href_value); -- } -- } -- -- ctx->state = RB_PODCAST_PARSER_STATE_IMG; -- -- } else if (!strcmp (name, "item")) { -- ctx->item_data = rb_podcast_initializa_item (); -- ctx->state = RB_PODCAST_PARSER_STATE_ITEM; -- } else if (!rb_validate_channel_propert (name)) { -- rb_debug ("Unknown property"); -- ctx->in_unknown_elt++; -- } else { -- ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY; -- } -- -- break; -- } -- -- case RB_PODCAST_PARSER_STATE_ITEM: -- { -- if (!strcmp (name, "enclosure")) { -- for (; *attrs; attrs +=2) { -- if (!strcmp (*attrs, "url")) { -- const char *url_value = *(attrs + 1); -- rb_set_item_value (ctx, "url", url_value); -- } else if (!strcmp (*attrs, "length")) { -- const char *length_value = *(attrs + 1); -- rb_set_item_value (ctx, "length", length_value); -- } -- } -- -- ctx->state = RB_PODCAST_PARSER_STATE_ITEM_PROPERTY; -- -- } else if (!rb_validate_item_propert (name)) { -- ctx->in_unknown_elt++; -- } else { -- ctx->state = RB_PODCAST_PARSER_STATE_ITEM_PROPERTY; -- } -- -- break; -- } -- -- case RB_PODCAST_PARSER_STATE_IMG: -- { -- if (strcmp (name, "url") != 0) { -- ctx->in_unknown_elt++; -- } else { -- ctx->state = RB_PODCAST_PARSER_STATE_IMG_PROPERTY; -- } -- -- break; -- } -- -- case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY: -- case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY: -- case RB_PODCAST_PARSER_STATE_IMG_PROPERTY: -- rb_debug ("nested element inside property; treating as unknown"); -- ctx->in_unknown_elt++; -- break; -- -- case RB_PODCAST_PARSER_STATE_END: -- break; -- default: -- g_warning ("Unknown podcast parser state: %d", ctx->state); -- break; -+ if (strcmp (key, TOTEM_PL_PARSER_FIELD_TITLE) == 0) { -+ item->title = g_strdup (value); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_URL) == 0) { -+ item->url = g_strdup (value); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_DESCRIPTION) == 0) { -+ item->description = g_strdup (value); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_AUTHOR) == 0) { -+ item->author = g_strdup (value); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_PUB_DATE) == 0) { -+ item->pub_date = totem_pl_parser_parse_date (value, FALSE); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_DURATION) == 0) { -+ item->duration = totem_pl_parser_parse_duration (value, FALSE); -+ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_FILESIZE) == 0) { -+ item->filesize = g_ascii_strtoull (value, NULL, 10); - } - } - - static void --rb_podcast_parser_end_element (struct RBPoadcastLoadContext *ctx, -- const char *name) -+entry_parsed (TotemPlParser *parser, -+ const char *uri, -+ GHashTable *metadata, -+ gpointer data) - { -- rb_debug ("End element: %s state: %d", name, ctx->state); -+ RBPodcastChannel *channel = (RBPodcastChannel *) data; -+ RBPodcastItem *item; - -- if (ctx->in_unknown_elt > 0) { -- ctx->in_unknown_elt--; -- rb_debug ("Unknown element"); -- return; -- } -- -- switch (ctx->state) { -- case RB_PODCAST_PARSER_STATE_START: -- ctx->state = RB_PODCAST_PARSER_STATE_END; -- break; -- -- case RB_PODCAST_PARSER_STATE_RSS: -- ctx->state = RB_PODCAST_PARSER_STATE_START; -- break; -- -- case RB_PODCAST_PARSER_STATE_CHANNEL: -- ctx->state = RB_PODCAST_PARSER_STATE_RSS; -- break; -- -- case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY: -- { -- rb_set_channel_value (ctx, name, ctx->prop_value->str); -- ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL; -- g_string_truncate (ctx->prop_value, 0); -- break; -- } -- -- case RB_PODCAST_PARSER_STATE_ITEM: -- { -- rb_insert_item (ctx); -- ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL; -- break; -- } -- -- case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY: -- { -- rb_set_item_value (ctx, name, ctx->prop_value->str); -- ctx->state = RB_PODCAST_PARSER_STATE_ITEM; -- g_string_truncate (ctx->prop_value, 0); -- break; -- } -- -- case RB_PODCAST_PARSER_STATE_IMG_PROPERTY: -- { -- rb_set_channel_value (ctx, "img", ctx->prop_value->str); -- ctx->state = RB_PODCAST_PARSER_STATE_IMG; -- g_string_truncate (ctx->prop_value, 0); -- break; -- } -- -- case RB_PODCAST_PARSER_STATE_IMG: -- ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL; -- break; -- -- case RB_PODCAST_PARSER_STATE_END: -- break; -- -- default: -- g_warning ("Unknown podcast parser state: %d", ctx->state); -- break; -- } -+ item = g_new0 (RBPodcastItem, 1); -+ g_hash_table_foreach (metadata, (GHFunc) entry_metadata_foreach, item); -+ channel->posts = g_list_prepend (channel->posts, item); - } - --static void --rb_podcast_parser_characters (struct RBPoadcastLoadContext *ctx, -- const char *data, -- guint len) --{ -- switch (ctx->state) { -- case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY: -- case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY: -- case RB_PODCAST_PARSER_STATE_IMG_PROPERTY: -- g_string_append_len (ctx->prop_value, data, len); -- break; -- case RB_PODCAST_PARSER_STATE_START: -- case RB_PODCAST_PARSER_STATE_IMG: -- case RB_PODCAST_PARSER_STATE_RSS: -- case RB_PODCAST_PARSER_STATE_CHANNEL: -- case RB_PODCAST_PARSER_STATE_ITEM: -- case RB_PODCAST_PARSER_STATE_END: -- break; -- default: -- g_warning ("Unknown podcast parser state: %d", ctx->state); -- break; -- } --} -- - gboolean - rb_podcast_parse_load_feed (RBPodcastChannel *data, - const char *file_name) - { -- xmlParserCtxtPtr parser; -- xmlSAXHandlerPtr sax_handler = NULL; - GnomeVFSResult result; - GnomeVFSFileInfo *info; -- gint file_size; -- gchar *buffer = NULL; -- const char *query_string; -+ TotemPlParser *plparser; - -- struct RBPoadcastLoadContext *ctx = NULL; -+ data->url = g_strdup (file_name); - -- data->url = xmlCharStrdup (file_name); -- -- /* if the URL has a .rss or .xml extension (before the query string), -+ /* if the URL has a .rss, .xml or .atom extension (before the query string), - * don't bother checking the MIME type. - */ -- query_string = strchr (file_name, '?'); -- if (query_string == NULL) { -- query_string = file_name + strlen (file_name); -- } -- -- if (strncmp (query_string - 4, ".rss", 4) == 0 || -- strncmp (query_string - 4, ".xml", 4) == 0) { -- rb_debug ("not checking mime type for %s", file_name); -+ if (rb_uri_could_be_podcast (file_name, &data->is_opml)) { -+ rb_debug ("not checking mime type for %s (should be %s file)", file_name, -+ data->is_opml ? "OPML" : "Podcast"); - } else { - gboolean invalid_mime_type; - -@@ -451,22 +140,33 @@ - - result = gnome_vfs_get_file_info (file_name, info, GNOME_VFS_FILE_INFO_DEFAULT); - -+ if ((result != GNOME_VFS_OK)) { -+ if (info->mime_type != NULL) { -+ rb_debug ("Invalid mime-type in podcast feed %s", info->mime_type); -+ } else { -+ rb_debug ("Couldn't get mime type for %s: %s", file_name, -+ gnome_vfs_result_to_string (result)); -+ } -+ gnome_vfs_file_info_unref (info); -+ return TRUE; -+ } -+ - if (info != NULL - && info->mime_type != NULL - && strstr (info->mime_type, "html") == NULL - && strstr (info->mime_type, "xml") == NULL -- && strstr (info->mime_type, "rss") == NULL) { -+ && strstr (info->mime_type, "rss") == NULL -+ && strstr (info->mime_type, "opml") == NULL) { - invalid_mime_type = TRUE; -+ } else if (info != NULL -+ && info->mime_type != NULL -+ && strstr (info->mime_type, "opml") != NULL) { -+ data->is_opml = TRUE; -+ invalid_mime_type = FALSE; - } else { - invalid_mime_type = FALSE; - } - -- if ((result != GNOME_VFS_OK)) { -- rb_debug ("Invalid mime-type in podcast feed %s", info->mime_type); -- gnome_vfs_file_info_unref (info); -- return TRUE; -- } -- - if (invalid_mime_type) { - GtkWidget *dialog; - -@@ -492,173 +192,22 @@ - return FALSE; - } - -- /* first download file by gnome_vfs for use gnome network configuration */ -- rb_debug ("reading podcast feed %s", file_name); -- result = gnome_vfs_read_entire_file (file_name, &file_size, &buffer); -- if (result != GNOME_VFS_OK) -- return TRUE; -+ plparser = totem_pl_parser_new (); -+ g_object_set (plparser, "recurse", FALSE, NULL); -+ g_signal_connect (G_OBJECT (plparser), "entry-parsed", G_CALLBACK (entry_parsed), data); -+ g_signal_connect (G_OBJECT (plparser), "playlist-started", G_CALLBACK (playlist_started), data); -+ g_signal_connect (G_OBJECT (plparser), "playlist-ended", G_CALLBACK (playlist_ended), data); - -- /* initializing parse */ -- sax_handler = g_new0 (xmlSAXHandler, 1); -- sax_handler->startElement = (startElementSAXFunc) rb_podcast_parser_start_element; -- sax_handler->endElement = (endElementSAXFunc) rb_podcast_parser_end_element; -- sax_handler->characters = (charactersSAXFunc) rb_podcast_parser_characters; -- xmlSubstituteEntitiesDefault (1); -- -- ctx = g_new0 (struct RBPoadcastLoadContext, 1); -- ctx->in_unknown_elt = 0; -- ctx->channel_data = data; -- ctx->prop_value = g_string_sized_new (512); -- -- parser = xmlCreateMemoryParserCtxt (buffer, file_size); -- if (parser == NULL) { -- g_free (sax_handler); -- g_free (buffer); -- g_string_free (ctx->prop_value, TRUE); -- g_free (ctx); -+ if (totem_pl_parser_parse (plparser, file_name, FALSE) != TOTEM_PL_PARSER_RESULT_SUCCESS) { -+ rb_debug ("Parsing %s as a Podcast failed", file_name); -+ g_object_unref (plparser); - return FALSE; - } -+ rb_debug ("Parsing %s as a Podcast succeeded", file_name); - -- ctx->xmlctx = parser; -- parser->userData = ctx; -- parser->sax = sax_handler; -- xmlParseDocument (parser); -- -- g_free (sax_handler); -- parser->sax = NULL; -- xmlFreeParserCtxt (parser); -- -- g_free (buffer); -- g_string_free (ctx->prop_value, TRUE); -- g_free (ctx); -- -- data->posts = g_list_reverse (data->posts); - return TRUE; - } - --static uintmax_t --rb_podcast_parse_date (const char *date_str) --{ -- struct tm tm; -- char *result; -- -- /* RFC 2822 date format */ -- result = strptime (date_str, "%a, %d %b %Y %T", &tm); -- -- /* same as above, but without comma */ -- if (result == NULL) { -- memset (&tm, 0, sizeof (struct tm)); -- result = strptime (date_str, "%a %d %b %Y %T", &tm); -- } -- -- /* close-to-RFC 2822, but with extra 0 */ -- if (result == NULL) { -- memset (&tm, 0, sizeof (struct tm)); -- result = strptime (date_str, "%a, %d %b %Y 0%T", &tm); -- } -- -- /* close-to-RFC 2822, but with no seconds */ -- if (result == NULL) { -- memset (&tm, 0, sizeof (struct tm)); -- result = strptime (date_str, "%a, %d %b %Y %R", &tm); -- } -- -- /* format without weekday */ -- if (result == NULL) { -- memset (&tm, 0, sizeof (struct tm)); -- result = strptime (date_str, "%d %b %Y %T", &tm); -- } -- -- /* reversed day and long month */ -- if (result == NULL) { -- memset (&tm, 0, sizeof (struct tm)); -- result = strptime (date_str, "%a, %B %d %Y %T", &tm); -- } -- -- /* ISO date like */ -- if (result == NULL) { -- memset (&tm, 0, sizeof (struct tm)); -- result = strptime (date_str, "%Y-%m-%d %T", &tm); -- } -- -- /* ISO date like without timezone */ -- if (result == NULL) { -- memset (&tm, 0, sizeof (struct tm)); -- result = strptime (date_str, "%Y-%m-%d", &tm); -- } -- -- /* Broken weekday short names */ -- if (result == NULL) { -- char *tmp; -- -- /* strip off the erroneous weekday */ -- tmp = strstr (date_str, ","); -- if (tmp != NULL) { -- tmp++; -- memset (&tm, 0, sizeof (struct tm)); -- result = strptime (tmp, "%d %b %Y %T", &tm); -- } -- } -- -- /* format with timezone offset from GMT */ -- if (result == NULL) { -- memset (&tm, 0, sizeof (struct tm)); -- result = strptime (date_str, "%a %b %d %T %z %Y", &tm); -- } -- -- /* format with timezone name */ -- if (result == NULL) { -- char *tmp; -- -- memset (&tm, 0, sizeof (struct tm)); -- -- /* match first part of time string */ -- result = strptime (date_str, "%a %b %d %T ", &tm); -- -- /* look for anything with a timezone name-like format -- i.e. at least one all caps alphabetical character */ -- if (result != NULL) { -- size_t n; -- -- n = strspn(result, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); -- tmp = result+n; -- -- /* make sure there was at least one character that matched */ -- if ((tmp != NULL) && n > 0) -- /* remaining part must be the year */ -- result = strptime (tmp, "%Y", &tm); -- else -- result = NULL; -- } -- } -- -- if (result == NULL) { -- rb_debug ("unable to convert date string %s", date_str); -- } -- -- return (uintmax_t) ( (result==NULL) ? 0 : mktime (&tm) ); --} -- --static gulong --rb_podcast_parse_time (const char *time_str) --{ -- struct tm tm; -- char *result; -- -- memset (&tm, 0, sizeof (struct tm)); -- result = strptime (time_str, "%H:%M:%S", &tm); -- if (result == NULL) { -- memset (&tm, 0, sizeof (struct tm)); -- result = strptime (time_str, "%M:%S", &tm); -- } -- if (result == NULL) { -- memset (&tm, 0, sizeof (struct tm)); -- rb_debug ("unable to convert duration string %s", time_str); -- } -- -- return ((tm.tm_hour * 60 + tm.tm_min) * 60 + tm.tm_sec); --} -- - void - rb_podcast_parse_channel_free (RBPodcastChannel *data) - { -@@ -671,8 +220,6 @@ - g_free (data->url); - g_free (data->title); - g_free (data->lang); -- g_free (data->subtitle); -- g_free (data->summary); - g_free (data->description); - g_free (data->author); - g_free (data->contact); -Index: podcast/Makefile.am -=================================================================== ---- podcast/Makefile.am (revision 5413) -+++ podcast/Makefile.am (working copy) -@@ -6,6 +6,9 @@ - rb-podcast-parse.c \ - rb-podcast-parse.h - -+librbpodcast_parse_la_LIBADD = \ -+ $(top_builddir)/lib/librb.la -+ - librbpodcast_la_SOURCES = \ - rb-feed-podcast-properties-dialog.c \ - rb-feed-podcast-properties-dialog.h \ -@@ -23,7 +26,8 @@ - test-podcast-parse.c - test_podcast_parse_LDADD = \ - librbpodcast_parse.la \ -- $(RHYTHMBOX_LIBS) -+ $(RHYTHMBOX_LIBS) \ -+ $(TOTEM_PLPARSER_LIBS) - - INCLUDES = \ - -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ -@@ -36,7 +40,8 @@ - -I$(top_srcdir)/metadata \ - -I$(top_srcdir)/library \ - -I$(top_builddir)/lib \ -- $(RHYTHMBOX_CFLAGS) -+ $(RHYTHMBOX_CFLAGS) \ -+ $(TOTEM_PLPARSER_CFLAGS) - - librbpodcast_la_LDFLAGS = -export-dynamic - -Index: podcast/rb-podcast-parse.h -=================================================================== ---- podcast/rb-podcast-parse.h (revision 5413) -+++ podcast/rb-podcast-parse.h (working copy) -@@ -23,36 +23,34 @@ - #define RB_PODCAST_PARSE_H - - #include --#include --#include - - typedef struct - { -- xmlChar* title; -- xmlChar* url; -- xmlChar* description; -- xmlChar* author; -- uintmax_t pub_date; -+ char* title; -+ char* url; -+ char* description; -+ char* author; -+ guint64 pub_date; - gulong duration; - guint64 filesize; --}RBPodcastItem; -+} RBPodcastItem; - - typedef struct - { -- xmlChar* url; -- xmlChar* title; -- xmlChar* lang; -- xmlChar* subtitle; -- xmlChar* summary; -- xmlChar* description; -- xmlChar* author; -- xmlChar* contact; -- xmlChar* img; -- uintmax_t pub_date; -- xmlChar* copyright; -+ char* url; -+ char* title; -+ char* lang; -+ char* description; -+ char* author; -+ char* contact; -+ char* img; -+ guint64 pub_date; -+ char* copyright; - -+ gboolean is_opml; -+ - GList *posts; --}RBPodcastChannel; -+} RBPodcastChannel; - - gboolean rb_podcast_parse_load_feed (RBPodcastChannel *data, const char *file_name); - void rb_podcast_parse_channel_free (RBPodcastChannel *data); -Index: configure.ac -=================================================================== ---- configure.ac (revision 5413) -+++ configure.ac (working copy) -@@ -34,7 +34,7 @@ - LIBGPOD_REQS=0.4 - MUSICBRAINZ_REQS=2.1.0 - NCB_MIN_REQS=2.9.0 --TOTEM_PLPARSER_REQS=1.1.5 -+TOTEM_PLPARSER_REQS=2.21.0 - VALA_REQS=0.0.8 - - AC_MSG_CHECKING([for GNU extension fwrite_unlocked]) -Index: lib/rb-file-helpers.c -=================================================================== ---- lib/rb-file-helpers.c (revision 5413) -+++ lib/rb-file-helpers.c (working copy) -@@ -774,6 +774,65 @@ - return g_utf8_strrchr (text_uri, -1, GNOME_VFS_URI_PATH_CHR)[1] == '.'; - } - -+gboolean -+rb_uri_could_be_podcast (const char *uri, gboolean *is_opml) -+{ -+ const char *query_string; -+ -+ if (is_opml != NULL) -+ *is_opml = FALSE; -+ -+ /* Check the scheme is a possible one first */ -+ if (g_str_has_prefix (uri, "http") == FALSE && -+ g_str_has_prefix (uri, "itpc:") == FALSE && -+ g_str_has_prefix (uri, "itms:") == FALSE) { -+ rb_debug ("'%s' can't be a Podcast or OPML file, not the right scheme", uri); -+ return FALSE; -+ } -+ -+ /* Now, check whether the iTunes Music Store link -+ * is a podcast */ -+ if (g_str_has_prefix (uri, "itms:") != FALSE -+ && strstr (uri, "phobos.apple.com") != NULL -+ && strstr (uri, "viewPodcast") != NULL) -+ return TRUE; -+ -+ query_string = strchr (uri, '?'); -+ if (query_string == NULL) { -+ query_string = uri + strlen (uri); -+ } -+ -+ /* FIXME hacks */ -+ if (strstr (uri, "rss") != NULL || -+ strstr (uri, "atom") != NULL || -+ strstr (uri, "feed") != NULL) { -+ rb_debug ("'%s' should be Podcast file, HACK", uri); -+ return TRUE; -+ } else if (strstr (uri, "opml") != NULL) { -+ rb_debug ("'%s' should be an OPML file, HACK", uri); -+ if (is_opml != NULL) -+ *is_opml = TRUE; -+ return TRUE; -+ } -+ -+ if (strncmp (query_string - 4, ".rss", 4) == 0 || -+ strncmp (query_string - 4, ".xml", 4) == 0 || -+ strncmp (query_string - 5, ".atom", 5) == 0 || -+ strncmp (uri, "itpc", 4) == 0 || -+ (strstr (uri, "phobos.apple.com/") != NULL && strstr (uri, "viewPodcast") != NULL) || -+ strstr (uri, "itunes.com/podcast") != NULL) { -+ rb_debug ("'%s' should be Podcast file", uri); -+ return TRUE; -+ } else if (strncmp (query_string - 5, ".opml", 5) == 0) { -+ rb_debug ("'%s' should be an OPML file", uri); -+ if (is_opml != NULL) -+ *is_opml = TRUE; -+ return TRUE; -+ } -+ -+ return FALSE; -+} -+ - char * - rb_uri_make_hidden (const char *text_uri) - { -Index: lib/rb-file-helpers.h -=================================================================== ---- lib/rb-file-helpers.h (revision 5413) -+++ lib/rb-file-helpers.h (working copy) -@@ -44,6 +44,7 @@ - gboolean rb_uri_is_writable (const char *uri); - gboolean rb_uri_is_local (const char *uri); - gboolean rb_uri_is_hidden (const char *uri); -+gboolean rb_uri_could_be_podcast (const char *uri, gboolean *is_opml); - char * rb_uri_make_hidden (const char *uri); - char * rb_uri_get_dir_name (const char *uri); - char * rb_uri_get_short_path_name (const char *uri); -Index: shell/rb-shell.c -=================================================================== ---- shell/rb-shell.c (revision 5413) -+++ shell/rb-shell.c (working copy) -@@ -3202,20 +3205,11 @@ - gboolean source_is_entry; - } PlaylistParseData; - --#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) - static void - handle_playlist_entry_cb (TotemPlParser *playlist, - const char *uri, - GHashTable *metadata, - PlaylistParseData *data) --#else --static void --handle_playlist_entry_cb (TotemPlParser *playlist, -- const char *uri, -- const char *title, -- const char *genre, -- PlaylistParseData *data) --#endif /* TOTEM_PL_PARSER_CHECK_VERSION */ - { - RBSource *source; - -@@ -3264,6 +3258,14 @@ - entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri); - playlist_source = NULL; - -+ /* If the URI points to a Podcast, pass it on to -+ * the Podcast source */ -+ if (rb_uri_could_be_podcast (uri, NULL)) { -+ rb_podcast_source_add_feed (shell->priv->podcast_source, uri); -+ rb_shell_select_source (shell, RB_SOURCE (shell->priv->podcast_source)); -+ return TRUE; -+ } -+ - if (entry == NULL) { - TotemPlParser *parser; - TotemPlParserResult result; -@@ -3277,15 +3279,9 @@ - rb_debug ("adding uri %s, play %d", uri, play); - parser = totem_pl_parser_new (); - --#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) - g_signal_connect_data (G_OBJECT (parser), "entry-parsed", - G_CALLBACK (handle_playlist_entry_cb), - &data, NULL, 0); --#else -- g_signal_connect_data (G_OBJECT (parser), "entry", -- G_CALLBACK (handle_playlist_entry_cb), -- &data, NULL, 0); --#endif /* TOTEM_PL_PARSER_CHECK_VERSION */ - - totem_pl_parser_add_ignored_mimetype (parser, "x-directory/normal"); - if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse")) -Index: shell/rb-playlist-manager.c -=================================================================== ---- shell/rb-playlist-manager.c (revision 5413) -+++ shell/rb-playlist-manager.c (working copy) -@@ -502,20 +502,11 @@ - return quark; - } - --#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) - static void - handle_playlist_entry_cb (TotemPlParser *playlist, - const char *uri_maybe, - GHashTable *metadata, - RBPlaylistManager *mgr) --#else --static void --handle_playlist_entry_cb (TotemPlParser *playlist, -- const char *uri_maybe, -- const char *title, -- const char *genre, -- RBPlaylistManager *mgr) --#endif /* TOTEM_PL_PARSER_CHECK_VERSION */ - { - char *uri; - #if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) -@@ -550,19 +541,29 @@ - } - - static void --playlist_load_start_cb (TotemPlParser *parser, const char *title, RBPlaylistManager *mgr) -+playlist_load_started_cb (TotemPlParser *parser, const char *uri, GHashTable *metadata, RBPlaylistManager *mgr) - { -- rb_debug ("loading new playlist %s", title); -+ const char *title; - -+ rb_debug ("loading new playlist %s", uri); -+ -+ title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE); -+ if (title == NULL) -+ title = _("Unnamed playlist"); -+ - mgr->priv->loading_playlist = - RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, title, FALSE)); - } - - static void --playlist_load_end_cb (TotemPlParser *parser, const char *title, RBPlaylistManager *mgr) -+playlist_load_ended_cb (TotemPlParser *parser, const char *uri, GHashTable *metadata, RBPlaylistManager *mgr) - { -- rb_debug ("finished loading playlist %s", title); -+ const char *title; - -+ rb_debug ("finished loading playlist %s", uri); -+ -+ title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE); -+ - if (title) { - g_object_set (mgr->priv->loading_playlist, "name", title, NULL); - mgr->priv->loading_playlist = NULL; -@@ -591,22 +592,16 @@ - { - TotemPlParser *parser = totem_pl_parser_new (); - --#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) - g_signal_connect_object (parser, "entry-parsed", - G_CALLBACK (handle_playlist_entry_cb), - mgr, 0); --#else -- g_signal_connect_object (parser, "entry", -- G_CALLBACK (handle_playlist_entry_cb), -- mgr, 0); --#endif /* TOTEM_PL_PARSER_CHECK_VERSION */ - -- g_signal_connect_object (parser, "playlist-start", -- G_CALLBACK (playlist_load_start_cb), -+ g_signal_connect_object (parser, "playlist-started", -+ G_CALLBACK (playlist_load_started_cb), - mgr, 0); - -- g_signal_connect_object (parser, "playlist-end", -- G_CALLBACK (playlist_load_end_cb), -+ g_signal_connect_object (parser, "playlist-ended", -+ G_CALLBACK (playlist_load_ended_cb), - mgr, 0); - - if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse")) -Index: shell/rb-shell-player.c -=================================================================== ---- shell/rb-shell-player.c (revision 5413) -+++ shell/rb-shell-player.c (working copy) -@@ -1264,20 +1264,11 @@ - PlaybackStartType play_type; - } OpenLocationThreadData; - --#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) - static void - playlist_entry_cb (TotemPlParser *playlist, - const char *uri, - GHashTable *metadata, - RBShellPlayer *player) --#else --static void --playlist_entry_cb (TotemPlParser *playlist, -- const char *uri, -- const char *title, -- const char *genre, -- RBShellPlayer *player) --#endif - { - rb_debug ("adding stream url %s", uri); - g_queue_push_tail (player->priv->playlist_urls, g_strdup (uri)); -@@ -1291,15 +1282,9 @@ - - playlist = totem_pl_parser_new (); - --#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) - g_signal_connect_data (G_OBJECT (playlist), "entry-parsed", - G_CALLBACK (playlist_entry_cb), - data->player, NULL, 0); --#else -- g_signal_connect_data (G_OBJECT (playlist), "entry", -- G_CALLBACK (playlist_entry_cb), -- data->player, NULL, 0); --#endif /* TOTEM_PL_PARSER_CHECK_VERSION */ - - totem_pl_parser_add_ignored_mimetype (playlist, "x-directory/normal"); - -Index: data/rhythmbox.schemas -=================================================================== ---- data/rhythmbox.schemas (revision 5413) -+++ data/rhythmbox.schemas (working copy) -@@ -136,9 +136,9 @@ - Main window X position. - - -- - -- -+ -+ - /schemas/apps/rhythmbox/state/window_position_y - /apps/rhythmbox/state/window_position_y - rhythmbox -@@ -150,10 +150,7 @@ - - - -- -- -- -- -+ - /schemas/apps/rhythmbox/state/window_height - /apps/rhythmbox/state/window_height - rhythmbox -@@ -439,6 +436,106 @@ - - - -+ /schemas/desktop/gnome/url-handlers/itpc/command -+ /desktop/gnome/url-handlers/itpc/command -+ rhythmbox -+ string -+ rhythmbox "%s" -+ -+ The command to handle ITPC scheme URLs -+ The command to handle ITPC scheme URLs. -+ -+ -+ -+ /schemas/desktop/gnome/url-handlers/itpc/needs_terminal -+ /desktop/gnome/url-handlers/itpc/needs_terminal -+ rhythmbox -+ bool -+ false -+ -+ Whether command to handle ITPC scheme URLs needs a terminal -+ Whether command to handle ITPC scheme URLs needs a terminal. -+ -+ -+ -+ /schemas/desktop/gnome/url-handlers/itpc/enabled -+ /desktop/gnome/url-handlers/itpc/enabled -+ rhythmbox -+ bool -+ true -+ -+ Whether command to handle ITPC scheme URLs is enabled -+ Whether command to handle ITPC scheme URLs is enabled. -+ -+ -+ -+ /schemas/desktop/gnome/url-handlers/itms/command -+ /desktop/gnome/url-handlers/itms/command -+ rhythmbox -+ string -+ rhythmbox "%s" -+ -+ The command to handle ITMS scheme URLs -+ The command to handle ITMS scheme URLs. -+ -+ -+ -+ /schemas/desktop/gnome/url-handlers/itms/needs_terminal -+ /desktop/gnome/url-handlers/itms/needs_terminal -+ rhythmbox -+ bool -+ false -+ -+ Whether command to handle ITMS scheme URLs needs a terminal -+ Whether command to handle ITMS scheme URLs needs a terminal. -+ -+ -+ -+ /schemas/desktop/gnome/url-handlers/itms/enabled -+ /desktop/gnome/url-handlers/itms/enabled -+ rhythmbox -+ bool -+ true -+ -+ Whether command to handle ITMS scheme URLs is enabled -+ Whether command to handle ITMS scheme URLs is enabled. -+ -+ -+ -+ /schemas/desktop/gnome/url-handlers/feed/command -+ /desktop/gnome/url-handlers/feed/command -+ rhythmbox -+ string -+ rhythmbox "%s" -+ -+ The command to handle FEED scheme URLs -+ The command to handle FEED scheme URLs. -+ -+ -+ -+ /schemas/desktop/gnome/url-handlers/feed/needs_terminal -+ /desktop/gnome/url-handlers/feed/needs_terminal -+ rhythmbox -+ bool -+ false -+ -+ Whether command to handle FEED scheme URLs needs a terminal -+ Whether command to handle FEED scheme URLs needs a terminal. -+ -+ -+ -+ /schemas/desktop/gnome/url-handlers/feed/enabled -+ /desktop/gnome/url-handlers/feed/enabled -+ rhythmbox -+ bool -+ true -+ -+ Whether command to handle FEED scheme URLs is enabled -+ Whether command to handle FEED scheme URLs is enabled. -+ -+ -+ -+ - /schemas/apps/rhythmbox/state/podcast/show_browser - /apps/rhythmbox/state/podcast/show_browser - rhythmbox diff --git a/rb-use-newer-plparser-9.patch b/rb-use-newer-plparser-9.patch new file mode 100644 index 0000000..b12df3f --- /dev/null +++ b/rb-use-newer-plparser-9.patch @@ -0,0 +1,1806 @@ +Index: podcast/test-podcast-parse.c +=================================================================== +--- podcast/test-podcast-parse.c (revision 5467) ++++ podcast/test-podcast-parse.c (working copy) +@@ -1,6 +1,7 @@ + + #include "config.h" + ++#include + #include + #include + +@@ -47,6 +48,7 @@ + GDate date = {0,}; + char datebuf[1024]; + ++ setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + +@@ -66,7 +68,6 @@ + g_date_strftime (datebuf, 1024, "%F %T", &date); + + g_print ("Podcast title: %s\n", data->title); +- g_print ("Summary: %s\n", data->summary); + g_print ("Description: %s\n", data->description); + g_print ("Author: %s\n", data->author); + g_print ("Date: %s\n", datebuf); +Index: podcast/plugin.symbols +=================================================================== +--- podcast/plugin.symbols (revision 0) ++++ podcast/plugin.symbols (revision 0) +@@ -0,0 +1,4 @@ ++NP_GetMIMEDescription ++NP_GetValue ++NP_Initialize ++NP_Shutdown +Index: podcast/rb-podcast-manager.c +=================================================================== +--- podcast/rb-podcast-manager.c (revision 5467) ++++ podcast/rb-podcast-manager.c (working copy) +@@ -833,7 +833,7 @@ + RBPodcastThreadInfo *info; + gchar *valid_url; + +- if (g_str_has_prefix (url, "feed://")) { ++ if (g_str_has_prefix (url, "feed://") || g_str_has_prefix (url, "itpc://")) { + char *tmp; + + tmp = g_strdup_printf ("http://%s", url + strlen ("feed://")); +@@ -915,7 +915,7 @@ + { + RBPodcastChannel *feed = g_new0 (RBPodcastChannel, 1); + +- if (rb_podcast_parse_load_feed (feed, info->url)) { ++ if (rb_podcast_parse_load_feed (feed, info->url) && (feed->is_opml == FALSE)) { + RBPodcastManagerParseResult *result; + + result = g_new0 (RBPodcastManagerParseResult, 1); +@@ -927,6 +927,16 @@ + (GSourceFunc) rb_podcast_manager_parse_complete_cb, + result, + (GDestroyNotify) rb_podcast_manager_free_parse_result); ++ } else if (feed->is_opml) { ++ GList *l; ++ ++ rb_debug ("Loading OPML feeds from %s", info->url); ++ ++ for (l = feed->posts; l != NULL; l = l->next) { ++ RBPodcastItem *item = l->data; ++ rb_podcast_manager_subscribe_feed (info->pd, item->url); ++ } ++ rb_podcast_parse_channel_free (feed); + } + + g_object_unref (info->pd); +@@ -1568,8 +1578,6 @@ + { + GValue description_val = { 0, }; + GValue title_val = { 0, }; +- GValue subtitle_val = { 0, }; +- GValue summary_val = { 0, }; + GValue lang_val = { 0, }; + GValue copyright_val = { 0, }; + GValue image_val = { 0, }; +@@ -1587,7 +1595,7 @@ + + GList *lst_songs; + +- if (data->title == NULL) { ++ if (data->title == NULL) { + g_list_free (data->posts); + g_free (data); + return; +@@ -1640,13 +1648,6 @@ + rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_ARTIST, &author_val); + g_value_unset (&author_val); + +- if (data->subtitle) { +- g_value_init (&subtitle_val, G_TYPE_STRING); +- g_value_set_string (&subtitle_val, (gchar *) data->subtitle); +- rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_SUBTITLE, &subtitle_val); +- g_value_unset (&subtitle_val); +- } +- + if (data->description) { + g_value_init (&description_val, G_TYPE_STRING); + g_value_set_string (&description_val, (gchar *) data->description); +@@ -1654,13 +1655,6 @@ + g_value_unset (&description_val); + } + +- if (data->summary) { +- g_value_init (&summary_val, G_TYPE_STRING); +- g_value_set_string (&summary_val, (gchar *) data->summary); +- rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_SUMMARY, &summary_val); +- g_value_unset (&summary_val); +- } +- + if (data->lang) { + g_value_init (&lang_val, G_TYPE_STRING); + g_value_set_string (&lang_val, (gchar *) data->lang); +Index: podcast/rb-podcast-parse.c +=================================================================== +--- podcast/rb-podcast-parse.c (revision 5467) ++++ podcast/rb-podcast-parse.c (working copy) +@@ -22,427 +22,116 @@ + + #include "config.h" + +-#define _XOPEN_SOURCE +-#define __EXTENSIONS__ /* get strptime */ + #include +-#include + +-#include +-#include +-#include ++#include + #include + #include + #include + + #include "rb-debug.h" + #include "rb-podcast-parse.h" ++#include "rb-file-helpers.h" + +-#define BUFFER_SIZE 256 +- +-struct RBPoadcastLoadContext +-{ +- guint in_unknown_elt; +- xmlParserCtxtPtr xmlctx; +- GString *prop_value; +- RBPodcastChannel *channel_data; +- RBPodcastItem *item_data; +- +- enum { +- RB_PODCAST_PARSER_STATE_START, +- RB_PODCAST_PARSER_STATE_RSS, +- RB_PODCAST_PARSER_STATE_CHANNEL, +- RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY, +- RB_PODCAST_PARSER_STATE_IMG, +- RB_PODCAST_PARSER_STATE_IMG_PROPERTY, +- RB_PODCAST_PARSER_STATE_ITEM, +- RB_PODCAST_PARSER_STATE_ITEM_PROPERTY, +- RB_PODCAST_PARSER_STATE_END, +- } state; +-}; +- +-static gboolean rb_validate_channel_propert (const char *name); +-static gboolean rb_validate_item_propert (const char *name); +-static uintmax_t rb_podcast_parse_date (const char* date_str); +-static gulong rb_podcast_parse_time (const char *time_str); +-static void rb_podcast_parser_start_element (struct RBPoadcastLoadContext* ctx, const char *name, const char **attrs); +-static void rb_podcast_parser_end_element (struct RBPoadcastLoadContext* ctx, const char *name); +-static void rb_podcast_parser_characters (struct RBPoadcastLoadContext* ctx, const char *data, guint len); +-static void rb_set_channel_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value); +-static void rb_set_item_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value); +- +-static RBPodcastItem * +-rb_podcast_initializa_item () +-{ +- RBPodcastItem *data = g_new0 (RBPodcastItem, 1); +- return data; +-} +- + static void +-rb_set_channel_value (struct RBPoadcastLoadContext *ctx, +- const char *name, +- const char *value) ++playlist_metadata_foreach (const char *key, ++ const char *value, ++ gpointer data) + { +- xmlChar *dvalue; ++ RBPodcastChannel *channel = (RBPodcastChannel *) data; + +- if (value == NULL) +- return; +- +- if (name == NULL) +- return; +- +- dvalue = xmlCharStrdup (value); +- g_strstrip ((char *)dvalue); +- +- if (!strcmp (name, "title")) { +- ctx->channel_data->title = dvalue; +- } else if (!strcmp (name, "language")) { +- ctx->channel_data->lang = dvalue; +- } else if (!strcmp (name, "itunes:subtitle")) { +- ctx->channel_data->subtitle = dvalue; +- } else if (!strcmp (name, "itunes:summary")) { +- ctx->channel_data->summary = dvalue; +- } else if (!strcmp (name, "description")) { +- ctx->channel_data->description = dvalue; +- } else if (!strcmp (name, "generator")) { +- if (ctx->channel_data->author == NULL) +- ctx->channel_data->author = dvalue; +- } else if (!strcmp (name, "itunes:author")) { +- g_free (ctx->channel_data->author); +- ctx->channel_data->author = dvalue; +- } else if (!strcmp (name, "webMaster")) { +- ctx->channel_data->contact = dvalue; +- } else if (!strcmp (name, "pubDate")) { +- ctx->channel_data->pub_date = rb_podcast_parse_date ((char *)dvalue); +- g_free (dvalue); +- } else if (!strcmp (name, "copyright")) { +- ctx->channel_data->copyright = dvalue; +- } else if (!strcmp (name, "img")) { +- ctx->channel_data->img = dvalue; +- } else { +- g_free (dvalue); ++ if (strcmp (key, TOTEM_PL_PARSER_FIELD_TITLE) == 0) { ++ channel->title = g_strdup (value); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_LANGUAGE) == 0) { ++ channel->lang = g_strdup (value); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_DESCRIPTION) == 0) { ++ channel->description = g_strdup (value); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_AUTHOR) == 0) { ++ channel->author = g_strdup (value); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_CONTACT) == 0) { ++ channel->contact = g_strdup (value); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_IMAGE_URL) == 0) { ++ channel->img = g_strdup (value); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_PUB_DATE) == 0) { ++ channel->pub_date = totem_pl_parser_parse_date (value, FALSE); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_COPYRIGHT) == 0) { ++ channel->copyright = g_strdup (value); + } + } + + static void +-rb_set_item_value (struct RBPoadcastLoadContext *ctx, +- const char *name, +- const char *value) ++playlist_started (TotemPlParser *parser, ++ const char *uri, ++ GHashTable *metadata, ++ gpointer data) + { +- xmlChar *dvalue; +- +- dvalue = xmlCharStrdup (value); +- g_strstrip ((char *)dvalue); +- +- if (!strcmp (name, "title")) { +- ctx->item_data->title = dvalue; +- } else if (!strcmp (name, "url")) { +- ctx->item_data->url = dvalue; +- } else if (!strcmp (name, "pubDate")) { +- ctx->item_data->pub_date = rb_podcast_parse_date ((char *)dvalue); +- g_free (dvalue); +- } else if (!strcmp (name, "description")) { +- ctx->item_data->description = dvalue; +- } else if (!strcmp (name, "author")) { +- ctx->item_data->author = dvalue; +- } else if (!strcmp (name, "itunes:duration")) { +- ctx->item_data->duration = rb_podcast_parse_time ((char *)dvalue); +- g_free (dvalue); +- } else if (!strcmp (name, "length")) { +- ctx->item_data->filesize = g_ascii_strtoull ((char *)dvalue, NULL, 10); +- } else { +- g_free (dvalue); +- } ++ g_hash_table_foreach (metadata, (GHFunc) playlist_metadata_foreach, data); + } + + static void +-rb_insert_item (struct RBPoadcastLoadContext *ctx) ++playlist_ended (TotemPlParser *parser, ++ const char *uri, ++ gpointer data) + { +- RBPodcastItem *data = ctx->item_data; ++ RBPodcastChannel *channel = (RBPodcastChannel *) data; + +- rb_debug ("Inserting item as post"); +- +- if (!data->url) { +- rb_debug ("Item does not have a URL, skipping"); +- return; +- } +- +- ctx->channel_data->posts = g_list_prepend (ctx->channel_data->posts, ctx->item_data); ++ channel->posts = g_list_reverse (channel->posts); + } + +-static gboolean +-rb_validate_channel_propert (const char *name) +-{ +- if (name == NULL) { +- return FALSE; +- } +- +- if (!strcmp (name, "title") || +- !strcmp (name, "language") || +- !strcmp (name, "itunes:subtitle") || +- !strcmp (name, "itunes:summary") || +- !strcmp (name, "description") || +- !strcmp (name, "generator") || +- !strcmp (name, "itunes:author") || +- !strcmp (name, "webMaster") || +- !strcmp (name, "lastBuildDate") || +- !strcmp (name, "pubDate") || +- !strcmp (name, "copyright")) { +- return TRUE; +- } else { +- return FALSE; +- } +- +-} +- +-static gboolean +-rb_validate_item_propert (const char *name) +-{ +- if (name == NULL) { +- return FALSE; +- } +- +- if (!strcmp (name, "title") || +- !strcmp (name, "url") || +- !strcmp (name, "pubDate") || +- !strcmp (name, "description") || +- !strcmp (name, "author") || +- !strcmp (name, "itunes:duration") ) { +- +- return TRUE; +- } else { +- return FALSE; +- } +-} +- + static void +-rb_podcast_parser_start_element (struct RBPoadcastLoadContext *ctx, +- const char *name, +- const char **attrs) ++entry_metadata_foreach (const char *key, ++ const char *value, ++ gpointer data) + { ++ RBPodcastItem *item = (RBPodcastItem *) data; + +- rb_debug ("Start element: %s state: %d", name, ctx->state); +- +- switch (ctx->state) { +- case RB_PODCAST_PARSER_STATE_START: +- { +- if (!strcmp (name, "rss")) { +- ctx->state = RB_PODCAST_PARSER_STATE_RSS; +- } else { +- ctx->in_unknown_elt++; +- } +- +- break; +- } +- +- case RB_PODCAST_PARSER_STATE_RSS: +- { +- if (!strcmp (name, "channel")) { +- ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL; +- } else { +- ctx->in_unknown_elt++; +- } +- +- break; +- } +- +- case RB_PODCAST_PARSER_STATE_CHANNEL: +- { +- if (strcmp (name, "image") == 0) +- { +- ctx->state = RB_PODCAST_PARSER_STATE_IMG; +- } else if (strcmp (name, "itunes:image") == 0) { +- for (; attrs && *attrs; attrs +=2) { +- if (!strcmp (*attrs, "href")) { +- const char *href_value = *(attrs + 1); +- rb_set_channel_value (ctx, "img", href_value); +- } +- } +- +- ctx->state = RB_PODCAST_PARSER_STATE_IMG; +- +- } else if (!strcmp (name, "item")) { +- ctx->item_data = rb_podcast_initializa_item (); +- ctx->state = RB_PODCAST_PARSER_STATE_ITEM; +- } else if (!rb_validate_channel_propert (name)) { +- rb_debug ("Unknown property"); +- ctx->in_unknown_elt++; +- } else { +- ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY; +- } +- +- break; +- } +- +- case RB_PODCAST_PARSER_STATE_ITEM: +- { +- if (!strcmp (name, "enclosure")) { +- for (; *attrs; attrs +=2) { +- if (!strcmp (*attrs, "url")) { +- const char *url_value = *(attrs + 1); +- rb_set_item_value (ctx, "url", url_value); +- } else if (!strcmp (*attrs, "length")) { +- const char *length_value = *(attrs + 1); +- rb_set_item_value (ctx, "length", length_value); +- } +- } +- +- ctx->state = RB_PODCAST_PARSER_STATE_ITEM_PROPERTY; +- +- } else if (!rb_validate_item_propert (name)) { +- ctx->in_unknown_elt++; +- } else { +- ctx->state = RB_PODCAST_PARSER_STATE_ITEM_PROPERTY; +- } +- +- break; +- } +- +- case RB_PODCAST_PARSER_STATE_IMG: +- { +- if (strcmp (name, "url") != 0) { +- ctx->in_unknown_elt++; +- } else { +- ctx->state = RB_PODCAST_PARSER_STATE_IMG_PROPERTY; +- } +- +- break; +- } +- +- case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY: +- case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY: +- case RB_PODCAST_PARSER_STATE_IMG_PROPERTY: +- rb_debug ("nested element inside property; treating as unknown"); +- ctx->in_unknown_elt++; +- break; +- +- case RB_PODCAST_PARSER_STATE_END: +- break; +- default: +- g_warning ("Unknown podcast parser state: %d", ctx->state); +- break; ++ if (strcmp (key, TOTEM_PL_PARSER_FIELD_TITLE) == 0) { ++ item->title = g_strdup (value); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_URL) == 0) { ++ item->url = g_strdup (value); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_DESCRIPTION) == 0) { ++ item->description = g_strdup (value); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_AUTHOR) == 0) { ++ item->author = g_strdup (value); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_PUB_DATE) == 0) { ++ item->pub_date = totem_pl_parser_parse_date (value, FALSE); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_DURATION) == 0) { ++ item->duration = totem_pl_parser_parse_duration (value, FALSE); ++ } else if (strcmp (key, TOTEM_PL_PARSER_FIELD_FILESIZE) == 0) { ++ item->filesize = g_ascii_strtoull (value, NULL, 10); + } + } + + static void +-rb_podcast_parser_end_element (struct RBPoadcastLoadContext *ctx, +- const char *name) ++entry_parsed (TotemPlParser *parser, ++ const char *uri, ++ GHashTable *metadata, ++ gpointer data) + { +- rb_debug ("End element: %s state: %d", name, ctx->state); ++ RBPodcastChannel *channel = (RBPodcastChannel *) data; ++ RBPodcastItem *item; + +- if (ctx->in_unknown_elt > 0) { +- ctx->in_unknown_elt--; +- rb_debug ("Unknown element"); +- return; +- } +- +- switch (ctx->state) { +- case RB_PODCAST_PARSER_STATE_START: +- ctx->state = RB_PODCAST_PARSER_STATE_END; +- break; +- +- case RB_PODCAST_PARSER_STATE_RSS: +- ctx->state = RB_PODCAST_PARSER_STATE_START; +- break; +- +- case RB_PODCAST_PARSER_STATE_CHANNEL: +- ctx->state = RB_PODCAST_PARSER_STATE_RSS; +- break; +- +- case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY: +- { +- rb_set_channel_value (ctx, name, ctx->prop_value->str); +- ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL; +- g_string_truncate (ctx->prop_value, 0); +- break; +- } +- +- case RB_PODCAST_PARSER_STATE_ITEM: +- { +- rb_insert_item (ctx); +- ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL; +- break; +- } +- +- case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY: +- { +- rb_set_item_value (ctx, name, ctx->prop_value->str); +- ctx->state = RB_PODCAST_PARSER_STATE_ITEM; +- g_string_truncate (ctx->prop_value, 0); +- break; +- } +- +- case RB_PODCAST_PARSER_STATE_IMG_PROPERTY: +- { +- rb_set_channel_value (ctx, "img", ctx->prop_value->str); +- ctx->state = RB_PODCAST_PARSER_STATE_IMG; +- g_string_truncate (ctx->prop_value, 0); +- break; +- } +- +- case RB_PODCAST_PARSER_STATE_IMG: +- ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL; +- break; +- +- case RB_PODCAST_PARSER_STATE_END: +- break; +- +- default: +- g_warning ("Unknown podcast parser state: %d", ctx->state); +- break; +- } ++ item = g_new0 (RBPodcastItem, 1); ++ g_hash_table_foreach (metadata, (GHFunc) entry_metadata_foreach, item); ++ channel->posts = g_list_prepend (channel->posts, item); + } + +-static void +-rb_podcast_parser_characters (struct RBPoadcastLoadContext *ctx, +- const char *data, +- guint len) +-{ +- switch (ctx->state) { +- case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY: +- case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY: +- case RB_PODCAST_PARSER_STATE_IMG_PROPERTY: +- g_string_append_len (ctx->prop_value, data, len); +- break; +- case RB_PODCAST_PARSER_STATE_START: +- case RB_PODCAST_PARSER_STATE_IMG: +- case RB_PODCAST_PARSER_STATE_RSS: +- case RB_PODCAST_PARSER_STATE_CHANNEL: +- case RB_PODCAST_PARSER_STATE_ITEM: +- case RB_PODCAST_PARSER_STATE_END: +- break; +- default: +- g_warning ("Unknown podcast parser state: %d", ctx->state); +- break; +- } +-} +- + gboolean + rb_podcast_parse_load_feed (RBPodcastChannel *data, + const char *file_name) + { +- xmlParserCtxtPtr parser; +- xmlSAXHandlerPtr sax_handler = NULL; + GnomeVFSResult result; + GnomeVFSFileInfo *info; +- gint file_size; +- gchar *buffer = NULL; +- const char *query_string; ++ TotemPlParser *plparser; + +- struct RBPoadcastLoadContext *ctx = NULL; ++ data->url = g_strdup (file_name); + +- data->url = xmlCharStrdup (file_name); +- +- /* if the URL has a .rss or .xml extension (before the query string), ++ /* if the URL has a .rss, .xml or .atom extension (before the query string), + * don't bother checking the MIME type. + */ +- query_string = strchr (file_name, '?'); +- if (query_string == NULL) { +- query_string = file_name + strlen (file_name); +- } +- +- if (strncmp (query_string - 4, ".rss", 4) == 0 || +- strncmp (query_string - 4, ".xml", 4) == 0) { +- rb_debug ("not checking mime type for %s", file_name); ++ if (rb_uri_could_be_podcast (file_name, &data->is_opml)) { ++ rb_debug ("not checking mime type for %s (should be %s file)", file_name, ++ data->is_opml ? "OPML" : "Podcast"); + } else { + gboolean invalid_mime_type; + +@@ -451,22 +140,33 @@ + + result = gnome_vfs_get_file_info (file_name, info, GNOME_VFS_FILE_INFO_DEFAULT); + ++ if ((result != GNOME_VFS_OK)) { ++ if (info->mime_type != NULL) { ++ rb_debug ("Invalid mime-type in podcast feed %s", info->mime_type); ++ } else { ++ rb_debug ("Couldn't get mime type for %s: %s", file_name, ++ gnome_vfs_result_to_string (result)); ++ } ++ gnome_vfs_file_info_unref (info); ++ return TRUE; ++ } ++ + if (info != NULL + && info->mime_type != NULL + && strstr (info->mime_type, "html") == NULL + && strstr (info->mime_type, "xml") == NULL +- && strstr (info->mime_type, "rss") == NULL) { ++ && strstr (info->mime_type, "rss") == NULL ++ && strstr (info->mime_type, "opml") == NULL) { + invalid_mime_type = TRUE; ++ } else if (info != NULL ++ && info->mime_type != NULL ++ && strstr (info->mime_type, "opml") != NULL) { ++ data->is_opml = TRUE; ++ invalid_mime_type = FALSE; + } else { + invalid_mime_type = FALSE; + } + +- if ((result != GNOME_VFS_OK)) { +- rb_debug ("Invalid mime-type in podcast feed %s", info->mime_type); +- gnome_vfs_file_info_unref (info); +- return TRUE; +- } +- + if (invalid_mime_type) { + GtkWidget *dialog; + +@@ -492,173 +192,22 @@ + return FALSE; + } + +- /* first download file by gnome_vfs for use gnome network configuration */ +- rb_debug ("reading podcast feed %s", file_name); +- result = gnome_vfs_read_entire_file (file_name, &file_size, &buffer); +- if (result != GNOME_VFS_OK) +- return TRUE; ++ plparser = totem_pl_parser_new (); ++ g_object_set (plparser, "recurse", FALSE, NULL); ++ g_signal_connect (G_OBJECT (plparser), "entry-parsed", G_CALLBACK (entry_parsed), data); ++ g_signal_connect (G_OBJECT (plparser), "playlist-started", G_CALLBACK (playlist_started), data); ++ g_signal_connect (G_OBJECT (plparser), "playlist-ended", G_CALLBACK (playlist_ended), data); + +- /* initializing parse */ +- sax_handler = g_new0 (xmlSAXHandler, 1); +- sax_handler->startElement = (startElementSAXFunc) rb_podcast_parser_start_element; +- sax_handler->endElement = (endElementSAXFunc) rb_podcast_parser_end_element; +- sax_handler->characters = (charactersSAXFunc) rb_podcast_parser_characters; +- xmlSubstituteEntitiesDefault (1); +- +- ctx = g_new0 (struct RBPoadcastLoadContext, 1); +- ctx->in_unknown_elt = 0; +- ctx->channel_data = data; +- ctx->prop_value = g_string_sized_new (512); +- +- parser = xmlCreateMemoryParserCtxt (buffer, file_size); +- if (parser == NULL) { +- g_free (sax_handler); +- g_free (buffer); +- g_string_free (ctx->prop_value, TRUE); +- g_free (ctx); ++ if (totem_pl_parser_parse (plparser, file_name, FALSE) != TOTEM_PL_PARSER_RESULT_SUCCESS) { ++ rb_debug ("Parsing %s as a Podcast failed", file_name); ++ g_object_unref (plparser); + return FALSE; + } ++ rb_debug ("Parsing %s as a Podcast succeeded", file_name); + +- ctx->xmlctx = parser; +- parser->userData = ctx; +- parser->sax = sax_handler; +- xmlParseDocument (parser); +- +- g_free (sax_handler); +- parser->sax = NULL; +- xmlFreeParserCtxt (parser); +- +- g_free (buffer); +- g_string_free (ctx->prop_value, TRUE); +- g_free (ctx); +- +- data->posts = g_list_reverse (data->posts); + return TRUE; + } + +-static uintmax_t +-rb_podcast_parse_date (const char *date_str) +-{ +- struct tm tm; +- char *result; +- +- /* RFC 2822 date format */ +- result = strptime (date_str, "%a, %d %b %Y %T", &tm); +- +- /* same as above, but without comma */ +- if (result == NULL) { +- memset (&tm, 0, sizeof (struct tm)); +- result = strptime (date_str, "%a %d %b %Y %T", &tm); +- } +- +- /* close-to-RFC 2822, but with extra 0 */ +- if (result == NULL) { +- memset (&tm, 0, sizeof (struct tm)); +- result = strptime (date_str, "%a, %d %b %Y 0%T", &tm); +- } +- +- /* close-to-RFC 2822, but with no seconds */ +- if (result == NULL) { +- memset (&tm, 0, sizeof (struct tm)); +- result = strptime (date_str, "%a, %d %b %Y %R", &tm); +- } +- +- /* format without weekday */ +- if (result == NULL) { +- memset (&tm, 0, sizeof (struct tm)); +- result = strptime (date_str, "%d %b %Y %T", &tm); +- } +- +- /* reversed day and long month */ +- if (result == NULL) { +- memset (&tm, 0, sizeof (struct tm)); +- result = strptime (date_str, "%a, %B %d %Y %T", &tm); +- } +- +- /* ISO date like */ +- if (result == NULL) { +- memset (&tm, 0, sizeof (struct tm)); +- result = strptime (date_str, "%Y-%m-%d %T", &tm); +- } +- +- /* ISO date like without timezone */ +- if (result == NULL) { +- memset (&tm, 0, sizeof (struct tm)); +- result = strptime (date_str, "%Y-%m-%d", &tm); +- } +- +- /* Broken weekday short names */ +- if (result == NULL) { +- char *tmp; +- +- /* strip off the erroneous weekday */ +- tmp = strstr (date_str, ","); +- if (tmp != NULL) { +- tmp++; +- memset (&tm, 0, sizeof (struct tm)); +- result = strptime (tmp, "%d %b %Y %T", &tm); +- } +- } +- +- /* format with timezone offset from GMT */ +- if (result == NULL) { +- memset (&tm, 0, sizeof (struct tm)); +- result = strptime (date_str, "%a %b %d %T %z %Y", &tm); +- } +- +- /* format with timezone name */ +- if (result == NULL) { +- char *tmp; +- +- memset (&tm, 0, sizeof (struct tm)); +- +- /* match first part of time string */ +- result = strptime (date_str, "%a %b %d %T ", &tm); +- +- /* look for anything with a timezone name-like format +- i.e. at least one all caps alphabetical character */ +- if (result != NULL) { +- size_t n; +- +- n = strspn(result, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); +- tmp = result+n; +- +- /* make sure there was at least one character that matched */ +- if ((tmp != NULL) && n > 0) +- /* remaining part must be the year */ +- result = strptime (tmp, "%Y", &tm); +- else +- result = NULL; +- } +- } +- +- if (result == NULL) { +- rb_debug ("unable to convert date string %s", date_str); +- } +- +- return (uintmax_t) ( (result==NULL) ? 0 : mktime (&tm) ); +-} +- +-static gulong +-rb_podcast_parse_time (const char *time_str) +-{ +- struct tm tm; +- char *result; +- +- memset (&tm, 0, sizeof (struct tm)); +- result = strptime (time_str, "%H:%M:%S", &tm); +- if (result == NULL) { +- memset (&tm, 0, sizeof (struct tm)); +- result = strptime (time_str, "%M:%S", &tm); +- } +- if (result == NULL) { +- memset (&tm, 0, sizeof (struct tm)); +- rb_debug ("unable to convert duration string %s", time_str); +- } +- +- return ((tm.tm_hour * 60 + tm.tm_min) * 60 + tm.tm_sec); +-} +- + void + rb_podcast_parse_channel_free (RBPodcastChannel *data) + { +@@ -671,8 +220,6 @@ + g_free (data->url); + g_free (data->title); + g_free (data->lang); +- g_free (data->subtitle); +- g_free (data->summary); + g_free (data->description); + g_free (data->author); + g_free (data->contact); +Index: podcast/Makefile.am +=================================================================== +--- podcast/Makefile.am (revision 5467) ++++ podcast/Makefile.am (working copy) +@@ -6,6 +6,9 @@ + rb-podcast-parse.c \ + rb-podcast-parse.h + ++librbpodcast_parse_la_LIBADD = \ ++ $(top_builddir)/lib/librb.la ++ + librbpodcast_la_SOURCES = \ + rb-feed-podcast-properties-dialog.c \ + rb-feed-podcast-properties-dialog.h \ +@@ -23,9 +26,10 @@ + test-podcast-parse.c + test_podcast_parse_LDADD = \ + librbpodcast_parse.la \ +- $(RHYTHMBOX_LIBS) ++ $(RHYTHMBOX_LIBS) \ ++ $(TOTEM_PLPARSER_LIBS) + +-INCLUDES = \ ++AM_CFLAGS = \ + -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ + -DG_LOG_DOMAIN=\"Rhythmbox\" \ + -I$(top_srcdir) \ +@@ -36,7 +40,25 @@ + -I$(top_srcdir)/metadata \ + -I$(top_srcdir)/library \ + -I$(top_builddir)/lib \ +- $(RHYTHMBOX_CFLAGS) ++ $(RHYTHMBOX_CFLAGS) \ ++ $(TOTEM_PLPARSER_CFLAGS) + + librbpodcast_la_LDFLAGS = -export-dynamic + ++if ENABLE_BROWSER_PLUGIN ++plugindir = ${libdir}/mozilla/plugins ++plugin_LTLIBRARIES = librhythmbox-itms-detection-plugin.la ++librhythmbox_itms_detection_plugin_la_SOURCES = rhythmbox-itms-plugin.cpp ++librhythmbox_itms_detection_plugin_la_CXXFLAGS = $(BROWSER_PLUGIN_CFLAGS) ++librhythmbox_itms_detection_plugin_la_LIBADD = $(BROWSER_PLUGIN_LIBS) ++ ++librhythmbox_itms_detection_plugin_la_LDFLAGS = \ ++ -avoid-version \ ++ -export-symbols $(srcdir)/plugin.symbols \ ++ -module \ ++ $(AM_LDFLAGS) ++ ++endif ++ ++EXTRA_DIST = rhythmbox-itms-plugin.cpp plugin.symbols ++ +Index: podcast/rhythmbox-itms-plugin.cpp +=================================================================== +--- podcast/rhythmbox-itms-plugin.cpp (revision 0) ++++ podcast/rhythmbox-itms-plugin.cpp (revision 0) +@@ -0,0 +1,215 @@ ++/* ++ * Copyright © 2007 Bastien Nocera ++ * Copyright © 2005 Jorn Baayen ++ * Copyright © 2005 Christian Persch ++ * ++ * Based on the work of: ++ * ++ * Copyright © 2004 Bastien Nocera ++ * Copyright © 2002 David A. Schleef ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library 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 ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++static NPNetscapeFuncs mozilla_functions; ++ ++static NPError ++plugin_new_instance (NPMIMEType mime_type, ++ NPP instance, ++ guint16 mode, ++ gint16 argc, ++ char **argn, ++ char **argv, ++ NPSavedData *saved) ++{ ++ return NPERR_INVALID_INSTANCE_ERROR; ++} ++ ++static NPError ++plugin_destroy_instance (NPP instance, ++ NPSavedData **save) ++{ ++ return NPERR_NO_ERROR; ++} ++ ++static NPError ++plugin_new_stream (NPP instance, ++ NPMIMEType type, ++ NPStream *stream_ptr, ++ NPBool seekable, ++ guint16 *stype) ++{ ++ return NPERR_INVALID_PARAM; ++} ++ ++static NPError ++plugin_stream_as_file (NPP instance, ++ NPStream* stream, ++ const char *filename) ++{ ++ return NPERR_INVALID_PARAM; ++} ++ ++static NPError ++plugin_destroy_stream (NPP instance, ++ NPStream *stream, ++ NPError reason) ++{ ++ return NPERR_NO_ERROR; ++} ++ ++static int32 ++plugin_write_ready (NPP instance, ++ NPStream *stream) ++{ ++ return 0; ++} ++ ++static int32 ++plugin_write (NPP instance, ++ NPStream *stream, ++ int32 offset, ++ int32 len, ++ gpointer buffer) ++{ ++ return -1; ++} ++ ++static NPError ++plugin_get_value (NPP instance, ++ NPPVariable variable, ++ gpointer value) ++{ ++ NPError err = NPERR_NO_ERROR; ++ ++ switch (variable) { ++ case NPPVpluginNameString: ++ *((char **) value) = "iTunes Application Detector"; ++ break; ++ ++ case NPPVpluginDescriptionString: ++ *((char **) value) = "This plug-in detects the presence of iTunes when opening iTunes Store URLs in a web page with Firefox."; ++ break; ++ ++ case NPPVpluginNeedsXEmbed: ++ *((NPBool *) value) = PR_FALSE; ++ break; ++ ++ default: ++ err = NPERR_INVALID_PARAM; ++ break; ++ } ++ ++ return err; ++} ++ ++NPError ++NP_GetValue (void *future, ++ NPPVariable variable, ++ gpointer value) ++{ ++ return plugin_get_value (NULL, variable, value); ++} ++ ++char * ++NP_GetMIMEDescription (void) ++{ ++ return "application/itunes-plugin::;"; ++} ++ ++NPError ++NP_Initialize (NPNetscapeFuncs *moz_funcs, ++ NPPluginFuncs *plugin_funcs) ++{ ++ if (moz_funcs == NULL || plugin_funcs == NULL) ++ return NPERR_INVALID_FUNCTABLE_ERROR; ++ ++ if ((moz_funcs->version >> 8) > NP_VERSION_MAJOR) ++ return NPERR_INCOMPATIBLE_VERSION_ERROR; ++ if (moz_funcs->size < sizeof (NPNetscapeFuncs)) ++ return NPERR_INVALID_FUNCTABLE_ERROR; ++ if (plugin_funcs->size < sizeof (NPPluginFuncs)) ++ return NPERR_INVALID_FUNCTABLE_ERROR; ++ ++ /* ++ * Copy all of the fields of the Mozilla function table into our ++ * copy so we can call back into Mozilla later. Note that we need ++ * to copy the fields one by one, rather than assigning the whole ++ * structure, because the Mozilla function table could actually be ++ * bigger than what we expect. ++ */ ++ mozilla_functions.size = moz_funcs->size; ++ mozilla_functions.version = moz_funcs->version; ++ mozilla_functions.geturl = moz_funcs->geturl; ++ mozilla_functions.posturl = moz_funcs->posturl; ++ mozilla_functions.requestread = moz_funcs->requestread; ++ mozilla_functions.newstream = moz_funcs->newstream; ++ mozilla_functions.write = moz_funcs->write; ++ mozilla_functions.destroystream = moz_funcs->destroystream; ++ mozilla_functions.status = moz_funcs->status; ++ mozilla_functions.uagent = moz_funcs->uagent; ++ mozilla_functions.memalloc = moz_funcs->memalloc; ++ mozilla_functions.memfree = moz_funcs->memfree; ++ mozilla_functions.memflush = moz_funcs->memflush; ++ mozilla_functions.reloadplugins = moz_funcs->reloadplugins; ++ mozilla_functions.getJavaEnv = moz_funcs->getJavaEnv; ++ mozilla_functions.getJavaPeer = moz_funcs->getJavaPeer; ++ mozilla_functions.geturlnotify = moz_funcs->geturlnotify; ++ mozilla_functions.posturlnotify = moz_funcs->posturlnotify; ++ mozilla_functions.getvalue = moz_funcs->getvalue; ++ mozilla_functions.setvalue = moz_funcs->setvalue; ++ mozilla_functions.invalidaterect = moz_funcs->invalidaterect; ++ mozilla_functions.invalidateregion = moz_funcs->invalidateregion; ++ mozilla_functions.forceredraw = moz_funcs->forceredraw; ++ mozilla_functions.geturl = moz_funcs->geturl; ++ ++ /* ++ * Set up a plugin function table that Mozilla will use to call ++ * into us. Mozilla needs to know about our version and size and ++ * have a UniversalProcPointer for every function we implement. ++ */ ++ ++ plugin_funcs->size = sizeof (NPPluginFuncs); ++ plugin_funcs->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; ++ plugin_funcs->newp = NewNPP_NewProc (plugin_new_instance); ++ plugin_funcs->destroy = NewNPP_DestroyProc (plugin_destroy_instance); ++ plugin_funcs->setwindow = NewNPP_SetWindowProc (NULL); ++ plugin_funcs->newstream = NewNPP_NewStreamProc (plugin_new_stream); ++ plugin_funcs->destroystream = NewNPP_DestroyStreamProc (plugin_destroy_stream); ++ plugin_funcs->asfile = NewNPP_StreamAsFileProc (plugin_stream_as_file); ++ plugin_funcs->writeready = NewNPP_WriteReadyProc (plugin_write_ready); ++ plugin_funcs->write = NewNPP_WriteProc (plugin_write); ++ plugin_funcs->print = NewNPP_PrintProc (NULL); ++ plugin_funcs->event = NewNPP_HandleEventProc (NULL); ++ plugin_funcs->urlnotify = NewNPP_URLNotifyProc (NULL); ++ plugin_funcs->javaClass = NULL; ++ plugin_funcs->getvalue = NewNPP_GetValueProc (plugin_get_value); ++ plugin_funcs->setvalue = NewNPP_SetValueProc (NULL); ++ ++ return NPERR_NO_ERROR; ++} ++ ++NPError ++NP_Shutdown (void) ++{ ++ return NPERR_NO_ERROR; ++} +Index: podcast/rb-podcast-parse.h +=================================================================== +--- podcast/rb-podcast-parse.h (revision 5467) ++++ podcast/rb-podcast-parse.h (working copy) +@@ -23,36 +23,34 @@ + #define RB_PODCAST_PARSE_H + + #include +-#include +-#include + + typedef struct + { +- xmlChar* title; +- xmlChar* url; +- xmlChar* description; +- xmlChar* author; +- uintmax_t pub_date; ++ char* title; ++ char* url; ++ char* description; ++ char* author; ++ guint64 pub_date; + gulong duration; + guint64 filesize; +-}RBPodcastItem; ++} RBPodcastItem; + + typedef struct + { +- xmlChar* url; +- xmlChar* title; +- xmlChar* lang; +- xmlChar* subtitle; +- xmlChar* summary; +- xmlChar* description; +- xmlChar* author; +- xmlChar* contact; +- xmlChar* img; +- uintmax_t pub_date; +- xmlChar* copyright; ++ char* url; ++ char* title; ++ char* lang; ++ char* description; ++ char* author; ++ char* contact; ++ char* img; ++ guint64 pub_date; ++ char* copyright; + ++ gboolean is_opml; ++ + GList *posts; +-}RBPodcastChannel; ++} RBPodcastChannel; + + gboolean rb_podcast_parse_load_feed (RBPodcastChannel *data, const char *file_name); + void rb_podcast_parse_channel_free (RBPodcastChannel *data); +Index: configure.ac +=================================================================== +--- configure.ac (revision 5467) ++++ configure.ac (working copy) +@@ -19,8 +19,9 @@ + + AC_ISC_POSIX + AC_PROG_CC ++AC_PROG_CXX + AC_STDC_HEADERS +-AM_PROG_LIBTOOL ++AM_PROG_LIBTOOL() + AC_C_BIGENDIAN + AC_CHECK_SIZEOF(long) + +@@ -35,7 +36,7 @@ + LIBGPOD_REQS=0.4 + MUSICBRAINZ_REQS=2.1.0 + NCB_MIN_REQS=2.9.0 +-TOTEM_PLPARSER_REQS=1.1.5 ++TOTEM_PLPARSER_REQS=2.21.0 + VALA_REQS=0.0.8 + + AC_MSG_CHECKING([for GNU extension fwrite_unlocked]) +@@ -967,7 +968,58 @@ + AM_PATH_CHECK([], have_check=yes, have_check=no) + AM_CONDITIONAL([HAVE_CHECK],[test "x$have_check" = "xyes"]) + ++dnl ================================================================ ++dnl Browser plugin ++dnl ================================================================ + ++AC_ARG_ENABLE([browser-plugin], ++ [AS_HELP_STRING([--enable-browser-plugin],[compile the iTunes detection browser plugin])], ++ [],[enable_browser_plugin=autodetect]) ++ ++if test "$enable_browser_plugin" != "no" ; then ++ AC_MSG_CHECKING([which gecko to use]) ++ ++ AC_ARG_WITH([gecko], ++ [AS_HELP_STRING([--with-gecko],[Which gecko engine to use (default: autodetect)])]) ++ ++ GECKOS="xulrunner firefox mozilla-firefox seamonkey mozilla" ++ gecko=$with_gecko ++ ++ if test -z "$with_gecko"; then ++ dnl Autodetect gecko ++ for g in $GECKOS; do ++ if $PKG_CONFIG --exists $g-plugin; then ++ gecko=$g ++ break; ++ fi ++ done ++ elif ! $PKG_CONFIG --exists $gecko-plugin; then ++ AC_MSG_ERROR([Gecko "$gecko" not found]) ++ fi ++ ++ if test -z "$gecko" -a "$enable_browser_plugin" = "autodetect"; then ++ dnl No gecko found, disable plugin ++ AC_MSG_WARN([No gecko found, disabling plugin]) ++ enable_browser_plugin=no ++ elif test -z "$gecko"; then ++ AC_MSG_ERROR([No gecko found]) ++ elif ! ( echo "$GECKOS" | egrep "(^| )$gecko(\$| )" > /dev/null); then ++ AC_MSG_ERROR([Unknown gecko "$gecko" specified]) ++ else ++ enable_browser_plugin=yes ++ fi ++ ++ AC_MSG_RESULT([$gecko]) ++fi ++ ++if test "$enable_browser_plugin" = "yes" ; then ++ PKG_CHECK_MODULES([BROWSER_PLUGIN], ++ [glib-2.0 $gecko-plugin], ++ [],[enable_browser_plugins=no]) ++fi ++ ++AM_CONDITIONAL([ENABLE_BROWSER_PLUGIN], test x$enable_browser_plugin = xyes) ++ + dnl ================================================================ + dnl end-game + dnl ================================================================ +@@ -1168,4 +1220,10 @@ + AC_MSG_NOTICE([ FM radio support disabled]) + fi + ++if test "x$enable_browser_plugin" != xno; then ++ AC_MSG_NOTICE([** iTunes detection browser plugin (for podcasts) enabled]) ++else ++ AC_MSG_NOTICE([ iTunes detection browser plugin (for podcasts) disabled]) ++fi ++ + AC_MSG_NOTICE([End options]) +Index: lib/rb-file-helpers.c +=================================================================== +--- lib/rb-file-helpers.c (revision 5467) ++++ lib/rb-file-helpers.c (working copy) +@@ -774,6 +774,65 @@ + return g_utf8_strrchr (text_uri, -1, GNOME_VFS_URI_PATH_CHR)[1] == '.'; + } + ++gboolean ++rb_uri_could_be_podcast (const char *uri, gboolean *is_opml) ++{ ++ const char *query_string; ++ ++ if (is_opml != NULL) ++ *is_opml = FALSE; ++ ++ /* Check the scheme is a possible one first */ ++ if (g_str_has_prefix (uri, "http") == FALSE && ++ g_str_has_prefix (uri, "itpc:") == FALSE && ++ g_str_has_prefix (uri, "itms:") == FALSE) { ++ rb_debug ("'%s' can't be a Podcast or OPML file, not the right scheme", uri); ++ return FALSE; ++ } ++ ++ /* Now, check whether the iTunes Music Store link ++ * is a podcast */ ++ if (g_str_has_prefix (uri, "itms:") != FALSE ++ && strstr (uri, "phobos.apple.com") != NULL ++ && strstr (uri, "viewPodcast") != NULL) ++ return TRUE; ++ ++ query_string = strchr (uri, '?'); ++ if (query_string == NULL) { ++ query_string = uri + strlen (uri); ++ } ++ ++ /* FIXME hacks */ ++ if (strstr (uri, "rss") != NULL || ++ strstr (uri, "atom") != NULL || ++ strstr (uri, "feed") != NULL) { ++ rb_debug ("'%s' should be Podcast file, HACK", uri); ++ return TRUE; ++ } else if (strstr (uri, "opml") != NULL) { ++ rb_debug ("'%s' should be an OPML file, HACK", uri); ++ if (is_opml != NULL) ++ *is_opml = TRUE; ++ return TRUE; ++ } ++ ++ if (strncmp (query_string - 4, ".rss", 4) == 0 || ++ strncmp (query_string - 4, ".xml", 4) == 0 || ++ strncmp (query_string - 5, ".atom", 5) == 0 || ++ strncmp (uri, "itpc", 4) == 0 || ++ (strstr (uri, "phobos.apple.com/") != NULL && strstr (uri, "viewPodcast") != NULL) || ++ strstr (uri, "itunes.com/podcast") != NULL) { ++ rb_debug ("'%s' should be Podcast file", uri); ++ return TRUE; ++ } else if (strncmp (query_string - 5, ".opml", 5) == 0) { ++ rb_debug ("'%s' should be an OPML file", uri); ++ if (is_opml != NULL) ++ *is_opml = TRUE; ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ + char * + rb_uri_make_hidden (const char *text_uri) + { +Index: lib/rb-file-helpers.h +=================================================================== +--- lib/rb-file-helpers.h (revision 5467) ++++ lib/rb-file-helpers.h (working copy) +@@ -44,6 +44,7 @@ + gboolean rb_uri_is_writable (const char *uri); + gboolean rb_uri_is_local (const char *uri); + gboolean rb_uri_is_hidden (const char *uri); ++gboolean rb_uri_could_be_podcast (const char *uri, gboolean *is_opml); + char * rb_uri_make_hidden (const char *uri); + char * rb_uri_get_dir_name (const char *uri); + char * rb_uri_get_short_path_name (const char *uri); +Index: shell/rb-shell.c +=================================================================== +--- shell/rb-shell.c (revision 5467) ++++ shell/rb-shell.c (working copy) +@@ -3208,20 +3208,11 @@ + gboolean source_is_entry; + } PlaylistParseData; + +-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) + static void + handle_playlist_entry_cb (TotemPlParser *playlist, + const char *uri, + GHashTable *metadata, + PlaylistParseData *data) +-#else +-static void +-handle_playlist_entry_cb (TotemPlParser *playlist, +- const char *uri, +- const char *title, +- const char *genre, +- PlaylistParseData *data) +-#endif /* TOTEM_PL_PARSER_CHECK_VERSION */ + { + RBSource *source; + +@@ -3270,6 +3261,14 @@ + entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri); + playlist_source = NULL; + ++ /* If the URI points to a Podcast, pass it on to ++ * the Podcast source */ ++ if (rb_uri_could_be_podcast (uri, NULL)) { ++ rb_podcast_source_add_feed (shell->priv->podcast_source, uri); ++ rb_shell_select_source (shell, RB_SOURCE (shell->priv->podcast_source)); ++ return TRUE; ++ } ++ + if (entry == NULL) { + TotemPlParser *parser; + TotemPlParserResult result; +@@ -3283,15 +3282,9 @@ + rb_debug ("adding uri %s, play %d", uri, play); + parser = totem_pl_parser_new (); + +-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) + g_signal_connect_data (G_OBJECT (parser), "entry-parsed", + G_CALLBACK (handle_playlist_entry_cb), + &data, NULL, 0); +-#else +- g_signal_connect_data (G_OBJECT (parser), "entry", +- G_CALLBACK (handle_playlist_entry_cb), +- &data, NULL, 0); +-#endif /* TOTEM_PL_PARSER_CHECK_VERSION */ + + totem_pl_parser_add_ignored_mimetype (parser, "x-directory/normal"); + if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse")) +Index: shell/rb-playlist-manager.c +=================================================================== +--- shell/rb-playlist-manager.c (revision 5467) ++++ shell/rb-playlist-manager.c (working copy) +@@ -502,20 +502,11 @@ + return quark; + } + +-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) + static void + handle_playlist_entry_cb (TotemPlParser *playlist, + const char *uri_maybe, + GHashTable *metadata, + RBPlaylistManager *mgr) +-#else +-static void +-handle_playlist_entry_cb (TotemPlParser *playlist, +- const char *uri_maybe, +- const char *title, +- const char *genre, +- RBPlaylistManager *mgr) +-#endif /* TOTEM_PL_PARSER_CHECK_VERSION */ + { + char *uri; + #if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) +@@ -550,19 +541,29 @@ + } + + static void +-playlist_load_start_cb (TotemPlParser *parser, const char *title, RBPlaylistManager *mgr) ++playlist_load_started_cb (TotemPlParser *parser, const char *uri, GHashTable *metadata, RBPlaylistManager *mgr) + { +- rb_debug ("loading new playlist %s", title); ++ const char *title; + ++ rb_debug ("loading new playlist %s", uri); ++ ++ title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE); ++ if (title == NULL) ++ title = _("Unnamed playlist"); ++ + mgr->priv->loading_playlist = + RB_STATIC_PLAYLIST_SOURCE (rb_playlist_manager_new_playlist (mgr, title, FALSE)); + } + + static void +-playlist_load_end_cb (TotemPlParser *parser, const char *title, RBPlaylistManager *mgr) ++playlist_load_ended_cb (TotemPlParser *parser, const char *uri, GHashTable *metadata, RBPlaylistManager *mgr) + { +- rb_debug ("finished loading playlist %s", title); ++ const char *title; + ++ rb_debug ("finished loading playlist %s", uri); ++ ++ title = g_hash_table_lookup (metadata, TOTEM_PL_PARSER_FIELD_TITLE); ++ + if (title) { + g_object_set (mgr->priv->loading_playlist, "name", title, NULL); + mgr->priv->loading_playlist = NULL; +@@ -591,22 +592,16 @@ + { + TotemPlParser *parser = totem_pl_parser_new (); + +-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) + g_signal_connect_object (parser, "entry-parsed", + G_CALLBACK (handle_playlist_entry_cb), + mgr, 0); +-#else +- g_signal_connect_object (parser, "entry", +- G_CALLBACK (handle_playlist_entry_cb), +- mgr, 0); +-#endif /* TOTEM_PL_PARSER_CHECK_VERSION */ + +- g_signal_connect_object (parser, "playlist-start", +- G_CALLBACK (playlist_load_start_cb), ++ g_signal_connect_object (parser, "playlist-started", ++ G_CALLBACK (playlist_load_started_cb), + mgr, 0); + +- g_signal_connect_object (parser, "playlist-end", +- G_CALLBACK (playlist_load_end_cb), ++ g_signal_connect_object (parser, "playlist-ended", ++ G_CALLBACK (playlist_load_ended_cb), + mgr, 0); + + if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse")) +Index: shell/rb-shell-player.c +=================================================================== +--- shell/rb-shell-player.c (revision 5467) ++++ shell/rb-shell-player.c (working copy) +@@ -120,10 +120,10 @@ + + static void rb_shell_player_entry_activated_cb (RBEntryView *view, + RhythmDBEntry *entry, +- RBShellPlayer *playa); ++ RBShellPlayer *player); + static void rb_shell_player_property_row_activated_cb (RBPropertyView *view, + const char *name, +- RBShellPlayer *playa); ++ RBShellPlayer *player); + static void rb_shell_player_sync_volume (RBShellPlayer *player, gboolean notify); + static void rb_shell_player_sync_replaygain (RBShellPlayer *player, + RhythmDBEntry *entry); +@@ -1299,20 +1299,11 @@ + PlaybackStartType play_type; + } OpenLocationThreadData; + +-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) + static void + playlist_entry_cb (TotemPlParser *playlist, + const char *uri, + GHashTable *metadata, + RBShellPlayer *player) +-#else +-static void +-playlist_entry_cb (TotemPlParser *playlist, +- const char *uri, +- const char *title, +- const char *genre, +- RBShellPlayer *player) +-#endif + { + rb_debug ("adding stream url %s", uri); + g_queue_push_tail (player->priv->playlist_urls, g_strdup (uri)); +@@ -1326,15 +1317,9 @@ + + playlist = totem_pl_parser_new (); + +-#if TOTEM_PL_PARSER_CHECK_VERSION(2,19,0) + g_signal_connect_data (G_OBJECT (playlist), "entry-parsed", + G_CALLBACK (playlist_entry_cb), + data->player, NULL, 0); +-#else +- g_signal_connect_data (G_OBJECT (playlist), "entry", +- G_CALLBACK (playlist_entry_cb), +- data->player, NULL, 0); +-#endif /* TOTEM_PL_PARSER_CHECK_VERSION */ + + totem_pl_parser_add_ignored_mimetype (playlist, "x-directory/normal"); + +@@ -1415,6 +1400,8 @@ + gboolean ret = TRUE; + + crossfade = rb_shell_player_get_crossfade (player, play_type); ++ ++ g_object_ref (entry); + ret = ret && rb_player_open (player->priv->mmplayer, location, entry, (GDestroyNotify) rhythmdb_entry_unref, error); + + ret = ret && rb_player_play (player->priv->mmplayer, crossfade, error); +@@ -2340,7 +2327,7 @@ + static void + rb_shell_player_entry_activated_cb (RBEntryView *view, + RhythmDBEntry *entry, +- RBShellPlayer *playa) ++ RBShellPlayer *player) + { + gboolean was_from_queue = FALSE; + RhythmDBEntry *prev_entry = NULL; +@@ -2359,44 +2346,44 @@ + + /* skip entries with no playback uri */ + playback_uri = rhythmdb_entry_get_playback_uri (entry); +- if (playback_uri == NULL) { ++ if (playback_uri == NULL) + return; +- } ++ + g_free (playback_uri); + + /* figure out where the previous entry came from */ +- if ((playa->priv->queue_source != NULL) && +- (playa->priv->current_playing_source == RB_SOURCE (playa->priv->queue_source))) { +- prev_entry = rb_shell_player_get_playing_entry (playa); ++ if ((player->priv->queue_source != NULL) && ++ (player->priv->current_playing_source == RB_SOURCE (player->priv->queue_source))) { ++ prev_entry = rb_shell_player_get_playing_entry (player); + was_from_queue = TRUE; + } + +- if (playa->priv->queue_source) { ++ if (player->priv->queue_source) { + RBEntryView *queue_sidebar; + +- g_object_get (playa->priv->queue_source, "sidebar", &queue_sidebar, NULL); ++ g_object_get (player->priv->queue_source, "sidebar", &queue_sidebar, NULL); + +- if (view == queue_sidebar || view == rb_source_get_entry_view (RB_SOURCE (playa->priv->queue_source))) { ++ if (view == queue_sidebar || view == rb_source_get_entry_view (RB_SOURCE (player->priv->queue_source))) { + + /* fall back to the current selected source once the queue is empty */ +- if (view == queue_sidebar && playa->priv->source == NULL) { +- rb_play_order_playing_source_changed (playa->priv->play_order, +- playa->priv->selected_source); +- playa->priv->source = playa->priv->selected_source; ++ if (view == queue_sidebar && player->priv->source == NULL) { ++ rb_play_order_playing_source_changed (player->priv->play_order, ++ player->priv->selected_source); ++ player->priv->source = player->priv->selected_source; + } + + /* queue entry activated: move it to the start of the queue */ +- rb_static_playlist_source_move_entry (RB_STATIC_PLAYLIST_SOURCE (playa->priv->queue_source), entry, 0); +- rb_shell_player_set_playing_source (playa, RB_SOURCE (playa->priv->queue_source)); ++ rb_static_playlist_source_move_entry (RB_STATIC_PLAYLIST_SOURCE (player->priv->queue_source), entry, 0); ++ rb_shell_player_set_playing_source (player, RB_SOURCE (player->priv->queue_source)); + + was_from_queue = FALSE; + source_set = TRUE; + jump_to_entry = TRUE; + } else { +- if (playa->priv->queue_only) { +- rb_source_add_to_queue (playa->priv->selected_source, +- RB_SOURCE (playa->priv->queue_source)); +- rb_shell_player_set_playing_source (playa, RB_SOURCE (playa->priv->queue_source)); ++ if (player->priv->queue_only) { ++ rb_source_add_to_queue (player->priv->selected_source, ++ RB_SOURCE (player->priv->queue_source)); ++ rb_shell_player_set_playing_source (player, RB_SOURCE (player->priv->queue_source)); + source_set = TRUE; + } + } +@@ -2405,18 +2392,18 @@ + } + + /* bail out if queue only */ +- if (playa->priv->queue_only) { ++ if (player->priv->queue_only) { + return; + } + + if (!source_set) { +- rb_shell_player_set_playing_source (playa, playa->priv->selected_source); ++ rb_shell_player_set_playing_source (player, player->priv->selected_source); + source_set = TRUE; + } + +- playa->priv->jump_to_playing_entry = jump_to_entry; +- if (!rb_shell_player_set_playing_entry (playa, entry, TRUE, FALSE, &error)) { +- rb_shell_player_error (playa, FALSE, error); ++ player->priv->jump_to_playing_entry = jump_to_entry; ++ if (!rb_shell_player_set_playing_entry (player, entry, TRUE, FALSE, &error)) { ++ rb_shell_player_error (player, FALSE, error); + g_clear_error (&error); + } + +@@ -2424,7 +2411,7 @@ + * so we'll start again from the start. + */ + if (was_from_queue && prev_entry != NULL) { +- rb_play_order_set_playing_entry (playa->priv->queue_play_order, NULL); ++ rb_play_order_set_playing_entry (player->priv->queue_play_order, NULL); + } + + if (prev_entry != NULL) { +Index: data/rhythmbox.schemas +=================================================================== +--- data/rhythmbox.schemas (revision 5467) ++++ data/rhythmbox.schemas (working copy) +@@ -136,9 +136,9 @@ + Main window X position. + + +- + +- ++ ++ + /schemas/apps/rhythmbox/state/window_position_y + /apps/rhythmbox/state/window_position_y + rhythmbox +@@ -150,10 +150,7 @@ + + + +- +- +- +- ++ + /schemas/apps/rhythmbox/state/window_height + /apps/rhythmbox/state/window_height + rhythmbox +@@ -439,6 +436,106 @@ + + + ++ /schemas/desktop/gnome/url-handlers/itpc/command ++ /desktop/gnome/url-handlers/itpc/command ++ rhythmbox ++ string ++ rhythmbox "%s" ++ ++ The command to handle ITPC scheme URLs ++ The command to handle ITPC scheme URLs. ++ ++ ++ ++ /schemas/desktop/gnome/url-handlers/itpc/needs_terminal ++ /desktop/gnome/url-handlers/itpc/needs_terminal ++ rhythmbox ++ bool ++ false ++ ++ Whether command to handle ITPC scheme URLs needs a terminal ++ Whether command to handle ITPC scheme URLs needs a terminal. ++ ++ ++ ++ /schemas/desktop/gnome/url-handlers/itpc/enabled ++ /desktop/gnome/url-handlers/itpc/enabled ++ rhythmbox ++ bool ++ true ++ ++ Whether command to handle ITPC scheme URLs is enabled ++ Whether command to handle ITPC scheme URLs is enabled. ++ ++ ++ ++ /schemas/desktop/gnome/url-handlers/itms/command ++ /desktop/gnome/url-handlers/itms/command ++ rhythmbox ++ string ++ rhythmbox "%s" ++ ++ The command to handle ITMS scheme URLs ++ The command to handle ITMS scheme URLs. ++ ++ ++ ++ /schemas/desktop/gnome/url-handlers/itms/needs_terminal ++ /desktop/gnome/url-handlers/itms/needs_terminal ++ rhythmbox ++ bool ++ false ++ ++ Whether command to handle ITMS scheme URLs needs a terminal ++ Whether command to handle ITMS scheme URLs needs a terminal. ++ ++ ++ ++ /schemas/desktop/gnome/url-handlers/itms/enabled ++ /desktop/gnome/url-handlers/itms/enabled ++ rhythmbox ++ bool ++ true ++ ++ Whether command to handle ITMS scheme URLs is enabled ++ Whether command to handle ITMS scheme URLs is enabled. ++ ++ ++ ++ /schemas/desktop/gnome/url-handlers/feed/command ++ /desktop/gnome/url-handlers/feed/command ++ rhythmbox ++ string ++ rhythmbox "%s" ++ ++ The command to handle FEED scheme URLs ++ The command to handle FEED scheme URLs. ++ ++ ++ ++ /schemas/desktop/gnome/url-handlers/feed/needs_terminal ++ /desktop/gnome/url-handlers/feed/needs_terminal ++ rhythmbox ++ bool ++ false ++ ++ Whether command to handle FEED scheme URLs needs a terminal ++ Whether command to handle FEED scheme URLs needs a terminal. ++ ++ ++ ++ /schemas/desktop/gnome/url-handlers/feed/enabled ++ /desktop/gnome/url-handlers/feed/enabled ++ rhythmbox ++ bool ++ true ++ ++ Whether command to handle FEED scheme URLs is enabled ++ Whether command to handle FEED scheme URLs is enabled. ++ ++ ++ ++ + /schemas/apps/rhythmbox/state/podcast/show_browser + /apps/rhythmbox/state/podcast/show_browser + rhythmbox diff --git a/rhythmbox.spec b/rhythmbox.spec index 96ecced..e4a6ac2 100644 --- a/rhythmbox.spec +++ b/rhythmbox.spec @@ -3,7 +3,7 @@ Name: rhythmbox Summary: Music Management Application Version: 0.11.3 -Release: 7%{?dist} +Release: 8%{?dist} License: GPLv2+ and GFDL+ Group: Applications/Multimedia URL: http://www.gnome.org/projects/rhythmbox/ @@ -45,6 +45,7 @@ BuildRequires: lirc-devel BuildRequires: libmtp-devel BuildRequires: gstreamer-python BuildRequires: perl(XML::Parser) +BuildRequires: xulrunner-devel # For the playlist parser patch BuildRequires: intltool autoconf automake libtool gettext check-devel @@ -53,7 +54,7 @@ ExcludeArch: s390 s390x # http://bugzilla.gnome.org/show_bug.cgi?id=346434 Patch1: rb-delete-ipod-tracks.patch # http://bugzilla.gnome.org/show_bug.cgi?id=484768 -Patch2: rb-use-newer-plparser-7.patch +Patch2: rb-use-newer-plparser-9.patch # http://bugzilla.gnome.org/show_bug.cgi?id=338308 Patch3: rhythmbox-0.11.3-add-missing-plugins-support.patch # http://bugzilla.gnome.org/show_bug.cgi?id=497430 @@ -209,6 +210,10 @@ fi %{_libdir}/rhythmbox/plugins/upnp_coherence %changelog +* Fri Nov 30 2007 - Bastien Nocera - 0.11.3-8 +- Update patch for the Podcast parsing to include the browser plugin + for the iTunes detection + * Fri Nov 30 2007 - Bastien Nocera - 0.11.3-7 - Add patch to avoid crashing if no Python plugins are enabled by default (#393531)