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 *****************************************************************************/
36 #include "vlc_filter.h"
37 #include "vlc_block.h"
40 #include "vlc_block.h"
41 #include "vlc_stream.h"
43 #include <vlc_charset.h>
45 #include "vlc_image.h"
47 /*****************************************************************************
49 *****************************************************************************/
50 static int CreateFilter ( vlc_object_t * );
51 static void DestroyFilter( vlc_object_t * );
52 static subpicture_t *Filter( filter_t *, mtime_t );
54 static int FetchRSS( filter_t * );
55 static void FreeRSS( filter_t * );
57 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
58 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
59 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
60 0x00000080, 0x000000FF, 0x0000FFFF};
61 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
62 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
63 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
64 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
67 /*****************************************************************************
68 * filter_sys_t: rss filter descriptor
69 *****************************************************************************/
74 char *psz_description;
81 char *psz_description;
87 struct rss_item_t *p_items;
95 int i_xoff, i_yoff; /* offsets for the display string in the video window */
96 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
100 char *psz_marquee; /* marquee string */
102 text_style_t *p_style; /* font control */
108 struct rss_feed_t *p_feeds;
111 time_t t_last_update;
120 #define MSG_TEXT N_("Feed URLs")
121 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) seperated URLs.")
122 #define SPEED_TEXT N_("Speed of feeds")
123 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
124 #define LENGTH_TEXT N_("Max length")
125 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
127 #define TTL_TEXT N_("Refresh time")
128 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
129 "of the feeds. 0 means that the feeds are never updated." )
130 #define IMAGE_TEXT N_("Feed images")
131 #define IMAGE_LONGTEXT N_("Display feed images if available.")
133 #define POSX_TEXT N_("X offset")
134 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
135 #define POSY_TEXT N_("Y offset")
136 #define POSY_LONGTEXT N_("Y offset, down from the top." )
137 #define OPACITY_TEXT N_("Opacity")
138 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
139 "overlay text. 0 = transparent, 255 = totally opaque." )
141 #define SIZE_TEXT N_("Font size, pixels")
142 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
145 #define COLOR_TEXT N_("Color")
146 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
147 "the video. This must be an hexadecimal (like HTML colors). The first two "\
148 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
149 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
151 #define POS_TEXT N_("Text position")
152 #define POS_LONGTEXT N_( \
153 "You can enforce the text position on the video " \
154 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
155 "also use combinations of these values, eg 6 = top-right).")
157 #define TITLE_TEXT N_("Title display mode")
158 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
160 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
161 static const char *ppsz_pos_descriptions[] =
162 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
163 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
171 static int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
172 static const char *ppsz_title_modes[] =
173 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
175 #define CFG_PREFIX "rss-"
177 /*****************************************************************************
179 *****************************************************************************/
181 set_capability( "sub filter", 1 );
182 set_shortname( "RSS / Atom" );
183 set_callbacks( CreateFilter, DestroyFilter );
184 set_category( CAT_VIDEO );
185 set_subcategory( SUBCAT_VIDEO_SUBPIC );
186 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, VLC_FALSE );
189 set_section( N_("Position"), NULL );
190 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
192 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
194 add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
196 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
198 set_section( N_("Font"), NULL );
199 /* 5 sets the default to top [1] left [4] */
200 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
201 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
203 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
206 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
207 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
210 set_section( N_("Misc"), NULL );
211 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
214 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
217 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_FALSE );
219 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, VLC_FALSE );
221 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, VLC_FALSE );
223 change_integer_list( pi_title_modes, ppsz_title_modes, 0 );
225 set_description( _("RSS and Atom feed display") );
226 add_shortcut( "rss" );
227 add_shortcut( "atom" );
230 static const char *ppsz_filter_options[] = {
231 "urls", "x", "y", "position", "color", "size", "speed", "length",
232 "ttl", "images", "title", NULL
235 /*****************************************************************************
236 * CreateFilter: allocates RSS video filter
237 *****************************************************************************/
238 static int CreateFilter( vlc_object_t *p_this )
240 filter_t *p_filter = (filter_t *)p_this;
244 /* Allocate structure */
245 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
248 msg_Err( p_filter, "out of memory" );
252 vlc_mutex_init( p_filter, &p_sys->lock );
253 vlc_mutex_lock( &p_sys->lock );
255 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
258 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
259 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
260 p_sys->i_cur_feed = 0;
261 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
262 p_sys->i_cur_char = 0;
264 p_sys->p_feeds = NULL;
265 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
266 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
267 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
268 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
270 p_sys->psz_marquee = (char *)malloc( p_sys->i_length + 1 );
271 if( p_sys->psz_marquee == NULL )
273 msg_Err( p_filter, "out of memory" );
274 vlc_mutex_unlock( &p_sys->lock );
275 vlc_mutex_destroy( &p_sys->lock );
279 p_sys->psz_marquee[p_sys->i_length] = '\0';
281 p_sys->p_style = malloc( sizeof( text_style_t ));
282 if( p_sys->p_style == NULL )
284 msg_Err( p_filter, "out of memory" );
285 free( p_sys->psz_marquee );
286 vlc_mutex_unlock( &p_sys->lock );
287 vlc_mutex_destroy( &p_sys->lock );
291 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
293 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
294 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
295 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
296 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
297 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
298 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
300 if( p_sys->b_images == VLC_TRUE && p_sys->p_style->i_font_size == -1 )
302 msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
305 if( FetchRSS( p_filter ) )
307 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
308 free( p_sys->p_style );
309 free( p_sys->psz_marquee );
310 vlc_mutex_unlock( &p_sys->lock );
311 vlc_mutex_destroy( &p_sys->lock );
315 p_sys->t_last_update = time( NULL );
317 if( p_sys->i_feeds == 0 )
319 free( p_sys->p_style );
320 free( p_sys->psz_marquee );
321 vlc_mutex_unlock( &p_sys->lock );
322 vlc_mutex_destroy( &p_sys->lock );
326 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
328 if( p_sys->p_feeds[i_feed].i_items == 0 )
330 free( p_sys->p_style );
331 free( p_sys->psz_marquee );
333 vlc_mutex_unlock( &p_sys->lock );
334 vlc_mutex_destroy( &p_sys->lock );
340 p_filter->pf_sub_filter = Filter;
341 p_sys->last_date = (mtime_t)0;
343 vlc_mutex_unlock( &p_sys->lock );
347 /*****************************************************************************
348 * DestroyFilter: destroy RSS video filter
349 *****************************************************************************/
350 static void DestroyFilter( vlc_object_t *p_this )
352 filter_t *p_filter = (filter_t *)p_this;
353 filter_sys_t *p_sys = p_filter->p_sys;
355 vlc_mutex_lock( &p_sys->lock );
357 if( p_sys->p_style ) free( p_sys->p_style );
358 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
359 free( p_sys->psz_urls );
361 vlc_mutex_unlock( &p_sys->lock );
362 vlc_mutex_destroy( &p_sys->lock );
365 /* Delete the RSS variables */
366 var_Destroy( p_filter, CFG_PREFIX "urls" );
367 var_Destroy( p_filter, CFG_PREFIX "speed" );
368 var_Destroy( p_filter, CFG_PREFIX "length" );
369 var_Destroy( p_filter, CFG_PREFIX "ttl" );
370 var_Destroy( p_filter, CFG_PREFIX "images" );
371 var_Destroy( p_filter, CFG_PREFIX "x" );
372 var_Destroy( p_filter, CFG_PREFIX "y" );
373 var_Destroy( p_filter, CFG_PREFIX "position" );
374 var_Destroy( p_filter, CFG_PREFIX "color");
375 var_Destroy( p_filter, CFG_PREFIX "opacity");
376 var_Destroy( p_filter, CFG_PREFIX "size");
377 var_Destroy( p_filter, CFG_PREFIX "title" );
380 /****************************************************************************
381 * Filter: the whole thing
382 ****************************************************************************
383 * This function outputs subpictures at regular time intervals.
384 ****************************************************************************/
385 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
387 filter_sys_t *p_sys = p_filter->p_sys;
390 subpicture_region_t *p_region;
394 struct rss_feed_t *p_feed;
396 memset( &fmt, 0, sizeof(video_format_t) );
398 vlc_mutex_lock( &p_sys->lock );
401 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
402 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
403 * p_sys->i_speed > date )
405 vlc_mutex_unlock( &p_sys->lock );
409 /* Do we need to update the feeds ? */
411 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
413 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
414 if( FetchRSS( p_filter ) )
416 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
417 vlc_mutex_unlock( &p_sys->lock );
418 return NULL; /* FIXME : we most likely messed up all the data,
419 * so we might need to do something about it */
421 p_sys->t_last_update = time( NULL );
424 p_sys->last_date = date;
426 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 )
428 p_sys->i_cur_char = 0;
430 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
432 if( p_sys->i_title == scroll_title )
433 p_sys->i_cur_item = -1;
435 p_sys->i_cur_item = 0;
436 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
440 p_spu = p_filter->pf_sub_buffer_new( p_filter );
443 vlc_mutex_unlock( &p_sys->lock );
447 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
449 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
450 if( !p_spu->p_region )
452 p_filter->pf_sub_buffer_del( p_filter, p_spu );
453 vlc_mutex_unlock( &p_sys->lock );
457 /* Generate the string that will be displayed. This string is supposed to
458 be p_sys->i_length characters long. */
459 i_item = p_sys->i_cur_item;
460 i_feed = p_sys->i_cur_feed;
461 p_feed = &p_sys->p_feeds[i_feed];
463 if( ( p_feed->p_pic && p_sys->i_title == default_title )
464 || p_sys->i_title == hide_title )
466 /* Don't display the feed's title if we have an image */
467 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
468 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
469 +p_sys->i_cur_char );
471 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
472 || p_sys->i_title == prepend_title )
474 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
475 p_sys->p_feeds[i_feed].psz_title,
476 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
477 +p_sys->i_cur_char );
479 else /* scrolling title */
482 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
483 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
484 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
486 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
487 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
488 +p_sys->i_cur_char );
491 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
494 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
495 snprintf( strchr( p_sys->psz_marquee, 0 ),
496 p_sys->i_length - strlen( p_sys->psz_marquee ),
498 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
501 /* Calls to snprintf might split multibyte UTF8 chars ...
502 * which freetype doesn't like. */
504 char *a = strdup( p_sys->psz_marquee );
506 char *b = p_sys->psz_marquee;
507 EnsureUTF8( p_sys->psz_marquee );
508 /* we want to use ' ' instead of '?' for erroneous chars */
511 if( *b != *a ) *b = ' ';
517 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
518 if( p_sys->p_style->i_font_size > 0 )
519 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
520 p_spu->i_start = date;
522 p_spu->b_ephemer = VLC_TRUE;
524 /* where to locate the string: */
525 if( p_sys->i_pos < 0 )
526 { /* set to an absolute xy */
527 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
528 p_spu->b_absolute = VLC_TRUE;
531 { /* set to one of the 9 relative locations */
532 p_spu->p_region->i_align = p_sys->i_pos;
533 p_spu->b_absolute = VLC_FALSE;
536 p_spu->i_x = p_sys->i_xoff;
537 p_spu->i_y = p_sys->i_yoff;
540 p_spu->p_region->p_style = p_sys->p_style;
544 /* Display the feed's image */
545 picture_t *p_pic = p_feed->p_pic;
546 video_format_t fmt_out;
548 memset( &fmt_out, 0, sizeof(video_format_t) );
550 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
551 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
552 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
554 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
556 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
558 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
561 msg_Err( p_filter, "cannot allocate SPU region" );
565 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
566 p_spu->p_region->p_next = p_region;
569 /* Offset text to display right next to the image */
570 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
573 vlc_mutex_unlock( &p_sys->lock );
577 /****************************************************************************
578 * RSS related functions
579 ****************************************************************************
580 * You should always lock the p_filter mutex before using any of these
582 ***************************************************************************/
584 #undef LoadImage /* do not conflict with Win32 API */
586 /****************************************************************************
587 * download and resize image located at psz_url
588 ***************************************************************************/
589 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
591 filter_sys_t *p_sys = p_filter->p_sys;
592 video_format_t fmt_in;
593 video_format_t fmt_out;
595 picture_t *p_pic = NULL;
596 image_handler_t *p_handler = image_HandlerCreate( p_filter );
598 memset( &fmt_in, 0, sizeof(video_format_t) );
599 memset( &fmt_out, 0, sizeof(video_format_t) );
601 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
602 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
606 msg_Warn( p_filter, "Unable to read image %s", psz_url );
608 else if( p_sys->p_style->i_font_size > 0 )
611 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
612 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
613 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
614 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
615 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
616 fmt_out.i_height = p_sys->p_style->i_font_size;
618 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
619 p_orig->pf_release( p_orig );
622 msg_Warn( p_filter, "Error while converting %s", psz_url );
630 image_HandlerDelete( p_handler );
635 /****************************************************************************
636 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
638 ***************************************************************************/
639 static char *removeWhiteChars( char *psz_src )
641 char *psz_src2 = strdup( psz_src );
642 char *psz_clean = strdup( psz_src2 );
645 while( ( *psz_clean == ' ' || *psz_clean == '\t'
646 || *psz_clean == '\n' || *psz_clean == '\r' )
647 && *psz_clean != '\0' )
651 i = strlen( psz_clean );
653 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
654 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
655 psz_clean[i+1] = '\0';
656 psz_clean2 = strdup( psz_clean );
661 /****************************************************************************
662 * FetchRSS (or Atom) feeds
663 ***************************************************************************/
664 static int FetchRSS( filter_t *p_filter)
666 filter_sys_t *p_sys = p_filter->p_sys;
668 stream_t *p_stream = NULL;
670 xml_reader_t *p_xml_reader = NULL;
672 char *psz_eltname = NULL;
673 char *psz_eltvalue = NULL;
674 char *psz_feed = NULL;
675 char *psz_buffer = NULL;
676 char *psz_buffer_2 = NULL;
680 vlc_bool_t b_is_item;
681 vlc_bool_t b_is_image;
687 while( p_sys->psz_urls[i_int] != 0 )
688 if( p_sys->psz_urls[i_int++] == '|' )
690 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
691 * sizeof( struct rss_feed_t ) );
693 p_xml = xml_Create( p_filter );
696 msg_Err( p_filter, "Failed to open XML parser" );
700 psz_buffer = strdup( p_sys->psz_urls );
701 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
702 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
704 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
706 if( psz_buffer == NULL ) break;
707 if( psz_buffer[0] == 0 ) psz_buffer++;
708 psz_feed = psz_buffer;
709 psz_buffer = strchr( psz_buffer, '|' );
710 if( psz_buffer != NULL ) psz_buffer[0] = 0;
712 p_feed->psz_title = NULL;
713 p_feed->psz_description = NULL;
714 p_feed->psz_link = NULL;
715 p_feed->psz_image = NULL;
716 p_feed->p_pic = NULL;
718 p_feed->p_items = NULL;
720 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
722 p_stream = stream_UrlNew( p_filter, psz_feed );
725 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
729 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
732 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
737 b_is_item = VLC_FALSE;
738 b_is_image = VLC_FALSE;
740 while( xml_ReaderRead( p_xml_reader ) == 1 )
742 switch( xml_ReaderNodeType( p_xml_reader ) )
748 case XML_READER_STARTELEM:
754 psz_eltname = xml_ReaderName( p_xml_reader );
760 msg_Dbg( p_filter, "element name: %s", psz_eltname );
762 if( !strcmp( psz_eltname, "item" ) /* rss */
763 || !strcmp( psz_eltname, "entry" ) ) /* atom */
765 b_is_item = VLC_TRUE;
767 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
768 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
769 p_feed->p_items[p_feed->i_items-1].psz_description
771 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
773 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
775 b_is_image = VLC_TRUE;
777 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
779 char *psz_href = NULL;
780 char *psz_rel = NULL;
781 while( xml_ReaderNextAttr( p_xml_reader )
784 char *psz_name = xml_ReaderName( p_xml_reader );
785 char *psz_value = xml_ReaderValue( p_xml_reader );
786 if( !strcmp( psz_name, "rel" ) )
790 else if( !strcmp( psz_name, "href" ) )
792 psz_href = psz_value;
800 if( psz_rel && psz_href )
802 if( !strcmp( psz_rel, "alternate" )
803 && b_is_item == VLC_FALSE
804 && b_is_image == VLC_FALSE
805 && !p_feed->psz_link )
807 p_feed->psz_link = psz_href;
809 /* this isn't in the rfc but i found some ... */
810 else if( ( !strcmp( psz_rel, "logo" )
811 || !strcmp( psz_rel, "icon" ) )
812 && b_is_item == VLC_FALSE
813 && b_is_image == VLC_FALSE
814 && !p_feed->psz_image )
816 p_feed->psz_image = psz_href;
825 if( psz_href ) free( psz_href );
827 if( psz_rel ) free( psz_rel );
831 case XML_READER_ENDELEM:
837 psz_eltname = xml_ReaderName( p_xml_reader );
843 msg_Dbg( p_filter, "element end : %s", psz_eltname );
845 if( !strcmp( psz_eltname, "item" ) /* rss */
846 || !strcmp( psz_eltname, "entry" ) ) /* atom */
848 b_is_item = VLC_FALSE;
851 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
853 b_is_image = VLC_FALSE;
859 case XML_READER_TEXT:
860 if( !psz_eltname ) break;
861 psz_eltvalue = xml_ReaderValue( p_xml_reader );
869 psz_clean = removeWhiteChars( psz_eltvalue );
870 free( psz_eltvalue ); psz_eltvalue = psz_clean;
873 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
875 if( b_is_item == VLC_TRUE )
877 struct rss_item_t *p_item;
878 p_item = p_feed->p_items+i_item;
879 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
880 && !p_item->psz_title )
882 p_item->psz_title = psz_eltvalue;
884 else if( !strcmp( psz_eltname, "link" ) /* rss */
885 && !p_item->psz_link )
887 p_item->psz_link = psz_eltvalue;
889 else if((!strcmp( psz_eltname, "description" ) /* rss */
890 || !strcmp( psz_eltname, "summary" ) ) /* atom */
891 && !p_item->psz_description )
893 p_item->psz_description = psz_eltvalue;
897 free( psz_eltvalue );
901 else if( b_is_image == VLC_TRUE )
903 if( !strcmp( psz_eltname, "url" ) /* rss */
904 && !p_feed->psz_image )
906 p_feed->psz_image = psz_eltvalue;
910 free( psz_eltvalue );
916 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
917 && !p_feed->psz_title )
919 p_feed->psz_title = psz_eltvalue;
921 else if( !strcmp( psz_eltname, "link" ) /* rss */
922 && !p_feed->psz_link )
924 p_feed->psz_link = psz_eltvalue;
926 else if((!strcmp( psz_eltname, "description" ) /* rss */
927 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
928 && !p_feed->psz_description )
930 p_feed->psz_description = psz_eltvalue;
932 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
933 || !strcmp( psz_eltname, "icon" ) ) /* atom */
934 && !p_feed->psz_image )
936 p_feed->psz_image = psz_eltvalue;
940 free( psz_eltvalue );
948 if( p_sys->b_images == VLC_TRUE
949 && p_feed->psz_image && !p_feed->p_pic )
951 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
954 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
955 if( p_stream ) stream_Delete( p_stream );
956 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
958 free( psz_buffer_2 );
959 if( p_xml ) xml_Delete( p_xml );
964 /****************************************************************************
966 ***************************************************************************/
967 static void FreeRSS( filter_t *p_filter)
969 filter_sys_t *p_sys = p_filter->p_sys;
971 struct rss_item_t *p_item;
972 struct rss_feed_t *p_feed;
977 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
979 p_feed = p_sys->p_feeds+i_feed;
980 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
982 p_item = p_feed->p_items+i_item;
983 free( p_item->psz_title );
984 free( p_item->psz_link );
985 free( p_item->psz_description );
987 free( p_feed->p_items );
988 free( p_feed->psz_title);
989 free( p_feed->psz_link );
990 free( p_feed->psz_description );
991 free( p_feed->psz_image );
992 if( p_feed->p_pic != NULL )
993 p_feed->p_pic->pf_release( p_feed->p_pic );
995 free( p_sys->p_feeds );