1 /* quvi
2  * Copyright (C) 2012,2013  Toni Gundogdu <legatvs@gmail.com>
3  *
4  * This file is part of quvi <http://quvi.sourceforge.net/>.
5  *
6  * This program is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU Affero General Public
8  * License as published by the Free Software Foundation, either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General
17  * Public License along with this program.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <libxml/xmlwriter.h>
24 #include <libxml/uri.h>
25 #include <glib/gprintf.h>
26 #include <glib/gi18n.h>
27 #include <quvi.h>
28 
29 #include "lutil.h"
30 /* -- */
31 #include "lprint.h"
32 
33 struct xml_s
34 {
35   xmlTextWriterPtr w;
36   quvi_media_t qm;
37   xmlDocPtr d;
38   quvi_t q;
39 };
40 
41 typedef struct xml_s *xml_t;
42 
43 #define _chk_r_e(c)\
44   do {\
45     const gint r = c;\
46     if (r != EXIT_SUCCESS)\
47       return (_xml_handle_free(p, r));\
48     else\
49       return (EXIT_SUCCESS);\
50   } while (0)
51 
52 #define _chk_r(c)\
53   do {\
54     const gint r = c;\
55     if (r != EXIT_SUCCESS)\
56       return (_xml_handle_free(p, r));\
57   } while (0)
58 
_xml_handle_new(quvi_t q,quvi_media_t qm,gpointer * dst)59 static gint _xml_handle_new(quvi_t q, quvi_media_t qm, gpointer *dst)
60 {
61   xml_t p;
62 
63   g_assert(dst != NULL);
64 
65   p = g_new0(struct xml_s, 1);
66   p->qm = qm;
67   p->q = q;
68 
69   p->w = xmlNewTextWriterDoc(&p->d, 0);
70   if (p->w == NULL)
71     {
72       lprint_xml_errmsg(_("while creating the XML writer"));
73       xmlFreeDoc(p->d);
74       g_free(p);
75       return (EXIT_FAILURE);
76     }
77 
78   if (xmlTextWriterStartDocument(p->w, NULL, "UTF-8", NULL) <0)
79     {
80       lprint_xml_errmsg(_("while starting the XML document"));
81       xmlFreeDoc(p->d);
82       g_free(p);
83       return (EXIT_FAILURE);
84     }
85   *dst = p;
86 
87   return (EXIT_SUCCESS);
88 }
89 
_xml_handle_free(gpointer data,const gint r)90 static gint _xml_handle_free(gpointer data, const gint r)
91 {
92   xml_t p = (xml_t) data;
93 
94   if (p == NULL)
95     return (r);
96 
97   if (p->w != NULL)
98     {
99       xmlTextWriterEndDocument(p->w);
100       xmlFreeTextWriter(p->w);
101     }
102 
103   if (p->d != NULL)
104     xmlFreeDoc(p->d);
105 
106   g_free(p);
107   return (r);
108 }
109 
110 typedef enum {START_R, END_R, ATTR, START_E, END_E} ErrorMessage;
111 
112 static const gchar *_msg[]=
113 {
114   N_("while starting the XML document root element `%s'"),
115   N_("while ending the XML document root element `%s'"),
116   N_("while writing the XML attribute `%s'"),
117   N_("while starting the XML element `%s'"),
118   N_("while ending the XML element `%s'"),
119   NULL
120 };
121 
_start_e(const xml_t p,const ErrorMessage e,const gchar * w)122 static gint _start_e(const xml_t p, const ErrorMessage e, const gchar *w)
123 {
124   gint r = EXIT_SUCCESS;
125   if (xmlTextWriterStartElement(p->w, BAD_CAST w) <0)
126     {
127       lprint_xml_errmsg("%s", g_dgettext(GETTEXT_PACKAGE, _msg[e]), w);
128       r = EXIT_FAILURE;
129     }
130   return (r);
131 }
132 
_end_e(const xml_t p,const ErrorMessage e,const gchar * w)133 static gint _end_e(const xml_t p, const ErrorMessage e, const gchar *w)
134 {
135   gint r = EXIT_SUCCESS;
136   if (xmlTextWriterEndElement(p->w) <0)
137     {
138       lprint_xml_errmsg("%s", g_dgettext(GETTEXT_PACKAGE, _msg[e]), w);
139       r = EXIT_FAILURE;
140     }
141   return (r);
142 }
143 
144 extern const gchar *reserved_chars;
145 
_write_attr(const xml_t p,const gchar * n,const gchar * s)146 static gint _write_attr(const xml_t p, const gchar *n, const gchar *s)
147 {
148   xmlChar *e;
149   gint r;
150 
151   e = xmlURIEscapeStr(BAD_CAST s, BAD_CAST reserved_chars);
152 
153 
154   r = (xmlTextWriterWriteAttribute(p->w, BAD_CAST n, e) <0)
155       ? EXIT_FAILURE
156       : EXIT_SUCCESS;
157 
158   xmlFree(e);
159 
160   if (r != EXIT_SUCCESS)
161     lprint_xml_errmsg("%s", g_dgettext(GETTEXT_PACKAGE, _msg[ATTR]), n);
162 
163   return (r);
164 }
165 
_attr_new(const xml_t p,const lutilPropertyType pt,const gchar * n,const gchar * s,const gdouble d)166 static gint _attr_new(const xml_t p, const lutilPropertyType pt,
167                       const gchar *n, const gchar *s, const gdouble d)
168 {
169   gint r;
170 
171   r = lutil_chk_property_ok(p->q, pt, n, lprint_xml_errmsg);
172   if (r != EXIT_SUCCESS)
173     return (EXIT_FAILURE);
174 
175   if (s != NULL)
176     r = _write_attr(p, n, s);
177   else
178     {
179       gchar *t = g_strdup_printf("%.0f", d);
180       r = _write_attr(p, n, t);
181       g_free(t);
182     }
183   return (r);
184 }
185 
_print_buffer(xml_t p)186 static gint _print_buffer(xml_t p)
187 {
188   xmlChar *b;
189 
190   g_assert(p != NULL);
191 
192   xmlTextWriterFlush(p->w);
193   xmlDocDumpFormatMemory(p->d, &b, NULL, 0);
194 
195   if (b == NULL)
196     {
197       lprint_xml_errmsg(_("while dumping the XML document"));
198       return (EXIT_FAILURE);
199     }
200   g_print("%s", b);
201   xmlFree(b);
202 
203   return (EXIT_SUCCESS);
204 }
205 
lprint_xml_errmsg(const gchar * fmt,...)206 void lprint_xml_errmsg(const gchar *fmt, ...)
207 {
208   va_list args;
209   gchar *s;
210 
211   va_start(args, fmt);
212   if (g_vasprintf(&s, fmt, args) >0)
213     {
214       xmlChar *e = xmlURIEscapeStr(BAD_CAST s, BAD_CAST reserved_chars);
215       g_printerr("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
216                  "<error message=\"%s\" />", e);
217       xmlFree(e);
218       g_free(s);
219     }
220   va_end(args);
221 }
222 
223 typedef lprint_cb_errmsg cem;
224 
225 /* media */
226 
lprint_xml_media_new(quvi_t q,quvi_media_t qm,gpointer * dst)227 gint lprint_xml_media_new(quvi_t q, quvi_media_t qm, gpointer *dst)
228 {
229   return (_xml_handle_new(q, qm, dst));
230 }
231 
lprint_xml_media_free(gpointer data)232 void lprint_xml_media_free(gpointer data)
233 {
234   _xml_handle_free(data, -1);
235 }
236 
lprint_xml_media_print_buffer(gpointer data)237 gint lprint_xml_media_print_buffer(gpointer data)
238 {
239   xml_t p = (xml_t) data;
240 
241   g_assert(data != NULL);
242   g_assert(p->w != NULL);
243   g_assert(p->d != NULL);
244 
245   if (_end_e(p, END_E, "media") != EXIT_SUCCESS)
246     return (EXIT_FAILURE);
247 
248   if (_end_e(p, END_R, "quvi") != EXIT_SUCCESS)
249     return (EXIT_FAILURE);
250   else
251     return (_print_buffer(p));
252 }
253 
_mp_attr_s(const xml_t p,const QuviMediaProperty qmp,const gchar * n)254 static gint _mp_attr_s(const xml_t p, const QuviMediaProperty qmp,
255                        const gchar *n)
256 {
257   gchar *s = NULL;
258   quvi_media_get(p->qm, qmp, &s);
259   _chk_r_e(_attr_new(p, UTIL_PROPERTY_TYPE_MEDIA, n, s, -1));
260 }
261 
262 #define _print_mp_attr_s(n)\
263   do {\
264     if (_mp_attr_s(p, n, #n) != EXIT_SUCCESS)\
265       return (EXIT_FAILURE);\
266   } while (0)
267 
268 #define _print_mp_attr_set_r_s(n)\
269   do {\
270     r = _mp_attr_s(p, n, #n);\
271   } while (0)
272 
_mp_attr_d(const xml_t p,const QuviMediaProperty qmp,const gchar * n)273 static gint _mp_attr_d(const xml_t p, const QuviMediaProperty qmp,
274                        const gchar *n)
275 {
276   gdouble d = 0;
277   quvi_media_get(p->qm, qmp, &d);
278   _chk_r_e(_attr_new(p, UTIL_PROPERTY_TYPE_MEDIA, n, NULL, d));
279 }
280 
281 #define _print_mp_attr_d(n)\
282   do {\
283     if (_mp_attr_d(p, n, #n) != EXIT_SUCCESS)\
284       return (EXIT_FAILURE);\
285   } while (0)
286 
_mi_attr_s(const xml_t p,const quvi_http_metainfo_t qmi,const QuviHTTPMetaInfoProperty qmip,const gchar * n)287 static gint _mi_attr_s(const xml_t p, const quvi_http_metainfo_t qmi,
288                        const QuviHTTPMetaInfoProperty qmip, const gchar *n)
289 {
290   gchar *s = NULL;
291   quvi_http_metainfo_get(qmi, qmip, &s);
292   _chk_r_e(_attr_new(p, UTIL_PROPERTY_TYPE_HTTP_METAINFO, n, s, -1));
293 }
294 
295 #define _print_mi_attr_s(n)\
296   do {\
297     if (_mi_attr_s(p, qmi, n, #n) != EXIT_SUCCESS)\
298       return (EXIT_FAILURE);\
299   } while (0)
300 
_mi_attr_d(const xml_t p,const quvi_http_metainfo_t qmi,const QuviHTTPMetaInfoProperty qmip,const gchar * n)301 static gint _mi_attr_d(const xml_t p, const quvi_http_metainfo_t qmi,
302                        const QuviHTTPMetaInfoProperty qmip, const gchar *n)
303 {
304   gdouble d = 0;
305   quvi_http_metainfo_get(qmi, qmip, &d);
306   _chk_r_e(_attr_new(p, UTIL_PROPERTY_TYPE_HTTP_METAINFO, n, NULL, d));
307 }
308 
309 #define _print_mi_attr_d(n)\
310   do {\
311     if (_mi_attr_d(p, qmi, n, #n) != EXIT_SUCCESS)\
312       return (EXIT_FAILURE);\
313   } while (0)
314 
315 static gint
_print_media_stream_properties(const xml_t p,const quvi_http_metainfo_t qmi)316 _print_media_stream_properties(const xml_t p, const quvi_http_metainfo_t qmi)
317 {
318   g_assert(p->w != NULL);
319   g_assert(p->d != NULL);
320 
321   _chk_r(_start_e(p, START_E, "stream"));
322 
323   _print_mp_attr_s(QUVI_MEDIA_STREAM_PROPERTY_VIDEO_ENCODING);
324   _print_mp_attr_s(QUVI_MEDIA_STREAM_PROPERTY_AUDIO_ENCODING);
325   _print_mp_attr_s(QUVI_MEDIA_STREAM_PROPERTY_CONTAINER);
326   _print_mp_attr_s(QUVI_MEDIA_STREAM_PROPERTY_URL);
327   _print_mp_attr_s(QUVI_MEDIA_STREAM_PROPERTY_ID);
328 
329   _print_mp_attr_d(QUVI_MEDIA_STREAM_PROPERTY_VIDEO_BITRATE_KBIT_S);
330   _print_mp_attr_d(QUVI_MEDIA_STREAM_PROPERTY_AUDIO_BITRATE_KBIT_S);
331   _print_mp_attr_d(QUVI_MEDIA_STREAM_PROPERTY_VIDEO_HEIGHT);
332   _print_mp_attr_d(QUVI_MEDIA_STREAM_PROPERTY_VIDEO_WIDTH);
333 
334   if (qmi != NULL)
335     {
336       _print_mi_attr_s(QUVI_HTTP_METAINFO_PROPERTY_FILE_EXTENSION);
337       _print_mi_attr_s(QUVI_HTTP_METAINFO_PROPERTY_CONTENT_TYPE);
338       _print_mi_attr_d(QUVI_HTTP_METAINFO_PROPERTY_LENGTH_BYTES);
339     }
340   return (_end_e(p, END_E, "stream"));
341 }
342 
343 #undef _print_mi_s
344 #undef _print_mi_d
345 
346 gint
lprint_xml_media_stream_properties(quvi_http_metainfo_t qmi,gpointer data)347 lprint_xml_media_stream_properties(quvi_http_metainfo_t qmi, gpointer data)
348 {
349   g_assert(data != NULL);
350   return (_print_media_stream_properties(data, qmi));
351 }
352 
lprint_xml_media_streams_available(quvi_t q,quvi_media_t qm)353 gint lprint_xml_media_streams_available(quvi_t q, quvi_media_t qm)
354 {
355   xml_t p;
356 
357   if (_xml_handle_new(q, qm, (gpointer*) &p) != EXIT_SUCCESS)
358     return (EXIT_FAILURE);
359 
360   _chk_r(_start_e(p, START_R, "quvi"));
361   _chk_r(_start_e(p, START_E, "media"));
362   _chk_r(_start_e(p, START_E, "streams"));
363 
364   while (quvi_media_stream_next(qm) == QUVI_TRUE)
365     {
366       _chk_r(_print_media_stream_properties(p, NULL));
367     }
368 
369   _chk_r(_end_e(p, END_E, "streams"));
370   _chk_r(_end_e(p, END_E, "media"));
371   _chk_r(_end_e(p, END_R, "quvi"));
372 
373   _chk_r(_print_buffer(p));
374 
375   return (_xml_handle_free(p, EXIT_SUCCESS));
376 }
377 
lprint_xml_media_properties(gpointer data)378 gint lprint_xml_media_properties(gpointer data)
379 {
380   xml_t p = (xml_t) data;
381 
382   g_assert(data != NULL);
383   g_assert(p->w != NULL);
384   g_assert(p->d != NULL);
385 
386   _chk_r(_start_e(p, START_R, "quvi"));
387   _chk_r(_start_e(p, START_E, "media"));
388 
389   _print_mp_attr_s(QUVI_MEDIA_PROPERTY_THUMBNAIL_URL);
390   _print_mp_attr_s(QUVI_MEDIA_PROPERTY_TITLE);
391   _print_mp_attr_s(QUVI_MEDIA_PROPERTY_ID);
392 
393   _print_mp_attr_d(QUVI_MEDIA_PROPERTY_START_TIME_MS);
394   _print_mp_attr_d(QUVI_MEDIA_PROPERTY_DURATION_MS);
395 
396   return (EXIT_SUCCESS);
397 }
398 
399 #undef _print_mp_s
400 #undef _print_mp_d
401 
402 /* playlist */
403 
lprint_xml_playlist_new(quvi_t q,gpointer * dst)404 gint lprint_xml_playlist_new(quvi_t q, gpointer *dst)
405 {
406   return (_xml_handle_new(q, NULL, dst));
407 }
408 
lprint_xml_playlist_free(gpointer dst)409 void lprint_xml_playlist_free(gpointer dst)
410 {
411   _xml_handle_free(dst, -1);
412 }
413 
lprint_xml_playlist_print_buffer(gpointer data)414 gint lprint_xml_playlist_print_buffer(gpointer data)
415 {
416   return (_print_buffer(data));
417 }
418 
_pp_attr_s(const xml_t p,const quvi_playlist_t qp,const QuviPlaylistProperty qpp,const gchar * n)419 static gint _pp_attr_s(const xml_t p, const quvi_playlist_t qp,
420                        const QuviPlaylistProperty qpp, const gchar *n)
421 {
422   gchar *s = NULL;
423   quvi_playlist_get(qp, qpp, &s);
424   _chk_r_e(_attr_new(p, UTIL_PROPERTY_TYPE_PLAYLIST, n, s, -1));
425 }
426 
427 #define _print_pp_attr_s(n)\
428   do {\
429     if (_pp_attr_s(p, qp, n, #n) != EXIT_SUCCESS)\
430       return (EXIT_FAILURE);\
431   } while (0)
432 
_pp_attr_d(const xml_t p,const quvi_playlist_t qp,const QuviPlaylistProperty qpp,const gchar * n)433 static gint _pp_attr_d(const xml_t p, const quvi_playlist_t qp,
434                        const QuviPlaylistProperty qpp, const gchar *n)
435 {
436   gdouble d = 0;
437   quvi_playlist_get(qp, qpp, &d);
438   _chk_r_e(_attr_new(p, UTIL_PROPERTY_TYPE_PLAYLIST, n, NULL, d));
439 }
440 
441 #define _print_pp_attr_d(n)\
442   do {\
443     if (_pp_attr_d(p, qp, n, #n) != EXIT_SUCCESS)\
444       return (EXIT_FAILURE);\
445   } while (0)
446 
lprint_xml_playlist_properties(quvi_playlist_t qp,gpointer data)447 gint lprint_xml_playlist_properties(quvi_playlist_t qp, gpointer data)
448 {
449   xml_t p = (xml_t) data;
450 
451   g_assert(data != NULL);
452   g_assert(qp != NULL);
453   g_assert(p->w != NULL);
454   g_assert(p->d != NULL);
455 
456   _chk_r(_start_e(p, START_R, "quvi"));
457   _chk_r(_start_e(p, START_E, "playlist"));
458 
459   _print_pp_attr_s(QUVI_PLAYLIST_PROPERTY_THUMBNAIL_URL);
460   _print_pp_attr_s(QUVI_PLAYLIST_PROPERTY_TITLE);
461   _print_pp_attr_s(QUVI_PLAYLIST_PROPERTY_ID);
462 
463   while (quvi_playlist_media_next(qp) == QUVI_TRUE)
464     {
465       _chk_r(_start_e(p, START_E, "media"));
466       _print_pp_attr_d(QUVI_PLAYLIST_MEDIA_PROPERTY_DURATION_MS);
467       _print_pp_attr_s(QUVI_PLAYLIST_MEDIA_PROPERTY_TITLE);
468       _print_pp_attr_s(QUVI_PLAYLIST_MEDIA_PROPERTY_URL);
469       _chk_r(_end_e(p, END_E, "media"));
470     }
471 
472   _chk_r(_end_e(p, END_E, "playlist"));
473   _chk_r_e(_end_e(p, END_R, "quvi"));
474 }
475 
476 #undef _print_pp_s
477 #undef _print_pp_d
478 
479 /* scan */
480 
lprint_xml_scan_new(quvi_t q,gpointer * dst)481 gint lprint_xml_scan_new(quvi_t q, gpointer *dst)
482 {
483   return (_xml_handle_new(q, NULL, dst));
484 }
485 
lprint_xml_scan_free(gpointer data)486 void lprint_xml_scan_free(gpointer data)
487 {
488   _xml_handle_free(data, -1);
489 }
490 
lprint_xml_scan_print_buffer(gpointer data)491 gint lprint_xml_scan_print_buffer(gpointer data)
492 {
493   return (_print_buffer(data));
494 }
495 
lprint_xml_scan_properties(quvi_scan_t qs,gpointer data)496 gint lprint_xml_scan_properties(quvi_scan_t qs, gpointer data)
497 {
498   const gchar *s;
499   xml_t p;
500 
501   g_assert(data != NULL);
502   g_assert(qs != NULL);
503 
504   p = (xml_t) data;
505 
506   _chk_r(_start_e(p, START_R, "quvi"));
507   _chk_r(_start_e(p, START_E, "scan"));
508 
509   while ( (s = quvi_scan_next_media_url(qs)) != NULL)
510     {
511       _chk_r(_start_e(p, START_E, "media"));
512       _chk_r(_write_attr(p, "url", s));
513       _chk_r(_end_e(p, END_E, "media"));
514     }
515 
516   _chk_r(_end_e(p, END_E, "scan"));
517   _chk_r_e(_end_e(p, END_R, "quvi"));
518 }
519 
520 /* subtitle */
521 
lprint_xml_subtitle_new(quvi_t q,gpointer * dst)522 gint lprint_xml_subtitle_new(quvi_t q, gpointer *dst)
523 {
524   return (_xml_handle_new(q, NULL, dst));
525 }
526 
lprint_xml_subtitle_free(gpointer data)527 void lprint_xml_subtitle_free(gpointer data)
528 {
529   _xml_handle_free(data, -1);
530 }
531 
lprint_xml_subtitle_print_buffer(gpointer data)532 gint lprint_xml_subtitle_print_buffer(gpointer data)
533 {
534   return (_print_buffer(data));
535 }
536 
_stp_attr_d(const xml_t p,const quvi_subtitle_type_t qst,const QuviSubtitleTypeProperty qstp,const gchar * n)537 static gint _stp_attr_d(const xml_t p, const quvi_subtitle_type_t qst,
538                         const QuviSubtitleTypeProperty qstp, const gchar *n)
539 {
540   gdouble d = 0;
541   quvi_subtitle_type_get(qst, qstp, &d);
542   _chk_r_e(_attr_new(p, UTIL_PROPERTY_TYPE_SUBTITLE_TYPE, n, NULL, d));
543 }
544 
545 #define _print_stp_attr_d(n)\
546   do {\
547     if (_stp_attr_d(p, t, n, #n) != EXIT_SUCCESS)\
548       return (EXIT_FAILURE);\
549   } while (0)
550 
_slp_attr_s(const xml_t p,const quvi_subtitle_lang_t qsl,const QuviSubtitleLangProperty qslp,const gchar * n)551 static gint _slp_attr_s(const xml_t p, const quvi_subtitle_lang_t qsl,
552                         const QuviSubtitleLangProperty qslp, const gchar *n)
553 {
554   gchar *s = NULL;
555   quvi_subtitle_lang_get(qsl, qslp, &s);
556   _chk_r_e(_attr_new(p, UTIL_PROPERTY_TYPE_SUBTITLE_LANGUAGE, n, s, -1));
557 }
558 
559 #define _print_slp_attr_s(n)\
560   do {\
561     if (_slp_attr_s(p, l, n, #n) != EXIT_SUCCESS)\
562       return (EXIT_FAILURE);\
563   } while (0)
564 
_write_lang_properties(const quvi_subtitle_lang_t l,const xml_t p)565 static gint _write_lang_properties(const quvi_subtitle_lang_t l,
566                                    const xml_t p)
567 {
568   _chk_r(_start_e(p, START_E, "language"));
569 
570   _print_slp_attr_s(QUVI_SUBTITLE_LANG_PROPERTY_TRANSLATED);
571   _print_slp_attr_s(QUVI_SUBTITLE_LANG_PROPERTY_ORIGINAL);
572   _print_slp_attr_s(QUVI_SUBTITLE_LANG_PROPERTY_CODE);
573   _print_slp_attr_s(QUVI_SUBTITLE_LANG_PROPERTY_URL);
574   _print_slp_attr_s(QUVI_SUBTITLE_LANG_PROPERTY_ID);
575 
576   _chk_r_e(_end_e(p, END_E, "language"));
577 }
578 
579 gint
lprint_xml_subtitle_lang_properties(quvi_subtitle_lang_t l,gpointer data)580 lprint_xml_subtitle_lang_properties(quvi_subtitle_lang_t l, gpointer data)
581 {
582   xml_t p = (xml_t) data;
583 
584   g_assert(p->w != NULL);
585   g_assert(p->d != NULL);
586 
587   _chk_r(_start_e(p, START_R, "quvi"));
588   _chk_r(_start_e(p, START_E, "media"));
589   _chk_r(_start_e(p, START_E, "subtitle"));
590   _chk_r(_write_lang_properties(l, p));
591   _chk_r(_end_e(p, END_E, "subtitle"));
592   _chk_r(_end_e(p, END_E, "media"));
593   _chk_r_e(_end_e(p, END_R, "quvi"));
594 }
595 
lprint_xml_subtitles_available(quvi_t q,quvi_subtitle_t qsub)596 gint lprint_xml_subtitles_available(quvi_t q, quvi_subtitle_t qsub)
597 {
598   quvi_subtitle_type_t t;
599   quvi_subtitle_lang_t l;
600   xml_t p;
601 
602   g_assert(qsub != NULL);
603   g_assert(q != NULL);
604 
605   if (_xml_handle_new(q, NULL, (gpointer*) &p) != EXIT_SUCCESS)
606     return (EXIT_FAILURE);
607 
608   _chk_r(_start_e(p, START_R, "quvi"));
609   _chk_r(_start_e(p, START_E, "media"));
610   _chk_r(_start_e(p, START_E, "subtitles"));
611 
612   while ( (t = quvi_subtitle_type_next(qsub)) != NULL)
613     {
614       _chk_r(_start_e(p, START_E, "type"));
615 
616       _print_stp_attr_d(QUVI_SUBTITLE_TYPE_PROPERTY_FORMAT);
617       _print_stp_attr_d(QUVI_SUBTITLE_TYPE_PROPERTY_TYPE);
618 
619       while ( (l = quvi_subtitle_lang_next(t)) != NULL)
620         {
621           _chk_r(_write_lang_properties(l, p));
622         }
623       _chk_r(_end_e(p, END_E, "type"));
624     }
625 
626   _chk_r(_end_e(p, END_E, "subtitles"));
627   _chk_r(_end_e(p, END_E, "media"));
628   _chk_r(_end_e(p, END_R, "quvi"));
629 
630   _chk_r(_print_buffer(p));
631 
632   return (_xml_handle_free(p, EXIT_SUCCESS));
633 }
634 
635 /* vim: set ts=2 sw=2 tw=72 expandtab: */
636