1 /*****************************************************************************
2 * podcast.c : podcast playlist imports
3 *****************************************************************************
4 * Copyright (C) 2005 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
28 #include <ctype.h> /* isspace() */
31 #include <vlc/input.h>
34 #include <errno.h> /* ENOMEM */
41 playlist_t *p_playlist;
43 xml_reader_t *p_xml_reader;
46 /*****************************************************************************
48 *****************************************************************************/
49 static int Demux( demux_t *p_demux);
50 static int Control( demux_t *p_demux, int i_query, va_list args );
52 /*****************************************************************************
53 * Import_podcast: main import function
54 *****************************************************************************/
55 int E_(Import_podcast)( vlc_object_t *p_this )
57 demux_t *p_demux = (demux_t *)p_this;
62 psz_ext = strrchr ( p_demux->psz_path, '.' );
64 if( p_demux->psz_demux && !strcmp(p_demux->psz_demux, "podcast") )
72 msg_Dbg( p_demux, "using podcast playlist import");
74 p_demux->pf_control = Control;
75 p_demux->pf_demux = Demux;
76 p_demux->p_sys = p_sys = malloc( sizeof(demux_sys_t) );
79 msg_Err( p_demux, "Out of memory" );
82 p_sys->psz_prefix = E_(FindPrefix)( p_demux );
83 p_sys->p_playlist = NULL;
85 p_sys->p_xml_reader = NULL;
90 /*****************************************************************************
91 * Deactivate: frees unused data
92 *****************************************************************************/
93 void E_(Close_podcast)( vlc_object_t *p_this )
95 demux_t *p_demux = (demux_t *)p_this;
96 demux_sys_t *p_sys = p_demux->p_sys;
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 );
105 /* "specs" : http://phobos.apple.com/static/iTunesRSS.html */
106 static int Demux( demux_t *p_demux )
108 demux_sys_t *p_sys = p_demux->p_sys;
109 playlist_t *p_playlist;
110 playlist_item_t *p_item, *p_current;
113 vlc_bool_t b_item = VLC_FALSE;
114 vlc_bool_t b_image = VLC_FALSE;
118 xml_reader_t *p_xml_reader;
119 char *psz_elname = NULL;
120 char *psz_item_mrl = NULL;
121 char *psz_item_size = NULL;
122 char *psz_item_type = NULL;
123 char *psz_item_name = NULL;
124 char *psz_item_date = NULL;
125 char *psz_item_author = NULL;
126 char *psz_item_category = NULL;
127 char *psz_item_duration = NULL;
128 char *psz_item_keywords = NULL;
129 char *psz_item_subtitle = NULL;
130 char *psz_item_summary = NULL;
133 p_playlist = (playlist_t *) vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
137 msg_Err( p_demux, "can't find playlist" );
140 p_sys->p_playlist = p_playlist;
142 b_play = E_(FindItem)( p_demux, p_playlist, &p_current );
144 playlist_ItemToNode( p_playlist, p_current );
145 p_current->input.i_type = ITEM_TYPE_PLAYLIST;
147 p_xml = p_sys->p_xml = xml_Create( p_demux );
148 if( !p_xml ) return -1;
150 psz_elname = stream_ReadLine( p_demux->s );
151 if( psz_elname ) free( psz_elname );
154 p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
155 if( !p_xml_reader ) return -1;
156 p_sys->p_xml_reader = p_xml_reader;
159 /* check root node */
160 if( xml_ReaderRead( p_xml_reader ) != 1 )
162 msg_Err( p_demux, "invalid file (no root node)" );
165 if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
166 ( psz_elname = xml_ReaderName( p_xml_reader ) ) == NULL ||
167 strcmp( psz_elname, "rss" ) )
169 msg_Err( p_demux, "invalid root node %i, %s",
170 xml_ReaderNodeType( p_xml_reader ), psz_elname );
171 if( psz_elname ) free( psz_elname );
174 free( psz_elname ); psz_elname = NULL;
176 while( (i_ret = xml_ReaderRead( p_xml_reader )) == 1 )
179 i_type = xml_ReaderNodeType( p_xml_reader );
187 case XML_READER_STARTELEM:
189 // Read the element name
190 if( psz_elname ) free( psz_elname );
191 psz_elname = xml_ReaderName( p_xml_reader );
192 if( !psz_elname ) return -1;
194 if( !strcmp( psz_elname, "item" ) )
198 else if( !strcmp( psz_elname, "image" ) )
203 // Read the attributes
204 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
206 char *psz_name = xml_ReaderName( p_xml_reader );
207 char *psz_value = xml_ReaderValue( p_xml_reader );
208 if( !psz_name || !psz_value ) return -1;
209 if( !strcmp( psz_elname, "enclosure" ) &&
210 !strcmp( psz_name, "url" ) )
212 psz_item_mrl = strdup( psz_value );
214 else if( !strcmp( psz_elname, "enclosure" ) &&
215 !strcmp( psz_name, "length" ) )
217 psz_item_size = strdup( psz_value );
219 else if( !strcmp( psz_elname, "enclosure" ) &&
220 !strcmp( psz_name, "type" ) )
222 psz_item_type = strdup( psz_value );
226 msg_Dbg( p_demux,"unhandled attribure %s in element %s",
227 psz_name, psz_elname );
234 case XML_READER_TEXT:
236 char *psz_text = xml_ReaderValue( p_xml_reader );
237 /* item specific meta data */
238 if( b_item == VLC_TRUE && !strcmp( psz_elname, "title" ) )
240 psz_item_name = strdup( psz_text );
242 else if( b_item == VLC_TRUE
243 && !strcmp( psz_elname, "pubDate" ) )
245 psz_item_date = strdup( psz_text );
247 else if( b_item == VLC_TRUE
248 && ( !strcmp( psz_elname, "itunes:author" )
249 ||!strcmp( psz_elname, "author" ) ) )
250 { /* <author> isn't standard iTunes podcast stuff */
251 psz_item_author = strdup( psz_text );
253 else if( b_item == VLC_TRUE
254 && !strcmp( psz_elname, "itunes:category" ) )
256 psz_item_category = strdup( psz_text );
258 else if( b_item == VLC_TRUE
259 && !strcmp( psz_elname, "itunes:duration" ) )
261 psz_item_duration = strdup( psz_text );
263 else if( b_item == VLC_TRUE
264 && !strcmp( psz_elname, "itunes:keywords" ) )
266 psz_item_keywords = strdup( psz_text );
268 else if( b_item == VLC_TRUE
269 && !strcmp( psz_elname, "itunes:subtitle" ) )
271 psz_item_subtitle = strdup( psz_text );
273 else if( b_item == VLC_TRUE
274 && ( !strcmp( psz_elname, "itunes:summary" )
275 ||!strcmp( psz_elname, "description" ) ) )
276 { /* <description> isn't standard iTunes podcast stuff */
277 psz_item_summary = strdup( psz_text );
279 /* toplevel meta data */
280 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
281 && !strcmp( psz_elname, "title" ) )
283 playlist_ItemSetName( p_current, psz_text );
285 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
286 && !strcmp( psz_elname, "link" ) )
288 vlc_input_item_AddInfo( &(p_current->input),
289 _("Meta-information"),
294 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
295 && !strcmp( psz_elname, "copyright" ) )
297 vlc_input_item_AddInfo( &(p_current->input),
298 _("Meta-information"),
299 _( "Podcast Copyright" ),
303 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
304 && !strcmp( psz_elname, "itunes:category" ) )
306 vlc_input_item_AddInfo( &(p_current->input),
307 _("Meta-information"),
308 _( "Podcast Category" ),
312 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
313 && !strcmp( psz_elname, "itunes:keywords" ) )
315 vlc_input_item_AddInfo( &(p_current->input),
316 _("Meta-information"),
317 _( "Podcast Keywords" ),
321 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
322 && !strcmp( psz_elname, "itunes:subtitle" ) )
324 vlc_input_item_AddInfo( &(p_current->input),
325 _("Meta-information"),
326 _( "Podcast Subtitle" ),
330 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
331 && ( !strcmp( psz_elname, "itunes:summary" )
332 ||!strcmp( psz_elname, "description" ) ) )
333 { /* <description> isn't standard iTunes podcast stuff */
334 vlc_input_item_AddInfo( &(p_current->input),
335 _("Meta-information"),
336 _( "Podcast Summary" ),
342 msg_Dbg( p_demux, "unhandled text in element '%s'",
349 case XML_READER_ENDELEM:
351 // Read the element name
353 psz_elname = xml_ReaderName( p_xml_reader );
354 if( !psz_elname ) return -1;
355 if( !strcmp( psz_elname, "item" ) )
357 p_item = playlist_ItemNew( p_playlist, psz_item_mrl,
359 if( p_item == NULL ) break;
360 playlist_NodeAddItem( p_playlist, p_item,
361 p_current->pp_parents[0]->i_view,
362 p_current, PLAYLIST_APPEND,
365 /* We need to declare the parents of the node as the
366 * * same of the parent's ones */
367 playlist_CopyParents( p_current, p_item );
371 vlc_input_item_AddInfo( &p_item->input,
372 _("Meta-information"),
373 _( "Podcast Publication Date" ),
377 if( psz_item_author )
379 vlc_input_item_AddInfo( &p_item->input,
380 _("Meta-information"),
381 _( "Podcast Author" ),
385 if( psz_item_category )
387 vlc_input_item_AddInfo( &p_item->input,
388 _("Meta-information"),
389 _( "Podcast Subcategory" ),
393 if( psz_item_duration )
395 vlc_input_item_AddInfo( &p_item->input,
396 _("Meta-information"),
397 _( "Podcast Duration" ),
401 if( psz_item_keywords )
403 vlc_input_item_AddInfo( &p_item->input,
404 _("Meta-information"),
405 _( "Podcast Keywords" ),
409 if( psz_item_subtitle )
411 vlc_input_item_AddInfo( &p_item->input,
412 _("Meta-information"),
413 _( "Podcast Subtitle" ),
417 if( psz_item_summary )
419 vlc_input_item_AddInfo( &p_item->input,
420 _("Meta-information"),
421 _( "Podcast Summary" ),
427 vlc_input_item_AddInfo( &p_item->input,
428 _("Meta-information"),
435 vlc_input_item_AddInfo( &p_item->input,
436 _("Meta-information"),
442 #define FREE(a) if( a ) free( a ); a = NULL;
443 FREE( psz_item_name );
444 FREE( psz_item_mrl );
445 FREE( psz_item_size );
446 FREE( psz_item_type );
447 FREE( psz_item_date );
448 FREE( psz_item_author );
449 FREE( psz_item_category );
450 FREE( psz_item_duration );
451 FREE( psz_item_keywords );
452 FREE( psz_item_subtitle );
453 FREE( psz_item_summary );
458 else if( !strcmp( psz_elname, "image" ) )
463 psz_elname = strdup("");
472 msg_Warn( p_demux, "error while parsing data" );
475 /* Go back and play the playlist */
476 if( b_play && p_playlist->status.p_item &&
477 p_playlist->status.p_item->i_children > 0 )
479 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
480 p_playlist->status.i_view,
481 p_playlist->status.p_item,
482 p_playlist->status.p_item->pp_children[0] );
485 vlc_object_release( p_playlist );
486 p_sys->p_playlist = NULL;
490 static int Control( demux_t *p_demux, int i_query, va_list args )