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 *****************************************************************************/
40 #include "vlc_filter.h"
41 #include "vlc_block.h"
44 #include "vlc_block.h"
45 #include "vlc_stream.h"
47 #include <vlc_charset.h>
49 #include "vlc_image.h"
53 /*****************************************************************************
55 *****************************************************************************/
56 static int CreateFilter ( vlc_object_t * );
57 static void DestroyFilter( vlc_object_t * );
58 static subpicture_t *Filter( filter_t *, mtime_t );
60 static int FetchRSS( filter_t * );
61 static void FreeRSS( filter_t * );
63 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
64 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
65 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
66 0x00000080, 0x000000FF, 0x0000FFFF};
67 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
68 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
69 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
70 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
73 /*****************************************************************************
74 * filter_sys_t: rss filter descriptor
75 *****************************************************************************/
80 char *psz_description;
87 char *psz_description;
93 struct rss_item_t *p_items;
101 int i_xoff, i_yoff; /* offsets for the display string in the video window */
102 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
106 char *psz_marquee; /* marquee string */
108 text_style_t *p_style; /* font control */
114 struct rss_feed_t *p_feeds;
117 time_t t_last_update;
126 #define MSG_TEXT N_("Feed URLs")
127 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) seperated URLs.")
128 #define SPEED_TEXT N_("Speed of feeds")
129 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
130 #define LENGTH_TEXT N_("Max length")
131 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
133 #define TTL_TEXT N_("Refresh time")
134 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
135 "of the feeds. 0 means that the feeds are never updated." )
136 #define IMAGE_TEXT N_("Feed images")
137 #define IMAGE_LONGTEXT N_("Display feed images if available.")
139 #define POSX_TEXT N_("X offset")
140 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
141 #define POSY_TEXT N_("Y offset")
142 #define POSY_LONGTEXT N_("Y offset, down from the top." )
143 #define OPACITY_TEXT N_("Opacity")
144 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
145 "overlay text. 0 = transparent, 255 = totally opaque." )
147 #define SIZE_TEXT N_("Font size, pixels")
148 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
151 #define COLOR_TEXT N_("Color")
152 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
153 "the video. This must be an hexadecimal (like HTML colors). The first two "\
154 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
155 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
157 #define POS_TEXT N_("Text position")
158 #define POS_LONGTEXT N_( \
159 "You can enforce the text position on the video " \
160 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
161 "also use combinations of these values, eg 6 = top-right).")
163 #define TITLE_TEXT N_("Title display mode")
164 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
166 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
167 static const char *ppsz_pos_descriptions[] =
168 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
169 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
177 static int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
178 static const char *ppsz_title_modes[] =
179 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
181 #define CFG_PREFIX "rss-"
183 /*****************************************************************************
185 *****************************************************************************/
187 set_capability( "sub filter", 1 );
188 set_shortname( "RSS / Atom" );
189 set_callbacks( CreateFilter, DestroyFilter );
190 set_category( CAT_VIDEO );
191 set_subcategory( SUBCAT_VIDEO_SUBPIC );
192 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, VLC_FALSE );
194 set_section( N_("Position"), NULL );
195 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
196 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
197 add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
198 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
200 set_section( N_("Font"), NULL );
201 /* 5 sets the default to top [1] left [4] */
202 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
203 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
204 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 );
209 set_section( N_("Misc"), NULL );
210 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
212 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
214 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_FALSE );
215 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, VLC_FALSE );
216 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, VLC_FALSE );
217 change_integer_list( pi_title_modes, ppsz_title_modes, 0 );
219 set_description( _("RSS and Atom feed display") );
220 add_shortcut( "rss" );
221 add_shortcut( "atom" );
224 static const char *ppsz_filter_options[] = {
225 "urls", "x", "y", "position", "color", "size", "speed", "length",
226 "ttl", "images", "title", NULL
229 /*****************************************************************************
230 * CreateFilter: allocates RSS video filter
231 *****************************************************************************/
232 static int CreateFilter( vlc_object_t *p_this )
234 filter_t *p_filter = (filter_t *)p_this;
238 /* Allocate structure */
239 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
242 msg_Err( p_filter, "out of memory" );
246 vlc_mutex_init( p_filter, &p_sys->lock );
247 vlc_mutex_lock( &p_sys->lock );
249 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
252 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
253 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
254 p_sys->i_cur_feed = 0;
255 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
256 p_sys->i_cur_char = 0;
258 p_sys->p_feeds = NULL;
259 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
260 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
261 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
262 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
264 p_sys->psz_marquee = (char *)malloc( p_sys->i_length + 1 );
265 if( p_sys->psz_marquee == NULL )
267 msg_Err( p_filter, "out of memory" );
268 vlc_mutex_unlock( &p_sys->lock );
269 vlc_mutex_destroy( &p_sys->lock );
273 p_sys->psz_marquee[p_sys->i_length] = '\0';
275 p_sys->p_style = malloc( sizeof( text_style_t ));
276 if( p_sys->p_style == NULL )
278 msg_Err( p_filter, "out of memory" );
279 free( p_sys->psz_marquee );
280 vlc_mutex_unlock( &p_sys->lock );
281 vlc_mutex_destroy( &p_sys->lock );
285 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
287 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
288 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
289 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
290 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
291 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
292 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
294 if( p_sys->b_images == VLC_TRUE && p_sys->p_style->i_font_size == -1 )
296 msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
299 if( FetchRSS( p_filter ) )
301 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
302 free( p_sys->p_style );
303 free( p_sys->psz_marquee );
304 vlc_mutex_unlock( &p_sys->lock );
305 vlc_mutex_destroy( &p_sys->lock );
309 p_sys->t_last_update = time( NULL );
311 if( p_sys->i_feeds == 0 )
313 free( p_sys->p_style );
314 free( p_sys->psz_marquee );
315 vlc_mutex_unlock( &p_sys->lock );
316 vlc_mutex_destroy( &p_sys->lock );
320 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
322 if( p_sys->p_feeds[i_feed].i_items == 0 )
324 free( p_sys->p_style );
325 free( p_sys->psz_marquee );
327 vlc_mutex_unlock( &p_sys->lock );
328 vlc_mutex_destroy( &p_sys->lock );
334 p_filter->pf_sub_filter = Filter;
335 p_sys->last_date = (mtime_t)0;
337 vlc_mutex_unlock( &p_sys->lock );
341 /*****************************************************************************
342 * DestroyFilter: destroy RSS video filter
343 *****************************************************************************/
344 static void DestroyFilter( vlc_object_t *p_this )
346 filter_t *p_filter = (filter_t *)p_this;
347 filter_sys_t *p_sys = p_filter->p_sys;
349 vlc_mutex_lock( &p_sys->lock );
351 if( p_sys->p_style ) free( p_sys->p_style );
352 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
353 free( p_sys->psz_urls );
355 vlc_mutex_unlock( &p_sys->lock );
356 vlc_mutex_destroy( &p_sys->lock );
359 /* Delete the RSS variables */
360 var_Destroy( p_filter, CFG_PREFIX "urls" );
361 var_Destroy( p_filter, CFG_PREFIX "speed" );
362 var_Destroy( p_filter, CFG_PREFIX "length" );
363 var_Destroy( p_filter, CFG_PREFIX "ttl" );
364 var_Destroy( p_filter, CFG_PREFIX "images" );
365 var_Destroy( p_filter, CFG_PREFIX "x" );
366 var_Destroy( p_filter, CFG_PREFIX "y" );
367 var_Destroy( p_filter, CFG_PREFIX "position" );
368 var_Destroy( p_filter, CFG_PREFIX "color");
369 var_Destroy( p_filter, CFG_PREFIX "opacity");
370 var_Destroy( p_filter, CFG_PREFIX "size");
371 var_Destroy( p_filter, CFG_PREFIX "title" );
374 /****************************************************************************
375 * Filter: the whole thing
376 ****************************************************************************
377 * This function outputs subpictures at regular time intervals.
378 ****************************************************************************/
379 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
381 filter_sys_t *p_sys = p_filter->p_sys;
384 subpicture_region_t *p_region;
388 struct rss_feed_t *p_feed;
390 memset( &fmt, 0, sizeof(video_format_t) );
392 vlc_mutex_lock( &p_sys->lock );
395 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
396 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
397 * p_sys->i_speed > date )
399 vlc_mutex_unlock( &p_sys->lock );
403 /* Do we need to update the feeds ? */
405 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
407 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
408 if( FetchRSS( p_filter ) )
410 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
411 vlc_mutex_unlock( &p_sys->lock );
412 return NULL; /* FIXME : we most likely messed up all the data,
413 * so we might need to do something about it */
415 p_sys->t_last_update = time( NULL );
418 p_sys->last_date = date;
420 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 )
422 p_sys->i_cur_char = 0;
424 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
426 if( p_sys->i_title == scroll_title )
427 p_sys->i_cur_item = -1;
429 p_sys->i_cur_item = 0;
430 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
434 p_spu = p_filter->pf_sub_buffer_new( p_filter );
437 vlc_mutex_unlock( &p_sys->lock );
441 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
443 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
444 if( !p_spu->p_region )
446 p_filter->pf_sub_buffer_del( p_filter, p_spu );
447 vlc_mutex_unlock( &p_sys->lock );
451 /* Generate the string that will be displayed. This string is supposed to
452 be p_sys->i_length characters long. */
453 i_item = p_sys->i_cur_item;
454 i_feed = p_sys->i_cur_feed;
455 p_feed = &p_sys->p_feeds[i_feed];
457 if( ( p_feed->p_pic && p_sys->i_title == default_title )
458 || p_sys->i_title == hide_title )
460 /* Don't display the feed's title if we have an image */
461 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
462 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
463 +p_sys->i_cur_char );
465 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
466 || p_sys->i_title == prepend_title )
468 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
469 p_sys->p_feeds[i_feed].psz_title,
470 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
471 +p_sys->i_cur_char );
473 else /* scrolling title */
476 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
477 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
478 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
480 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
481 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
482 +p_sys->i_cur_char );
485 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
488 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
489 snprintf( strchr( p_sys->psz_marquee, 0 ),
490 p_sys->i_length - strlen( p_sys->psz_marquee ),
492 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
495 /* Calls to snprintf might split multibyte UTF8 chars ...
496 * which freetype doesn't like. */
498 char *a = strdup( p_sys->psz_marquee );
500 char *b = p_sys->psz_marquee;
501 EnsureUTF8( p_sys->psz_marquee );
502 /* we want to use ' ' instead of '?' for erroneous chars */
505 if( *b != *a ) *b = ' ';
511 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
512 if( p_sys->p_style->i_font_size > 0 )
513 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
514 p_spu->i_start = date;
516 p_spu->b_ephemer = VLC_TRUE;
518 /* where to locate the string: */
519 if( p_sys->i_pos < 0 )
520 { /* set to an absolute xy */
521 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
522 p_spu->b_absolute = VLC_TRUE;
525 { /* set to one of the 9 relative locations */
526 p_spu->p_region->i_align = p_sys->i_pos;
527 p_spu->b_absolute = VLC_FALSE;
530 p_spu->i_x = p_sys->i_xoff;
531 p_spu->i_y = p_sys->i_yoff;
534 p_spu->p_region->p_style = p_sys->p_style;
538 /* Display the feed's image */
539 picture_t *p_pic = p_feed->p_pic;
540 video_format_t fmt_out;
542 memset( &fmt_out, 0, sizeof(video_format_t) );
544 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
545 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
546 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
548 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
550 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
552 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
555 msg_Err( p_filter, "cannot allocate SPU region" );
559 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
560 p_spu->p_region->p_next = p_region;
563 /* Offset text to display right next to the image */
564 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
567 vlc_mutex_unlock( &p_sys->lock );
571 /****************************************************************************
572 * RSS related functions
573 ****************************************************************************
574 * You should always lock the p_filter mutex before using any of these
576 ***************************************************************************/
578 #undef LoadImage /* do not conflict with Win32 API */
580 /****************************************************************************
581 * download and resize image located at psz_url
582 ***************************************************************************/
583 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
585 filter_sys_t *p_sys = p_filter->p_sys;
586 video_format_t fmt_in;
587 video_format_t fmt_out;
589 picture_t *p_pic = NULL;
590 image_handler_t *p_handler = image_HandlerCreate( p_filter );
592 memset( &fmt_in, 0, sizeof(video_format_t) );
593 memset( &fmt_out, 0, sizeof(video_format_t) );
595 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
596 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
600 msg_Warn( p_filter, "Unable to read image %s", psz_url );
602 else if( p_sys->p_style->i_font_size > 0 )
605 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
606 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
607 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
608 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
609 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
610 fmt_out.i_height = p_sys->p_style->i_font_size;
612 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
613 p_orig->pf_release( p_orig );
616 msg_Warn( p_filter, "Error while converting %s", psz_url );
624 image_HandlerDelete( p_handler );
629 /****************************************************************************
630 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
632 ***************************************************************************/
633 static char *removeWhiteChars( char *psz_src )
635 char *psz_src2 = strdup( psz_src );
636 char *psz_clean = strdup( psz_src2 );
639 while( ( *psz_clean == ' ' || *psz_clean == '\t'
640 || *psz_clean == '\n' || *psz_clean == '\r' )
641 && *psz_clean != '\0' )
645 i = strlen( psz_clean );
647 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
648 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
649 psz_clean[i+1] = '\0';
650 psz_clean2 = strdup( psz_clean );
655 /****************************************************************************
656 * FetchRSS (or Atom) feeds
657 ***************************************************************************/
658 static int FetchRSS( filter_t *p_filter)
660 filter_sys_t *p_sys = p_filter->p_sys;
662 stream_t *p_stream = NULL;
664 xml_reader_t *p_xml_reader = NULL;
666 char *psz_eltname = NULL;
667 char *psz_eltvalue = NULL;
668 char *psz_feed = NULL;
669 char *psz_buffer = NULL;
670 char *psz_buffer_2 = NULL;
674 vlc_bool_t b_is_item;
675 vlc_bool_t b_is_image;
681 while( p_sys->psz_urls[i_int] != 0 )
682 if( p_sys->psz_urls[i_int++] == '|' )
684 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
685 * sizeof( struct rss_feed_t ) );
687 p_xml = xml_Create( p_filter );
690 msg_Err( p_filter, "Failed to open XML parser" );
694 psz_buffer = strdup( p_sys->psz_urls );
695 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
696 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
698 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
700 if( psz_buffer == NULL ) break;
701 if( psz_buffer[0] == 0 ) psz_buffer++;
702 psz_feed = psz_buffer;
703 psz_buffer = strchr( psz_buffer, '|' );
704 if( psz_buffer != NULL ) psz_buffer[0] = 0;
706 p_feed->psz_title = NULL;
707 p_feed->psz_description = NULL;
708 p_feed->psz_link = NULL;
709 p_feed->psz_image = NULL;
710 p_feed->p_pic = NULL;
712 p_feed->p_items = NULL;
714 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
716 p_stream = stream_UrlNew( p_filter, psz_feed );
719 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
723 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
726 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
731 b_is_item = VLC_FALSE;
732 b_is_image = VLC_FALSE;
734 while( xml_ReaderRead( p_xml_reader ) == 1 )
736 switch( xml_ReaderNodeType( p_xml_reader ) )
742 case XML_READER_STARTELEM:
748 psz_eltname = xml_ReaderName( p_xml_reader );
754 msg_Dbg( p_filter, "element name: %s", psz_eltname );
756 if( !strcmp( psz_eltname, "item" ) /* rss */
757 || !strcmp( psz_eltname, "entry" ) ) /* atom */
759 b_is_item = VLC_TRUE;
761 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
762 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
763 p_feed->p_items[p_feed->i_items-1].psz_description
765 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
767 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
769 b_is_image = VLC_TRUE;
771 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
773 char *psz_href = NULL;
774 char *psz_rel = NULL;
775 while( xml_ReaderNextAttr( p_xml_reader )
778 char *psz_name = xml_ReaderName( p_xml_reader );
779 char *psz_value = xml_ReaderValue( p_xml_reader );
780 if( !strcmp( psz_name, "rel" ) )
784 else if( !strcmp( psz_name, "href" ) )
786 psz_href = psz_value;
794 if( psz_rel && psz_href )
796 if( !strcmp( psz_rel, "alternate" )
797 && b_is_item == VLC_FALSE
798 && b_is_image == VLC_FALSE
799 && !p_feed->psz_link )
801 p_feed->psz_link = psz_href;
803 /* this isn't in the rfc but i found some ... */
804 else if( ( !strcmp( psz_rel, "logo" )
805 || !strcmp( psz_rel, "icon" ) )
806 && b_is_item == VLC_FALSE
807 && b_is_image == VLC_FALSE
808 && !p_feed->psz_image )
810 p_feed->psz_image = psz_href;
819 if( psz_href ) free( psz_href );
821 if( psz_rel ) free( psz_rel );
825 case XML_READER_ENDELEM:
831 psz_eltname = xml_ReaderName( p_xml_reader );
837 msg_Dbg( p_filter, "element end : %s", psz_eltname );
839 if( !strcmp( psz_eltname, "item" ) /* rss */
840 || !strcmp( psz_eltname, "entry" ) ) /* atom */
842 b_is_item = VLC_FALSE;
845 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
847 b_is_image = VLC_FALSE;
853 case XML_READER_TEXT:
854 if( !psz_eltname ) break;
855 psz_eltvalue = xml_ReaderValue( p_xml_reader );
863 psz_clean = removeWhiteChars( psz_eltvalue );
864 free( psz_eltvalue ); psz_eltvalue = psz_clean;
867 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
869 if( b_is_item == VLC_TRUE )
871 struct rss_item_t *p_item;
872 p_item = p_feed->p_items+i_item;
873 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
874 && !p_item->psz_title )
876 p_item->psz_title = psz_eltvalue;
878 else if( !strcmp( psz_eltname, "link" ) /* rss */
879 && !p_item->psz_link )
881 p_item->psz_link = psz_eltvalue;
883 else if((!strcmp( psz_eltname, "description" ) /* rss */
884 || !strcmp( psz_eltname, "summary" ) ) /* atom */
885 && !p_item->psz_description )
887 p_item->psz_description = psz_eltvalue;
891 free( psz_eltvalue );
895 else if( b_is_image == VLC_TRUE )
897 if( !strcmp( psz_eltname, "url" ) /* rss */
898 && !p_feed->psz_image )
900 p_feed->psz_image = psz_eltvalue;
904 free( psz_eltvalue );
910 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
911 && !p_feed->psz_title )
913 p_feed->psz_title = psz_eltvalue;
915 else if( !strcmp( psz_eltname, "link" ) /* rss */
916 && !p_feed->psz_link )
918 p_feed->psz_link = psz_eltvalue;
920 else if((!strcmp( psz_eltname, "description" ) /* rss */
921 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
922 && !p_feed->psz_description )
924 p_feed->psz_description = psz_eltvalue;
926 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
927 || !strcmp( psz_eltname, "icon" ) ) /* atom */
928 && !p_feed->psz_image )
930 p_feed->psz_image = psz_eltvalue;
934 free( psz_eltvalue );
942 if( p_sys->b_images == VLC_TRUE
943 && p_feed->psz_image && !p_feed->p_pic )
945 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
948 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
949 if( p_stream ) stream_Delete( p_stream );
950 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
952 free( psz_buffer_2 );
953 if( p_xml ) xml_Delete( p_xml );
958 /****************************************************************************
960 ***************************************************************************/
961 static void FreeRSS( filter_t *p_filter)
963 filter_sys_t *p_sys = p_filter->p_sys;
965 struct rss_item_t *p_item;
966 struct rss_feed_t *p_feed;
971 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
973 p_feed = p_sys->p_feeds+i_feed;
974 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
976 p_item = p_feed->p_items+i_item;
977 free( p_item->psz_title );
978 free( p_item->psz_link );
979 free( p_item->psz_description );
981 free( p_feed->p_items );
982 free( p_feed->psz_title);
983 free( p_feed->psz_link );
984 free( p_feed->psz_description );
985 free( p_feed->psz_image );
986 if( p_feed->p_pic != NULL )
987 p_feed->p_pic->pf_release( p_feed->p_pic );
989 free( p_sys->p_feeds );