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 )
75 xml_reader_t *p_xml_reader;
76 char *psz_elname = NULL;
77 char *psz_item_mrl = NULL;
78 char *psz_item_size = NULL;
79 char *psz_item_type = NULL;
80 char *psz_item_name = NULL;
81 char *psz_item_date = NULL;
82 char *psz_item_author = NULL;
83 char *psz_item_category = NULL;
84 char *psz_item_duration = NULL;
85 char *psz_item_keywords = NULL;
86 char *psz_item_subtitle = NULL;
87 char *psz_item_summary = NULL;
88 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_ReaderNextNode( p_xml_reader, &node ) != XML_READER_STARTELEM )
104 msg_Err( p_demux, "invalid file (no root node)" );
108 if( strcmp( node, "rss" ) )
110 msg_Err( p_demux, "invalid root node <%s>", node );
114 p_subitems = input_item_node_Create( p_current_input );
116 while( (i_type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
120 case XML_READER_STARTELEM:
123 psz_elname = strdup( node );
124 if( unlikely(!node) )
127 if( !strcmp( node, "item" ) )
129 else if( !strcmp( node, "image" ) )
132 // Read the attributes
134 while( (attr = xml_ReaderNextAttr( p_xml_reader )) )
136 char *psz_value = xml_ReaderValue( p_xml_reader );
143 if( !strcmp( node, "enclosure" ) )
145 if( !strcmp( attr, "url" ) )
147 free( psz_item_mrl );
148 psz_item_mrl = psz_value;
150 else if( !strcmp( attr, "length" ) )
152 free( psz_item_size );
153 psz_item_size = psz_value;
155 else if( !strcmp( attr, "type" ) )
157 free( psz_item_type );
158 psz_item_type = psz_value;
162 msg_Dbg( p_demux,"unhandled attribute %s in element %s",
169 msg_Dbg( p_demux,"unhandled attribute %s in element %s",
177 case XML_READER_TEXT:
179 if(!psz_elname) break;
181 /* item specific meta data */
186 if( !strcmp( psz_elname, "title" ) )
188 else if( !strcmp( psz_elname, "itunes:author" ) ||
189 !strcmp( psz_elname, "author" ) )
190 /* <author> isn't standard iTunes podcast stuff */
191 p = &psz_item_author;
192 else if( !strcmp( psz_elname, "itunes:summary" ) ||
193 !strcmp( psz_elname, "description" ) )
194 /* <description> isn't standard iTunes podcast stuff */
195 p = &psz_item_summary;
196 else if( !strcmp( psz_elname, "pubDate" ) )
198 else if( !strcmp( psz_elname, "itunes:category" ) )
199 p = &psz_item_category;
200 else if( !strcmp( psz_elname, "itunes:duration" ) )
201 p = &psz_item_duration;
202 else if( !strcmp( psz_elname, "itunes:keywords" ) )
203 p = &psz_item_keywords;
204 else if( !strcmp( psz_elname, "itunes:subtitle" ) )
205 p = &psz_item_subtitle;
212 /* toplevel meta data */
215 if( !strcmp( psz_elname, "title" ) )
216 input_item_SetName( p_current_input, node );
217 #define ADD_GINFO( info, name ) \
218 else if( !strcmp( psz_elname, name ) ) \
219 input_item_AddInfo( p_current_input, _("Podcast Info"), \
221 ADD_GINFO( _("Podcast Link"), "link" )
222 ADD_GINFO( _("Podcast Copyright"), "copyright" )
223 ADD_GINFO( _("Podcast Category"), "itunes:category" )
224 ADD_GINFO( _("Podcast Keywords"), "itunes:keywords" )
225 ADD_GINFO( _("Podcast Subtitle"), "itunes:subtitle" )
227 else if( !strcmp( psz_elname, "itunes:summary" ) ||
228 !strcmp( psz_elname, "description" ) )
229 { /* <description> isn't standard iTunes podcast stuff */
230 input_item_AddInfo( p_current_input,
231 _( "Podcast Info" ), _( "Podcast Summary" ),
237 if( !strcmp( psz_elname, "url" ) )
240 psz_art_url = strdup( node );
243 msg_Dbg( p_demux, "unhandled text in element <%s>",
250 case XML_READER_ENDELEM:
252 FREENULL( psz_elname );
254 if( !strcmp( node, "item" ) )
256 if( psz_item_mrl == NULL )
258 msg_Err( p_demux, "invalid XML (no enclosure markup)" );
262 p_input = input_item_New( p_demux, psz_item_mrl, psz_item_name );
263 FREENULL( psz_item_mrl );
264 FREENULL( psz_item_name );
266 if( p_input == NULL )
267 break; /* FIXME: meta data memory leaks? */
269 /* Set the duration if available */
270 if( psz_item_duration )
271 input_item_SetDuration( p_input, strTimeToMTime( psz_item_duration ) );
273 #define ADD_INFO( info, field ) \
275 input_item_AddInfo( p_input, _( "Podcast Info" ), (info), "%s", \
278 ADD_INFO( _("Podcast Publication Date"), psz_item_date );
279 ADD_INFO( _("Podcast Author"), psz_item_author );
280 ADD_INFO( _("Podcast Subcategory"), psz_item_category );
281 ADD_INFO( _("Podcast Duration"), psz_item_duration );
282 ADD_INFO( _("Podcast Keywords"), psz_item_keywords );
283 ADD_INFO( _("Podcast Subtitle"), psz_item_subtitle );
284 ADD_INFO( _("Podcast Summary"), psz_item_summary );
285 ADD_INFO( _("Podcast Type"), psz_item_type );
288 /* Add the global art url to this item, if any */
290 input_item_SetArtURL( p_input, psz_art_url );
294 input_item_AddInfo( p_input,
299 FREENULL( psz_item_size );
301 input_item_node_AppendItem( p_subitems, p_input );
302 vlc_gc_decref( p_input );
305 else if( !strcmp( psz_elname, "image" ) )
316 msg_Warn( p_demux, "error while parsing data" );
321 xml_ReaderDelete( p_xml_reader );
323 input_item_node_PostAndDelete( p_subitems );
324 vlc_gc_decref(p_current_input);
325 return 0; /* Needed for correct operation of go back */
328 free( psz_item_name );
329 free( psz_item_mrl );
330 free( psz_item_size );
331 free( psz_item_type );
332 free( psz_item_date );
333 free( psz_item_author );
334 free( psz_item_category );
335 free( psz_item_duration );
336 free( psz_item_keywords );
337 free( psz_item_subtitle );
338 free( psz_item_summary );
343 xml_ReaderDelete( p_xml_reader );
345 input_item_node_Delete( p_subitems );
347 vlc_gc_decref(p_current_input);
351 static int Control( demux_t *p_demux, int i_query, va_list args )
353 VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
357 static mtime_t strTimeToMTime( const char *psz )
360 switch( sscanf( psz, "%u:%u:%u", &h, &m, &s ) )
363 return (mtime_t)( ( h*60 + m )*60 + s ) * 1000000;
365 return (mtime_t)( h*60 + m ) * 1000000;