[rhythmbox] podcast: rearrange podcast parsing code



commit 614353cd92bdd5d5ed3a7735a5161d2c36793f56
Author: Jonathan Matthew <jonathan d14n org>
Date:   Fri Sep 4 23:34:31 2020 +1000

    podcast: rearrange podcast parsing code
    
    Switch to totem_pl_parser_parse_async so we don't have to manage
    threads, and move mime type checking and other error handling out
    to the podcast manager code to simplify the parser interface.

 podcast/rb-podcast-add-dialog.c  |  40 ++--
 podcast/rb-podcast-main-source.c |   2 +-
 podcast/rb-podcast-manager.c     | 384 ++++++++++++++++++---------------------
 podcast/rb-podcast-manager.h     |   7 -
 podcast/rb-podcast-parse.c       | 135 ++++++--------
 podcast/rb-podcast-parse.h       |  12 +-
 podcast/test-podcast-parse.c     |  56 +++---
 7 files changed, 296 insertions(+), 340 deletions(-)
---
diff --git a/podcast/rb-podcast-add-dialog.c b/podcast/rb-podcast-add-dialog.c
index e7befa45f..794a2744f 100644
--- a/podcast/rb-podcast-add-dialog.c
+++ b/podcast/rb-podcast-add-dialog.c
@@ -286,11 +286,22 @@ typedef struct {
        gboolean single;
        GError *error;
        int reset_count;
-} ParseThreadData;
+} ParseData;
 
