4 * This file is part of maevies
5 * Copyright (C) 2010 Simón Pena <spenap@gmail.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
19 #include "mvs-minfo-provider.h"
21 #include <libxml/parser.h>
22 #include <libxml/xpath.h>
23 #include <json-glib/json-glib.h>
25 #include "mvs-tmdb-movie.h"
26 #include "mvs-tmdb-image.h"
27 #include "mvs-watc-movie.h"
29 #define TMDB_API_KEY "249e1a42df9bee09fac5e92d3a51396b"
30 #define TMDB_LANGUAGE "en"
31 #define TMDB_FORMAT "xml"
32 #define TMDB_METHOD "Movie.search"
33 #define TMDB_BASE_URL "http://api.themoviedb.org/2.1/%s/%s/%s/%s/%s"
34 #define TMDB_MOVIE_XPATH "/OpenSearchDescription/movies/movie"
36 #define WATC_BASE_URL "http://whatsafterthecredits.com/api.php?action=%s&format=%s&search=%s"
37 #define WATC_ACTION "opensearch"
38 #define WATC_FORMAT "json"
40 G_DEFINE_TYPE (MvsMInfoProvider, mvs_minfo_provider, G_TYPE_OBJECT)
47 #define GET_PRIVATE(o) \
48 (G_TYPE_INSTANCE_GET_PRIVATE ((o), MVS_TYPE_MINFO_PROVIDER, MvsMInfoProviderPrivate))
50 struct _MvsMInfoProviderPrivate {
61 signals[LAST_SIGNAL] = { 0 };
65 mvs_minfo_provider_get_property (GObject *object, guint property_id,
66 GValue *value, GParamSpec *pspec)
68 MvsMInfoProvider *self = MVS_MINFO_PROVIDER (object);
70 switch (property_id) {
72 g_value_set_string (value, self->priv->format);
75 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
80 mvs_minfo_provider_set_property (GObject *object, guint property_id,
81 const GValue *value, GParamSpec *pspec)
83 MvsMInfoProvider *self = MVS_MINFO_PROVIDER (object);
85 switch (property_id) {
87 mvs_minfo_provider_set_format (self,
88 g_value_get_string (value));
91 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
96 mvs_minfo_provider_finalize (GObject *object)
98 MvsMInfoProvider *self = MVS_MINFO_PROVIDER (object);
100 g_free (self->priv->format);
102 G_OBJECT_CLASS (mvs_minfo_provider_parent_class)->finalize (object);
106 mvs_minfo_provider_class_init (MvsMInfoProviderClass *klass)
108 GObjectClass *object_class = G_OBJECT_CLASS (klass);
110 g_type_class_add_private (klass, sizeof (MvsMInfoProviderPrivate));
112 object_class->get_property = mvs_minfo_provider_get_property;
113 object_class->set_property = mvs_minfo_provider_set_property;
114 object_class->finalize = mvs_minfo_provider_finalize;
116 g_object_class_install_property
117 (object_class, PROP_FORMAT,
118 g_param_spec_string ("format", "The format", "The format",
120 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
122 signals[RESPONSE_RECEIVED] = g_signal_new ("response-received",
123 MVS_TYPE_MINFO_PROVIDER,
124 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
128 g_cclosure_marshal_VOID__UINT_POINTER,
137 mvs_minfo_provider_init (MvsMInfoProvider *self)
139 self->priv = GET_PRIVATE (self);
140 self->priv->format = NULL;
141 self->priv->service = MVS_SERVICE_TMDB;
145 mvs_minfo_provider_new (void)
147 return g_object_new (MVS_TYPE_MINFO_PROVIDER, NULL);
151 create_tmdb_image (xmlNodePtr node)
153 MvsTmdbImage *image = mvs_tmdb_image_new ();
157 /* <image type="poster"
158 * url="http://....jpg"
160 * id="4bc91...e007304"/> */
162 for (i = 0; i < LAST_FIELD; i ++) {
163 value = xmlGetProp (node, image_fields[i]);
165 g_object_set (image, image_fields[i], value, NULL);
174 create_tmdb_movie (xmlNodePtr node)
176 xmlNodePtr cur_node = NULL;
177 MvsTmdbMovie *movie_info = mvs_tmdb_movie_new ();
178 GList *image_list = NULL;
180 /* We use the loop to append each property to the movie object */
181 for (cur_node = node; cur_node; cur_node = cur_node->next) {
182 if (cur_node->type == XML_ELEMENT_NODE) {
185 if (g_strcmp0 (cur_node->name, "images") == 0) {
186 xmlNodePtr cur_image = NULL;
187 for (cur_image = cur_node->children; cur_image;
188 cur_image = cur_image->next) {
190 MvsTmdbImage *tmdb_image = create_tmdb_image (cur_image);
191 image_list = g_list_append (image_list, tmdb_image);
195 value = xmlNodeGetContent (cur_node);
196 g_object_set (movie_info, cur_node->name, value, NULL);
202 mvs_tmdb_movie_set_images (movie_info, image_list);
208 generate_list (xmlNodeSetPtr node_set)
213 for (i = 0; i < node_set->nodeNr; i++) {
214 xmlNodePtr node = node_set->nodeTab[i];
215 if (node->type == XML_ELEMENT_NODE) {
216 MvsTmdbMovie *movie_info =
217 create_tmdb_movie (node->children);
219 list = g_list_prepend (list, movie_info);
224 list = g_list_reverse (list);
230 parse_xml (const char *xml_data, goffset length)
233 xmlDocPtr document = xmlReadMemory (xml_data, length,
236 XML_PARSE_NOBLANKS | XML_PARSE_RECOVER);
237 g_return_if_fail (document);
239 xmlXPathContextPtr context_ptr = xmlXPathNewContext (document);
241 xmlXPathObjectPtr xpath_obj =
242 xmlXPathEvalExpression (TMDB_MOVIE_XPATH, context_ptr);
244 xmlNodeSetPtr nodeset = xpath_obj->nodesetval;
246 if (nodeset->nodeNr > 0) {
247 list = generate_list (nodeset);
250 xmlXPathFreeObject (xpath_obj);
251 xmlXPathFreeContext (context_ptr);
252 xmlFreeDoc (document);
258 parse_json (const char *json_data, goffset length)
260 JsonParser *parser = NULL;
261 JsonNode *root = NULL;
262 GError *error = NULL;
265 parser = json_parser_new ();
267 json_parser_load_from_data (parser, json_data, length, &error);
270 g_warning ("Unable to parse data '%s': %s\n",
271 json_data, error->message);
272 g_error_free (error);
273 g_object_unref (parser);
278 root = json_parser_get_root (parser);
279 JsonArray *response = json_node_get_array (root);
281 /* The response is expected with the following format:
282 * [ SEARCH_TERM ,[ SEARCH_RESULT_1, SEARCH_RESULT_N]] */
284 if (json_array_get_length (response) != 2) {
286 g_warning ("Wrong response format: %s\n", json_data);
288 g_object_unref (parser);
292 const gchar *search_term = json_array_get_string_element (response, 0);
293 g_message ("Searched for: %s\n", search_term);
295 JsonArray *results = json_array_get_array_element (response, 1);
297 int array_length = json_array_get_length (results);
299 for (i = 0; i < array_length; i++) {
300 const gchar *result =
301 json_array_get_string_element (results, i);
302 MvsWatcMovie *watc_movie = mvs_watc_movie_new (result);
303 list = g_list_prepend (list, watc_movie);
306 g_object_unref (parser);
309 list = g_list_reverse (list);
315 process_response_cb (SoupSession *session, SoupMessage *message,
318 MvsMInfoProvider *self = MVS_MINFO_PROVIDER (user_data);
319 const gchar *mime = NULL;
323 if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code) ||
324 message->response_body->length <= 0) {
326 g_print ("%s\n", message->reason_phrase);
330 mime = soup_message_headers_get_content_type
331 (message->response_headers, NULL);
332 g_message ("Mime type: %s\n", mime);
334 if (g_strcmp0 (mime, "text/xml") == 0) {
335 list = parse_xml (message->response_body->data,
336 message->response_body->length);
337 service = MVS_SERVICE_TMDB;
339 else if (g_strcmp0 (mime, "application/json") == 0) {
340 list = parse_json (message->response_body->data,
341 message->response_body->length);
342 service = MVS_SERVICE_WATC;
346 g_signal_emit (self, signals[RESPONSE_RECEIVED], 0, service, list);
350 get_query_uri (MvsMInfoProvider *self, const char *query)
354 if (self->priv->service == MVS_SERVICE_TMDB) {
355 /* METHOD/LANGUAGE/FORMAT/APIKEY/MOVIENAME */
356 uri = g_strdup_printf (TMDB_BASE_URL, TMDB_METHOD,
363 else if (self->priv->service == MVS_SERVICE_WATC) {
364 /* WATCBASE_URL/ACTION/FORMAT/QUERY */
365 uri = g_strdup_printf (WATC_BASE_URL,
371 g_warning ("Service unsupported\n");
374 g_message ("%s", uri);
379 mvs_minfo_provider_query (MvsMInfoProvider *self, MvsService service,
382 g_return_val_if_fail (MVS_IS_MINFO_PROVIDER (self), FALSE);
384 self->priv->service = service;
386 SoupSession *session = NULL;
387 SoupMessage *message = NULL;
388 gboolean message_queued = FALSE;
390 gchar *uri = get_query_uri (self, query);
392 g_return_val_if_fail (uri, FALSE);
394 session = soup_session_async_new ();
395 message = soup_message_new ("GET", uri);
398 soup_session_queue_message (session, message,
399 process_response_cb, self);
400 message_queued = TRUE;
405 return message_queued;
409 mvs_minfo_provider_set_format (MvsMInfoProvider *self,
412 g_return_val_if_fail (MVS_IS_MINFO_PROVIDER (self), FALSE);
414 g_free (self->priv->format);
416 self->priv->format = g_strdup (format);