1 /*****************************************************************************
2 * podcast.c : podcast playlist imports
3 *****************************************************************************
4 * Copyright (C) 2005-2009 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_demux.h>
37 /*****************************************************************************
39 *****************************************************************************/
40 static int Demux( demux_t *p_demux);
41 static int Control( demux_t *p_demux, int i_query, va_list args );
42 static mtime_t strTimeToMTime( const char *psz );
44 /*****************************************************************************
45 * Import_podcast: main import function
46 *****************************************************************************/
47 int Import_podcast( vlc_object_t *p_this )
49 demux_t *p_demux = (demux_t *)p_this;
51 if( !demux_IsForced( p_demux, "podcast" ) )
54 p_demux->pf_demux = Demux;
55 p_demux->pf_control = Control;
56 msg_Dbg( p_demux, "using podcast reader" );
61 /*****************************************************************************
62 * Deactivate: frees unused data
63 *****************************************************************************/
64 void Close_podcast( vlc_object_t *p_this )
69 /* "specs" : http://phobos.apple.com/static/iTunesRSS.html */
70 static int Demux( demux_t *p_demux )
76 xml_reader_t *p_xml_reader;
77 char *psz_elname = NULL;
78 char *psz_item_mrl = NULL;
79 char *psz_item_size = NULL;
80 char *psz_item_type = NULL;
81 char *psz_item_name = NULL;
82 char *psz_item_date = NULL;
83 char *psz_item_author = NULL;
84 char *psz_item_category = NULL;
85 char *psz_item_duration = NULL;
86 char *psz_item_keywords = NULL;
87 char *psz_item_subtitle = NULL;
88 char *psz_item_summary = NULL;
89 char *psz_art_url = NULL;
91 input_item_t *p_input;
92 input_item_node_t *p_subitems = NULL;
94 input_item_t *p_current_input = GetCurrentItem(p_demux);
96 p_xml_reader = xml_ReaderCreate( p_demux, p_demux->s );
101 /* check root node */
102 if( xml_ReaderRead( p_xml_reader ) != 1 )
104 msg_Err( p_demux, "invalid file (no root node)" );
108 while( xml_ReaderNodeType( p_xml_reader ) == XML_READER_NONE )
110 if( xml_ReaderRead( p_xml_reader ) != 1 )
112 msg_Err( p_demux, "invalid file (no root node)" );
117 if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
118 ( psz_elname = xml_ReaderName( p_xml_reader ) ) == NULL ||
119 strcmp( psz_elname, "rss" ) )
121 msg_Err( p_demux, "invalid root node %i, %s",
122 xml_ReaderNodeType( p_xml_reader ), psz_elname );
125 FREENULL( psz_elname );
127 p_subitems = input_item_node_Create( p_current_input );
129 while( (i_ret = xml_ReaderRead( p_xml_reader )) == 1 )
132 i_type = xml_ReaderNodeType( p_xml_reader );
139 case XML_READER_STARTELEM:
141 // Read the element name
143 psz_elname = xml_ReaderName( p_xml_reader );
147 if( !strcmp( psz_elname, "item" ) )
151 else if( !strcmp( psz_elname, "image" ) )
156 // Read the attributes
157 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
159 char *psz_name = xml_ReaderName( p_xml_reader );
160 char *psz_value = xml_ReaderValue( p_xml_reader );
161 if( !psz_name || !psz_value )
168 if( !strcmp( psz_elname, "enclosure" ) )
170 if( !strcmp( psz_name, "url" ) )
172 free( psz_item_mrl );
173 psz_item_mrl = psz_value;
175 else if( !strcmp( psz_name, "length" ) )
177 free( psz_item_size );
178 psz_item_size = psz_value;
180 else if( !strcmp( psz_name, "type" ) )
182 free( psz_item_type );
183 psz_item_type = psz_value;
187 msg_Dbg( p_demux,"unhandled attribute %s in element %s",
188 psz_name, psz_elname );
194 msg_Dbg( p_demux,"unhandled attribute %s in element %s",
195 psz_name, psz_elname );
202 case XML_READER_TEXT:
204 if(!psz_elname) break;
206 char *psz_text = xml_ReaderValue( p_xml_reader );
208 #define SET_DATA( field, name ) \
209 else if( !strcmp( psz_elname, name ) ) \
213 /* item specific meta data */
216 if( !strcmp( psz_elname, "title" ) )
218 psz_item_name = psz_text;
220 else if( !strcmp( psz_elname, "itunes:author" ) ||
221 !strcmp( psz_elname, "author" ) )
222 { /* <author> isn't standard iTunes podcast stuff */
223 psz_item_author = psz_text;
225 else if( !strcmp( psz_elname, "itunes:summary" ) ||
226 !strcmp( psz_elname, "description" ) )
227 { /* <description> isn't standard iTunes podcast stuff */
228 psz_item_summary = psz_text;
230 SET_DATA( psz_item_date, "pubDate" )
231 SET_DATA( psz_item_category, "itunes:category" )
232 SET_DATA( psz_item_duration, "itunes:duration" )
233 SET_DATA( psz_item_keywords, "itunes:keywords" )
234 SET_DATA( psz_item_subtitle, "itunes:subtitle" )
240 /* toplevel meta data */
241 else if( b_image == false )
243 if( !strcmp( psz_elname, "title" ) )
245 input_item_SetName( p_current_input, psz_text );
247 #define ADD_GINFO( info, name ) \
248 else if( !strcmp( psz_elname, name ) ) \
250 input_item_AddInfo( p_current_input, _("Podcast Info"), \
251 _( info ), "%s", psz_text ); \
253 ADD_GINFO( "Podcast Link", "link" )
254 ADD_GINFO( "Podcast Copyright", "copyright" )
255 ADD_GINFO( "Podcast Category", "itunes:category" )
256 ADD_GINFO( "Podcast Keywords", "itunes:keywords" )
257 ADD_GINFO( "Podcast Subtitle", "itunes:subtitle" )
259 else if( !strcmp( psz_elname, "itunes:summary" ) ||
260 !strcmp( psz_elname, "description" ) )
261 { /* <description> isn't standard iTunes podcast stuff */
262 input_item_AddInfo( p_current_input,
263 _( "Podcast Info" ), _( "Podcast Summary" ),
270 if( !strcmp( psz_elname, "url" ) )
273 psz_art_url = psz_text;
277 msg_Dbg( p_demux, "unhandled text in element '%s'",
285 case XML_READER_ENDELEM:
287 // Read the element name
289 psz_elname = xml_ReaderName( p_xml_reader );
292 if( !strcmp( psz_elname, "item" ) )
294 if( psz_item_mrl == NULL )
296 msg_Err( p_demux, "invalid XML (no enclosure markup)" );
299 p_input = input_item_New( p_demux, psz_item_mrl, psz_item_name );
300 if( p_input == NULL ) break;
301 #define ADD_INFO( info, field ) \
302 if( field ) { input_item_AddInfo( p_input, \
303 _( "Podcast Info" ), _( info ), "%s", field ); }
304 ADD_INFO( "Podcast Publication Date", psz_item_date );
305 ADD_INFO( "Podcast Author", psz_item_author );
306 ADD_INFO( "Podcast Subcategory", psz_item_category );
307 ADD_INFO( "Podcast Duration", psz_item_duration );
308 ADD_INFO( "Podcast Keywords", psz_item_keywords );
309 ADD_INFO( "Podcast Subtitle", psz_item_subtitle );
310 ADD_INFO( "Podcast Summary", psz_item_summary );
311 ADD_INFO( "Podcast Type", psz_item_type );
314 /* Set the duration if available */
315 if( psz_item_duration )
316 input_item_SetDuration( p_input, strTimeToMTime( psz_item_duration ) );
317 /* Add the global art url to this item, if any */
319 input_item_SetArtURL( p_input, psz_art_url );
323 input_item_AddInfo( p_input,
329 input_item_node_AppendItem( p_subitems, p_input );
330 vlc_gc_decref( p_input );
331 FREENULL( psz_item_name );
332 FREENULL( psz_item_mrl );
333 FREENULL( psz_item_size );
334 FREENULL( psz_item_type );
335 FREENULL( psz_item_date );
336 FREENULL( psz_item_author );
337 FREENULL( psz_item_category );
338 FREENULL( psz_item_duration );
339 FREENULL( psz_item_keywords );
340 FREENULL( psz_item_subtitle );
341 FREENULL( psz_item_summary );
344 else if( !strcmp( psz_elname, "image" ) )
349 psz_elname = strdup( "" );
358 msg_Warn( p_demux, "error while parsing data" );
363 xml_ReaderDelete( p_xml_reader );
365 input_item_node_PostAndDelete( p_subitems );
366 vlc_gc_decref(p_current_input);
367 return 0; /* Needed for correct operation of go back */
370 free( psz_item_name );
371 free( psz_item_mrl );
372 free( psz_item_size );
373 free( psz_item_type );
374 free( psz_item_date );
375 free( psz_item_author );
376 free( psz_item_category );
377 free( psz_item_duration );
378 free( psz_item_keywords );
379 free( psz_item_subtitle );
380 free( psz_item_summary );
385 xml_ReaderDelete( p_xml_reader );
387 input_item_node_Delete( p_subitems );
389 vlc_gc_decref(p_current_input);
393 static int Control( demux_t *p_demux, int i_query, va_list args )
395 VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
399 static mtime_t strTimeToMTime( const char *psz )
402 switch( sscanf( psz, "%u:%u:%u", &h, &m, &s ) )
405 return (mtime_t)( ( h*60 + m )*60 + s ) * 1000000;
407 return (mtime_t)( h*60 + m ) * 1000000;