-static gboolean
-parse_finished (ParseThreadData *data)
+static void
+parse_cb (RBPodcastChannel *channel, GError *error, gpointer user_data)
 {
+       ParseData *data = user_data;
+
+       if (error != NULL) {
+               /* fake up a channel with just the url as the title, allowing the user
+                * to subscribe to the podcast anyway.
+                */
+               data->channel->url = g_strdup (data->url);
+               data->channel->title = g_strdup (data->url);
+               g_error_free (error);
+       }
+
        if (data->reset_count != data->dialog->priv->reset_count) {
                rb_debug ("dialog reset while parsing");
                rb_podcast_parse_channel_free (data->channel);
@@ -298,7 +309,6 @@ parse_finished (ParseThreadData *data)
                g_clear_error (&data->error);
                g_free (data->url);
                g_free (data);
-               return FALSE;
        }
 
        if (data->error != NULL) {
@@ -374,30 +384,14 @@ parse_finished (ParseThreadData *data)
        g_clear_error (&data->error);
        g_free (data->url);
        g_free (data);
-       return FALSE;
-}
-
-static gpointer
-parse_thread (ParseThreadData *data)
-{
-       if (rb_podcast_parse_load_feed (data->channel, data->url, FALSE, &data->error) == FALSE) {
-               /* fake up a channel with just the url as the title, allowing the user
-                * to subscribe to the podcast anyway.
-                */
-               data->channel->url = g_strdup (data->url);
-               data->channel->title = g_strdup (data->url);
-       }
-
-       g_idle_add ((GSourceFunc) parse_finished, data);
-       return NULL;
 }
 
 static void
 parse_in_thread (RBPodcastAddDialog *dialog, const char *text, gboolean existing, gboolean single)
 {
-       ParseThreadData *data;
+       ParseData *data;
 
-       data = g_new0 (ParseThreadData, 1);
+       data = g_new0 (ParseData, 1);
        data->dialog = g_object_ref (dialog);
        data->url = g_strdup (text);
        data->channel = g_new0 (RBPodcastChannel, 1);
@@ -405,7 +399,7 @@ parse_in_thread (RBPodcastAddDialog *dialog, const char *text, gboolean existing
        data->single = single;
        data->reset_count = dialog->priv->reset_count;
 
-       g_thread_new ("podcast parser", (GThreadFunc) parse_thread, data);
+       rb_podcast_parse_load_feed (data->channel, data->url, NULL, parse_cb, data);
 }
 
 static void
diff --git a/podcast/rb-podcast-main-source.c b/podcast/rb-podcast-main-source.c
index d59d85f74..45a54558c 100644
--- a/podcast/rb-podcast-main-source.c
+++ b/podcast/rb-podcast-main-source.c
@@ -380,7 +380,7 @@ impl_constructed (GObject *object)
                                source, 0);
 
        g_signal_connect_object (podcast_mgr,
-                                "process_error",
+                                "feed-parse-error",
                                 G_CALLBACK (feed_error_cb),
                                 source, 0);
 
diff --git a/podcast/rb-podcast-manager.c b/podcast/rb-podcast-manager.c
index 96485c35f..a0768923e 100644
--- a/podcast/rb-podcast-manager.c
+++ b/podcast/rb-podcast-manager.c
@@ -68,20 +68,11 @@ enum
 {
        START_DOWNLOAD,
        FINISH_DOWNLOAD,
-       PROCESS_ERROR,
+       FEED_PARSE_ERROR,
        FEED_UPDATES_AVAILABLE,
        LAST_SIGNAL
 };
 
-/* passed from feed parsing threads back to main thread */
-typedef struct
-{
-       GError                  *error;
-       RBPodcastChannel        *channel;
-       RBPodcastManager        *pd;
-       gboolean                 automatic;
-} RBPodcastManagerParseResult;
-
 typedef struct
 {
        RBPodcastManager *pd;
@@ -106,7 +97,8 @@ typedef struct
        RBPodcastManager *pd;
        char *url;
        gboolean automatic;
-       gboolean existing_feed;
+       RBPodcastChannel *channel;
+       GError *error;
 } RBPodcastUpdate;
 
 struct RBPodcastManagerPrivate
@@ -116,8 +108,8 @@ struct RBPodcastManagerPrivate
        RBPodcastDownload *active_download;
        guint source_sync;
        int updating;
-       gboolean shutdown;
        RBExtDB *art_store;
+       GCancellable *update_cancel;
 
        GArray *searches;
        GSettings *settings;
@@ -151,12 +143,7 @@ static void rb_podcast_manager_save_metadata               (RBPodcastManager *pd,
 static void rb_podcast_manager_db_entry_added_cb       (RBPodcastManager *pd,
                                                         RhythmDBEntry *entry);
 static gboolean rb_podcast_manager_next_file           (RBPodcastManager * pd);
-static void rb_podcast_manager_handle_feed_error       (RBPodcastManager *mgr,
-                                                        const char *url,
-                                                        GError *error,
-                                                        gboolean emit);
 
-static gpointer rb_podcast_manager_thread_parse_feed   (RBPodcastUpdate *info);
 static void podcast_settings_changed_cb                        (GSettings *settings,
                                                         const char *key,
                                                         RBPodcastManager *mgr);
@@ -203,7 +190,7 @@ rb_podcast_manager_class_init (RBPodcastManagerClass *klass)
               g_signal_new ("start_download",
                                G_OBJECT_CLASS_TYPE (object_class),
                                G_SIGNAL_RUN_LAST,
-                               G_STRUCT_OFFSET (RBPodcastManagerClass, start_download),
+                               0,
                                NULL, NULL,
                                NULL,
                                G_TYPE_NONE,
@@ -214,7 +201,7 @@ rb_podcast_manager_class_init (RBPodcastManagerClass *klass)
               g_signal_new ("finish_download",
                                G_OBJECT_CLASS_TYPE (object_class),
                                G_SIGNAL_RUN_LAST,
-                               G_STRUCT_OFFSET (RBPodcastManagerClass, finish_download),
+                               0,
                                NULL, NULL,
                                NULL,
                                G_TYPE_NONE,
@@ -225,18 +212,18 @@ rb_podcast_manager_class_init (RBPodcastManagerClass *klass)
               g_signal_new ("feed_updates_available",
                                G_OBJECT_CLASS_TYPE (object_class),
                                G_SIGNAL_RUN_LAST,
-                               G_STRUCT_OFFSET (RBPodcastManagerClass, feed_updates_available),
+                               0,
                                NULL, NULL,
                                NULL,
                                G_TYPE_NONE,
                                1,
                                RHYTHMDB_TYPE_ENTRY);
 
-       rb_podcast_manager_signals[PROCESS_ERROR] =
-              g_signal_new ("process_error",
+       rb_podcast_manager_signals[FEED_PARSE_ERROR] =
+              g_signal_new ("feed-parse-error",
                                G_OBJECT_CLASS_TYPE (object_class),
                                G_SIGNAL_RUN_LAST,
-                               G_STRUCT_OFFSET (RBPodcastManagerClass, process_error),
+                               0,
                                NULL, NULL,
                                NULL,
                                G_TYPE_NONE,
@@ -255,7 +242,6 @@ rb_podcast_manager_init (RBPodcastManager *pd)
 
        pd->priv->source_sync = 0;
        pd->priv->db = NULL;
-
 }
 
 static void
@@ -303,6 +289,8 @@ rb_podcast_manager_constructed (GObject *object)
                                                                PACKAGE "/" VERSION,
                                                                NULL);
 
+       pd->priv->update_cancel = g_cancellable_new ();
+
        rb_podcast_manager_start_update_timer (pd);
 }
 
@@ -624,8 +612,8 @@ void
 rb_podcast_manager_update_feeds (RBPodcastManager *pd)
 {
        RhythmDBQueryResultList *list;
-        RhythmDBEntry *entry;
-        const char *uri;
+       RhythmDBEntry *entry;
+       const char *uri;
        guint status;
        GList *l;
 
@@ -672,181 +660,210 @@ rb_podcast_manager_update_feeds_cb (gpointer data)
        return FALSE;
 }
 
+static void
+podcast_update_free (RBPodcastUpdate *update)
+{
+       RBPodcastManager *pd = update->pd;
+
+       if (--pd->priv->updating == 0) {
+               g_object_notify (G_OBJECT (pd), "updating");
+       }
+       g_object_unref (pd);
 
-gboolean
-rb_podcast_manager_subscribe_feed (RBPodcastManager *pd, const char *url, gboolean automatic)
+       g_clear_error (&update->error);
+       rb_podcast_parse_channel_free (update->channel);
+       g_free (update->url);
+       g_free (update);
+}
+
+static void
+feed_parse_cb (RBPodcastChannel *channel, GError *error, gpointer user_data)
 {
-       RBPodcastUpdate *info;
+       RBPodcastUpdate *update = user_data;
+       RBPodcastManager *pd = update->pd;
        RhythmDBEntry *entry;
-       GFile *feed;
-       char *feed_url;
-       gboolean existing_feed;
+       GValue v = {0,};
+       gboolean existing = FALSE;
 
-       if (g_str_has_prefix (url, "feed://") || g_str_has_prefix (url, "itpc://")) {
-               char *tmp;
+       if (error == NULL) {
+               if (channel->is_opml) {
+                       GList *l;
 
-               tmp = g_strdup_printf ("http://%s";, url + strlen ("feed://"));
-               feed = g_file_new_for_uri (tmp);
-               g_free (tmp);
-       } else {
-               feed = g_file_new_for_uri (url);
-       }
+                       rb_debug ("Loading OPML feeds from %s", channel->url);
 
-       /* hmm.  can we check if the GFile we got is something useful? */
-#if 0
-       if (valid_url == NULL) {
-               rb_error_dialog (NULL, _("Invalid URL"),
-                                _("The URL \"%s\" is not valid, please check it."), url);
-               return FALSE;
-       }
-#endif
+                       for (l = channel->posts; l != NULL; l = l->next) {
+                               RBPodcastItem *item = l->data;
+                               /* assume the feeds don't already exist */
+                               rb_podcast_manager_subscribe_feed (pd, item->url, FALSE);
+                       }
+               } else {
+                       rb_podcast_manager_add_parsed_feed (pd, channel);
+               }
+       } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+               rb_debug ("podcast update cancelled");
+               g_error_free (error);
+       } else {
+               /* set the error in the feed entry, if one exists */
+               entry = rhythmdb_entry_lookup_by_location (pd->priv->db, channel->url);
+               if (entry != NULL && rhythmdb_entry_get_entry_type (entry) == 
RHYTHMDB_ENTRY_TYPE_PODCAST_FEED) {
+                       g_value_init (&v, G_TYPE_STRING);
+                       g_value_set_string (&v, error->message);
+                       rhythmdb_entry_set (pd->priv->db, entry, RHYTHMDB_PROP_PLAYBACK_ERROR, &v);
+                       g_value_unset (&v);
+
+                       g_value_init (&v, G_TYPE_ULONG);
+                       g_value_set_ulong (&v, RHYTHMDB_PODCAST_FEED_STATUS_NORMAL);
+                       rhythmdb_entry_set (pd->priv->db, entry, RHYTHMDB_PROP_STATUS, &v);
+                       g_value_unset (&v);
 
-       feed_url = g_file_get_uri (feed);               /* not sure this buys us anything at all */
-       entry = rhythmdb_entry_lookup_by_location (pd->priv->db, feed_url);
-       if (entry) {
-               GValue v = {0,};
-               if (rhythmdb_entry_get_entry_type (entry) != RHYTHMDB_ENTRY_TYPE_PODCAST_FEED) {
-                       /* added as something else, probably iradio */
-                       rb_error_dialog (NULL, _("URL already added"),
-                                        _("The URL \"%s\" has already been added as a radio station. "
-                                        "If this is a podcast feed, please remove the radio station."), url);
-                       return FALSE;
+                       rhythmdb_commit (pd->priv->db);
+                       existing = TRUE;
                }
-               existing_feed = TRUE;
 
-               g_value_init (&v, G_TYPE_ULONG);
-               g_value_set_ulong (&v, RHYTHMDB_PODCAST_FEED_STATUS_UPDATING);
-               rhythmdb_entry_set (pd->priv->db, entry, RHYTHMDB_PROP_STATUS, &v);
-               rhythmdb_commit (pd->priv->db);
-               g_value_unset (&v);
-       } else {
-               existing_feed = FALSE;
+               /* if this was a result of a direct user action, emit the error signal too */
+               if (update->automatic) {
+                       gchar *error_msg;
+                       error_msg = g_strdup_printf (_("There was a problem adding this podcast: %s.  Please 
verify the URL: %s"),
+                                                    error->message, channel->url);
+                       g_signal_emit (pd,
+                                      rb_podcast_manager_signals[FEED_PARSE_ERROR],
+                                      0, channel->url, error_msg, existing);
+                       g_free (error_msg);
+               }
+               g_error_free (error);
        }
 
-       info = g_new0 (RBPodcastUpdate, 1);
-       info->pd = g_object_ref (pd);
-       info->url = feed_url;
-       info->automatic = automatic;
-       info->existing_feed = existing_feed;
+       podcast_update_free (update);
+}
+
+static void
+start_feed_parse (RBPodcastManager *pd, RBPodcastUpdate *update)
+{
        pd->priv->updating++;
        if (pd->priv->updating == 1) {
                g_object_notify (G_OBJECT (pd), "updating");
        }
 
-       g_thread_new ("podcast-parse",
-                     (GThreadFunc) rb_podcast_manager_thread_parse_feed,
-                     info);
-
-       return TRUE;
+       rb_podcast_parse_load_feed (update->channel, update->url, pd->priv->update_cancel, feed_parse_cb, 
update);
 }
 
 static void
-rb_podcast_manager_free_parse_result (RBPodcastManagerParseResult *result)
+confirm_bad_mime_type_response_cb (GtkDialog *dialog, int response, RBPodcastUpdate *update)
 {
-       rb_podcast_parse_channel_free (result->channel);
-       g_object_unref (result->pd);
-       g_clear_error (&result->error);
-       g_free (result);
+       if (response == GTK_RESPONSE_YES) {
+               start_feed_parse (update->pd, update);
+       } else {
+               podcast_update_free (update);
+       }
+
+       gtk_widget_destroy (GTK_WIDGET (dialog));
 }
 
-static gboolean
-rb_podcast_manager_parse_complete_cb (RBPodcastManagerParseResult *result)
+static void
+mime_type_check_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-       if (result->pd->priv->shutdown) {
-               return FALSE;
-       }
-
-       if (result->error) {
-               rb_podcast_manager_handle_feed_error (result->pd,
-                                                     (char *)result->channel->url,
-                                                     result->error,
-                                                     (result->automatic == FALSE));
-       } else if (result->channel->is_opml) {
-               GList *l;
+       RBPodcastUpdate *update = user_data;
+       GFileInfo *file_info;
+       GtkWidget *dialog;
+       char *content_type;
+       GError *error = NULL;
 
-               rb_debug ("Loading OPML feeds from %s", result->channel->url);
+       file_info = g_file_query_info_finish (G_FILE (source_object), res, &error);
+       if (file_info == NULL) {
+               g_object_unref (source_object);
+               feed_parse_cb (update->channel, error, update);
+               return;
+       }
 
-               for (l = result->channel->posts; l != NULL; l = l->next) {
-                       RBPodcastItem *item = l->data;
-                       /* assume the feeds don't already exist */
-                       rb_podcast_manager_subscribe_feed (result->pd, item->url, FALSE);
-               }
+       content_type = g_file_info_get_attribute_as_string (file_info, 
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+       if (content_type != NULL
+           && strstr (content_type, "html") == NULL
+           && strstr (content_type, "xml") == NULL
+           && strstr (content_type, "rss") == NULL
+           && strstr (content_type, "opml") == NULL) {
+               dialog = gtk_message_dialog_new (NULL, 0,
+                                                GTK_MESSAGE_QUESTION,
+                                                GTK_BUTTONS_YES_NO,
+                                                _("The URL '%s' does not appear to be a podcast feed. "
+                                                "It may be the wrong URL, or the feed may be broken. "
+                                                "Would you like Rhythmbox to attempt to use it anyway?"),
+                                                update->url);
+               gtk_widget_show_all (dialog);
+               g_signal_connect (dialog, "response", G_CALLBACK (confirm_bad_mime_type_response_cb), update);
+               g_clear_error (&error);
+       } else if (content_type != NULL && strstr (content_type, "opml") != NULL) {
+               update->channel->is_opml = TRUE;
+               start_feed_parse (update->pd, update);
        } else {
-               rb_podcast_manager_add_parsed_feed (result->pd, result->channel);
-       }
-       if (--result->pd->priv->updating == 0) {
-               g_object_notify (G_OBJECT (result->pd), "updating");
+               start_feed_parse (update->pd, update);
        }
 
-       return FALSE;
+       g_free (content_type);
+       g_object_unref (file_info);
+       g_object_unref (source_object);
 }
 
-static void
-confirm_bad_mime_type_response_cb (GtkDialog *dialog, int response, RBPodcastUpdate *info)
+gboolean
+rb_podcast_manager_subscribe_feed (RBPodcastManager *pd, const char *url, gboolean automatic)
 {
-       if (response == GTK_RESPONSE_YES) {
-               /* set the 'existing feed' flag to avoid the mime type check */
-               info->existing_feed = TRUE;
-               g_thread_new ("podcast-parse",
-                             (GThreadFunc) rb_podcast_manager_thread_parse_feed,
-                             info);
+       RBPodcastUpdate *update;
+       RhythmDBEntry *entry;
+       GFile *feed;
+       char *feed_url;
+
+       if (g_str_has_prefix (url, "feed://") || g_str_has_prefix (url, "itpc://")) {
+               char *tmp;
+
+               tmp = g_strdup_printf ("http://%s";, url + strlen ("feed://"));
+               feed = g_file_new_for_uri (tmp);
+               g_free (tmp);
        } else {
-               g_free (info->url);
-               g_free (info);
-               if (--info->pd->priv->updating == 0) {
-                       g_object_notify (G_OBJECT (info->pd), "updating");
-               }
+               feed = g_file_new_for_uri (url);
        }
 
-       gtk_widget_destroy (GTK_WIDGET (dialog));
-}
+       feed_url = g_file_get_uri (feed);
 
-static gboolean
-confirm_bad_mime_type (RBPodcastUpdate *info)
-{
-       GtkWidget *dialog;
-       dialog = gtk_message_dialog_new (NULL, 0,
-                                        GTK_MESSAGE_QUESTION,
-                                        GTK_BUTTONS_YES_NO,
-                                        _("The URL '%s' does not appear to be a podcast feed. "
-                                        "It may be the wrong URL, or the feed may be broken. "
-                                        "Would you like Rhythmbox to attempt to use it anyway?"),
-                                        info->url);
-       gtk_widget_show_all (dialog);
-       g_signal_connect (dialog, "response", G_CALLBACK (confirm_bad_mime_type_response_cb), info);
-       return FALSE;
-}
+       update = g_new0 (RBPodcastUpdate, 1);
+       update->pd = g_object_ref (pd);
+       update->url = feed_url;
+       update->automatic = automatic;
+       update->channel = g_new0 (RBPodcastChannel, 1);
 
-static gpointer
-rb_podcast_manager_thread_parse_feed (RBPodcastUpdate *info)
-{
-       RBPodcastChannel *feed = g_new0 (RBPodcastChannel, 1);
-       RBPodcastManagerParseResult *result;
-
-       result = g_new0 (RBPodcastManagerParseResult, 1);
-       result->channel = feed;
-       result->pd = info->pd;          /* adopts our reference */
-       result->automatic = info->automatic;
-
-       g_clear_error (&result->error);
-
-       rb_debug ("attempting to parse feed %s", info->url);
-       if (rb_podcast_parse_load_feed (feed, info->url, info->existing_feed, &result->error) == FALSE) {
-               if (g_error_matches (result->error,
-                                    RB_PODCAST_PARSE_ERROR,
-                                    RB_PODCAST_PARSE_ERROR_MIME_TYPE)) {
-                       g_idle_add ((GSourceFunc) confirm_bad_mime_type, info);
-                       return NULL;
+       entry = rhythmdb_entry_lookup_by_location (pd->priv->db, feed_url);
+       if (entry) {
+               GValue v = {0,};
+               if (rhythmdb_entry_get_entry_type (entry) != RHYTHMDB_ENTRY_TYPE_PODCAST_FEED) {
+                       /* added as something else, probably iradio */
+                       rb_error_dialog (NULL, _("URL already added"),
+                                        _("The URL \"%s\" has already been added as a radio station. "
+                                        "If this is a podcast feed, please remove the radio station."), url);
+                       g_object_unref (feed);
+                       podcast_update_free (update);
+                       return FALSE;
                }
+
+               g_value_init (&v, G_TYPE_ULONG);
+               g_value_set_ulong (&v, RHYTHMDB_PODCAST_FEED_STATUS_UPDATING);
+               rhythmdb_entry_set (pd->priv->db, entry, RHYTHMDB_PROP_STATUS, &v);
+               rhythmdb_commit (pd->priv->db);
+               g_value_unset (&v);
+
+               start_feed_parse (pd, update);
+       } else if (rb_uri_could_be_podcast (feed_url, NULL)) {
+               rb_debug ("not checking mime type for %s", feed_url);
+               start_feed_parse (pd, update);
+       } else {
+               g_file_query_info_async (feed,
+                                        G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+                                        0,
+                                        G_PRIORITY_DEFAULT,
+                                        pd->priv->update_cancel,
+                                        mime_type_check_cb,
+                                        update);
        }
 
-       g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
-                        (GSourceFunc) rb_podcast_manager_parse_complete_cb,
-                        result,
-                        (GDestroyNotify) rb_podcast_manager_free_parse_result);
-       g_free (info->url);
-       g_free (info);
-       return NULL;
+       g_object_unref (feed);
+       return TRUE;
 }
 
 RhythmDBEntry *
@@ -1598,45 +1615,6 @@ rb_podcast_manager_add_parsed_feed (RBPodcastManager *pd, RBPodcastChannel *data
        rhythmdb_commit (db);
 }
 
-static void
-rb_podcast_manager_handle_feed_error (RBPodcastManager *mgr,
-                                     const char *url,
-                                     GError *error,
-                                     gboolean emit)
-{
-       RhythmDBEntry *entry;
-       GValue v = {0,};
-       gboolean existing = FALSE;
-
-       /* set the error in the feed entry, if one exists */
-       entry = rhythmdb_entry_lookup_by_location (mgr->priv->db, url);
-       if (entry != NULL && rhythmdb_entry_get_entry_type (entry) == RHYTHMDB_ENTRY_TYPE_PODCAST_FEED) {
-               g_value_init (&v, G_TYPE_STRING);
-               g_value_set_string (&v, error->message);
-               rhythmdb_entry_set (mgr->priv->db, entry, RHYTHMDB_PROP_PLAYBACK_ERROR, &v);
-               g_value_unset (&v);
-
-               g_value_init (&v, G_TYPE_ULONG);
-               g_value_set_ulong (&v, RHYTHMDB_PODCAST_FEED_STATUS_NORMAL);
-               rhythmdb_entry_set (mgr->priv->db, entry, RHYTHMDB_PROP_STATUS, &v);
-               g_value_unset (&v);
-
-               rhythmdb_commit (mgr->priv->db);
-               existing = TRUE;
-       }
-
-       /* if this was a result of a direct user action, emit the error signal too */
-       if (emit) {
-               gchar *error_msg;
-               error_msg = g_strdup_printf (_("There was a problem adding this podcast: %s.  Please verify 
the URL: %s"),
-                                            error->message, url);
-               g_signal_emit (mgr,
-                              rb_podcast_manager_signals[PROCESS_ERROR],
-                              0, url, error_msg, existing);
-               g_free (error_msg);
-       }
-}
-
 void
 rb_podcast_manager_shutdown (RBPodcastManager *pd)
 {
@@ -1644,14 +1622,14 @@ rb_podcast_manager_shutdown (RBPodcastManager *pd)
 
        g_assert (rb_is_main_thread ());
 
+       g_cancellable_cancel (pd->priv->update_cancel);
+
        lst = g_list_reverse (g_list_copy (pd->priv->download_list));
        for (l = lst; l != NULL; l = l->next) {
                RBPodcastDownload *data = (RBPodcastDownload *) l->data;
                cancel_download (data);
        }
        g_list_free (lst);
-
-       pd->priv->shutdown = TRUE;
 }
 
 char *
diff --git a/podcast/rb-podcast-manager.h b/podcast/rb-podcast-manager.h
index 14c3ee718..cf7e669b6 100644
--- a/podcast/rb-podcast-manager.h
+++ b/podcast/rb-podcast-manager.h
@@ -55,13 +55,6 @@ typedef struct
 typedef struct
 {
        GObjectClass parent_class;
-
-       /* signals */
-       void        (*start_download)                   (RBPodcastManager* pd, RhythmDBEntry *entry);
-       void        (*finish_download)                  (RBPodcastManager* pd, RhythmDBEntry *entry);
-       void        (*feed_updates_available)           (RBPodcastManager* pd, RhythmDBEntry *entry);
-       void        (*process_error)                    (RBPodcastManager* pd, const char *url, const char 
*error, gboolean existing);
-
 } RBPodcastManagerClass;
 
 GType                   rb_podcast_manager_get_type                    (void);
diff --git a/podcast/rb-podcast-parse.c b/podcast/rb-podcast-parse.c
index 0c2879abc..937f6f732 100644
--- a/podcast/rb-podcast-parse.c
+++ b/podcast/rb-podcast-parse.c
@@ -40,6 +40,12 @@
 #include "rb-podcast-parse.h"
 #include "rb-file-helpers.h"
 
+typedef struct {
+       RBPodcastChannel *channel;
+       RBPodcastParseCallback callback;
+       gpointer user_data;
+} RBPodcastParseData;
+
 GQuark
 rb_podcast_parse_error_quark (void)
 {
@@ -148,98 +154,65 @@ entry_parsed (TotemPlParser *parser,
        channel->posts = g_list_prepend (channel->posts, item);
 }
 
-gboolean
-rb_podcast_parse_load_feed (RBPodcastChannel *data,
-                           const char *file_name,
-                           gboolean existing_feed,
-                           GError **error)
+static void
+parse_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-       GFile *file;
-       GFileInfo *fileinfo;
-       TotemPlParser *plparser;
-
-       data->url = g_strdup (file_name);
-
-       /* if the URL has a .rss, .xml or .atom extension (before the query string),
-        * don't bother checking the MIME type.
-        */
-       if (rb_uri_could_be_podcast (file_name, &data->is_opml) || existing_feed) {
-               rb_debug ("not checking mime type for %s (should be %s file)", file_name,
-                         data->is_opml ? "OPML" : "Podcast");
-       } else {
-               GError *ferror = NULL;
-               char *content_type;
-
-               rb_debug ("checking mime type for %s", file_name);
-
-               file = g_file_new_for_uri (file_name);
-               fileinfo = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0, NULL, &ferror);
-               if (ferror != NULL) {
-                       g_set_error (error,
-                                    RB_PODCAST_PARSE_ERROR,
-                                    RB_PODCAST_PARSE_ERROR_FILE_INFO,
-                                    _("Unable to check file type: %s"),
-                                    ferror->message);
-                       g_object_unref (file);
-                       g_clear_error (&ferror);
-                       return FALSE;
-               }
+       RBPodcastParseData *data = user_data;
+       RBPodcastChannel *channel = data->channel;
+       GError *error = NULL;
 
-               content_type = g_file_info_get_attribute_as_string (fileinfo, 
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
-               g_object_unref (file);
-               g_object_unref (fileinfo);
-
-               if (content_type != NULL
-                   && strstr (content_type, "html") == NULL
-                   && strstr (content_type, "xml") == NULL
-                   && strstr (content_type, "rss") == NULL
-                   && strstr (content_type, "opml") == NULL) {
-                       g_set_error (error,
-                                    RB_PODCAST_PARSE_ERROR,
-                                    RB_PODCAST_PARSE_ERROR_MIME_TYPE,
-                                    _("Unexpected file type: %s"),
-                                    content_type);
-                       g_free (content_type);
-                       return FALSE;
-               } else if (content_type != NULL
-                          && strstr (content_type, "opml") != NULL) {
-                       data->is_opml = TRUE;
-               }
-
-               g_free (content_type);
-       }
+       totem_pl_parser_parse_finish (TOTEM_PL_PARSER (source_object), res, &error);
+       if (error) {
+               rb_debug ("parsing %s as a podcast failed: %s", channel->url, error->message);
+               g_clear_error (&error);
 
-       plparser = totem_pl_parser_new ();
-       g_object_set (plparser, "recurse", FALSE, "force", TRUE, 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);
-
-       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_set_error (error,
+               g_set_error (&error,
                             RB_PODCAST_PARSE_ERROR,
                             RB_PODCAST_PARSE_ERROR_XML_PARSE,
                             _("Unable to parse the feed contents"));
-               g_object_unref (plparser);
-               return FALSE;
-       }
-       g_object_unref (plparser);
-
-       /* treat empty feeds, or feeds that don't contain any downloadable items, as
-        * an error.
-        */
-       if (data->posts == NULL) {
-               rb_debug ("Parsing %s as a podcast succeeded, but the feed contains no downloadable items", 
file_name);
-               g_set_error (error,
+       } else if (channel->posts == NULL) {
+               /*
+                * treat empty feeds, or feeds that don't contain any downloadable items, as
+                * an error.
+                */
+               rb_debug ("parsing %s as a podcast succeeded, but the feed contains no downloadable items", 
channel->url);
+               g_set_error (&error,
                             RB_PODCAST_PARSE_ERROR,
                             RB_PODCAST_PARSE_ERROR_NO_ITEMS,
                             _("The feed does not contain any downloadable items"));
-               return FALSE;
+       } else {
+               rb_debug ("parsing %s as a podcast succeeded", channel->url);
        }
 
-       rb_debug ("Parsing %s as a Podcast succeeded", file_name);
-       return TRUE;
+       data->callback (channel, error, data->user_data);
+       g_object_unref (source_object);
+       g_free (data);
+}
+
+void
+rb_podcast_parse_load_feed (RBPodcastChannel *channel,
+                           const char *feed_url,
+                           GCancellable *cancellable,
+                           RBPodcastParseCallback callback,
+                           gpointer user_data)
+{
+       TotemPlParser *plparser;
+       RBPodcastParseData *data;
+
+       channel->url = g_strdup (feed_url);
+
+       data = g_new0 (RBPodcastParseData, 1);
+       data->channel = channel;
+       data->callback = callback;
+       data->user_data = user_data;
+
+       plparser = totem_pl_parser_new ();
+       g_object_set (plparser, "recurse", FALSE, "force", TRUE, NULL);
+       g_signal_connect (plparser, "entry-parsed", G_CALLBACK (entry_parsed), channel);
+       g_signal_connect (plparser, "playlist-started", G_CALLBACK (playlist_started), channel);
+       g_signal_connect (plparser, "playlist-ended", G_CALLBACK (playlist_ended), channel);
+
+       totem_pl_parser_parse_async (plparser, channel->url, FALSE, cancellable, parse_cb, data);
 }
 
 RBPodcastChannel *
diff --git a/podcast/rb-podcast-parse.h b/podcast/rb-podcast-parse.h
index abe85a5d8..7b6d8b3f1 100644
--- a/podcast/rb-podcast-parse.h
+++ b/podcast/rb-podcast-parse.h
@@ -29,6 +29,7 @@
 #define RB_PODCAST_PARSE_H
 
 #include <glib.h>
+#include <gio/gio.h>
 
 typedef enum
 {
@@ -75,10 +76,13 @@ GType       rb_podcast_item_get_type (void);
 #define RB_TYPE_PODCAST_CHANNEL        (rb_podcast_channel_get_type ())
 #define RB_TYPE_PODCAST_ITEM (rb_podcast_item_get_type ())
 
-gboolean rb_podcast_parse_load_feed    (RBPodcastChannel *data,
-                                        const char *url,
-                                        gboolean existing_feed,
-                                        GError **error);
+typedef void (*RBPodcastParseCallback) (RBPodcastChannel *data, GError *error, gpointer user_data);
+
+void   rb_podcast_parse_load_feed (RBPodcastChannel *data,
+                                   const char *url,
+                                   GCancellable *cancellable,
+                                   RBPodcastParseCallback callback,
+                                   gpointer user_data);
 
 RBPodcastChannel *rb_podcast_parse_channel_copy (RBPodcastChannel *data);
 RBPodcastItem *rb_podcast_parse_item_copy (RBPodcastItem *data);
diff --git a/podcast/test-podcast-parse.c b/podcast/test-podcast-parse.c
index 0ad8c37f8..07458c03d 100644
--- a/podcast/test-podcast-parse.c
+++ b/podcast/test-podcast-parse.c
@@ -66,39 +66,30 @@ rb_debug_realf (const char *func,
                    file, line, func, buffer);
 }
 
-int main (int argc, char **argv)
+static void
+parse_cb (RBPodcastChannel *channel, GError *error, gpointer user_data)
 {
-       RBPodcastChannel *data;
+       GMainLoop *ml = user_data;
        GList *l;
        GDate date = {0,};
        char datebuf[1024];
-       GError *error = NULL;
-
-       setlocale (LC_ALL, "");
-       bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
-       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-
-       if (argv[2] != NULL && strcmp (argv[2], "--debug") == 0) {
-               debug = TRUE;
-       }
 
-       data = g_new0 (RBPodcastChannel, 1);
-       if (rb_podcast_parse_load_feed (data, argv[1], FALSE, &error) == FALSE) {
-               g_warning ("Couldn't parse %s: %s", argv[1], error->message);
-               g_clear_error (&error);
-               return 1;
+       if (error) {
+               g_warning ("Couldn't parse %s: %s", channel->url, error->message);
+               g_main_loop_quit (ml);
+               return;
        }
 
-       g_date_set_time_t (&date, data->pub_date);
+       g_date_set_time_t (&date, channel->pub_date);
        g_date_strftime (datebuf, 1024, "%F %T", &date);
 
-       g_print ("Podcast title: %s\n", data->title);
-       g_print ("Description: %s\n", data->description);
-       g_print ("Author: %s\n", data->author);
+       g_print ("Podcast title: %s\n", channel->title);
+       g_print ("Description: %s\n", channel->description);
+       g_print ("Author: %s\n", channel->author);
        g_print ("Date: %s\n", datebuf);
        g_print ("\n");
 
-       for (l = data->posts; l != NULL; l = l->next) {
+       for (l = channel->posts; l != NULL; l = l->next) {
                RBPodcastItem *item = l->data;
 
                g_date_set_time_t (&date, item->pub_date);
@@ -112,6 +103,29 @@ int main (int argc, char **argv)
                g_print ("\n");
        }
 
+       g_main_loop_quit (ml);
+}
+
+int
+main (int argc, char **argv)
+{
+       RBPodcastChannel *data;
+       GMainLoop *ml;
+
+       setlocale (LC_ALL, "");
+       bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+       if (argv[2] != NULL && strcmp (argv[2], "--debug") == 0) {
+               debug = TRUE;
+       }
+
+       ml = g_main_loop_new (NULL, FALSE);
+       data = g_new0 (RBPodcastChannel, 1);
+       rb_podcast_parse_load_feed (data, argv[1], NULL, parse_cb, ml);
+
+       g_main_loop_run (ml);
+
        return 0;
 }
 


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