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"
51 /*****************************************************************************
53 *****************************************************************************/
54 static int CreateFilter ( vlc_object_t * );
55 static void DestroyFilter( vlc_object_t * );
56 static subpicture_t *Filter( filter_t *, mtime_t );
58 static int FetchRSS( filter_t * );
59 static void FreeRSS( filter_t * );
61 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
62 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
63 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
64 0x00000080, 0x000000FF, 0x0000FFFF};
65 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
66 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
67 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
68 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
71 /*****************************************************************************
72 * filter_sys_t: rss filter descriptor
73 *****************************************************************************/
78 char *psz_description;
85 char *psz_description;
91 struct rss_item_t *p_items;
99 int i_xoff, i_yoff; /* offsets for the display string in the video window */
100 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
104 char *psz_marquee; /* marquee string */
106 text_style_t *p_style; /* font control */
112 struct rss_feed_t *p_feeds;
115 time_t t_last_update;
124 #define MSG_TEXT N_("Feed URLs")
125 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) seperated URLs.")
126 #define SPEED_TEXT N_("Speed of feeds")
127 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
128 #define LENGTH_TEXT N_("Max length")
129 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
131 #define TTL_TEXT N_("Refresh time")
132 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
133 "of the feeds. 0 means that the feeds are never updated." )
134 #define IMAGE_TEXT N_("Feed images")
135 #define IMAGE_LONGTEXT N_("Display feed images if available.")
137 #define POSX_TEXT N_("X offset")
138 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
139 #define POSY_TEXT N_("Y offset")
140 #define POSY_LONGTEXT N_("Y offset, down from the top." )
141 #define OPACITY_TEXT N_("Opacity")
142 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
143 "overlay text. 0 = transparent, 255 = totally opaque." )
145 #define SIZE_TEXT N_("Font size, pixels")
146 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
149 #define COLOR_TEXT N_("Color")
150 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
151 "the video. This must be an hexadecimal (like HTML colors). The first two "\
152 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
153 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
155 #define POS_TEXT N_("Text position")
156 #define POS_LONGTEXT N_( \
157 "You can enforce the text position on the video " \
158 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
159 "also use combinations of these values, eg 6 = top-right).")
161 #define TITLE_TEXT N_("Title display mode")
162 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
164 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
165 static const char *ppsz_pos_descriptions[] =
166 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
167 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
175 static int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
176 static const char *ppsz_title_modes[] =
177 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
179 #define CFG_PREFIX "rss-"
181 /*****************************************************************************
183 *****************************************************************************/
185 set_capability( "sub filter", 1 );
186 set_shortname( "RSS / Atom" );
187 set_callbacks( CreateFilter, DestroyFilter );
188 set_category( CAT_VIDEO );
189 set_subcategory( SUBCAT_VIDEO_SUBPIC );
190 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, VLC_FALSE );
192 set_section( N_("Position"), NULL );
193 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
194 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
195 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 );
202 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
204 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
205 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
207 set_section( N_("Misc"), NULL );
208 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
210 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
212 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_FALSE );
213 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, VLC_FALSE );
214 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, VLC_FALSE );
215 change_integer_list( pi_title_modes, ppsz_title_modes, 0 );
217 set_description( _("RSS and Atom feed display") );
218 add_shortcut( "rss" );
219 add_shortcut( "atom" );
222 static const char *ppsz_filter_options[] = {
223 "urls", "x", "y", "position", "color", "size", "speed", "length",
224 "ttl", "images", "title", NULL
227 /*****************************************************************************
228 * CreateFilter: allocates RSS video filter
229 *****************************************************************************/
230 static int CreateFilter( vlc_object_t *p_this )
232 filter_t *p_filter = (filter_t *)p_this;
236 /* Allocate structure */
237 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
240 msg_Err( p_filter, "out of memory" );
244 vlc_mutex_init( p_filter, &p_sys->lock );
245 vlc_mutex_lock( &p_sys->lock );
247 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
250 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
251 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
252 p_sys->i_cur_feed = 0;
253 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
254 p_sys->i_cur_char = 0;
256 p_sys->p_feeds = NULL;
257 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
258 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
259 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
260 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
262 p_sys->psz_marquee = (char *)malloc( p_sys->i_length + 1 );
263 if( p_sys->psz_marquee == NULL )
265 msg_Err( p_filter, "out of memory" );
266 vlc_mutex_unlock( &p_sys->lock );
267 vlc_mutex_destroy( &p_sys->lock );
271 p_sys->psz_marquee[p_sys->i_length] = '\0';
273 p_sys->p_style = malloc( sizeof( text_style_t ));
274 if( p_sys->p_style == NULL )
276 msg_Err( p_filter, "out of memory" );
277 free( p_sys->psz_marquee );
278 vlc_mutex_unlock( &p_sys->lock );
279 vlc_mutex_destroy( &p_sys->lock );
283 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
285 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
286 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
287 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
288 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
289 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
290 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
292 if( p_sys->b_images == VLC_TRUE && p_sys->p_style->i_font_size == -1 )
294 msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
297 if( FetchRSS( p_filter ) )
299 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
300 free( p_sys->p_style );
301 free( p_sys->psz_marquee );
302 vlc_mutex_unlock( &p_sys->lock );
303 vlc_mutex_destroy( &p_sys->lock );
307 p_sys->t_last_update = time( NULL );
309 if( p_sys->i_feeds == 0 )
311 free( p_sys->p_style );
312 free( p_sys->psz_marquee );
313 vlc_mutex_unlock( &p_sys->lock );
314 vlc_mutex_destroy( &p_sys->lock );
318 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
320 if( p_sys->p_feeds[i_feed].i_items == 0 )
322 free( p_sys->p_style );
323 free( p_sys->psz_marquee );
325 vlc_mutex_unlock( &p_sys->lock );
326 vlc_mutex_destroy( &p_sys->lock );
332 p_filter->pf_sub_filter = Filter;
333 p_sys->last_date = (mtime_t)0;
335 vlc_mutex_unlock( &p_sys->lock );
339 /*****************************************************************************
340 * DestroyFilter: destroy RSS video filter
341 *****************************************************************************/
342 static void DestroyFilter( vlc_object_t *p_this )
344 filter_t *p_filter = (filter_t *)p_this;
345 filter_sys_t *p_sys = p_filter->p_sys;
347 vlc_mutex_lock( &p_sys->lock );
349 if( p_sys->p_style ) free( p_sys->p_style );
350 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
351 free( p_sys->psz_urls );
353 vlc_mutex_unlock( &p_sys->lock );
354 vlc_mutex_destroy( &p_sys->lock );
357 /* Delete the RSS variables */
358 var_Destroy( p_filter, CFG_PREFIX "urls" );
359 var_Destroy( p_filter, CFG_PREFIX "speed" );
360 var_Destroy( p_filter, CFG_PREFIX "length" );
361 var_Destroy( p_filter, CFG_PREFIX "ttl" );
362 var_Destroy( p_filter, CFG_PREFIX "images" );
363 var_Destroy( p_filter, CFG_PREFIX "x" );
364 var_Destroy( p_filter, CFG_PREFIX "y" );
365 var_Destroy( p_filter, CFG_PREFIX "position" );
366 var_Destroy( p_filter, CFG_PREFIX "color");
367 var_Destroy( p_filter, CFG_PREFIX "opacity");
368 var_Destroy( p_filter, CFG_PREFIX "size");
369 var_Destroy( p_filter, CFG_PREFIX "title" );
372 /****************************************************************************
373 * Filter: the whole thing
374 ****************************************************************************
375 * This function outputs subpictures at regular time intervals.
376 ****************************************************************************/
377 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
379 filter_sys_t *p_sys = p_filter->p_sys;
382 subpicture_region_t *p_region;
386 struct rss_feed_t *p_feed;
388 memset( &fmt, 0, sizeof(video_format_t) );
390 vlc_mutex_lock( &p_sys->lock );
393 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
394 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
395 * p_sys->i_speed > date )
397 vlc_mutex_unlock( &p_sys->lock );
401 /* Do we need to update the feeds ? */
403 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
405 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
406 if( FetchRSS( p_filter ) )
408 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
409 vlc_mutex_unlock( &p_sys->lock );
410 return NULL; /* FIXME : we most likely messed up all the data,
411 * so we might need to do something about it */
413 p_sys->t_last_update = time( NULL );
416 p_sys->last_date = date;
418 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 )
420 p_sys->i_cur_char = 0;
422 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
424 if( p_sys->i_title == scroll_title )
425 p_sys->i_cur_item = -1;
427 p_sys->i_cur_item = 0;
428 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
432 p_spu = p_filter->pf_sub_buffer_new( p_filter );
435 vlc_mutex_unlock( &p_sys->lock );
439 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
441 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
442 if( !p_spu->p_region )
444 p_filter->pf_sub_buffer_del( p_filter, p_spu );
445 vlc_mutex_unlock( &p_sys->lock );
449 /* Generate the string that will be displayed. This string is supposed to
450 be p_sys->i_length characters long. */
451 i_item = p_sys->i_cur_item;
452 i_feed = p_sys->i_cur_feed;
453 p_feed = &p_sys->p_feeds[i_feed];
455 if( ( p_feed->p_pic && p_sys->i_title == default_title )
456 || p_sys->i_title == hide_title )
458 /* Don't display the feed's title if we have an image */
459 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
460 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
461 +p_sys->i_cur_char );
463 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
464 || p_sys->i_title == prepend_title )
466 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
467 p_sys->p_feeds[i_feed].psz_title,
468 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
469 +p_sys->i_cur_char );
471 else /* scrolling title */
474 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
475 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
476 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
478 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
479 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
480 +p_sys->i_cur_char );
483 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
486 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
487 snprintf( strchr( p_sys->psz_marquee, 0 ),
488 p_sys->i_length - strlen( p_sys->psz_marquee ),
490 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
493 /* Calls to snprintf might split multibyte UTF8 chars ...
494 * which freetype doesn't like. */
496 char *a = strdup( p_sys->psz_marquee );
498 char *b = p_sys->psz_marquee;
499 EnsureUTF8( p_sys->psz_marquee );
500 /* we want to use ' ' instead of '?' for erroneous chars */
503 if( *b != *a ) *b = ' ';
509 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
510 if( p_sys->p_style->i_font_size > 0 )
511 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
512 p_spu->i_start = date;
514 p_spu->b_ephemer = VLC_TRUE;
516 /* where to locate the string: */
517 if( p_sys->i_pos < 0 )
518 { /* set to an absolute xy */
519 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
520 p_spu->b_absolute = VLC_TRUE;
523 { /* set to one of the 9 relative locations */
524 p_spu->p_region->i_align = p_sys->i_pos;
525 p_spu->b_absolute = VLC_FALSE;
528 p_spu->i_x = p_sys->i_xoff;
529 p_spu->i_y = p_sys->i_yoff;
532 p_spu->p_region->p_style = p_sys->p_style;
536 /* Display the feed's image */
537 picture_t *p_pic = p_feed->p_pic;
538 video_format_t fmt_out;
540 memset( &fmt_out, 0, sizeof(video_format_t) );
542 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
543 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
544 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
546 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
548 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
550 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
553 msg_Err( p_filter, "cannot allocate SPU region" );
557 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
558 p_spu->p_region->p_next = p_region;
561 /* Offset text to display right next to the image */
562 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
565 vlc_mutex_unlock( &p_sys->lock );
569 /****************************************************************************
570 * RSS related functions
571 ****************************************************************************
572 * You should always lock the p_filter mutex before using any of these
574 ***************************************************************************/
576 #undef LoadImage /* do not conflict with Win32 API */
578 /****************************************************************************
579 * download and resize image located at psz_url
580 ***************************************************************************/
581 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
583 filter_sys_t *p_sys = p_filter->p_sys;
584 video_format_t fmt_in;
585 video_format_t fmt_out;
587 picture_t *p_pic = NULL;
588 image_handler_t *p_handler = image_HandlerCreate( p_filter );
590 memset( &fmt_in, 0, sizeof(video_format_t) );
591 memset( &fmt_out, 0, sizeof(video_format_t) );
593 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
594 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
598 msg_Warn( p_filter, "Unable to read image %s", psz_url );
600 else if( p_sys->p_style->i_font_size > 0 )
603 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
604 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
605 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
606 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
607 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
608 fmt_out.i_height = p_sys->p_style->i_font_size;
610 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
611 p_orig->pf_release( p_orig );
614 msg_Warn( p_filter, "Error while converting %s", psz_url );
622 image_HandlerDelete( p_handler );
627 /****************************************************************************
628 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
630 ***************************************************************************/
631 static char *removeWhiteChars( char *psz_src )
633 char *psz_src2 = strdup( psz_src );
634 char *psz_clean = strdup( psz_src2 );
637 while( ( *psz_clean == ' ' || *psz_clean == '\t'
638 || *psz_clean == '\n' || *psz_clean == '\r' )
639 && *psz_clean != '\0' )
643 i = strlen( psz_clean );
645 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
646 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
647 psz_clean[i+1] = '\0';
648 psz_clean2 = strdup( psz_clean );
653 /****************************************************************************
654 * FetchRSS (or Atom) feeds
655 ***************************************************************************/
656 static int FetchRSS( filter_t *p_filter)
658 filter_sys_t *p_sys = p_filter->p_sys;
660 stream_t *p_stream = NULL;
662 xml_reader_t *p_xml_reader = NULL;
664 char *psz_eltname = NULL;
665 char *psz_eltvalue = NULL;
666 char *psz_feed = NULL;
667 char *psz_buffer = NULL;
668 char *psz_buffer_2 = NULL;
672 vlc_bool_t b_is_item;
673 vlc_bool_t b_is_image;
679 while( p_sys->psz_urls[i_int] != 0 )
680 if( p_sys->psz_urls[i_int++] == '|' )
682 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
683 * sizeof( struct rss_feed_t ) );
685 p_xml = xml_Create( p_filter );
688 msg_Err( p_filter, "Failed to open XML parser" );
692 psz_buffer = strdup( p_sys->psz_urls );
693 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
694 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
696 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
698 if( psz_buffer == NULL ) break;
699 if( psz_buffer[0] == 0 ) psz_buffer++;
700 psz_feed = psz_buffer;
701 psz_buffer = strchr( psz_buffer, '|' );
702 if( psz_buffer != NULL ) psz_buffer[0] = 0;
704 p_feed->psz_title = NULL;
705 p_feed->psz_description = NULL;
706 p_feed->psz_link = NULL;
707 p_feed->psz_image = NULL;
708 p_feed->p_pic = NULL;
710 p_feed->p_items = NULL;
712 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
714 p_stream = stream_UrlNew( p_filter, psz_feed );
717 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
721 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
724 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
729 b_is_item = VLC_FALSE;
730 b_is_image = VLC_FALSE;
732 while( xml_ReaderRead( p_xml_reader ) == 1 )
734 switch( xml_ReaderNodeType( p_xml_reader ) )
740 case XML_READER_STARTELEM:
746 psz_eltname = xml_ReaderName( p_xml_reader );
752 msg_Dbg( p_filter, "element name: %s", psz_eltname );
754 if( !strcmp( psz_eltname, "item" ) /* rss */
755 || !strcmp( psz_eltname, "entry" ) ) /* atom */
757 b_is_item = VLC_TRUE;
759 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
760 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
761 p_feed->p_items[p_feed->i_items-1].psz_description
763 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
765 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
767 b_is_image = VLC_TRUE;
769 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
771 char *psz_href = NULL;
772 char *psz_rel = NULL;
773 while( xml_ReaderNextAttr( p_xml_reader )
776 char *psz_name = xml_ReaderName( p_xml_reader );
777 char *psz_value = xml_ReaderValue( p_xml_reader );
778 if( !strcmp( psz_name, "rel" ) )
782 else if( !strcmp( psz_name, "href" ) )
784 psz_href = psz_value;
792 if( psz_rel && psz_href )
794 if( !strcmp( psz_rel, "alternate" )
795 && b_is_item == VLC_FALSE
796 && b_is_image == VLC_FALSE
797 && !p_feed->psz_link )
799 p_feed->psz_link = psz_href;
801 /* this isn't in the rfc but i found some ... */
802 else if( ( !strcmp( psz_rel, "logo" )
803 || !strcmp( psz_rel, "icon" ) )
804 && b_is_item == VLC_FALSE
805 && b_is_image == VLC_FALSE
806 && !p_feed->psz_image )
808 p_feed->psz_image = psz_href;
817 if( psz_href ) free( psz_href );
819 if( psz_rel ) free( psz_rel );
823 case XML_READER_ENDELEM:
829 psz_eltname = xml_ReaderName( p_xml_reader );
835 msg_Dbg( p_filter, "element end : %s", psz_eltname );
837 if( !strcmp( psz_eltname, "item" ) /* rss */
838 || !strcmp( psz_eltname, "entry" ) ) /* atom */
840 b_is_item = VLC_FALSE;
843 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
845 b_is_image = VLC_FALSE;
851 case XML_READER_TEXT:
852 if( !psz_eltname ) break;
853 psz_eltvalue = xml_ReaderValue( p_xml_reader );
861 psz_clean = removeWhiteChars( psz_eltvalue );
862 free( psz_eltvalue ); psz_eltvalue = psz_clean;
865 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
867 if( b_is_item == VLC_TRUE )
869 struct rss_item_t *p_item;
870 p_item = p_feed->p_items+i_item;
871 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
872 && !p_item->psz_title )
874 p_item->psz_title = psz_eltvalue;
876 else if( !strcmp( psz_eltname, "link" ) /* rss */
877 && !p_item->psz_link )
879 p_item->psz_link = psz_eltvalue;
881 else if((!strcmp( psz_eltname, "description" ) /* rss */
882 || !strcmp( psz_eltname, "summary" ) ) /* atom */
883 && !p_item->psz_description )
885 p_item->psz_description = psz_eltvalue;
889 free( psz_eltvalue );
893 else if( b_is_image == VLC_TRUE )
895 if( !strcmp( psz_eltname, "url" ) /* rss */
896 && !p_feed->psz_image )
898 p_feed->psz_image = psz_eltvalue;
902 free( psz_eltvalue );
908 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
909 && !p_feed->psz_title )
911 p_feed->psz_title = psz_eltvalue;
913 else if( !strcmp( psz_eltname, "link" ) /* rss */
914 && !p_feed->psz_link )
916 p_feed->psz_link = psz_eltvalue;
918 else if((!strcmp( psz_eltname, "description" ) /* rss */
919 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
920 && !p_feed->psz_description )
922 p_feed->psz_description = psz_eltvalue;
924 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
925 || !strcmp( psz_eltname, "icon" ) ) /* atom */
926 && !p_feed->psz_image )
928 p_feed->psz_image = psz_eltvalue;
932 free( psz_eltvalue );
940 if( p_sys->b_images == VLC_TRUE
941 && p_feed->psz_image && !p_feed->p_pic )
943 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
946 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
947 if( p_stream ) stream_Delete( p_stream );
948 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
950 free( psz_buffer_2 );
951 if( p_xml ) xml_Delete( p_xml );
956 /****************************************************************************
958 ***************************************************************************/
959 static void FreeRSS( filter_t *p_filter)
961 filter_sys_t *p_sys = p_filter->p_sys;
963 struct rss_item_t *p_item;
964 struct rss_feed_t *p_feed;
969 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
971 p_feed = p_sys->p_feeds+i_feed;
972 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
974 p_item = p_feed->p_items+i_item;
975 free( p_item->psz_title );
976 free( p_item->psz_link );
977 free( p_item->psz_description );
979 free( p_feed->p_items );
980 free( p_feed->psz_title);
981 free( p_feed->psz_link );
982 free( p_feed->psz_description );
983 free( p_feed->psz_image );
984 if( p_feed->p_pic != NULL )
985 p_feed->p_pic->pf_release( p_feed->p_pic );
987 free( p_sys->p_feeds );