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 );
188 set_section( N_("Position"), NULL );
189 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
190 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
191 add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
192 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
194 set_section( N_("Font"), NULL );
195 /* 5 sets the default to top [1] left [4] */
196 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
197 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
198 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
200 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
201 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
203 set_section( N_("Misc"), NULL );
204 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
206 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
208 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_FALSE );
209 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, VLC_FALSE );
210 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, VLC_FALSE );
211 change_integer_list( pi_title_modes, ppsz_title_modes, 0 );
213 set_description( _("RSS and Atom feed display") );
214 add_shortcut( "rss" );
215 add_shortcut( "atom" );
218 static const char *ppsz_filter_options[] = {
219 "urls", "x", "y", "position", "color", "size", "speed", "length",
220 "ttl", "images", "title", NULL
223 /*****************************************************************************
224 * CreateFilter: allocates RSS video filter
225 *****************************************************************************/
226 static int CreateFilter( vlc_object_t *p_this )
228 filter_t *p_filter = (filter_t *)p_this;
232 /* Allocate structure */
233 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
236 msg_Err( p_filter, "out of memory" );
240 vlc_mutex_init( p_filter, &p_sys->lock );
241 vlc_mutex_lock( &p_sys->lock );
243 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
246 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
247 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
248 p_sys->i_cur_feed = 0;
249 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
250 p_sys->i_cur_char = 0;
252 p_sys->p_feeds = NULL;
253 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
254 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
255 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
256 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
258 p_sys->psz_marquee = (char *)malloc( p_sys->i_length + 1 );
259 if( p_sys->psz_marquee == NULL )
261 msg_Err( p_filter, "out of memory" );
262 vlc_mutex_unlock( &p_sys->lock );
263 vlc_mutex_destroy( &p_sys->lock );
267 p_sys->psz_marquee[p_sys->i_length] = '\0';
269 p_sys->p_style = malloc( sizeof( text_style_t ));
270 if( p_sys->p_style == NULL )
272 msg_Err( p_filter, "out of memory" );
273 free( p_sys->psz_marquee );
274 vlc_mutex_unlock( &p_sys->lock );
275 vlc_mutex_destroy( &p_sys->lock );
279 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
281 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
282 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
283 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
284 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
285 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
286 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
288 if( p_sys->b_images == VLC_TRUE && p_sys->p_style->i_font_size == -1 )
290 msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
293 if( FetchRSS( p_filter ) )
295 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
296 free( p_sys->p_style );
297 free( p_sys->psz_marquee );
298 vlc_mutex_unlock( &p_sys->lock );
299 vlc_mutex_destroy( &p_sys->lock );
303 p_sys->t_last_update = time( NULL );
305 if( p_sys->i_feeds == 0 )
307 free( p_sys->p_style );
308 free( p_sys->psz_marquee );
309 vlc_mutex_unlock( &p_sys->lock );
310 vlc_mutex_destroy( &p_sys->lock );
314 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
316 if( p_sys->p_feeds[i_feed].i_items == 0 )
318 free( p_sys->p_style );
319 free( p_sys->psz_marquee );
321 vlc_mutex_unlock( &p_sys->lock );
322 vlc_mutex_destroy( &p_sys->lock );
328 p_filter->pf_sub_filter = Filter;
329 p_sys->last_date = (mtime_t)0;
331 vlc_mutex_unlock( &p_sys->lock );
335 /*****************************************************************************
336 * DestroyFilter: destroy RSS video filter
337 *****************************************************************************/
338 static void DestroyFilter( vlc_object_t *p_this )
340 filter_t *p_filter = (filter_t *)p_this;
341 filter_sys_t *p_sys = p_filter->p_sys;
343 vlc_mutex_lock( &p_sys->lock );
345 if( p_sys->p_style ) free( p_sys->p_style );
346 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
347 free( p_sys->psz_urls );
349 vlc_mutex_unlock( &p_sys->lock );
350 vlc_mutex_destroy( &p_sys->lock );
353 /* Delete the RSS variables */
354 var_Destroy( p_filter, CFG_PREFIX "urls" );
355 var_Destroy( p_filter, CFG_PREFIX "speed" );
356 var_Destroy( p_filter, CFG_PREFIX "length" );
357 var_Destroy( p_filter, CFG_PREFIX "ttl" );
358 var_Destroy( p_filter, CFG_PREFIX "images" );
359 var_Destroy( p_filter, CFG_PREFIX "x" );
360 var_Destroy( p_filter, CFG_PREFIX "y" );
361 var_Destroy( p_filter, CFG_PREFIX "position" );
362 var_Destroy( p_filter, CFG_PREFIX "color");
363 var_Destroy( p_filter, CFG_PREFIX "opacity");
364 var_Destroy( p_filter, CFG_PREFIX "size");
365 var_Destroy( p_filter, CFG_PREFIX "title" );
368 /****************************************************************************
369 * Filter: the whole thing
370 ****************************************************************************
371 * This function outputs subpictures at regular time intervals.
372 ****************************************************************************/
373 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
375 filter_sys_t *p_sys = p_filter->p_sys;
378 subpicture_region_t *p_region;
382 struct rss_feed_t *p_feed;
384 memset( &fmt, 0, sizeof(video_format_t) );
386 vlc_mutex_lock( &p_sys->lock );
389 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
390 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
391 * p_sys->i_speed > date )
393 vlc_mutex_unlock( &p_sys->lock );
397 /* Do we need to update the feeds ? */
399 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
401 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
402 if( FetchRSS( p_filter ) )
404 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
405 vlc_mutex_unlock( &p_sys->lock );
406 return NULL; /* FIXME : we most likely messed up all the data,
407 * so we might need to do something about it */
409 p_sys->t_last_update = time( NULL );
412 p_sys->last_date = date;
414 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 )
416 p_sys->i_cur_char = 0;
418 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
420 if( p_sys->i_title == scroll_title )
421 p_sys->i_cur_item = -1;
423 p_sys->i_cur_item = 0;
424 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
428 p_spu = p_filter->pf_sub_buffer_new( p_filter );
431 vlc_mutex_unlock( &p_sys->lock );
435 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
437 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
438 if( !p_spu->p_region )
440 p_filter->pf_sub_buffer_del( p_filter, p_spu );
441 vlc_mutex_unlock( &p_sys->lock );
445 /* Generate the string that will be displayed. This string is supposed to
446 be p_sys->i_length characters long. */
447 i_item = p_sys->i_cur_item;
448 i_feed = p_sys->i_cur_feed;
449 p_feed = &p_sys->p_feeds[i_feed];
451 if( ( p_feed->p_pic && p_sys->i_title == default_title )
452 || p_sys->i_title == hide_title )
454 /* Don't display the feed's title if we have an image */
455 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
456 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
457 +p_sys->i_cur_char );
459 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
460 || p_sys->i_title == prepend_title )
462 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
463 p_sys->p_feeds[i_feed].psz_title,
464 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
465 +p_sys->i_cur_char );
467 else /* scrolling title */
470 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
471 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
472 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
474 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
475 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
476 +p_sys->i_cur_char );
479 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
482 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
483 snprintf( strchr( p_sys->psz_marquee, 0 ),
484 p_sys->i_length - strlen( p_sys->psz_marquee ),
486 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
489 /* Calls to snprintf might split multibyte UTF8 chars ...
490 * which freetype doesn't like. */
492 char *a = strdup( p_sys->psz_marquee );
494 char *b = p_sys->psz_marquee;
495 EnsureUTF8( p_sys->psz_marquee );
496 /* we want to use ' ' instead of '?' for erroneous chars */
499 if( *b != *a ) *b = ' ';
505 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
506 if( p_sys->p_style->i_font_size > 0 )
507 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
508 p_spu->i_start = date;
510 p_spu->b_ephemer = VLC_TRUE;
512 /* where to locate the string: */
513 if( p_sys->i_pos < 0 )
514 { /* set to an absolute xy */
515 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
516 p_spu->b_absolute = VLC_TRUE;
519 { /* set to one of the 9 relative locations */
520 p_spu->p_region->i_align = p_sys->i_pos;
521 p_spu->b_absolute = VLC_FALSE;
524 p_spu->i_x = p_sys->i_xoff;
525 p_spu->i_y = p_sys->i_yoff;
528 p_spu->p_region->p_style = p_sys->p_style;
532 /* Display the feed's image */
533 picture_t *p_pic = p_feed->p_pic;
534 video_format_t fmt_out;
536 memset( &fmt_out, 0, sizeof(video_format_t) );
538 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
539 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
540 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
542 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
544 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
546 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
549 msg_Err( p_filter, "cannot allocate SPU region" );
553 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
554 p_spu->p_region->p_next = p_region;
557 /* Offset text to display right next to the image */
558 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
561 vlc_mutex_unlock( &p_sys->lock );
565 /****************************************************************************
566 * RSS related functions
567 ****************************************************************************
568 * You should always lock the p_filter mutex before using any of these
570 ***************************************************************************/
572 #undef LoadImage /* do not conflict with Win32 API */
574 /****************************************************************************
575 * download and resize image located at psz_url
576 ***************************************************************************/
577 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
579 filter_sys_t *p_sys = p_filter->p_sys;
580 video_format_t fmt_in;
581 video_format_t fmt_out;
583 picture_t *p_pic = NULL;
584 image_handler_t *p_handler = image_HandlerCreate( p_filter );
586 memset( &fmt_in, 0, sizeof(video_format_t) );
587 memset( &fmt_out, 0, sizeof(video_format_t) );
589 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
590 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
594 msg_Warn( p_filter, "Unable to read image %s", psz_url );
596 else if( p_sys->p_style->i_font_size > 0 )
599 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
600 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
601 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
602 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
603 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
604 fmt_out.i_height = p_sys->p_style->i_font_size;
606 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
607 p_orig->pf_release( p_orig );
610 msg_Warn( p_filter, "Error while converting %s", psz_url );
618 image_HandlerDelete( p_handler );
623 /****************************************************************************
624 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
626 ***************************************************************************/
627 static char *removeWhiteChars( char *psz_src )
629 char *psz_src2 = strdup( psz_src );
630 char *psz_clean = strdup( psz_src2 );
633 while( ( *psz_clean == ' ' || *psz_clean == '\t'
634 || *psz_clean == '\n' || *psz_clean == '\r' )
635 && *psz_clean != '\0' )
639 i = strlen( psz_clean );
641 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
642 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
643 psz_clean[i+1] = '\0';
644 psz_clean2 = strdup( psz_clean );
649 /****************************************************************************
650 * FetchRSS (or Atom) feeds
651 ***************************************************************************/
652 static int FetchRSS( filter_t *p_filter)
654 filter_sys_t *p_sys = p_filter->p_sys;
656 stream_t *p_stream = NULL;
658 xml_reader_t *p_xml_reader = NULL;
660 char *psz_eltname = NULL;
661 char *psz_eltvalue = NULL;
662 char *psz_feed = NULL;
663 char *psz_buffer = NULL;
664 char *psz_buffer_2 = NULL;
668 vlc_bool_t b_is_item;
669 vlc_bool_t b_is_image;
675 while( p_sys->psz_urls[i_int] != 0 )
676 if( p_sys->psz_urls[i_int++] == '|' )
678 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
679 * sizeof( struct rss_feed_t ) );
681 p_xml = xml_Create( p_filter );
684 msg_Err( p_filter, "Failed to open XML parser" );
688 psz_buffer = strdup( p_sys->psz_urls );
689 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
690 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
692 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
694 if( psz_buffer == NULL ) break;
695 if( psz_buffer[0] == 0 ) psz_buffer++;
696 psz_feed = psz_buffer;
697 psz_buffer = strchr( psz_buffer, '|' );
698 if( psz_buffer != NULL ) psz_buffer[0] = 0;
700 p_feed->psz_title = NULL;
701 p_feed->psz_description = NULL;
702 p_feed->psz_link = NULL;
703 p_feed->psz_image = NULL;
704 p_feed->p_pic = NULL;
706 p_feed->p_items = NULL;
708 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
710 p_stream = stream_UrlNew( p_filter, psz_feed );
713 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
717 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
720 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
725 b_is_item = VLC_FALSE;
726 b_is_image = VLC_FALSE;
728 while( xml_ReaderRead( p_xml_reader ) == 1 )
730 switch( xml_ReaderNodeType( p_xml_reader ) )
736 case XML_READER_STARTELEM:
742 psz_eltname = xml_ReaderName( p_xml_reader );
748 msg_Dbg( p_filter, "element name: %s", psz_eltname );
750 if( !strcmp( psz_eltname, "item" ) /* rss */
751 || !strcmp( psz_eltname, "entry" ) ) /* atom */
753 b_is_item = VLC_TRUE;
755 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
756 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
757 p_feed->p_items[p_feed->i_items-1].psz_description
759 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
761 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
763 b_is_image = VLC_TRUE;
765 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
767 char *psz_href = NULL;
768 char *psz_rel = NULL;
769 while( xml_ReaderNextAttr( p_xml_reader )
772 char *psz_name = xml_ReaderName( p_xml_reader );
773 char *psz_value = xml_ReaderValue( p_xml_reader );
774 if( !strcmp( psz_name, "rel" ) )
778 else if( !strcmp( psz_name, "href" ) )
780 psz_href = psz_value;
788 if( psz_rel && psz_href )
790 if( !strcmp( psz_rel, "alternate" )
791 && b_is_item == VLC_FALSE
792 && b_is_image == VLC_FALSE
793 && !p_feed->psz_link )
795 p_feed->psz_link = psz_href;
797 /* this isn't in the rfc but i found some ... */
798 else if( ( !strcmp( psz_rel, "logo" )
799 || !strcmp( psz_rel, "icon" ) )
800 && b_is_item == VLC_FALSE
801 && b_is_image == VLC_FALSE
802 && !p_feed->psz_image )
804 p_feed->psz_image = psz_href;
813 if( psz_href ) free( psz_href );
815 if( psz_rel ) free( psz_rel );
819 case XML_READER_ENDELEM:
825 psz_eltname = xml_ReaderName( p_xml_reader );
831 msg_Dbg( p_filter, "element end : %s", psz_eltname );
833 if( !strcmp( psz_eltname, "item" ) /* rss */
834 || !strcmp( psz_eltname, "entry" ) ) /* atom */
836 b_is_item = VLC_FALSE;
839 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
841 b_is_image = VLC_FALSE;
847 case XML_READER_TEXT:
848 if( !psz_eltname ) break;
849 psz_eltvalue = xml_ReaderValue( p_xml_reader );
857 psz_clean = removeWhiteChars( psz_eltvalue );
858 free( psz_eltvalue ); psz_eltvalue = psz_clean;
861 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
863 if( b_is_item == VLC_TRUE )
865 struct rss_item_t *p_item;
866 p_item = p_feed->p_items+i_item;
867 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
868 && !p_item->psz_title )
870 p_item->psz_title = psz_eltvalue;
872 else if( !strcmp( psz_eltname, "link" ) /* rss */
873 && !p_item->psz_link )
875 p_item->psz_link = psz_eltvalue;
877 else if((!strcmp( psz_eltname, "description" ) /* rss */
878 || !strcmp( psz_eltname, "summary" ) ) /* atom */
879 && !p_item->psz_description )
881 p_item->psz_description = psz_eltvalue;
885 free( psz_eltvalue );
889 else if( b_is_image == VLC_TRUE )
891 if( !strcmp( psz_eltname, "url" ) /* rss */
892 && !p_feed->psz_image )
894 p_feed->psz_image = psz_eltvalue;
898 free( psz_eltvalue );
904 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
905 && !p_feed->psz_title )
907 p_feed->psz_title = psz_eltvalue;
909 else if( !strcmp( psz_eltname, "link" ) /* rss */
910 && !p_feed->psz_link )
912 p_feed->psz_link = psz_eltvalue;
914 else if((!strcmp( psz_eltname, "description" ) /* rss */
915 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
916 && !p_feed->psz_description )
918 p_feed->psz_description = psz_eltvalue;
920 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
921 || !strcmp( psz_eltname, "icon" ) ) /* atom */
922 && !p_feed->psz_image )
924 p_feed->psz_image = psz_eltvalue;
928 free( psz_eltvalue );
936 if( p_sys->b_images == VLC_TRUE
937 && p_feed->psz_image && !p_feed->p_pic )
939 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
942 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
943 if( p_stream ) stream_Delete( p_stream );
944 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
946 free( psz_buffer_2 );
947 if( p_xml ) xml_Delete( p_xml );
952 /****************************************************************************
954 ***************************************************************************/
955 static void FreeRSS( filter_t *p_filter)
957 filter_sys_t *p_sys = p_filter->p_sys;
959 struct rss_item_t *p_item;
960 struct rss_feed_t *p_feed;
965 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
967 p_feed = p_sys->p_feeds+i_feed;
968 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
970 p_item = p_feed->p_items+i_item;
971 free( p_item->psz_title );
972 free( p_item->psz_link );
973 free( p_item->psz_description );
975 free( p_feed->p_items );
976 free( p_feed->psz_title);
977 free( p_feed->psz_link );
978 free( p_feed->psz_description );
979 free( p_feed->psz_image );
980 if( p_feed->p_pic != NULL )
981 p_feed->p_pic->pf_release( p_feed->p_pic );
983 free( p_sys->p_feeds );