1 /*****************************************************************************
2 * podcast.c : podcast playlist imports
3 *****************************************************************************
4 * Copyright (C) 2005-2009 VLC authors and VideoLAN
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_demux.h>
37 /*****************************************************************************
39 *****************************************************************************/
40 static int Demux( demux_t *p_demux);
41 static mtime_t strTimeToMTime( const char *psz );
43 /*****************************************************************************
44 * Import_podcast: main import function
45 *****************************************************************************/
46 int Import_podcast( vlc_object_t *p_this )
48 demux_t *p_demux = (demux_t *)p_this;
50 if( !demux_IsForced( p_demux, "podcast" ) )
53 p_demux->pf_demux = Demux;
54 p_demux->pf_control = Control;
55 msg_Dbg( p_demux, "using podcast reader" );
60 /* "specs" : http://phobos.apple.com/static/iTunesRSS.html */
61 static int Demux( demux_t *p_demux )
66 xml_reader_t *p_xml_reader;
67 char *psz_elname = NULL;
68 char *psz_item_mrl = NULL;
69 char *psz_item_size = NULL;
70 char *psz_item_type = NULL;
71 char *psz_item_name = NULL;
72 char *psz_item_date = NULL;
73 char *psz_item_author = NULL;
74 char *psz_item_category = NULL;
75 char *psz_item_duration = NULL;
76 char *psz_item_keywords = NULL;
77 char *psz_item_subtitle = NULL;
78 char *psz_item_summary = NULL;
79 char *psz_art_url = NULL;
82 input_item_t *p_input;
83 input_item_node_t *p_subitems = NULL;
85 input_item_t *p_current_input = GetCurrentItem(p_demux);
87 p_xml_reader = xml_ReaderCreate( p_demux, p_demux->s );
93 if( xml_ReaderNextNode( p_xml_reader, &node ) != XML_READER_STARTELEM )
95 msg_Err( p_demux, "invalid file (no root node)" );
99 if( strcmp( node, "rss" ) )
101 msg_Err( p_demux, "invalid root node <%s>", node );
105 p_subitems = input_item_node_Create( p_current_input );
107 while( (i_type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
111 case XML_READER_STARTELEM:
114 psz_elname = strdup( node );
115 if( unlikely(!node) )
118 if( !strcmp( node, "item" ) )
120 else if( !strcmp( node, "image" ) )
123 // Read the attributes
124 const char *attr, *value;
125 while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) )
127 if( !strcmp( node, "enclosure" ) )
130 if( !strcmp( attr, "url" ) )
132 else if( !strcmp( attr, "length" ) )
134 else if( !strcmp( attr, "type" ) )
139 *p = strdup( value );
142 msg_Dbg( p_demux,"unhandled attribute %s in <%s>",
146 msg_Dbg( p_demux,"unhandled attribute %s in <%s>",
152 case XML_READER_TEXT:
154 if(!psz_elname) break;
156 /* item specific meta data */
161 if( !strcmp( psz_elname, "title" ) )
163 else if( !strcmp( psz_elname, "itunes:author" ) ||
164 !strcmp( psz_elname, "author" ) )
165 /* <author> isn't standard iTunes podcast stuff */
166 p = &psz_item_author;
167 else if( !strcmp( psz_elname, "itunes:summary" ) ||
168 !strcmp( psz_elname, "description" ) )
169 /* <description> isn't standard iTunes podcast stuff */
170 p = &psz_item_summary;
171 else if( !strcmp( psz_elname, "pubDate" ) )
173 else if( !strcmp( psz_elname, "itunes:category" ) )
174 p = &psz_item_category;
175 else if( !strcmp( psz_elname, "itunes:duration" ) )
176 p = &psz_item_duration;
177 else if( !strcmp( psz_elname, "itunes:keywords" ) )
178 p = &psz_item_keywords;
179 else if( !strcmp( psz_elname, "itunes:subtitle" ) )
180 p = &psz_item_subtitle;
187 /* toplevel meta data */
190 if( !strcmp( psz_elname, "title" ) )
191 input_item_SetName( p_current_input, node );
192 #define ADD_GINFO( info, name ) \
193 else if( !strcmp( psz_elname, name ) ) \
194 input_item_AddInfo( p_current_input, _("Podcast Info"), \
196 ADD_GINFO( _("Podcast Link"), "link" )
197 ADD_GINFO( _("Podcast Copyright"), "copyright" )
198 ADD_GINFO( _("Podcast Category"), "itunes:category" )
199 ADD_GINFO( _("Podcast Keywords"), "itunes:keywords" )
200 ADD_GINFO( _("Podcast Subtitle"), "itunes:subtitle" )
202 else if( !strcmp( psz_elname, "itunes:summary" ) ||
203 !strcmp( psz_elname, "description" ) )
204 { /* <description> isn't standard iTunes podcast stuff */
205 input_item_AddInfo( p_current_input,
206 _( "Podcast Info" ), _( "Podcast Summary" ),
212 if( !strcmp( psz_elname, "url" ) )
215 psz_art_url = strdup( node );
218 msg_Dbg( p_demux, "unhandled text in element <%s>",
225 case XML_READER_ENDELEM:
227 FREENULL( psz_elname );
229 if( !strcmp( node, "item" ) )
231 if( psz_item_mrl == NULL )
234 msg_Warn( p_demux, "invalid XML item, skipping %s",
237 msg_Warn( p_demux, "invalid XML item, skipped" );
238 FREENULL( psz_item_name );
239 FREENULL( psz_item_size );
240 FREENULL( psz_item_type );
241 FREENULL( psz_item_date );
242 FREENULL( psz_item_author );
243 FREENULL( psz_item_category );
244 FREENULL( psz_item_duration );
245 FREENULL( psz_item_keywords );
246 FREENULL( psz_item_subtitle );
247 FREENULL( psz_item_summary );
248 FREENULL( psz_art_url );
249 FREENULL( psz_elname );
253 p_input = input_item_New( psz_item_mrl, psz_item_name );
254 FREENULL( psz_item_mrl );
255 FREENULL( psz_item_name );
257 if( p_input == NULL )
258 break; /* FIXME: meta data memory leaks? */
260 /* Set the duration if available */
261 if( psz_item_duration )
262 input_item_SetDuration( p_input, strTimeToMTime( psz_item_duration ) );
264 #define ADD_INFO( info, field ) \
266 input_item_AddInfo( p_input, _( "Podcast Info" ), (info), "%s", \
269 ADD_INFO( _("Podcast Publication Date"), psz_item_date );
270 ADD_INFO( _("Podcast Author"), psz_item_author );
271 ADD_INFO( _("Podcast Subcategory"), psz_item_category );
272 ADD_INFO( _("Podcast Duration"), psz_item_duration );
273 ADD_INFO( _("Podcast Keywords"), psz_item_keywords );
274 ADD_INFO( _("Podcast Subtitle"), psz_item_subtitle );
275 ADD_INFO( _("Podcast Summary"), psz_item_summary );
276 ADD_INFO( _("Podcast Type"), psz_item_type );
279 /* Add the global art url to this item, if any */
281 input_item_SetArtURL( p_input, psz_art_url );
285 input_item_AddInfo( p_input,
290 FREENULL( psz_item_size );
292 input_item_node_AppendItem( p_subitems, p_input );
293 vlc_gc_decref( p_input );
296 else if( !strcmp( node, "image" ) )
307 msg_Warn( p_demux, "error while parsing data" );
312 xml_ReaderDelete( p_xml_reader );
314 input_item_node_PostAndDelete( p_subitems );
315 vlc_gc_decref(p_current_input);
316 return 0; /* Needed for correct operation of go back */
319 free( psz_item_name );
320 free( psz_item_mrl );
321 free( psz_item_size );
322 free( psz_item_type );
323 free( psz_item_date );
324 free( psz_item_author );
325 free( psz_item_category );
326 free( psz_item_duration );
327 free( psz_item_keywords );
328 free( psz_item_subtitle );
329 free( psz_item_summary );
334 xml_ReaderDelete( p_xml_reader );
336 input_item_node_Delete( p_subitems );
338 vlc_gc_decref(p_current_input);
342 static mtime_t strTimeToMTime( const char *psz )
345 switch( sscanf( psz, "%u:%u:%u", &h, &m, &s ) )
348 return (mtime_t)( ( h*60 + m )*60 + s ) * 1000000;
350 return (mtime_t)( h*60 + m ) * 1000000;