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