]> git.sesse.net Git - vlc/blob - modules/demux/playlist/podcast.c
* New ASX parser
[vlc] / modules / demux / playlist / podcast.c
1 /*****************************************************************************
2  * podcast.c : podcast playlist imports
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
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
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <ctype.h>                                              /* isspace() */
29
30 #include <vlc/vlc.h>
31 #include <vlc/input.h>
32 #include <vlc/intf.h>
33
34 #include <errno.h>                                                 /* ENOMEM */
35 #include "playlist.h"
36 #include "vlc_xml.h"
37
38 struct demux_sys_t
39 {
40     char *psz_prefix;
41     playlist_t *p_playlist;
42     xml_t *p_xml;
43     xml_reader_t *p_xml_reader;
44 };
45
46 /*****************************************************************************
47  * Local prototypes
48  *****************************************************************************/
49 static int Demux( demux_t *p_demux);
50 static int Control( demux_t *p_demux, int i_query, va_list args );
51
52 /*****************************************************************************
53  * Import_podcast: main import function
54  *****************************************************************************/
55 int E_(Import_podcast)( vlc_object_t *p_this )
56 {
57     demux_t *p_demux = (demux_t *)p_this;
58     demux_sys_t *p_sys;
59
60     char    *psz_ext;
61
62     psz_ext = strrchr ( p_demux->psz_path, '.' );
63
64     if( p_demux->psz_demux && !strcmp(p_demux->psz_demux, "podcast") )
65     {
66         ;
67     }
68     else
69     {
70         return VLC_EGENERIC;
71     }
72     msg_Dbg( p_demux, "using podcast playlist import");
73
74     p_demux->pf_control = Control;
75     p_demux->pf_demux = Demux;
76     p_demux->p_sys = p_sys = malloc( sizeof(demux_sys_t) );
77     if( p_sys == NULL )
78     {
79         msg_Err( p_demux, "out of memory" );
80         return VLC_ENOMEM;
81     }
82     p_sys->psz_prefix = E_(FindPrefix)( p_demux );
83     p_sys->p_playlist = NULL;
84     p_sys->p_xml = NULL;
85     p_sys->p_xml_reader = NULL;
86
87     return VLC_SUCCESS;
88 }
89
90 /*****************************************************************************
91  * Deactivate: frees unused data
92  *****************************************************************************/
93 void E_(Close_podcast)( vlc_object_t *p_this )
94 {
95     demux_t *p_demux = (demux_t *)p_this;
96     demux_sys_t *p_sys = p_demux->p_sys;
97
98     if( p_sys->psz_prefix ) free( p_sys->psz_prefix );
99     if( p_sys->p_playlist ) vlc_object_release( p_sys->p_playlist );
100     if( p_sys->p_xml_reader ) xml_ReaderDelete( p_sys->p_xml, p_sys->p_xml_reader );
101     if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
102     free( p_sys );
103 }
104
105 /* "specs" : http://phobos.apple.com/static/iTunesRSS.html */
106 static int Demux( demux_t *p_demux )
107 {
108     demux_sys_t *p_sys = p_demux->p_sys;
109
110     vlc_bool_t b_item = VLC_FALSE;
111     vlc_bool_t b_image = VLC_FALSE;
112     int i_ret;
113
114     xml_t *p_xml;
115     xml_reader_t *p_xml_reader;
116     char *psz_elname = NULL;
117     char *psz_item_mrl = NULL;
118     char *psz_item_size = NULL;
119     char *psz_item_type = NULL;
120     char *psz_item_name = NULL;
121     char *psz_item_date = NULL;
122     char *psz_item_author = NULL;
123     char *psz_item_category = NULL;
124     char *psz_item_duration = NULL;
125     char *psz_item_keywords = NULL;
126     char *psz_item_subtitle = NULL;
127     char *psz_item_summary = NULL;
128     int i_type;
129
130     INIT_PLAYLIST_STUFF;
131
132     p_xml = p_sys->p_xml = xml_Create( p_demux );
133     if( !p_xml ) return -1;
134
135 /*    psz_elname = stream_ReadLine( p_demux->s );
136     if( psz_elname ) free( psz_elname );
137     psz_elname = 0;*/
138
139     p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
140     if( !p_xml_reader ) return -1;
141     p_sys->p_xml_reader = p_xml_reader;
142
143     /* xml */
144     /* check root node */
145     if( xml_ReaderRead( p_xml_reader ) != 1 )
146     {
147         msg_Err( p_demux, "invalid file (no root node)" );
148         return -1;
149     }
150     if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
151         ( psz_elname = xml_ReaderName( p_xml_reader ) ) == NULL ||
152         strcmp( psz_elname, "rss" ) )
153     {
154         msg_Err( p_demux, "invalid root node %i, %s",
155                  xml_ReaderNodeType( p_xml_reader ), psz_elname );
156         if( psz_elname ) free( psz_elname );
157         return -1;
158     }
159     free( psz_elname ); psz_elname = NULL;
160
161     while( (i_ret = xml_ReaderRead( p_xml_reader )) == 1 )
162     {
163         // Get the node type
164         i_type = xml_ReaderNodeType( p_xml_reader );
165         switch( i_type )
166         {
167             // Error
168             case -1:
169                 return -1;
170                 break;
171
172             case XML_READER_STARTELEM:
173             {
174                 // Read the element name
175                 if( psz_elname ) free( psz_elname );
176                 psz_elname = xml_ReaderName( p_xml_reader );
177                 if( !psz_elname ) return -1;
178
179                 if( !strcmp( psz_elname, "item" ) )
180                 {
181                     b_item = VLC_TRUE;
182                 }
183                 else if( !strcmp( psz_elname, "image" ) )
184                 {
185                     b_item = VLC_TRUE;
186                 }
187
188                 // Read the attributes
189                 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
190                 {
191                     char *psz_name = xml_ReaderName( p_xml_reader );
192                     char *psz_value = xml_ReaderValue( p_xml_reader );
193                     if( !psz_name || !psz_value ) return -1;
194                     if( !strcmp( psz_elname, "enclosure" ) &&
195                         !strcmp( psz_name, "url" ) )
196                     {
197                         psz_item_mrl = strdup( psz_value );
198                     }
199                     else if( !strcmp( psz_elname, "enclosure" ) &&
200                         !strcmp( psz_name, "length" ) )
201                     {
202                         psz_item_size = strdup( psz_value );
203                     }
204                     else if( !strcmp( psz_elname, "enclosure" ) &&
205                         !strcmp( psz_name, "type" ) )
206                     {
207                         psz_item_type = strdup( psz_value );
208                     }
209                     else
210                     {
211                         msg_Dbg( p_demux,"unhandled attribure %s in element %s",
212                                   psz_name, psz_elname );
213                     }
214                     free( psz_name );
215                     free( psz_value );
216                 }
217                 break;
218             }
219             case XML_READER_TEXT:
220             {
221                 char *psz_text = xml_ReaderValue( p_xml_reader );
222                 /* item specific meta data */
223                 if( b_item == VLC_TRUE && !strcmp( psz_elname, "title" ) )
224                 {
225                     psz_item_name = strdup( psz_text );
226                 }
227                 else if( b_item == VLC_TRUE
228                          && !strcmp( psz_elname, "pubDate" ) )
229                 {
230                     psz_item_date = strdup( psz_text );
231                 }
232                 else if( b_item == VLC_TRUE
233                          && ( !strcmp( psz_elname, "itunes:author" )
234                             ||!strcmp( psz_elname, "author" ) ) )
235                 { /* <author> isn't standard iTunes podcast stuff */
236                     psz_item_author = strdup( psz_text );
237                 }
238                 else if( b_item == VLC_TRUE
239                          && !strcmp( psz_elname, "itunes:category" ) )
240                 {
241                     psz_item_category = strdup( psz_text );
242                 }
243                 else if( b_item == VLC_TRUE
244                          && !strcmp( psz_elname, "itunes:duration" ) )
245                 {
246                     psz_item_duration = strdup( psz_text );
247                 }
248                 else if( b_item == VLC_TRUE
249                          && !strcmp( psz_elname, "itunes:keywords" ) )
250                 {
251                     psz_item_keywords = strdup( psz_text );
252                 }
253                 else if( b_item == VLC_TRUE
254                          && !strcmp( psz_elname, "itunes:subtitle" ) )
255                 {
256                     psz_item_subtitle = strdup( psz_text );
257                 }
258                 else if( b_item == VLC_TRUE
259                          && ( !strcmp( psz_elname, "itunes:summary" )
260                             ||!strcmp( psz_elname, "description" ) ) )
261                 { /* <description> isn't standard iTunes podcast stuff */
262                     psz_item_summary = strdup( psz_text );
263                 }
264                 /* toplevel meta data */
265                 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
266                          && !strcmp( psz_elname, "title" ) )
267                 {
268                     playlist_ItemSetName( p_current, psz_text );
269                 }
270                 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
271                          && !strcmp( psz_elname, "link" ) )
272                 {
273                     vlc_input_item_AddInfo( p_current->p_input,
274                                             _( "Podcast Info" ),
275                                             _( "Podcast Link" ),
276                                             "%s",
277                                             psz_text );
278                 }
279                 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
280                          && !strcmp( psz_elname, "copyright" ) )
281                 {
282                     vlc_input_item_AddInfo( p_current->p_input,
283                                             _( "Podcast Info" ),
284                                             _( "Podcast Copyright" ),
285                                             "%s",
286                                             psz_text );
287                 }
288                 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
289                          && !strcmp( psz_elname, "itunes:category" ) )
290                 {
291                     vlc_input_item_AddInfo( p_current->p_input,
292                                             _( "Podcast Info" ),
293                                             _( "Podcast Category" ),
294                                             "%s",
295                                             psz_text );
296                 }
297                 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
298                          && !strcmp( psz_elname, "itunes:keywords" ) )
299                 {
300                     vlc_input_item_AddInfo( p_current->p_input,
301                                             _( "Podcast Info" ),
302                                             _( "Podcast Keywords" ),
303                                             "%s",
304                                             psz_text );
305                 }
306                 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
307                          && !strcmp( psz_elname, "itunes:subtitle" ) )
308                 {
309                     vlc_input_item_AddInfo( p_current->p_input,
310                                             _( "Podcast Info" ),
311                                             _( "Podcast Subtitle" ),
312                                             "%s",
313                                             psz_text );
314                 }
315                 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
316                          && ( !strcmp( psz_elname, "itunes:summary" )
317                             ||!strcmp( psz_elname, "description" ) ) )
318                 { /* <description> isn't standard iTunes podcast stuff */
319                     vlc_input_item_AddInfo( p_current->p_input,
320                                             _( "Podcast Info" ),
321                                             _( "Podcast Summary" ),
322                                             "%s",
323                                             psz_text );
324                 }
325                 else
326                 {
327                     msg_Dbg( p_demux, "unhandled text in element '%s'",
328                               psz_elname );
329                 }
330                 free( psz_text );
331                 break;
332             }
333             // End element
334             case XML_READER_ENDELEM:
335             {
336                 // Read the element name
337                 free( psz_elname );
338                 psz_elname = xml_ReaderName( p_xml_reader );
339                 if( !psz_elname ) return -1;
340                 if( !strcmp( psz_elname, "item" ) )
341                 {
342                     p_input = input_ItemNewExt( p_playlist, psz_item_mrl,
343                                                 psz_item_name, 0, NULL, -1 );
344                     if( p_input == NULL ) break;
345                     if( psz_item_date )
346                     {
347                         vlc_input_item_AddInfo( p_input,
348                                                 _( "Podcast Info" ),
349                                                 _( "Podcast Publication Date" ),
350                                                 "%s",
351                                                 psz_item_date );
352                     }
353                     if( psz_item_author )
354                     {
355                         vlc_input_item_AddInfo( p_input,
356                                                 _( "Podcast Info" ),
357                                                 _( "Podcast Author" ),
358                                                 "%s",
359                                                 psz_item_author );
360                     }
361                     if( psz_item_category )
362                     {
363                         vlc_input_item_AddInfo( p_input,
364                                                 _( "Podcast Info" ),
365                                                 _( "Podcast Subcategory" ),
366                                                 "%s",
367                                                 psz_item_category );
368                     }
369                     if( psz_item_duration )
370                     {
371                         vlc_input_item_AddInfo( p_input,
372                                                 _( "Podcast Info" ),
373                                                 _( "Podcast Duration" ),
374                                                 "%s",
375                                                 psz_item_duration );
376                     }
377                     if( psz_item_keywords )
378                     {
379                         vlc_input_item_AddInfo( p_input,
380                                                 _( "Podcast Info" ),
381                                                 _( "Podcast Keywords" ),
382                                                 "%s",
383                                                 psz_item_keywords );
384                     }
385                     if( psz_item_subtitle )
386                     {
387                         vlc_input_item_AddInfo( p_input,
388                                                 _( "Podcast Info" ),
389                                                 _( "Podcast Subtitle" ),
390                                                 "%s",
391                                                 psz_item_subtitle );
392                     }
393                     if( psz_item_summary )
394                     {
395                         vlc_input_item_AddInfo( p_input,
396                                                 _( "Podcast Info" ),
397                                                 _( "Podcast Summary" ),
398                                                 "%s",
399                                                 psz_item_summary );
400                     }
401                     if( psz_item_size )
402                     {
403                         vlc_input_item_AddInfo( p_input,
404                                                 _( "Podcast Info" ),
405                                                 _( "Podcast Size" ),
406                                                 "%s bytes",
407                                                 psz_item_size );
408                     }
409                     if( psz_item_type )
410                     {
411                         vlc_input_item_AddInfo( p_input,
412                                                 _( "Podcast Info" ),
413                                                 _( "Podcast Type" ),
414                                                 "%s",
415                                                 psz_item_type );
416                     }
417
418                     msg_Dbg( p_demux, "Adding WHEREVER\n" );
419                     playlist_AddWhereverNeeded( p_playlist, p_input, p_current,
420                           p_item_in_category, (i_parent_id > 0 ) ? VLC_TRUE:
421                                                 VLC_FALSE, PLAYLIST_APPEND );
422 #define FREE(a) if( a ) free( a ); a = NULL;
423                     FREE( psz_item_name );
424                     FREE( psz_item_mrl );
425                     FREE( psz_item_size );
426                     FREE( psz_item_type );
427                     FREE( psz_item_date );
428                     FREE( psz_item_author );
429                     FREE( psz_item_category );
430                     FREE( psz_item_duration );
431                     FREE( psz_item_keywords );
432                     FREE( psz_item_subtitle );
433                     FREE( psz_item_summary );
434 #undef FREE
435
436                     b_item = VLC_FALSE;
437                 }
438                 else if( !strcmp( psz_elname, "image" ) )
439                 {
440                     b_image = VLC_FALSE;
441                 }
442                 free( psz_elname );
443                 psz_elname = strdup("");
444
445                 break;
446             }
447         }
448     }
449
450     if( i_ret != 0 )
451     {
452         msg_Warn( p_demux, "error while parsing data" );
453     }
454
455     HANDLE_PLAY_AND_RELEASE;
456     return VLC_SUCCESS;
457 }
458
459 static int Control( demux_t *p_demux, int i_query, va_list args )
460 {
461     return VLC_EGENERIC;
462 }