1 /*****************************************************************************
2 * rss.c : rss/atom feed display video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2003-2006 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 /*****************************************************************************
25 * Atom : http://www.ietf.org/rfc/rfc4287.txt
26 * RSS : http://www.rssboard.org/rss-specification
27 *****************************************************************************/
29 /*****************************************************************************
31 *****************************************************************************/
32 #include <stdlib.h> /* malloc(), free() */
38 #include "vlc_filter.h"
39 #include "vlc_block.h"
42 #include "vlc_block.h"
43 #include "vlc_stream.h"
45 #include <vlc_charset.h>
47 #include "vlc_image.h"
49 /*****************************************************************************
51 *****************************************************************************/
52 static int CreateFilter ( vlc_object_t * );
53 static void DestroyFilter( vlc_object_t * );
54 static subpicture_t *Filter( filter_t *, mtime_t );
56 static int FetchRSS( filter_t * );
57 static void FreeRSS( filter_t * );
59 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
60 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
61 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
62 0x00000080, 0x000000FF, 0x0000FFFF};
63 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
64 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
65 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
66 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
69 /*****************************************************************************
70 * filter_sys_t: rss filter descriptor
71 *****************************************************************************/
76 char *psz_description;
83 char *psz_description;
89 struct rss_item_t *p_items;
97 int i_xoff, i_yoff; /* offsets for the display string in the video window */
98 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
102 char *psz_marquee; /* marquee string */
104 text_style_t *p_style; /* font control */
110 struct rss_feed_t *p_feeds;
113 time_t t_last_update;
122 #define MSG_TEXT N_("Feed URLs")
123 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) seperated URLs.")
124 #define SPEED_TEXT N_("Speed of feeds")
125 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
126 #define LENGTH_TEXT N_("Max length")
127 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
129 #define TTL_TEXT N_("Refresh time")
130 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
131 "of the feeds. 0 means that the feeds are never updated." )
132 #define IMAGE_TEXT N_("Feed images")
133 #define IMAGE_LONGTEXT N_("Display feed images if available.")
135 #define POSX_TEXT N_("X offset")
136 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
137 #define POSY_TEXT N_("Y offset")
138 #define POSY_LONGTEXT N_("Y offset, down from the top." )
139 #define OPACITY_TEXT N_("Opacity")
140 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
141 "overlay text. 0 = transparent, 255 = totally opaque." )
143 #define SIZE_TEXT N_("Font size, pixels")
144 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
147 #define COLOR_TEXT N_("Color")
148 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
149 "the video. This must be an hexadecimal (like HTML colors). The first two "\
150 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
151 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
153 #define POS_TEXT N_("Text position")
154 #define POS_LONGTEXT N_( \
155 "You can enforce the text position on the video " \
156 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
157 "also use combinations of these values, eg 6 = top-right).")
159 #define TITLE_TEXT N_("Title display mode")
160 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
162 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
163 static const char *ppsz_pos_descriptions[] =
164 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
165 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
173 static int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
174 static const char *ppsz_title_modes[] =
175 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
177 #define CFG_PREFIX "rss-"
179 /*****************************************************************************
181 *****************************************************************************/
183 set_capability( "sub filter", 0 );
184 set_shortname( "RSS / Atom" );
185 set_callbacks( CreateFilter, DestroyFilter );
186 set_category( CAT_VIDEO );
187 set_subcategory( SUBCAT_VIDEO_SUBPIC );
188 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, VLC_FALSE );
190 set_section( N_("Position"), NULL );
191 add_integer( CFG_PREFIX "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
192 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
193 add_integer( CFG_PREFIX "position", 5, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
194 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
196 set_section( N_("Font"), NULL );
197 /* 5 sets the default to top [1] left [4] */
198 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
199 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
200 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
202 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
203 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
205 set_section( N_("Misc"), NULL );
206 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
208 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
210 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_FALSE );
211 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, VLC_FALSE );
212 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, VLC_FALSE );
213 change_integer_list( pi_title_modes, ppsz_title_modes, 0 );
215 set_description( _("RSS and Atom feed display") );
216 add_shortcut( "rss" );
217 add_shortcut( "atom" );
220 static const char *ppsz_filter_options[] = {
221 "urls", "x", "y", "position", "color", "size", "speed", "length",
222 "ttl", "images", "title", NULL
225 /*****************************************************************************
226 * CreateFilter: allocates RSS video filter
227 *****************************************************************************/
228 static int CreateFilter( vlc_object_t *p_this )
230 filter_t *p_filter = (filter_t *)p_this;
234 /* Allocate structure */
235 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
238 msg_Err( p_filter, "out of memory" );
242 vlc_mutex_init( p_filter, &p_sys->lock );
243 vlc_mutex_lock( &p_sys->lock );
245 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
248 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
249 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
250 p_sys->i_cur_feed = 0;
251 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
252 p_sys->i_cur_char = 0;
254 p_sys->p_feeds = NULL;
255 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
256 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
257 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
258 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
259 p_sys->psz_marquee = (char *)malloc( p_sys->i_length );
261 p_sys->p_style = malloc( sizeof( text_style_t ));
262 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
264 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
265 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
266 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
267 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
268 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
269 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
271 if( p_sys->b_images == VLC_TRUE && p_sys->p_style->i_font_size == -1 )
273 msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
276 if( FetchRSS( p_filter ) )
278 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
279 vlc_mutex_unlock( &p_sys->lock );
282 p_sys->t_last_update = time( NULL );
284 if( p_sys->i_feeds == 0 )
286 vlc_mutex_unlock( &p_sys->lock );
289 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
290 if( p_sys->p_feeds[i_feed].i_items == 0 )
292 vlc_mutex_unlock( &p_sys->lock );
297 p_filter->pf_sub_filter = Filter;
298 p_sys->last_date = (mtime_t)0;
300 vlc_mutex_unlock( &p_sys->lock );
304 /*****************************************************************************
305 * DestroyFilter: destroy RSS video filter
306 *****************************************************************************/
307 static void DestroyFilter( vlc_object_t *p_this )
309 filter_t *p_filter = (filter_t *)p_this;
310 filter_sys_t *p_sys = p_filter->p_sys;
312 vlc_mutex_lock( &p_sys->lock );
314 if( p_sys->p_style ) free( p_sys->p_style );
315 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
316 free( p_sys->psz_urls );
318 vlc_mutex_unlock( &p_sys->lock );
319 vlc_mutex_destroy( &p_sys->lock );
322 /* Delete the RSS variables */
323 var_Destroy( p_filter, CFG_PREFIX "urls" );
324 var_Destroy( p_filter, CFG_PREFIX "speed" );
325 var_Destroy( p_filter, CFG_PREFIX "length" );
326 var_Destroy( p_filter, CFG_PREFIX "ttl" );
327 var_Destroy( p_filter, CFG_PREFIX "images" );
328 var_Destroy( p_filter, CFG_PREFIX "x" );
329 var_Destroy( p_filter, CFG_PREFIX "y" );
330 var_Destroy( p_filter, CFG_PREFIX "position" );
331 var_Destroy( p_filter, CFG_PREFIX "color");
332 var_Destroy( p_filter, CFG_PREFIX "opacity");
333 var_Destroy( p_filter, CFG_PREFIX "size");
334 var_Destroy( p_filter, CFG_PREFIX "title" );
337 /****************************************************************************
338 * Filter: the whole thing
339 ****************************************************************************
340 * This function outputs subpictures at regular time intervals.
341 ****************************************************************************/
342 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
344 filter_sys_t *p_sys = p_filter->p_sys;
346 video_format_t fmt = {0};
348 subpicture_region_t *p_region;
352 struct rss_feed_t *p_feed;
354 vlc_mutex_lock( &p_sys->lock );
357 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
358 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
359 * p_sys->i_speed > date )
361 vlc_mutex_unlock( &p_sys->lock );
365 /* Do we need to update the feeds ? */
367 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
369 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
370 if( FetchRSS( p_filter ) )
372 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
373 vlc_mutex_unlock( &p_sys->lock );
374 return NULL; /* FIXME : we most likely messed up all the data,
375 * so we might need to do something about it */
377 p_sys->t_last_update = time( NULL );
380 p_sys->last_date = date;
382 if( p_sys->i_cur_item == -1 ? p_sys->p_feeds[p_sys->i_cur_feed].psz_title[p_sys->i_cur_char] == 0 : p_sys->p_feeds[p_sys->i_cur_feed].p_items[p_sys->i_cur_item].psz_title[p_sys->i_cur_char] == 0 )
384 p_sys->i_cur_char = 0;
386 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
388 if( p_sys->i_title == scroll_title )
389 p_sys->i_cur_item = -1;
391 p_sys->i_cur_item = 0;
392 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
396 p_spu = p_filter->pf_sub_buffer_new( p_filter );
399 vlc_mutex_unlock( &p_sys->lock );
403 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
405 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
406 if( !p_spu->p_region )
408 p_filter->pf_sub_buffer_del( p_filter, p_spu );
409 vlc_mutex_unlock( &p_sys->lock );
413 /* Generate the string that will be displayed. This string is supposed to
414 be p_sys->i_length characters long. */
415 i_item = p_sys->i_cur_item;
416 i_feed = p_sys->i_cur_feed;
417 p_feed = &p_sys->p_feeds[i_feed];
419 if( ( p_feed->p_pic && p_sys->i_title == default_title )
420 || p_sys->i_title == hide_title )
422 /* Don't display the feed's title if we have an image */
423 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
424 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
425 +p_sys->i_cur_char );
427 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
428 || p_sys->i_title == prepend_title )
430 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
431 p_sys->p_feeds[i_feed].psz_title,
432 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
433 +p_sys->i_cur_char );
435 else /* scrolling title */
438 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
439 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
440 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
442 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
443 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
444 +p_sys->i_cur_char );
447 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
450 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
451 snprintf( strchr( p_sys->psz_marquee, 0 ),
452 p_sys->i_length - strlen( p_sys->psz_marquee ),
454 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
457 /* Calls to snprintf might split multibyte UTF8 chars ...
458 * which freetype doesn't like. */
460 char *a = strdup( p_sys->psz_marquee );
462 char *b = p_sys->psz_marquee;
463 EnsureUTF8( p_sys->psz_marquee );
464 /* we want to use ' ' instead of '?' for erroneous chars */
467 if( *b != *a ) *b = ' ';
473 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
474 if( p_sys->p_style->i_font_size > 0 )
475 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
476 p_spu->i_start = date;
478 p_spu->b_ephemer = VLC_TRUE;
480 /* where to locate the string: */
481 if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
482 { /* set to one of the 9 relative locations */
483 p_spu->i_flags = p_sys->i_pos;
486 p_spu->b_absolute = VLC_FALSE;
489 { /* set to an absolute xy, referenced to upper left corner */
490 p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
491 p_spu->i_x = p_sys->i_xoff;
492 p_spu->i_y = p_sys->i_yoff;
493 p_spu->b_absolute = VLC_TRUE;
497 p_spu->p_region->p_style = p_sys->p_style;
501 /* Display the feed's image */
502 picture_t *p_pic = p_feed->p_pic;
503 video_format_t fmt_out = {0};
505 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
506 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
507 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
509 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
511 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
513 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
516 msg_Err( p_filter, "cannot allocate SPU region" );
520 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
521 p_spu->p_region->p_next = p_region;
524 /* Offset text to display right next to the image */
525 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
528 vlc_mutex_unlock( &p_sys->lock );
532 /****************************************************************************
533 * RSS related functions
534 ****************************************************************************
535 * You should always lock the p_filter mutex before using any of these
537 ***************************************************************************/
539 #undef LoadImage /* do not conflict with Win32 API */
541 /****************************************************************************
542 * download and resize image located at psz_url
543 ***************************************************************************/
544 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
546 filter_sys_t *p_sys = p_filter->p_sys;
548 video_format_t fmt_in={0}, fmt_out={0};
549 picture_t *p_orig, *p_pic=NULL;
550 image_handler_t *p_handler = image_HandlerCreate( p_filter );
552 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
553 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
557 msg_Warn( p_filter, "Unable to read image %s", psz_url );
559 else if( p_sys->p_style->i_font_size > 0 )
562 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
563 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
564 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
565 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
566 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
567 fmt_out.i_height = p_sys->p_style->i_font_size;
569 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
570 p_orig->pf_release( p_orig );
573 msg_Warn( p_filter, "Error while converting %s", psz_url );
581 image_HandlerDelete( p_handler );
586 /****************************************************************************
587 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
589 ***************************************************************************/
590 static char *removeWhiteChars( char *psz_src )
592 char *psz_src2 = strdup( psz_src );
593 char *psz_clean = strdup( psz_src2 );
596 while( ( *psz_clean == ' ' || *psz_clean == '\t'
597 || *psz_clean == '\n' || *psz_clean == '\r' )
598 && *psz_clean != '\0' )
602 i = strlen( psz_clean );
604 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
605 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
606 psz_clean[i+1] = '\0';
607 psz_clean2 = strdup( psz_clean );
612 /****************************************************************************
613 * FetchRSS (or Atom) feeds
614 ***************************************************************************/
615 static int FetchRSS( filter_t *p_filter)
617 filter_sys_t *p_sys = p_filter->p_sys;
619 stream_t *p_stream = NULL;
621 xml_reader_t *p_xml_reader = NULL;
623 char *psz_eltname = NULL;
624 char *psz_eltvalue = NULL;
625 char *psz_feed = NULL;
626 char *psz_buffer = NULL;
627 char *psz_buffer_2 = NULL;
631 vlc_bool_t b_is_item;
632 vlc_bool_t b_is_image;
638 while( p_sys->psz_urls[i_int] != 0 )
639 if( p_sys->psz_urls[i_int++] == '|' )
641 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
642 * sizeof( struct rss_feed_t ) );
644 p_xml = xml_Create( p_filter );
647 msg_Err( p_filter, "Failed to open XML parser" );
651 psz_buffer = strdup( p_sys->psz_urls );
652 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
653 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
655 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
657 if( psz_buffer == NULL ) break;
658 if( psz_buffer[0] == 0 ) psz_buffer++;
659 psz_feed = psz_buffer;
660 psz_buffer = strchr( psz_buffer, '|' );
661 if( psz_buffer != NULL ) psz_buffer[0] = 0;
663 p_feed->psz_title = NULL;
664 p_feed->psz_description = NULL;
665 p_feed->psz_link = NULL;
666 p_feed->psz_image = NULL;
667 p_feed->p_pic = NULL;
669 p_feed->p_items = NULL;
671 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
673 p_stream = stream_UrlNew( p_filter, psz_feed );
676 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
680 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
683 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
688 b_is_item = VLC_FALSE;
689 b_is_image = VLC_FALSE;
691 while( xml_ReaderRead( p_xml_reader ) == 1 )
693 switch( xml_ReaderNodeType( p_xml_reader ) )
699 case XML_READER_STARTELEM:
705 psz_eltname = xml_ReaderName( p_xml_reader );
711 msg_Dbg( p_filter, "element name: %s", psz_eltname );
713 if( !strcmp( psz_eltname, "item" ) /* rss */
714 || !strcmp( psz_eltname, "entry" ) ) /* atom */
716 b_is_item = VLC_TRUE;
718 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
719 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
720 p_feed->p_items[p_feed->i_items-1].psz_description
722 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
724 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
726 b_is_image = VLC_TRUE;
728 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
730 char *psz_href = NULL;
731 char *psz_rel = NULL;
732 while( xml_ReaderNextAttr( p_xml_reader )
735 char *psz_name = xml_ReaderName( p_xml_reader );
736 char *psz_value = xml_ReaderValue( p_xml_reader );
737 if( !strcmp( psz_name, "rel" ) )
741 else if( !strcmp( psz_name, "href" ) )
743 psz_href = psz_value;
751 if( psz_rel && psz_href )
753 if( !strcmp( psz_rel, "alternate" )
754 && b_is_item == VLC_FALSE
755 && b_is_image == VLC_FALSE
756 && !p_feed->psz_link )
758 p_feed->psz_link = psz_href;
760 /* this isn't in the rfc but i found some ... */
761 else if( ( !strcmp( psz_rel, "logo" )
762 || !strcmp( psz_rel, "icon" ) )
763 && b_is_item == VLC_FALSE
764 && b_is_image == VLC_FALSE
765 && !p_feed->psz_image )
767 p_feed->psz_image = psz_href;
776 if( psz_href ) free( psz_href );
778 if( psz_rel ) free( psz_rel );
782 case XML_READER_ENDELEM:
788 psz_eltname = xml_ReaderName( p_xml_reader );
794 msg_Dbg( p_filter, "element end : %s", psz_eltname );
796 if( !strcmp( psz_eltname, "item" ) /* rss */
797 || !strcmp( psz_eltname, "entry" ) ) /* atom */
799 b_is_item = VLC_FALSE;
802 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
804 b_is_image = VLC_FALSE;
810 case XML_READER_TEXT:
811 if( !psz_eltname ) break;
812 psz_eltvalue = xml_ReaderValue( p_xml_reader );
820 psz_clean = removeWhiteChars( psz_eltvalue );
821 free( psz_eltvalue ); psz_eltvalue = psz_clean;
824 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
826 if( b_is_item == VLC_TRUE )
828 struct rss_item_t *p_item;
829 p_item = p_feed->p_items+i_item;
830 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
831 && !p_item->psz_title )
833 p_item->psz_title = psz_eltvalue;
835 else if( !strcmp( psz_eltname, "link" ) /* rss */
836 && !p_item->psz_link )
838 p_item->psz_link = psz_eltvalue;
840 else if((!strcmp( psz_eltname, "description" ) /* rss */
841 || !strcmp( psz_eltname, "summary" ) ) /* atom */
842 && !p_item->psz_description )
844 p_item->psz_description = psz_eltvalue;
848 free( psz_eltvalue );
852 else if( b_is_image == VLC_TRUE )
854 if( !strcmp( psz_eltname, "url" ) /* rss */
855 && !p_feed->psz_image )
857 p_feed->psz_image = psz_eltvalue;
861 free( psz_eltvalue );
867 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
868 && !p_feed->psz_title )
870 p_feed->psz_title = psz_eltvalue;
872 else if( !strcmp( psz_eltname, "link" ) /* rss */
873 && !p_feed->psz_link )
875 p_feed->psz_link = psz_eltvalue;
877 else if((!strcmp( psz_eltname, "description" ) /* rss */
878 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
879 && !p_feed->psz_description )
881 p_feed->psz_description = psz_eltvalue;
883 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
884 || !strcmp( psz_eltname, "icon" ) ) /* atom */
885 && !p_feed->psz_image )
887 p_feed->psz_image = psz_eltvalue;
891 free( psz_eltvalue );
899 if( p_sys->b_images == VLC_TRUE
900 && p_feed->psz_image && !p_feed->p_pic )
902 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
905 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
906 if( p_stream ) stream_Delete( p_stream );
907 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
909 free( psz_buffer_2 );
910 if( p_xml ) xml_Delete( p_xml );
915 /****************************************************************************
917 ***************************************************************************/
918 static void FreeRSS( filter_t *p_filter)
920 filter_sys_t *p_sys = p_filter->p_sys;
922 struct rss_item_t *p_item;
923 struct rss_feed_t *p_feed;
928 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
930 p_feed = p_sys->p_feeds+i_feed;
931 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
933 p_item = p_feed->p_items+i_item;
934 free( p_item->psz_title );
935 free( p_item->psz_link );
936 free( p_item->psz_description );
938 free( p_feed->p_items );
939 free( p_feed->psz_title);
940 free( p_feed->psz_link );
941 free( p_feed->psz_description );
942 free( p_feed->psz_image );
943 if( p_feed->p_pic != NULL )
944 p_feed->p_pic->pf_release( p_feed->p_pic );
946 free( p_sys->p_feeds );