Checked memory frees and added minor code fixes
[maevies] / src / mvs-minfo-provider.c
index ca63a3d..af1fdf9 100644 (file)
 
 #include "mvs-minfo-provider.h"
 
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <json-glib/json-glib.h>
+
+#include "mvs-tmdb-movie.h"
+#include "mvs-watc-movie.h"
+
 #define TMDB_API_KEY "249e1a42df9bee09fac5e92d3a51396b"
 #define TMDB_LANGUAGE "en"
 #define TMDB_FORMAT "xml"
 #define TMDB_METHOD "Movie.search"
 #define TMDB_BASE_URL "http://api.themoviedb.org/2.1/%s/%s/%s/%s/%s"
+#define TMDB_MOVIE_XPATH "/OpenSearchDescription/movies/movie"
+
+#define WATC_BASE_URL "http://whatsafterthecredits.com/api.php?action=%s&format=%s&search=%s"
+#define WATC_ACTION "opensearch"
+#define WATC_FORMAT "json"
 
 G_DEFINE_TYPE (MvsMInfoProvider, mvs_minfo_provider, G_TYPE_OBJECT)
 
@@ -36,8 +48,18 @@ enum {
 
 struct _MvsMInfoProviderPrivate {
         gchar *format;
+        MvsService service;
 };
 
+enum {
+        RESPONSE_RECEIVED,
+        LAST_SIGNAL
+};
+
+static guint
+signals[LAST_SIGNAL] = { 0 };
+
+
 static void
 mvs_minfo_provider_get_property (GObject *object, guint property_id,
                          GValue *value, GParamSpec *pspec)
@@ -96,6 +118,15 @@ mvs_minfo_provider_class_init (MvsMInfoProviderClass *klass)
                                       TMDB_FORMAT,
                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
+        signals[RESPONSE_RECEIVED] = g_signal_new ("response-received", MVS_TYPE_MINFO_PROVIDER,
+                        G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+                        0,
+                        NULL,
+                        NULL,
+                        g_cclosure_marshal_VOID__POINTER,
+                        G_TYPE_NONE,
+                        1,
+                        G_TYPE_POINTER);
 }
 
 static void
@@ -103,6 +134,7 @@ mvs_minfo_provider_init (MvsMInfoProvider *self)
 {
         self->priv = GET_PRIVATE (self);
         self->priv->format = NULL;
+        self->priv->service = MVS_SERVICE_TMDB;
 }
 
 MvsMInfoProvider*
@@ -111,12 +143,137 @@ mvs_minfo_provider_new (void)
         return g_object_new (MVS_TYPE_MINFO_PROVIDER, NULL);
 }
 
+static MvsTmdbMovie*
+create_tmdb_movie (xmlNodePtr node)
+{
+        xmlNodePtr cur_node = NULL;
+        MvsTmdbMovie *movie_info = mvs_tmdb_movie_new ();
+
+        /* We use the loop to append each property to the movie object */
+        for (cur_node = node; cur_node; cur_node = cur_node->next) {
+                if (cur_node->type == XML_ELEMENT_NODE) {
+                        const gchar *value = xmlNodeGetContent (cur_node);
+
+                        g_object_set (movie_info, cur_node->name, value, NULL);
+                }
+        }
+        return movie_info;
+}
+
+static GList*
+generate_list (xmlNodeSetPtr node_set)
+{
+        int i = 0;
+        GList *list = NULL;
+
+        for (i = 0; i < node_set->nodeNr; i++) {
+                xmlNodePtr node = node_set->nodeTab[i];
+                if (node->type == XML_ELEMENT_NODE) {
+                        MvsTmdbMovie *movie_info =
+                                        create_tmdb_movie (node->children);
+                        if (movie_info)
+                                list = g_list_prepend (list, movie_info);
+                }
+        }
+
+        if (list)
+                list = g_list_reverse (list);
+
+        return list;
+}
+
+static GList*
+parse_xml (const char *xml_data, goffset length)
+{
+        GList *list = NULL;
+        xmlDocPtr document = xmlReadMemory (xml_data, length,
+                        NULL,
+                        NULL,
+                        XML_PARSE_NOBLANKS | XML_PARSE_RECOVER);
+        g_return_if_fail (document);
+
+        xmlXPathContextPtr context_ptr = xmlXPathNewContext (document);
+
+        xmlXPathObjectPtr xpath_obj =
+                        xmlXPathEvalExpression (TMDB_MOVIE_XPATH, context_ptr);
+
+        xmlNodeSetPtr nodeset = xpath_obj->nodesetval;
+
+        if (nodeset->nodeNr > 0) {
+                list = generate_list (nodeset);
+        }
+
+        xmlXPathFreeNodeSetList (xpath_obj);
+        xmlXPathFreeContext (context_ptr);
+        xmlFreeDoc (document);
+
+        return list;
+}
+
+static GList *
+parse_json (const char *json_data, goffset length)
+{
+        JsonParser *parser = NULL;
+        JsonNode *root = NULL;
+        GError *error = NULL;
+        GList *list = NULL;
+
+        parser = json_parser_new ();
+
+        json_parser_load_from_data (parser, json_data, length, &error);
+        if (error)
+        {
+                g_warning ("Unable to parse data '%s': %s\n",
+                                json_data, error->message);
+                g_error_free (error);
+                g_object_unref (parser);
+                return list;
+        }
+
+        /* Don't free */
+        root = json_parser_get_root (parser);
+        JsonArray *response = json_node_get_array (root);
+
+        /* The response is expected with the following format:
+         * [ SEARCH_TERM ,[ SEARCH_RESULT_1, SEARCH_RESULT_N]] */
+
+        if (json_array_get_length (response) != 2) {
+
+                g_warning ("Wrong response format: %s\n", json_data);
+
+                g_object_unref (parser);
+                return list;
+        }
+
+        const gchar *search_term = json_array_get_string_element (response, 0);
+        g_message ("Searched for: %s\n", search_term);
+
+        JsonArray *results = json_array_get_array_element (response, 1);
+        int i;
+        int array_length = json_array_get_length (results);
+
+        for (i = 0; i < array_length; i++) {
+                const gchar *result =
+                                json_array_get_string_element (results, i);
+                MvsWatcMovie *watc_movie = mvs_watc_movie_new (result);
+                list = g_list_prepend (list, watc_movie);
+        }
+
+        g_object_unref (parser);
+
+        if (list)
+                list = g_list_reverse (list);
+
+        return list;
+}
+
 static void
