Movie Info Provider: Added format property
[maevies] / examples / gmovies.c
1 /* Queries Google movies for the theaters of a city and parses
2  * the response using libxml2. 
3  */ 
4
5 #include <libxml/HTMLparser.h>
6 #include <libxml/tree.h>
7 #include <rest/rest-proxy.h>
8 #include <glib.h>
9 #include <stdio.h>
10 #include <unistd.h>
11
12
13 typedef struct struct_theater
14 {
15         gchar* name;
16         gchar* address;
17         xmlNodePtr data; //points to the tree node with the theater info, including movies
18 } Theater;
19
20
21 typedef struct struct_showtime
22 {
23         gchar* time;
24 } ShowTime;
25
26
27 typedef struct struct_movie
28 {
29         gchar* title;
30         int rating;
31         gchar* info;
32         GList* showTimes;
33 } Movie;
34         
35
36 /** Search for a sibling node by the name of the sibling node */
37 xmlNodePtr getSiblingByName(xmlNodePtr node, xmlChar* name, int nameLen)
38 {
39         
40         xmlNodePtr sibling = node->next;
41         while((sibling != NULL) && (strncmp(sibling->name, name, nameLen) != 0)) {
42                 sibling = sibling->next;
43         }
44         
45         return sibling;
46         
47
48
49 /** Search a child node by its node name */
50 xmlNodePtr getChildByName(xmlNodePtr node, xmlChar* name, int nameLen)
51 {
52         return getSiblingByName(node->children, name, nameLen);
53 }
54
55 /** Search the first sibling node that has an attribute 'attr' 
56  * with the value 'attrValue' */
57 xmlNodePtr getFirstSiblingByAttributeValue(
58         xmlNodePtr sibling, xmlChar* attr, xmlChar * attrValue, int attrValueLen)
59 {
60         xmlNodePtr tempNode = sibling;
61         xmlNodePtr result = NULL;
62         
63         while ((tempNode != NULL) && (result == NULL)) {
64                 xmlChar* value = xmlGetProp(tempNode, attr);
65                 if (value != NULL) {
66                         if (strncmp(value, attrValue, attrValueLen) == 0) {
67                                 result = tempNode;
68                         }
69                         free(value);
70                 }
71                 tempNode = tempNode->next;
72         }
73         
74         return result;
75 }
76
77 /** Search the first child node that has an attribute 'attr' with
78  * value 'attrValue' */
79 xmlNodePtr getFirstChildByAttributeValue(
80         xmlNodePtr node, xmlChar* attr, xmlChar * attrValue, int attrValueLen)
81 {
82         return getFirstSiblingByAttributeValue(node->children, attr, attrValue, attrValueLen);
83 }
84
85 /** Advances N sibling nodes in the node list */
86 xmlNodePtr jumpXSiblings(xmlNodePtr node, int siblings)
87 {
88         xmlNodePtr r = node;
89         
90         int i = 0;
91         for(; i<siblings; i++) {
92                 r = r->next;
93         }
94         
95         return r;
96 }
97
98
99 int childrenCount(xmlNodePtr node)
100 {
101         int i=0;
102         xmlNodePtr nav = node->children;
103         while(nav != NULL) {
104                 i++;
105                 nav = nav->next;
106         }
107         
108         return i;
109 }
110
111
112 /** Search the <div> with the results and returns it, or NULL
113  * if it couldn't be found */
114 xmlNodePtr getMovieResultsDiv(xmlNodePtr root)
115 {
116         //<body>
117         xmlNodePtr body = getSiblingByName(root->children, "body", 4);
118         
119         //<div id="results">
120         xmlNodePtr tempNode = getFirstChildByAttributeValue(body, "id", "results", 8);
121         
122         if (tempNode == NULL) {
123                 //no results
124                 return NULL;
125         }
126         
127         //<div id="movie_results">
128         tempNode = getFirstChildByAttributeValue(tempNode, "id", "movie_results", 14);
129         
130         if (tempNode == NULL) {
131                 //no results
132                 return NULL;
133         }
134         
135         //<div class="movie_results">
136         tempNode = getFirstChildByAttributeValue(tempNode, "class", "movie_results", 14);
137         
138         if (tempNode == NULL) {
139                 //no results
140                 return NULL;
141         }
142         
143         return tempNode;
144         
145 }
146
147
148 /** Parses the results and returns a list with all the theaters.
149  * Theater info is parsed and returned as Theater 'objects", the movie
150  * info is not parsed */
151 GList* getTheaterList(xmlNodePtr movieResults)
152 {
153         //<div class="movie_results"><div class="theater"/><div class="theater"/>...
154         xmlNodePtr nav = movieResults->children;
155         xmlNodePtr tmp1, tmp2, tmp3 = NULL;
156         
157         GList* resultList = NULL;
158         
159         while(nav != NULL) {
160                 tmp1 = getFirstSiblingByAttributeValue(nav, "class", "theater", 7);
161                 
162                 /*
163                  *  <div class="theater">
164                  *              <div class="desc">
165                  *                      <div class="name"/><div class="info"/>
166                  *              </div>
167                  *              <div class="showtimes"/>
168                  * </div> 
169                  */
170                 
171                 if (tmp1 != NULL) { //its theater data
172                         Theater* t = malloc(sizeof(Theater));
173                         tmp2 = getFirstChildByAttributeValue(tmp1, "class", "desc", 4);
174                         if (tmp2 != NULL) { //has desc
175                                 tmp3 = getFirstChildByAttributeValue(tmp2, "class", "name", 4);
176                                 if ((tmp3 != NULL) && (tmp3->children != NULL) && (tmp3->children->children != NULL)) { //<div class="name"><a><span/>
177                                         t->name = xmlNodeGetContent(tmp3->children->children);
178                                 }
179                                 tmp3 = getFirstChildByAttributeValue(tmp2, "class", "info", 4);
180                                 if (tmp3 != NULL) { //<div class="info">
181                                         t->address = xmlNodeGetContent(tmp3);
182                                 }       
183                         }
184                         t->data = tmp1;
185                         resultList = g_list_append(resultList, t);
186                 }
187                 nav = nav->next;
188         }
189         
190         return resultList;
191         
192 }
193
194 GList* getShowtimes(gchar* times)
195 {
196         GList* resultList = NULL;
197         
198         gchar** timesArray = g_strsplit(times, " ", -1);
199         
200         int i = 0;
201         for(i=0; timesArray[i] != NULL; i++) {
202                 ShowTime* st = malloc(sizeof(ShowTime));
203                 st->time = g_strndup(timesArray[i], 5);
204                 resultList = g_list_append(resultList, st);
205         }
206         
207         g_strfreev(timesArray);
208         
209         return resultList;
210 }
211
212
213 GList* getMovieList(xmlNodePtr movieSideDiv)
214 {
215         
216         xmlNodePtr nav = movieSideDiv->children;
217         xmlNodePtr tmp1, tmp2, tmp3 = NULL;
218         
219         GList* resultList = NULL;
220         
221         while(nav != NULL) {
222                 tmp1 = getFirstSiblingByAttributeValue(nav, "class", "movie", 5);
223                 if (tmp1 != NULL) { //is a movie
224                         Movie* m = malloc(sizeof(Movie));
225                         tmp2 = getFirstChildByAttributeValue(tmp1, "class", "name", 4);
226                         if ((tmp2 != NULL) && (tmp2->children != NULL) && (tmp2->children->children != NULL)) { //<div class="name"><a><span/>
227                                 m->title = xmlNodeGetContent(tmp2->children->children);
228                         }
229                         tmp2 = getFirstChildByAttributeValue(tmp1, "class", "info", 4);
230                         if (tmp2 != NULL) {
231                                 m->info = xmlNodeGetContent(tmp2);
232                         }
233                         tmp2 = getFirstChildByAttributeValue(tmp1, "class", "times", 5);
234                         if (tmp2 != NULL) {
235                                 gchar* showtimesStr = xmlNodeGetContent(tmp2);
236                                 GList* showtimes = getShowtimes(showtimesStr);
237                                 free(showtimesStr);
238                                 m->showTimes = showtimes;
239                         }
240                         resultList = g_list_append(resultList, m);
241                 }
242                 
243                 nav = nav->next;
244         }
245                                 
246         return resultList;
247 }
248
249 GList* getTheaterMovies(Theater* t)
250 {
251         
252         /*
253          * <div class="theater">
254          *              <div class="desc"/>
255          *              <div class="showtimes">
256          *                      <div class="show_left">
257          *                              <div class="movie/>
258          *                              ...
259          *                      </div>
260          *                      <div class="show_right">
261          *                              <div class="movie"/>
262          *                              ...
263          *                      </div>
264          *              </div>
265          * </div>
266          */
267         
268         GList* left = NULL;
269         GList* right = NULL;
270         xmlNodePtr showtimesDiv = getFirstChildByAttributeValue(t->data, "class", "showtimes", 9);
271         if (showtimesDiv != NULL) {
272                 
273                 xmlNodePtr showLeft = getFirstChildByAttributeValue(showtimesDiv, "class", "show_left", 9);     
274                 if (showLeft != NULL)
275                         left = getMovieList(showLeft);
276                         
277                 xmlNodePtr showRight = getFirstChildByAttributeValue(showtimesDiv, "class", "show_right", 10);  
278                 if (showRight != NULL)
279                         right = getMovieList(showRight);        
280                         
281                 return g_list_concat(left, right);
282                         
283         }
284                 
285         
286 }
287
288
289 void deleteShowTime(ShowTime* st)
290 {
291         free(st->time);
292         free(st);
293 }
294
295
296 void deleteShowTimeList(GList * showTimeList)
297 {
298         g_list_foreach(showTimeList, (GFunc) deleteShowTime, NULL);
299         g_list_free(showTimeList);
300 }
301
302
303 void deleteMovie(Movie* m)
304 {
305         free(m->title);
306         free(m->info);
307         deleteShowTimeList(m->showTimes);
308         free(m);
309 }
310
311
312 void deleteMovieList(GList* movieList)
313 {
314         g_list_foreach(movieList, (GFunc) deleteMovie, NULL);
315         g_list_free(movieList);
316 }
317
318
319 void deleteTheater(Theater* t)
320 {
321         free(t->name);
322         free(t->address);
323         free(t);
324 }
325
326 void deleteTheaterList(GList* theaterList)
327 {
328         g_list_foreach(theaterList, (GFunc) deleteTheater, NULL);
329         g_list_free(theaterList);
330 }
331
332
333 void showTime(ShowTime* st, gpointer nothing)
334 {
335         printf("%s ", st->time);
336 }
337
338 void showMovie(Movie* m, gpointer nothing)
339 {
340         printf("        Title = %s\n", m->title);
341         printf("        Info = %s\n", m->info);
342         printf("        Schedule = ");
343         g_list_foreach(m->showTimes, (GFunc) showTime, NULL);
344         printf("\n");
345 }
346
347
348 void showTheater(Theater * t, gpointer nothing)
349 {
350         printf("Name = %s\n", t->name);
351         printf("Info = %s\n", t->address);
352         GList* movieList = getTheaterMovies(t);
353         g_list_foreach(movieList, (GFunc) showMovie, NULL);
354         deleteMovieList(movieList);
355         printf("\n");
356 }
357
358
359 int main (int argc, char ** argv)
360 {
361
362         if (argc != 2) {
363                 printf("usage: gmovies city_name\n");
364                 exit(-1);
365         }
366
367         RestProxy *proxy;
368         RestProxyCall *call;
369         const gchar *payload;
370         const char *city = argv[1];
371         gssize len;
372
373         g_thread_init(NULL);
374         g_type_init();
375
376         proxy = rest_proxy_new(
377                         "http://www.google.com/movies",
378                         FALSE);
379         call = rest_proxy_new_call(proxy);
380
381         rest_proxy_call_add_params(call,
382                         "near", city,
383                         NULL);
384         rest_proxy_call_run(call, NULL, NULL);
385
386         payload = rest_proxy_call_get_payload(call);
387         len = rest_proxy_call_get_payload_length(call);
388
389         htmlDocPtr doc = htmlReadMemory(payload, len, "http://movies.google.com", "UTF-8", HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
390
391         xmlNodePtr root = xmlDocGetRootElement(doc);//html
392         
393         //get the body node
394         xmlNodePtr movieResults = getMovieResultsDiv(root);     
395
396         if (movieResults == NULL) {
397                 printf("NO RESULTS.\n");
398                 exit(-1);
399         }
400         
401         GList* theaterList = getTheaterList(movieResults);
402         
403         g_list_foreach(theaterList, (GFunc) showTheater, NULL);
404         
405         deleteTheaterList(theaterList);
406         
407         xmlFreeDoc(doc);
408
409         g_object_unref(call);
410         g_object_unref(proxy);
411         
412         exit(0);
413
414 }
415