]> git.sesse.net Git - vlc/blob - modules/demux/playlist/podcast.c
xspf: fix type limit warning
[vlc] / modules / demux / playlist / podcast.c
1 /*****************************************************************************
2  * podcast.c : podcast playlist imports
3  *****************************************************************************
4  * Copyright (C) 2005-2009 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_demux.h>
33
34 #include "playlist.h"
35 #include <vlc_xml.h>
36 #include <vlc_strings.h>
37
38 /*****************************************************************************
39  * Local prototypes
40  *****************************************************************************/
41 static int Demux( demux_t *p_demux);
42 static mtime_t strTimeToMTime( const char *psz );
43
44 /*****************************************************************************
45  * Import_podcast: main import function
46  *****************************************************************************/
47 int Import_podcast( vlc_object_t *p_this )
48 {
49     demux_t *p_demux = (demux_t *)p_this;
50
51     if( !demux_IsForced( p_demux, "podcast" ) )
52         return VLC_EGENERIC;
53
54     p_demux->pf_demux = Demux;
55     p_demux->pf_control = Control;
56     msg_Dbg( p_demux, "using podcast reader" );
57
58     return VLC_SUCCESS;
59 }
60
61 /* "specs" : http://phobos.apple.com/static/iTunesRSS.html */
62 static int Demux( demux_t *p_demux )
63 {
64     bool b_item = false;
65     bool b_image = false;
66
67     xml_reader_t *p_xml_reader;
68     char *psz_elname = NULL;
69     char *psz_item_mrl = NULL;
70     char *psz_item_size = NULL;
71     char *psz_item_type = NULL;
72     char *psz_item_name = NULL;
73     char *psz_item_date = NULL;
74     char *psz_item_author = NULL;
75     char *psz_item_category = NULL;
76     char *psz_item_duration = NULL;
77     char *psz_item_keywords = NULL;
78     char *psz_item_subtitle = NULL;
79     char *psz_item_summary = NULL;
80     char *psz_art_url = NULL;
81     const char *node;
82     int i_type;
83     input_item_t *p_input;
84     input_item_node_t *p_subitems = NULL;
85
86     input_item_t *p_current_input = GetCurrentItem(p_demux);
87
88     p_xml_reader = xml_ReaderCreate( p_demux, p_demux->s );
89     if( !p_xml_reader )
90         goto error;
91
92     /* xml */
93     /* check root node */
94     if( xml_ReaderNextNode( p_xml_reader, &node ) != XML_READER_STARTELEM )
95     {
96         msg_Err( p_demux, "invalid file (no root node)" );
97         goto error;
98     }
99
100     if( strcmp( node, "rss" ) )
101     {
102         msg_Err( p_demux, "invalid root node <%s>", node );
103         goto error;
104     }
105
106     p_subitems = input_item_node_Create( p_current_input );
107
108     while( (i_type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
109     {
110         switch( i_type )
111         {
112             case XML_READER_STARTELEM:
113             {
114                 free( psz_elname );
115                 psz_elname = strdup( node );
116                 if( unlikely(!node) )
117                     goto error;
118
119                 if( !strcmp( node, "item" ) )
120                     b_item = true;
121                 else if( !strcmp( node, "image" ) )
122                     b_image = true;
123
124                 // Read the attributes
125                 const char *attr, *value;
126                 while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) )
127                 {
128                     if( !strcmp( node, "enclosure" ) )
129                     {
130                         char **p = NULL;
131                         if( !strcmp( attr, "url" ) )
132                             p = &psz_item_mrl;
133                         else if( !strcmp( attr, "length" ) )
134                             p = &psz_item_size;
135                         else if( !strcmp( attr, "type" ) )
136                             p = &psz_item_type;
137                         if( p != NULL )
138                         {
139                             free( *p );
140                             *p = strdup( value );
141                         }
142                         else
143                             msg_Dbg( p_demux,"unhandled attribute %s in <%s>",
144                                      attr, node );
145                     }
146                     else
147                         msg_Dbg( p_demux,"unhandled attribute %s in <%s>",
148                                  attr, node );
149                 }
150                 break;
151             }
152
153             case XML_READER_TEXT:
154             {
155                 if(!psz_elname) break;
156
157                 /* item specific meta data */
158                 if( b_item )
159                 {
160                     char **p;
161
162                     if( !strcmp( psz_elname, "title" ) )
163                         p = &psz_item_name;
164                     else if( !strcmp( psz_elname, "itunes:author" ) ||
165                              !strcmp( psz_elname, "author" ) )
166                         /* <author> isn't standard iTunes podcast stuff */
167                         p = &psz_item_author;
168                     else if( !strcmp( psz_elname, "itunes:summary" ) ||
169                              !strcmp( psz_elname, "description" ) )
170                         /* <description> isn't standard iTunes podcast stuff */
171                         p = &psz_item_summary;
172                     else if( !strcmp( psz_elname, "pubDate" ) )
173                         p = &psz_item_date;
174                     else if( !strcmp( psz_elname, "itunes:category" ) )
175                         p = &psz_item_category;
176                     else if( !strcmp( psz_elname, "itunes:duration" ) )
177                         p = &psz_item_duration;
178                     else if( !strcmp( psz_elname, "itunes:keywords" ) )
179                         p = &psz_item_keywords;
180                     else if( !strcmp( psz_elname, "itunes:subtitle" ) )
181                         p = &psz_item_subtitle;
182                     else
183                         break;
184
185                     free( *p );
186                     *p = strdup( node );
187                 }
188                 /* toplevel meta data */
189                 else if( !b_image )
190                 {
191                     if( !strcmp( psz_elname, "title" ) )
192                         input_item_SetName( p_current_input, node );
193 #define ADD_GINFO( info, name ) \
194     else if( !strcmp( psz_elname, name ) ) \
195         input_item_AddInfo( p_current_input, _("Podcast Info"), \
196                             info, "%s", node );
197                     ADD_GINFO( _("Podcast Link"), "link" )
198                     ADD_GINFO( _("Podcast Copyright"), "copyright" )
199                     ADD_GINFO( _("Podcast Category"), "itunes:category" )
200                     ADD_GINFO( _("Podcast Keywords"), "itunes:keywords" )
201                     ADD_GINFO( _("Podcast Subtitle"), "itunes:subtitle" )
202 #undef ADD_GINFO
203                     else if( !strcmp( psz_elname, "itunes:summary" ) ||
204                              !strcmp( psz_elname, "description" ) )
205                     { /* <description> isn't standard iTunes podcast stuff */
206                         input_item_AddInfo( p_current_input,
207                             _( "Podcast Info" ), _( "Podcast Summary" ),
208                             "%s", node );
209                     }
210                 }
211                 else
212                 {
213                     if( !strcmp( psz_elname, "url" ) )
214                     {
215                         free( psz_art_url );
216                         psz_art_url = strdup( node );
217                     }
218                     else
219                         msg_Dbg( p_demux, "unhandled text in element <%s>",
220                                  psz_elname );
221                 }
222                 break;
223             }
224
225             // End element
226             case XML_READER_ENDELEM:
227             {
228                 FREENULL( psz_elname );
229
230                 if( !strcmp( node, "item" ) )
231                 {
232                     if( psz_item_mrl == NULL )
233                     {
234                         if (psz_item_name)
235                             msg_Warn( p_demux, "invalid XML item, skipping %s",
236                                       psz_item_name );
237                         else
238                             msg_Warn( p_demux, "invalid XML item, skipped" );
239                         FREENULL( psz_item_name );
240                         FREENULL( psz_item_size );
241                         FREENULL( psz_item_type );
242                         FREENULL( psz_item_date );
243                         FREENULL( psz_item_author );
244                         FREENULL( psz_item_category );
245                         FREENULL( psz_item_duration );
246                         FREENULL( psz_item_keywords );
247                         FREENULL( psz_item_subtitle );
248                         FREENULL( psz_item_summary );
249                         FREENULL( psz_art_url );
250                         FREENULL( psz_elname );
251                         continue;
252                     }
253
254                     resolve_xml_special_chars( psz_item_mrl );
255                     resolve_xml_special_chars( psz_item_name );
256                     p_input = input_item_New( psz_item_mrl, psz_item_name );
257                     FREENULL( psz_item_mrl );
258                     FREENULL( psz_item_name );
259
260                     if( p_input == NULL )
261                         break; /* FIXME: meta data memory leaks? */
262
263                     /* Set the duration if available */
264                     if( psz_item_duration )
265                         input_item_SetDuration( p_input, strTimeToMTime( psz_item_duration ) );
266
267 #define ADD_INFO( info, field ) \
268     if( field ) { \
269         input_item_AddInfo( p_input, _( "Podcast Info" ), (info), "%s", \
270                             (field) ); \
271         FREENULL( field ); }
272                     ADD_INFO( _("Podcast Publication Date"), psz_item_date  );
273                     ADD_INFO( _("Podcast Author"), psz_item_author );
274                     ADD_INFO( _("Podcast Subcategory"), psz_item_category );
275                     ADD_INFO( _("Podcast Duration"), psz_item_duration );
276                     ADD_INFO( _("Podcast Keywords"), psz_item_keywords );
277                     ADD_INFO( _("Podcast Subtitle"), psz_item_subtitle );
278                     ADD_INFO( _("Podcast Summary"), psz_item_summary );
279                     ADD_INFO( _("Podcast Type"), psz_item_type );
280 #undef ADD_INFO
281
282                     /* Add the global art url to this item, if any */
283                     if( psz_art_url )
284                     {
285                         resolve_xml_special_chars( psz_art_url );
286                         input_item_SetArtURL( p_input, psz_art_url );
287                     }
288
289                     if( psz_item_size )
290                     {
291                         input_item_AddInfo( p_input,
292                                                 _( "Podcast Info" ),
293                                                 _( "Podcast Size" ),
294                                                 _("%s bytes"),
295                                                 psz_item_size );
296                         FREENULL( psz_item_size );
297                     }
298                     input_item_node_AppendItem( p_subitems, p_input );
299                     vlc_gc_decref( p_input );
300                     b_item = false;
301                 }
302                 else if( !strcmp( node, "image" ) )
303                 {
304                     b_image = false;
305                 }
306                 break;
307             }
308         }
309     }
310
311     if( i_type < 0 )
312     {
313         msg_Warn( p_demux, "error while parsing data" );
314     }
315
316     free( psz_art_url );
317     free( psz_elname );
318     xml_ReaderDelete( p_xml_reader );
319
320     input_item_node_PostAndDelete( p_subitems );
321     vlc_gc_decref(p_current_input);
322     return 0; /* Needed for correct operation of go back */
323
324 error:
325     free( psz_item_name );
326     free( psz_item_mrl );
327     free( psz_item_size );
328     free( psz_item_type );
329     free( psz_item_date );
330     free( psz_item_author );
331     free( psz_item_category );
332     free( psz_item_duration );
333     free( psz_item_keywords );
334     free( psz_item_subtitle );
335     free( psz_item_summary );
336     free( psz_art_url );
337     free( psz_elname );
338
339     if( p_xml_reader )
340         xml_ReaderDelete( p_xml_reader );
341     if( p_subitems )
342         input_item_node_Delete( p_subitems );
343
344     vlc_gc_decref(p_current_input);
345     return -1;
346 }
347
348 static mtime_t strTimeToMTime( const char *psz )
349 {
350     int h, m, s;
351     switch( sscanf( psz, "%u:%u:%u", &h, &m, &s ) )
352     {
353     case 3:
354         return (mtime_t)( ( h*60 + m )*60 + s ) * 1000000;
355     case 2:
356         return (mtime_t)( h*60 + m ) * 1000000;
357         break;
358     default:
359         return -1;
360     }
361 }