-parse_response (SoupSession *session, SoupMessage *message,
+process_response_cb (SoupSession *session, SoupMessage *message,
                     gpointer user_data)
 {
         MvsMInfoProvider *self = MVS_MINFO_PROVIDER (user_data);
         const gchar *mime = NULL;
+        GList *list = NULL;
 
         if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code) ||
                         message->response_body->length <= 0) {
@@ -127,33 +284,57 @@ parse_response (SoupSession *session, SoupMessage *message,
 
                 mime = soup_message_headers_get_content_type
                                 (message->response_headers, NULL);
-
-                g_print ("Response OK. Mime type: %s\n", mime);
-                g_print ("Content:\n%s\n", message->response_body->data);
+                g_message ("Mime type: %s\n", mime);
+
+                if (g_strcmp0 (mime, "text/xml") == 0)
+                        list = parse_xml (message->response_body->data,
+                                        message->response_body->length);
+                else if (g_strcmp0 (mime, "application/json") == 0)
+                        list = parse_json (message->response_body->data,
+                                        message->response_body->length);
         }
 
-        g_signal_emit (self, signals[RESPONSE_RECEIVED], 0);
+        g_signal_emit (self, signals[RESPONSE_RECEIVED], 0, list);
+
+        g_list_free (list);
 }
 
 static gchar *
 get_query_uri (MvsMInfoProvider *self, const char *query)
 {
-        /* METHOD/LANGUAGE/FORMAT/APIKEY/MOVIENAME */
-        gchar *uri = g_strdup_printf (TMDB_BASE_URL, TMDB_METHOD,
-                        TMDB_LANGUAGE,
-                        self->priv->format,
-                        TMDB_API_KEY,
-                        query);
+        gchar *uri = NULL;
+
+        if (self->priv->service == MVS_SERVICE_TMDB) {
+                /* METHOD/LANGUAGE/FORMAT/APIKEY/MOVIENAME */
+                uri = g_strdup_printf (TMDB_BASE_URL, TMDB_METHOD,
+                                TMDB_LANGUAGE,
+                                self->priv->format,
+                                TMDB_API_KEY,
+                                query);
+
+        }
+        else if (self->priv->service == MVS_SERVICE_WATC) {
+                /* WATCBASE_URL/ACTION/FORMAT/QUERY */
+                uri = g_strdup_printf (WATC_BASE_URL,
+                                WATC_ACTION,
+                                WATC_FORMAT,
+                                query);
+        }
+        else {
+                g_warning ("Service unsupported\n");
+        }
 
         return uri;
 }
 
 gboolean
-mvs_minfo_provider_query (MvsMInfoProvider *self,
+mvs_minfo_provider_query (MvsMInfoProvider *self, MvsService service,
                           const gchar *query)
 {
         g_return_val_if_fail (MVS_IS_MINFO_PROVIDER (self), FALSE);
 
+        self->priv->service = service;
+
         SoupSession *session = NULL;
         SoupMessage *message = NULL;
         gboolean message_queued = FALSE;
@@ -167,7 +348,7 @@ mvs_minfo_provider_query (MvsMInfoProvider *self,
 
         if (message) {
                 soup_session_queue_message (session, message,
-                                parse_response, self);
+                                process_response_cb, self);
                 message_queued = TRUE;
         }