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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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;
110 vlc_bool_t b_item = VLC_FALSE;
111 vlc_bool_t b_image = VLC_FALSE;
112 int i_ret, i_parent_id;
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;
132 p_xml = p_sys->p_xml = xml_Create( p_demux );
133 if( !p_xml ) return -1;
135 /* psz_elname = stream_ReadLine( p_demux->s );
136 if( psz_elname ) free( psz_elname );
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;
144 /* check root node */
145 if( xml_ReaderRead( p_xml_reader ) != 1 )
147 msg_Err( p_demux, "invalid file (no root node)" );
150 if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
151 ( psz_elname = xml_ReaderName( p_xml_reader ) ) == NULL ||
152 strcmp( psz_elname, "rss" ) )
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 );
159 free( psz_elname ); psz_elname = NULL;
161 while( (i_ret = xml_ReaderRead( p_xml_reader )) == 1 )
164 i_type = xml_ReaderNodeType( p_xml_reader );
172 case XML_READER_STARTELEM:
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;
179 if( !strcmp( psz_elname, "item" ) )
183 else if( !strcmp( psz_elname, "image" ) )
188 // Read the attributes
189 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
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" ) )
197 psz_item_mrl = strdup( psz_value );
199 else if( !strcmp( psz_elname, "enclosure" ) &&
200 !strcmp( psz_name, "length" ) )
202 psz_item_size = strdup( psz_value );
204 else if( !strcmp( psz_elname, "enclosure" ) &&
205 !strcmp( psz_name, "type" ) )
207 psz_item_type = strdup( psz_value );
211 msg_Dbg( p_demux,"unhandled attribure %s in element %s",
212 psz_name, psz_elname );
219 case XML_READER_TEXT:
221 char *psz_text = xml_ReaderValue( p_xml_reader );
222 /* item specific meta data */
223 if( b_item == VLC_TRUE && !strcmp( psz_elname, "title" ) )
225 psz_item_name = strdup( psz_text );
227 else if( b_item == VLC_TRUE
228 && !strcmp( psz_elname, "pubDate" ) )
230 psz_item_date = strdup( psz_text );
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 );
238 else if( b_item == VLC_TRUE
239 && !strcmp( psz_elname, "itunes:category" ) )
241 psz_item_category = strdup( psz_text );
243 else if( b_item == VLC_TRUE
244 && !strcmp( psz_elname, "itunes:duration" ) )
246 psz_item_duration = strdup( psz_text );
248 else if( b_item == VLC_TRUE
249 && !strcmp( psz_elname, "itunes:keywords" ) )
251 psz_item_keywords = strdup( psz_text );
253 else if( b_item == VLC_TRUE
254 && !strcmp( psz_elname, "itunes:subtitle" ) )
256 psz_item_subtitle = strdup( psz_text );
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 );
264 /* toplevel meta data */
265 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
266 && !strcmp( psz_elname, "title" ) )
268 playlist_ItemSetName( p_current, psz_text );
270 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
271 && !strcmp( psz_elname, "link" ) )
273 vlc_input_item_AddInfo( p_current->p_input,
279 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
280 && !strcmp( psz_elname, "copyright" ) )
282 vlc_input_item_AddInfo( p_current->p_input,
284 _( "Podcast Copyright" ),
288 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
289 && !strcmp( psz_elname, "itunes:category" ) )
291 vlc_input_item_AddInfo( p_current->p_input,
293 _( "Podcast Category" ),
297 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
298 && !strcmp( psz_elname, "itunes:keywords" ) )
300 vlc_input_item_AddInfo( p_current->p_input,
302 _( "Podcast Keywords" ),
306 else if( b_item == VLC_FALSE && b_image == VLC_FALSE
307 && !strcmp( psz_elname, "itunes:subtitle" ) )
309 vlc_input_item_AddInfo( p_current->p_input,
311 _( "Podcast Subtitle" ),
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,
321 _( "Podcast Summary" ),
327 msg_Dbg( p_demux, "unhandled text in element '%s'",
334 case XML_READER_ENDELEM:
336 // Read the element name
338 psz_elname = xml_ReaderName( p_xml_reader );
339 if( !psz_elname ) return -1;
340 if( !strcmp( psz_elname, "item" ) )
342 p_input = input_ItemNewExt( p_playlist, psz_item_mrl,
343 psz_item_name, 0, NULL, -1 );
344 if( p_input == NULL ) break;
347 vlc_input_item_AddInfo( p_input,
349 _( "Podcast Publication Date" ),
353 if( psz_item_author )
355 vlc_input_item_AddInfo( p_input,
357 _( "Podcast Author" ),
361 if( psz_item_category )
363 vlc_input_item_AddInfo( p_input,
365 _( "Podcast Subcategory" ),
369 if( psz_item_duration )
371 vlc_input_item_AddInfo( p_input,
373 _( "Podcast Duration" ),
377 if( psz_item_keywords )
379 vlc_input_item_AddInfo( p_input,
381 _( "Podcast Keywords" ),
385 if( psz_item_subtitle )
387 vlc_input_item_AddInfo( p_input,
389 _( "Podcast Subtitle" ),
393 if( psz_item_summary )
395 vlc_input_item_AddInfo( p_input,
397 _( "Podcast Summary" ),
403 vlc_input_item_AddInfo( p_input,
411 vlc_input_item_AddInfo( p_input,
418 fprintf( stderr, "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 );
438 else if( !strcmp( psz_elname, "image" ) )
443 psz_elname = strdup("");
452 msg_Warn( p_demux, "error while parsing data" );
455 HANDLE_PLAY_AND_RELEASE;
459 static int Control( demux_t *p_demux, int i_query, va_list args )