TMDB Movie: Added properties
[maevies] / src / mvs-minfo-provider.c
1 /*
2  * mvs-minfo-provider.c
3  *
4  * This file is part of maevies
5  * Copyright (C) 2010 Simón Pena <spenap@gmail.com>
6  *
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.
11  *
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.
16  *
17  */
18
19 #include "mvs-minfo-provider.h"
20
21 #include <libxml/parser.h>
22 #include <libxml/xpath.h>
23
24 #define TMDB_API_KEY "249e1a42df9bee09fac5e92d3a51396b"
25 #define TMDB_LANGUAGE "en"
26 #define TMDB_FORMAT "xml"
27 #define TMDB_METHOD "Movie.search"
28 #define TMDB_BASE_URL "http://api.themoviedb.org/2.1/%s/%s/%s/%s/%s"
29 #define TMDB_MOVIE_XPATH "/OpenSearchDescription/movies/movie"
30
31 G_DEFINE_TYPE (MvsMInfoProvider, mvs_minfo_provider, G_TYPE_OBJECT)
32
33 enum {
34         PROP_0,
35         PROP_FORMAT,
36 };
37
38 #define GET_PRIVATE(o) \
39   (G_TYPE_INSTANCE_GET_PRIVATE ((o), MVS_TYPE_MINFO_PROVIDER, MvsMInfoProviderPrivate))
40
41 struct _MvsMInfoProviderPrivate {
42         gchar *format;
43 };
44
45 enum {
46         RESPONSE_RECEIVED,
47         LAST_SIGNAL
48 };
49
50 static guint
51 signals[LAST_SIGNAL] = { 0 };
52
53
54 static void
55 mvs_minfo_provider_get_property (GObject *object, guint property_id,
56                          GValue *value, GParamSpec *pspec)
57 {
58         MvsMInfoProvider *self = MVS_MINFO_PROVIDER (object);
59
60         switch (property_id) {
61         case PROP_FORMAT:
62                 g_value_set_string (value, self->priv->format);
63                 break;
64         default:
65                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
66         }
67 }
68
69 static void
70 mvs_minfo_provider_set_property (GObject *object, guint property_id,
71                          const GValue *value, GParamSpec *pspec)
72 {
73         MvsMInfoProvider *self = MVS_MINFO_PROVIDER (object);
74
75         switch (property_id) {
76         case PROP_FORMAT:
77                 mvs_minfo_provider_set_format (self,
78                                 g_value_get_string (value));
79                 break;
80         default:
81                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
82         }
83 }
84
85 static void
86 mvs_minfo_provider_finalize (GObject *object)
87 {
88         MvsMInfoProvider *self = MVS_MINFO_PROVIDER (object);
89
90         g_free (self->priv->format);
91
92         G_OBJECT_CLASS (mvs_minfo_provider_parent_class)->finalize (object);
93 }
94
95 static void
96 mvs_minfo_provider_class_init (MvsMInfoProviderClass *klass)
97 {
98         GObjectClass *object_class = G_OBJECT_CLASS (klass);
99
100         g_type_class_add_private (klass, sizeof (MvsMInfoProviderPrivate));
101
102         object_class->get_property = mvs_minfo_provider_get_property;
103         object_class->set_property = mvs_minfo_provider_set_property;
104         object_class->finalize = mvs_minfo_provider_finalize;
105
106         g_object_class_install_property
107                 (object_class, PROP_FORMAT,
108                  g_param_spec_string ("format", "The format", "The format",
109                                       TMDB_FORMAT,
110                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
111
112         signals[RESPONSE_RECEIVED] = g_signal_new ("response-received", MVS_TYPE_MINFO_PROVIDER,
113                         G_SIGNAL_RUN_LAST,
114                         0,
115                         NULL,
116                         NULL,
117                         g_cclosure_marshal_VOID__VOID,
118                         G_TYPE_NONE,
119                         0,
120                         NULL);
121 }
122
123 static void
124 mvs_minfo_provider_init (MvsMInfoProvider *self)
125 {
126         self->priv = GET_PRIVATE (self);
127         self->priv->format = NULL;
128 }
129
130 MvsMInfoProvider*
131 mvs_minfo_provider_new (void)
132 {
133         return g_object_new (MVS_TYPE_MINFO_PROVIDER, NULL);
134 }
135
136 static void
137 display_movie_info (xmlNodePtr node)
138 {
139         xmlNodePtr cur_node = NULL;
140
141         for (cur_node = node; cur_node; cur_node = cur_node->next) {
142                 if (cur_node->type == XML_ELEMENT_NODE) {
143                         gchar *value = xmlNodeGetContent (cur_node);
144                         g_print ("[%s] = %s\n", cur_node->name, value);
145                 }
146         }
147 }
148
149 static void
150 iterate_list (xmlNodeSetPtr node_set)
151 {
152         int i = 0;
153
154         for (i = 0; i < node_set->nodeNr; i++) {
155                 xmlNodePtr node = node_set->nodeTab[i];
156                 if (node->type == XML_ELEMENT_NODE) {
157                         display_movie_info (node->children);
158                 }
159         }
160 }
161
162 static void
163 parse_xml (const char *xml_data, goffset length)
164 {
165         xmlDocPtr document = xmlReadMemory (xml_data, length,
166                         NULL,
167                         NULL,
168                         XML_PARSE_NOBLANKS | XML_PARSE_RECOVER);
169         g_return_if_fail (document);
170
171         xmlXPathContextPtr context_ptr = xmlXPathNewContext (document);
172
173         xmlXPathObjectPtr xpath_obj =
174                         xmlXPathEvalExpression (TMDB_MOVIE_XPATH, context_ptr);
175
176         xmlNodeSetPtr nodeset = xpath_obj->nodesetval;
177
178         if (nodeset->nodeNr > 0) {
179                 iterate_list (nodeset);
180         }
181
182         xmlXPathFreeNodeSetList (xpath_obj);
183         xmlXPathFreeContext (context_ptr);
184         xmlFreeDoc (document);
185 }
186
187 static void
188 response_callback (SoupSession *session, SoupMessage *message,
189                     gpointer user_data)
190 {
191         MvsMInfoProvider *self = MVS_MINFO_PROVIDER (user_data);
192         const gchar *mime = NULL;
193
194         if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code) ||
195                         message->response_body->length <= 0) {
196
197                 g_print ("%s\n", message->reason_phrase);
198         }
199         else {
200
201                 mime = soup_message_headers_get_content_type
202                                 (message->response_headers, NULL);
203
204                 parse_xml (message->response_body->data,
205                            message->response_body->length);
206         }
207
208         g_signal_emit (self, signals[RESPONSE_RECEIVED], 0);
209 }
210
211 static gchar *
212 get_query_uri (MvsMInfoProvider *self, const char *query)
213 {
214         /* METHOD/LANGUAGE/FORMAT/APIKEY/MOVIENAME */
215         gchar *uri = g_strdup_printf (TMDB_BASE_URL, TMDB_METHOD,
216                         TMDB_LANGUAGE,
217                         self->priv->format,
218                         TMDB_API_KEY,
219                         query);
220
221         return uri;
222 }
223
224 gboolean
225 mvs_minfo_provider_query (MvsMInfoProvider *self,
226                           const gchar *query)
227 {
228         g_return_val_if_fail (MVS_IS_MINFO_PROVIDER (self), FALSE);
229
230         SoupSession *session = NULL;
231         SoupMessage *message = NULL;
232         gboolean message_queued = FALSE;
233
234         gchar *uri = get_query_uri (self, query);
235
236         g_return_val_if_fail (uri, FALSE);
237
238         session = soup_session_async_new ();
239         message = soup_message_new ("GET", uri);
240
241         if (message) {
242                 soup_session_queue_message (session, message,
243                                 response_callback, self);
244                 message_queued = TRUE;
245         }
246
247         g_free (uri);
248
249         return message_queued;
250 }
251
252 gboolean
253 mvs_minfo_provider_set_format (MvsMInfoProvider *self,
254                                const gchar *format)
255 {
256         g_return_val_if_fail (MVS_IS_MINFO_PROVIDER (self), FALSE);
257
258         g_free (self->priv->format);
259
260         self->priv->format = g_strdup (format);
261
262         return TRUE;
263 }