summaryrefslogtreecommitdiff
path: root/src/trg-rss-model.c
diff options
context:
space:
mode:
authorGravatar Alan F <ajf@eth0.org.uk>2014-03-10 09:10:24 +0000
committerGravatar Alan F <ajf@eth0.org.uk>2014-03-10 09:10:24 +0000
commit1a5447c0138fd25e3f6d78f78593f5b70be5614a (patch)
tree9468304a4688a63788363bcf2457b1767aa513a9 /src/trg-rss-model.c
parentdd0c7d8317dd199b6dd9d612ebb863c4da6cf242 (diff)
parent118b702bec50d9872699357a379413f69e9b176e (diff)
Merge branch 'rss-viewer'. This is the biggest feature I've attempted to add for quite a long time. Quite a lot of refactoring of the HTTP client and threadpool has been done (to make it not send session tokens etc), uploads (was too much code duplication), and the add dialog was done to implement this. Don't expect this to be some fully automatable RSS client, use something like flexget for that, this is just a simple view on RSS feeds. It uses the rss-glib library, which you will probably need to install yourself to optionally use this feature. There are probably bugs still, so report them.
Diffstat (limited to 'src/trg-rss-model.c')
-rw-r--r--src/trg-rss-model.c259
1 files changed, 259 insertions, 0 deletions
diff --git a/src/trg-rss-model.c b/src/trg-rss-model.c
new file mode 100644
index 0000000..f898f34
--- /dev/null
+++ b/src/trg-rss-model.c
@@ -0,0 +1,259 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2011-2013 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_RSSGLIB
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+#include <rss-glib/rss-glib.h>
+
+#include "config.h"
+#include "torrent.h"
+#include "trg-client.h"
+#include "trg-model.h"
+#include "trg-rss-model.h"
+
+enum {
+ PROP_0, PROP_CLIENT
+};
+
+enum {
+ SIGNAL_GET_ERROR, SIGNAL_PARSE_ERROR, SIGNAL_COUNT
+};
+
+static guint signals[SIGNAL_COUNT] = { 0 };
+
+G_DEFINE_TYPE(TrgRssModel, trg_rss_model, GTK_TYPE_LIST_STORE)
+#define TRG_RSS_MODEL_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_RSS_MODEL, TrgRssModelPrivate))
+typedef struct _TrgRssModelPrivate TrgRssModelPrivate;
+
+struct _TrgRssModelPrivate {
+ TrgClient *client;
+ GHashTable *table;
+ guint index;
+};
+
+typedef struct {
+ TrgRssModel *model;
+ gchar *feed_id;
+ gchar *feed_url;
+ GError *error;
+ trg_response *response;
+} feed_update;
+
+static void feed_update_free(feed_update *update) {
+ if (update->error)
+ g_error_free(update->error);
+
+ g_free(update->feed_id);
+ g_free(update->feed_url);
+
+ g_free(update);
+}
+
+static gboolean on_rss_receive(gpointer data) {
+ trg_response *response = (trg_response *) data;
+ feed_update *update = (feed_update*) response->cb_data;
+ TrgRssModel *model = update->model;
+ TrgRssModelPrivate *priv = TRG_RSS_MODEL_GET_PRIVATE(model);
+
+ if (response->status == CURLE_OK) {
+ RssParser* parser = rss_parser_new();
+ GError *error = NULL;
+ if (rss_parser_load_from_data(parser, response->raw,
+ response->size, &error)) {
+ RssDocument *doc = rss_parser_get_document(parser);
+ GtkTreeIter iter;
+ GList *list, *tmp;
+ gchar *title;
+
+ list = rss_document_get_items(doc);
+
+ for (tmp = list; tmp != NULL; tmp = tmp->next) {
+ RssItem *item = (RssItem*) tmp->data;
+ const gchar *guid = rss_item_get_guid(item);
+ if (g_hash_table_lookup(priv->table, guid) != (void*) 1) {
+ gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, RSSCOL_ID,
+ guid, RSSCOL_TITLE, rss_item_get_title(item),
+ RSSCOL_LINK, rss_item_get_link(item), RSSCOL_FEED,
+ update->feed_id, RSSCOL_PUBDATE,
+ rss_item_get_pub_date(item), -1);
+ g_hash_table_insert(priv->table, g_strdup(guid), (void*) 1);
+ }
+ }
+
+ g_list_free(list);
+ g_object_unref(doc);
+ g_object_unref(parser);
+ } else {
+ rss_parse_error perror;
+ perror.error = error;
+ perror.feed_id = update->feed_id;
+
+ g_signal_emit(model, signals[SIGNAL_PARSE_ERROR], 0,
+ &perror);
+
+ g_message("parse error: %s", error->message);
+ g_error_free(error);
+ }
+ } else {
+ rss_get_error get_error;
+ get_error.error_code = response->status;
+ get_error.feed_id = update->feed_id;
+
+ g_signal_emit(model, signals[SIGNAL_GET_ERROR], 0,
+ &get_error);
+ }
+
+ trg_response_free(response);
+ feed_update_free(update);
+
+ return FALSE;
+}
+
+void trg_rss_model_update(TrgRssModel * model) {
+ TrgRssModelPrivate *priv = TRG_RSS_MODEL_GET_PRIVATE(model);
+ TrgPrefs *prefs = trg_client_get_prefs(priv->client);
+ JsonArray *feeds = trg_prefs_get_rss(prefs);
+ GList *li;
+
+ if (!feeds)
+ return;
+
+ for (li = json_array_get_elements(feeds); li != NULL;
+ li = g_list_next(li)) {
+ JsonObject *feed = json_node_get_object((JsonNode *) li->data);
+ const gchar *url = json_object_get_string_member(feed, "url");
+ const gchar *id = json_object_get_string_member(feed, "id");
+
+ if (!url || !id)
+ continue;
+
+ feed_update *update = g_new0(feed_update, 1);
+ update->feed_url = g_strdup(url);
+ update->feed_id = g_strdup(id);
+ update->model = model;
+
+ async_http_request(priv->client, update->feed_url, on_rss_receive,
+ update);
+ }
+
+ /*trg_model_remove_removed(GTK_LIST_STORE(model),
+ RSSCOL_UPDATESERIAL, updateSerial);*/
+}
+
+static void trg_rss_model_set_property(GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec G_GNUC_UNUSED) {
+ TrgRssModelPrivate *priv = TRG_RSS_MODEL_GET_PRIVATE(object);
+
+ switch (prop_id) {
+ case PROP_CLIENT:
+ priv->client = g_value_get_pointer(value);
+ break;
+ }
+}
+
+static void trg_rss_model_get_property(GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec G_GNUC_UNUSED) {
+}
+
+static GObject *trg_rss_model_constructor(GType type,
+ guint n_construct_properties, GObjectConstructParam * construct_params) {
+ GObject *obj = G_OBJECT_CLASS
+ (trg_rss_model_parent_class)->constructor(type,
+ n_construct_properties, construct_params);
+ TrgRssModelPrivate *priv = TRG_RSS_MODEL_GET_PRIVATE(obj);
+
+ priv->table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+ return obj;
+}
+
+static void trg_rss_model_dispose(GObject * object) {
+ TrgRssModelPrivate *priv = TRG_RSS_MODEL_GET_PRIVATE(object);
+ g_hash_table_destroy(priv->table);
+ G_OBJECT_CLASS(trg_rss_model_parent_class)->dispose(object);
+}
+
+static void trg_rss_model_class_init(TrgRssModelClass * klass) {
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(TrgRssModelPrivate));
+
+ object_class->set_property = trg_rss_model_set_property;
+ object_class->get_property = trg_rss_model_get_property;
+ object_class->constructor = trg_rss_model_constructor;
+ object_class->dispose = trg_rss_model_dispose;
+
+ g_object_class_install_property(object_class, PROP_CLIENT,
+ g_param_spec_pointer("client", "client", "client",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
+ | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK
+ | G_PARAM_STATIC_BLURB));
+
+ signals[SIGNAL_GET_ERROR] = g_signal_new("get-error",
+ G_TYPE_FROM_CLASS
+ (object_class),
+ G_SIGNAL_RUN_LAST |
+ G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET
+ (TrgRssModelClass,
+ get_error),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+
+ signals[SIGNAL_PARSE_ERROR] = g_signal_new("parse-error",
+ G_TYPE_FROM_CLASS
+ (object_class),
+ G_SIGNAL_RUN_LAST |
+ G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET
+ (TrgRssModelClass,
+ parse_error),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+}
+
+static void trg_rss_model_init(TrgRssModel * self) {
+ GType column_types[RSSCOL_COLUMNS];
+
+ column_types[RSSCOL_ID] = G_TYPE_STRING;
+ column_types[RSSCOL_TITLE] = G_TYPE_STRING;
+ column_types[RSSCOL_LINK] = G_TYPE_STRING;
+ column_types[RSSCOL_FEED] = G_TYPE_STRING;
+ column_types[RSSCOL_PUBDATE] = G_TYPE_STRING;
+ column_types[RSSCOL_UPLOADED] = G_TYPE_BOOLEAN;
+
+ gtk_list_store_set_column_types(GTK_LIST_STORE(self), RSSCOL_COLUMNS,
+ column_types);
+}
+
+TrgRssModel *trg_rss_model_new(TrgClient *client) {
+ return g_object_new(TRG_TYPE_RSS_MODEL, "client", client, NULL);
+}
+
+#endif