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>
8 * RĂ©mi Duraffort <ivoire -at- videolan -dot- org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
26 * Atom : http://www.ietf.org/rfc/rfc4287.txt
27 * RSS : http://www.rssboard.org/rss-specification
28 *****************************************************************************/
30 /*****************************************************************************
32 *****************************************************************************/
40 #include <vlc_common.h>
41 #include <vlc_plugin.h>
43 #include <vlc_filter.h>
44 #include <vlc_block.h>
47 #include <vlc_stream.h>
49 #include <vlc_charset.h>
51 #include <vlc_image.h>
53 #include <vlc_memory.h>
57 /*****************************************************************************
59 *****************************************************************************/
60 static int CreateFilter ( vlc_object_t * );
61 static void DestroyFilter( vlc_object_t * );
62 static subpicture_t *Filter( filter_t *, mtime_t );
64 static struct rss_feed_t *FetchRSS( filter_t * );
65 static void FreeRSS( struct rss_feed_t *, int );
66 static int ParseUrls( filter_t *, char * );
68 static void Fetch( void * );
70 static const int pi_color_values[] = {
71 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
72 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
73 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
74 0x00000080, 0x000000FF, 0x0000FFFF};
75 static const char *const ppsz_color_descriptions[] = {
76 N_("Default"), N_("Black"),
77 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
78 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
79 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
82 /*****************************************************************************
83 * filter_sys_t: rss filter descriptor
84 *****************************************************************************/
86 typedef struct rss_item_t
89 char *psz_description;
93 typedef struct rss_feed_t
97 char *psz_description;
109 vlc_timer_t timer; /* Timer to refresh the rss feeds */
112 int i_xoff, i_yoff; /* offsets for the display string in the video window */
113 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
117 char *psz_marquee; /* marquee string */
119 text_style_t *p_style; /* font control */
134 #define MSG_TEXT N_("Feed URLs")
135 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) separated URLs.")
136 #define SPEED_TEXT N_("Speed of feeds")
137 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
138 #define LENGTH_TEXT N_("Max length")
139 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
141 #define TTL_TEXT N_("Refresh time")
142 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
143 "of the feeds. 0 means that the feeds are never updated." )
144 #define IMAGE_TEXT N_("Feed images")
145 #define IMAGE_LONGTEXT N_("Display feed images if available.")
147 #define POSX_TEXT N_("X offset")
148 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
149 #define POSY_TEXT N_("Y offset")
150 #define POSY_LONGTEXT N_("Y offset, down from the top." )
151 #define OPACITY_TEXT N_("Opacity")
152 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
153 "overlay text. 0 = transparent, 255 = totally opaque." )
155 #define SIZE_TEXT N_("Font size, pixels")
156 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
159 #define COLOR_TEXT N_("Color")
160 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
161 "the video. This must be an hexadecimal (like HTML colors). The first two "\
162 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
163 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
165 #define POS_TEXT N_("Text position")
166 #define POS_LONGTEXT N_( \
167 "You can enforce the text position on the video " \
168 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
169 "also use combinations of these values, eg 6 = top-right).")
171 #define TITLE_TEXT N_("Title display mode")
172 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
174 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
175 static const char *const ppsz_pos_descriptions[] =
176 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
177 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
185 static const int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
186 static const char *const ppsz_title_modes[] =
187 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
189 #define CFG_PREFIX "rss-"
191 /*****************************************************************************
193 *****************************************************************************/
195 set_capability( "sub filter", 1 )
196 set_shortname( "RSS / Atom" )
197 set_callbacks( CreateFilter, DestroyFilter )
198 set_category( CAT_VIDEO )
199 set_subcategory( SUBCAT_VIDEO_SUBPIC )
200 add_string( CFG_PREFIX "urls", NULL, NULL, MSG_TEXT, MSG_LONGTEXT, false )
202 set_section( N_("Position"), NULL )
203 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, true )
204 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, true )
205 add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, false )
206 change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL )
208 set_section( N_("Font"), NULL )
209 /* 5 sets the default to top [1] left [4] */
210 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
211 OPACITY_TEXT, OPACITY_LONGTEXT, false )
212 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
214 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
215 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, false )
217 set_section( N_("Misc"), NULL )
218 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
220 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
222 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, false )
223 add_bool( CFG_PREFIX "images", true, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, false )
224 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, false )
225 change_integer_list( pi_title_modes, ppsz_title_modes, NULL )
227 set_description( N_("RSS and Atom feed display") )
228 add_shortcut( "rss" )
229 add_shortcut( "atom" )
232 static const char *const ppsz_filter_options[] = {
233 "urls", "x", "y", "position", "color", "size", "speed", "length",
234 "ttl", "images", "title", NULL
237 /*****************************************************************************
238 * CreateFilter: allocates RSS video filter
239 *****************************************************************************/
240 static int CreateFilter( vlc_object_t *p_this )
242 filter_t *p_filter = (filter_t *)p_this;
247 /* Allocate structure */
248 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
252 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
255 /* Get the urls to parse: must be non empty */
256 psz_urls = var_CreateGetNonEmptyString( p_filter, CFG_PREFIX "urls" );
259 msg_Err( p_filter, "The list of urls must not be empty" );
264 /* Fill the p_sys structure with the configuration */
265 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
266 p_sys->i_cur_feed = 0;
267 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
268 p_sys->i_cur_char = 0;
270 p_sys->p_feeds = NULL;
271 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
272 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
273 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
275 i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
277 p_sys->psz_marquee = malloc( p_sys->i_length + 1 );
278 if( p_sys->psz_marquee == NULL )
284 p_sys->psz_marquee[p_sys->i_length] = '\0';
286 p_sys->p_style = text_style_New();
287 if( p_sys->p_style == NULL )
290 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
291 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
292 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
293 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
294 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
295 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
297 if( p_sys->b_images && p_sys->p_style->i_font_size == -1 )
299 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus be displayed without being resized" );
303 if( ParseUrls( p_filter, psz_urls ) )
307 vlc_mutex_init( &p_sys->lock );
308 p_filter->pf_sub_filter = Filter;
309 p_sys->last_date = (mtime_t)0;
310 p_sys->b_fetched = false;
312 /* Create and arm the timer */
313 if( vlc_timer_create( &p_sys->timer, Fetch, p_filter ) )
315 vlc_mutex_destroy( &p_sys->lock );
318 vlc_timer_schedule( p_sys->timer, false, 1,
319 (mtime_t)(i_ttl)*1000000 );
326 text_style_Delete( p_sys->p_style );
327 free( p_sys->psz_marquee );
332 /*****************************************************************************
333 * DestroyFilter: destroy RSS video filter
334 *****************************************************************************/
335 static void DestroyFilter( vlc_object_t *p_this )
337 filter_t *p_filter = (filter_t *)p_this;
338 filter_sys_t *p_sys = p_filter->p_sys;
340 vlc_timer_destroy( p_sys->timer );
341 vlc_mutex_destroy( &p_sys->lock );
343 text_style_Delete( p_sys->p_style );
344 free( p_sys->psz_marquee );
345 FreeRSS( p_sys->p_feeds, p_sys->i_feeds );
349 /****************************************************************************
350 * Filter: the whole thing
351 ****************************************************************************
352 * This function outputs subpictures at regular time intervals.
353 ****************************************************************************/
354 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
356 filter_sys_t *p_sys = p_filter->p_sys;
359 subpicture_region_t *p_region;
364 memset( &fmt, 0, sizeof(video_format_t) );
366 vlc_mutex_lock( &p_sys->lock );
368 /* Check if the feeds have been fetched and that we have some feeds */
369 /* TODO: check that we have items for each feeds */
370 if( !p_sys->b_fetched && p_sys->i_feeds > 0 )
372 vlc_mutex_unlock( &p_sys->lock );
377 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
378 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
379 * p_sys->i_speed > date )
381 vlc_mutex_unlock( &p_sys->lock );
385 p_sys->last_date = date;
387 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 )
389 p_sys->i_cur_char = 0;
391 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
393 if( p_sys->i_title == scroll_title )
394 p_sys->i_cur_item = -1;
396 p_sys->i_cur_item = 0;
397 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
401 p_spu = filter_NewSubpicture( p_filter );
404 vlc_mutex_unlock( &p_sys->lock );
408 fmt.i_chroma = VLC_CODEC_TEXT;
410 p_spu->p_region = subpicture_region_New( &fmt );
411 if( !p_spu->p_region )
413 p_filter->pf_sub_buffer_del( p_filter, p_spu );
414 vlc_mutex_unlock( &p_sys->lock );
418 /* Generate the string that will be displayed. This string is supposed to
419 be p_sys->i_length characters long. */
420 i_item = p_sys->i_cur_item;
421 i_feed = p_sys->i_cur_feed;
422 p_feed = &p_sys->p_feeds[i_feed];
424 if( ( p_feed->p_pic && p_sys->i_title == default_title )
425 || p_sys->i_title == hide_title )
427 /* Don't display the feed's title if we have an image */
428 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
429 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
430 +p_sys->i_cur_char );
432 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
433 || p_sys->i_title == prepend_title )
435 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
436 p_sys->p_feeds[i_feed].psz_title,
437 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
438 +p_sys->i_cur_char );
440 else /* scrolling title */
443 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
444 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
445 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
447 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
448 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
449 +p_sys->i_cur_char );
452 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
455 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
456 snprintf( strchr( p_sys->psz_marquee, 0 ),
457 p_sys->i_length - strlen( p_sys->psz_marquee ),
459 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
462 /* Calls to snprintf might split multibyte UTF8 chars ...
463 * which freetype doesn't like. */
465 char *a = strdup( p_sys->psz_marquee );
467 char *b = p_sys->psz_marquee;
468 EnsureUTF8( p_sys->psz_marquee );
469 /* we want to use ' ' instead of '?' for erroneous chars */
472 if( *b != *a ) *b = ' ';
478 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
479 if( p_sys->p_style->i_font_size > 0 )
480 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
481 p_spu->i_start = date;
483 p_spu->b_ephemer = true;
485 /* where to locate the string: */
486 if( p_sys->i_pos < 0 )
487 { /* set to an absolute xy */
488 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
489 p_spu->b_absolute = true;
492 { /* set to one of the 9 relative locations */
493 p_spu->p_region->i_align = p_sys->i_pos;
494 p_spu->b_absolute = false;
497 p_spu->p_region->p_style = text_style_Duplicate( p_sys->p_style );
501 /* Display the feed's image */
502 picture_t *p_pic = p_feed->p_pic;
503 video_format_t fmt_out;
505 memset( &fmt_out, 0, sizeof(video_format_t) );
507 fmt_out.i_chroma = VLC_CODEC_YUVA;
508 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
509 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
511 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
513 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
515 p_region = subpicture_region_New( &fmt_out );
518 msg_Err( p_filter, "cannot allocate SPU region" );
522 p_region->i_x = p_sys->i_xoff;
523 p_region->i_y = p_sys->i_yoff;
524 /* FIXME the copy is probably not needed anymore */
525 picture_Copy( p_region->p_picture, p_pic );
526 p_spu->p_region->p_next = p_region;
529 /* Offset text to display right next to the image */
530 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
533 vlc_mutex_unlock( &p_sys->lock );
537 /****************************************************************************
538 * RSS related functions
539 ****************************************************************************
540 * You should always lock the p_filter mutex before using any of these
542 ***************************************************************************/
544 #undef LoadImage /* do not conflict with Win32 API */
546 /****************************************************************************
547 * download and resize image located at psz_url
548 ***************************************************************************/
549 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
551 filter_sys_t *p_sys = p_filter->p_sys;
552 video_format_t fmt_in;
553 video_format_t fmt_out;
555 picture_t *p_pic = NULL;
556 image_handler_t *p_handler = image_HandlerCreate( p_filter );
558 memset( &fmt_in, 0, sizeof(video_format_t) );
559 memset( &fmt_out, 0, sizeof(video_format_t) );
561 fmt_out.i_chroma = VLC_CODEC_YUVA;
562 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
566 msg_Warn( p_filter, "Unable to read image %s", psz_url );
568 else if( p_sys->p_style->i_font_size > 0 )
571 fmt_in.i_chroma = VLC_CODEC_YUVA;
572 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
573 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
574 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
575 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
576 fmt_out.i_height = p_sys->p_style->i_font_size;
578 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
579 picture_Release( p_orig );
582 msg_Warn( p_filter, "Error while converting %s", psz_url );
590 image_HandlerDelete( p_handler );
595 /****************************************************************************
596 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
598 ***************************************************************************/
599 static char *removeWhiteChars( const char *psz_src )
601 char *psz_src2,*psz_clean, *psz_clean2;
602 psz_src2 = psz_clean = strdup( psz_src );
605 while( ( *psz_clean == ' ' || *psz_clean == '\t'
606 || *psz_clean == '\n' || *psz_clean == '\r' )
607 && *psz_clean != '\0' )
611 i = strlen( psz_clean );
613 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
614 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
615 psz_clean[i+1] = '\0';
616 psz_clean2 = strdup( psz_clean );
622 /****************************************************************************
623 * Parse url list, psz_urls must be non empty (TODO: check it !)
624 ***************************************************************************/
625 static int ParseUrls( filter_t *p_filter, char *psz_urls )
627 filter_sys_t *p_sys = p_filter->p_sys;
628 char *psz_urls2 = psz_urls;
632 /* Count the number of feeds */
635 if( *psz_urls == '|' )
640 /* Allocate the structure */
641 p_sys->p_feeds = malloc( p_sys->i_feeds * sizeof( rss_feed_t ) );
642 if( !p_sys->p_feeds )
645 /* Loop on all urls and fill in the struct */
646 psz_urls = psz_urls2;
647 for( int i = 0; i < p_sys->i_feeds; i++ )
649 rss_feed_t* p_feed = p_sys->p_feeds + i;
652 if( i < p_sys->i_feeds - 1 )
654 psz_end = strchr( psz_urls, '|' );
661 p_feed->p_items = NULL;
662 p_feed->psz_title = NULL;
663 p_feed->psz_link = NULL;
664 p_feed->psz_description = NULL;
665 p_feed->psz_image = NULL;
666 p_feed->p_pic = NULL;
667 p_feed->psz_url = strdup( psz_urls );
669 psz_urls = psz_end + 1;
677 /****************************************************************************
679 ***************************************************************************/
680 static bool ParseFeed( filter_t *p_filter, xml_reader_t *p_xml_reader,
683 VLC_UNUSED(p_filter);
684 char *psz_eltname = NULL;
686 bool b_is_item = false;
687 bool b_is_image = false;
691 while( xml_ReaderRead( p_xml_reader ) == 1 )
693 switch( xml_ReaderNodeType( p_xml_reader ) )
699 case XML_READER_STARTELEM:
701 psz_eltname = xml_ReaderName( p_xml_reader );
706 msg_Dbg( p_filter, "element name: %s", psz_eltname );
709 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
713 p_feed->p_items = realloc_or_free( p_feed->p_items,
714 p_feed->i_items * sizeof( rss_item_t ) );
715 assert( p_feed->p_items );
716 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
717 p_feed->p_items[p_feed->i_items-1].psz_description = NULL;
718 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
721 else if( !strcmp( psz_eltname, "image" ) )
726 else if( !strcmp( psz_eltname, "link" ) )
728 char *psz_href = NULL;
729 char *psz_rel = NULL;
730 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
732 char *psz_name = xml_ReaderName( p_xml_reader );
733 char *psz_value = xml_ReaderValue( p_xml_reader );
734 if( !strcmp( psz_name, "rel" ) )
739 else if( !strcmp( psz_name, "href" ) )
742 psz_href = psz_value;
751 /* "rel" and "href" must be defined */
752 if( psz_rel && psz_href )
754 if( !strcmp( psz_rel, "alternate" ) && !b_is_item &&
755 !b_is_image && !p_feed->psz_link )
757 p_feed->psz_link = psz_href;
759 /* this isn't in the rfc but i found some ... */
760 else if( ( !strcmp( psz_rel, "logo" ) ||
761 !strcmp( psz_rel, "icon" ) )
762 && !b_is_item && !b_is_image
763 && !p_feed->psz_image )
765 p_feed->psz_image = psz_href;
780 case XML_READER_ENDELEM:
782 psz_eltname = xml_ReaderName( p_xml_reader );
787 msg_Dbg( p_filter, "element end : %s", psz_eltname );
790 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
796 else if( !strcmp( psz_eltname, "image" ) )
800 FREENULL( psz_eltname );
803 case XML_READER_TEXT:
807 char *psz_eltvalue = xml_ReaderValue( p_xml_reader );
811 char *psz_clean = removeWhiteChars( psz_eltvalue );
812 free( psz_eltvalue );
813 psz_eltvalue = psz_clean;
816 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
818 /* Is it an item ? */
821 rss_item_t *p_item = p_feed->p_items+i_item;
823 if( !strcmp( psz_eltname, "title" ) && !p_item->psz_title )
825 p_item->psz_title = psz_eltvalue;
827 else if( !strcmp( psz_eltname, "link" ) /* rss */
828 && !p_item->psz_link )
830 p_item->psz_link = psz_eltvalue;
833 else if( ( !strcmp( psz_eltname, "description" ) ||
834 !strcmp( psz_eltname, "summary" ) )
835 && !p_item->psz_description )
837 p_item->psz_description = psz_eltvalue;
841 free( psz_eltvalue );
844 /* Is it an image ? */
845 else if( b_is_image )
847 if( !strcmp( psz_eltname, "url" ) && !p_feed->psz_image )
848 p_feed->psz_image = psz_eltvalue;
850 free( psz_eltvalue );
855 if( !strcmp( psz_eltname, "title" ) && !p_feed->psz_title )
857 p_feed->psz_title = psz_eltvalue;
860 else if( !strcmp( psz_eltname, "link" ) && !p_feed->psz_link )
862 p_feed->psz_link = psz_eltvalue;
865 else if( ( !strcmp( psz_eltname, "description" ) ||
866 !strcmp( psz_eltname, "subtitle" ) )
867 && !p_feed->psz_description )
869 p_feed->psz_description = psz_eltvalue;
872 else if( ( !strcmp( psz_eltname, "logo" ) ||
873 !strcmp( psz_eltname, "icon" ) )
874 && !p_feed->psz_image )
876 p_feed->psz_image = psz_eltvalue;
880 free( psz_eltvalue );
897 /****************************************************************************
898 * FetchRSS (or Atom) feeds
899 ***************************************************************************/
900 static rss_feed_t* FetchRSS( filter_t *p_filter )
902 filter_sys_t *p_sys = p_filter->p_sys;
906 xml_reader_t *p_xml_reader;
909 /* These data are not modified after the creation of the module so we don't
910 need to hold the lock */
911 int i_feeds = p_sys->i_feeds;
912 bool b_images = p_sys->b_images;
914 /* Allocate a new structure */
915 rss_feed_t *p_feeds = malloc( i_feeds * sizeof( rss_feed_t ) );
919 p_xml = xml_Create( p_filter );
922 msg_Err( p_filter, "Failed to open XML parser" );
927 /* Fetch all feeds and parse them */
928 for( i_feed = 0; i_feed < i_feeds; i_feed++ )
930 rss_feed_t *p_feed = p_feeds + i_feed;
931 rss_feed_t *p_old_feed = p_sys->p_feeds + i_feed;
933 /* Initialize the structure */
934 p_feed->psz_title = NULL;
935 p_feed->psz_description = NULL;
936 p_feed->psz_link = NULL;
937 p_feed->psz_image = NULL;
938 p_feed->p_pic = NULL;
940 p_feed->p_items = NULL;
942 p_feed->psz_url = strdup( p_old_feed->psz_url );
945 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", p_feed->psz_url );
947 p_stream = stream_UrlNew( p_filter, p_feed->psz_url );
950 msg_Err( p_filter, "Failed to open %s for reading", p_feed->psz_url );
955 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
958 msg_Err( p_filter, "Failed to open %s for parsing", p_feed->psz_url );
963 if( !ParseFeed( p_filter, p_xml_reader, p_feed ) )
966 /* If we have a image: load it if requiere */
967 if( b_images && p_feed->psz_image && !p_feed->p_pic )
969 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
972 msg_Dbg( p_filter, "done with %s RSS/Atom feed", p_feed->psz_url );
973 xml_ReaderDelete( p_xml, p_xml_reader );
974 stream_Delete( p_stream );
981 FreeRSS( p_feeds, i_feed + 1 );
983 xml_ReaderDelete( p_xml, p_xml_reader );
985 stream_Delete( p_stream );
992 /****************************************************************************
994 ***************************************************************************/
995 static void FreeRSS( rss_feed_t *p_feeds, int i_feeds )
997 for( int i_feed = 0; i_feed < i_feeds; i_feed++ )
999 rss_feed_t *p_feed = p_feeds+i_feed;
1000 for( int i_item = 0; i_item < p_feed->i_items; i_item++ )
1002 rss_item_t *p_item = p_feed->p_items+i_item;
1003 free( p_item->psz_title );
1004 free( p_item->psz_link );
1005 free( p_item->psz_description );
1007 free( p_feed->p_items );
1008 free( p_feed->psz_title);
1009 free( p_feed->psz_link );
1010 free( p_feed->psz_description );
1011 free( p_feed->psz_image );
1012 if( p_feed->p_pic != NULL )
1013 picture_Release( p_feed->p_pic );
1014 free( p_feed->psz_url );
1019 static void Fetch( void *p_data )
1021 filter_t *p_filter = p_data;
1022 filter_sys_t *p_sys = p_filter->p_sys;
1024 msg_Dbg( p_filter, "Updating the rss feeds" );
1025 rss_feed_t *p_feeds = FetchRSS( p_filter );
1028 msg_Err( p_filter, "Unable to fetch the feeds" );
1032 rss_feed_t *p_old_feeds = p_sys->p_feeds;
1034 vlc_mutex_lock( &p_sys->lock );
1035 /* Update the feeds */
1036 p_sys->p_feeds = p_feeds;
1037 p_sys->b_fetched = true;
1038 /* Set all current info to the original values */
1039 p_sys->i_cur_feed = 0;
1040 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
1041 p_sys->i_cur_char = 0;
1042 vlc_mutex_unlock( &p_sys->lock );
1045 FreeRSS( p_old_feeds, p_sys->i_feeds );