1 /*****************************************************************************
2 * rss.c : rss/atom feed display video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2003-2006 VLC authors and VideoLAN
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 it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * 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 *****************************************************************************/
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
41 #include <vlc_filter.h>
42 #include <vlc_block.h>
44 #include <vlc_stream.h>
46 #include <vlc_charset.h>
48 #include <vlc_image.h>
52 /*****************************************************************************
54 *****************************************************************************/
55 static int CreateFilter ( vlc_object_t * );
56 static void DestroyFilter( vlc_object_t * );
57 static subpicture_t *Filter( filter_t *, mtime_t );
59 static struct rss_feed_t *FetchRSS( filter_t * );
60 static void FreeRSS( struct rss_feed_t *, int );
61 static int ParseUrls( filter_t *, char * );
63 static void Fetch( void * );
65 static const int pi_color_values[] = {
66 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
67 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
68 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
69 0x00000080, 0x000000FF, 0x0000FFFF};
70 static const char *const ppsz_color_descriptions[] = {
71 N_("Default"), N_("Black"),
72 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
73 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
74 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
77 /*****************************************************************************
78 * filter_sys_t: rss filter descriptor
79 *****************************************************************************/
81 typedef struct rss_item_t
84 char *psz_description;
88 typedef struct rss_feed_t
92 char *psz_description;
104 vlc_timer_t timer; /* Timer to refresh the rss feeds */
107 int i_xoff, i_yoff; /* offsets for the display string in the video window */
108 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
112 char *psz_marquee; /* marquee string */
114 text_style_t *p_style; /* font control */
129 #define MSG_TEXT N_("Feed URLs")
130 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) separated URLs.")
131 #define SPEED_TEXT N_("Speed of feeds")
132 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
133 #define LENGTH_TEXT N_("Max length")
134 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
136 #define TTL_TEXT N_("Refresh time")
137 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
138 "of the feeds. 0 means that the feeds are never updated." )
139 #define IMAGE_TEXT N_("Feed images")
140 #define IMAGE_LONGTEXT N_("Display feed images if available.")
142 #define POSX_TEXT N_("X offset")
143 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
144 #define POSY_TEXT N_("Y offset")
145 #define POSY_LONGTEXT N_("Y offset, down from the top." )
146 #define OPACITY_TEXT N_("Opacity")
147 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
148 "overlay text. 0 = transparent, 255 = totally opaque." )
150 #define SIZE_TEXT N_("Font size, pixels")
151 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
154 #define COLOR_TEXT N_("Color")
155 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
156 "the video. This must be an hexadecimal (like HTML colors). The first two "\
157 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
158 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
160 #define POS_TEXT N_("Text position")
161 #define POS_LONGTEXT N_( \
162 "You can enforce the text position on the video " \
163 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
164 "also use combinations of these values, eg 6 = top-right).")
166 #define TITLE_TEXT N_("Title display mode")
167 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
169 #define RSS_HELP N_("Display a RSS or ATOM Feed on your video")
171 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
172 static const char *const ppsz_pos_descriptions[] =
173 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
174 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
182 static const int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
183 static const char *const ppsz_title_modes[] =
184 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
186 #define CFG_PREFIX "rss-"
188 /*****************************************************************************
190 *****************************************************************************/
192 set_capability( "sub source", 1 )
193 set_shortname( N_("RSS / Atom") )
195 set_callbacks( CreateFilter, DestroyFilter )
196 set_category( CAT_VIDEO )
197 set_subcategory( SUBCAT_VIDEO_SUBPIC )
198 add_string( CFG_PREFIX "urls", NULL, MSG_TEXT, MSG_LONGTEXT, false )
200 set_section( N_("Position"), NULL )
201 add_integer( CFG_PREFIX "x", 0, POSX_TEXT, POSX_LONGTEXT, true )
202 add_integer( CFG_PREFIX "y", 0, POSY_TEXT, POSY_LONGTEXT, true )
203 add_integer( CFG_PREFIX "position", -1, POS_TEXT, POS_LONGTEXT, false )
204 change_integer_list( pi_pos_values, ppsz_pos_descriptions )
206 set_section( N_("Font"), NULL )
207 /* 5 sets the default to top [1] left [4] */
208 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255,
209 OPACITY_TEXT, OPACITY_LONGTEXT, false )
210 add_rgb( CFG_PREFIX "color", 0xFFFFFF, COLOR_TEXT, COLOR_LONGTEXT,
212 change_integer_list( pi_color_values, ppsz_color_descriptions )
213 add_integer( CFG_PREFIX "size", -1, SIZE_TEXT, SIZE_LONGTEXT, false )
214 change_integer_range( -1, 4096)
216 set_section( N_("Misc"), NULL )
217 add_integer( CFG_PREFIX "speed", 100000, SPEED_TEXT, SPEED_LONGTEXT,
219 add_integer( CFG_PREFIX "length", 60, LENGTH_TEXT, LENGTH_LONGTEXT,
221 add_integer( CFG_PREFIX "ttl", 1800, TTL_TEXT, TTL_LONGTEXT, false )
222 add_bool( CFG_PREFIX "images", true, IMAGE_TEXT, IMAGE_LONGTEXT, false )
223 add_integer( CFG_PREFIX "title", default_title, TITLE_TEXT, TITLE_LONGTEXT, false )
224 change_integer_list( pi_title_modes, ppsz_title_modes )
226 set_description( N_("RSS and Atom feed display") )
227 add_shortcut( "rss", "atom" )
230 static const char *const ppsz_filter_options[] = {
231 "urls", "x", "y", "position", "color", "size", "speed", "length",
232 "ttl", "images", "title", NULL
235 /*****************************************************************************
236 * CreateFilter: allocates RSS video filter
237 *****************************************************************************/
238 static int CreateFilter( vlc_object_t *p_this )
240 filter_t *p_filter = (filter_t *)p_this;
245 /* Allocate structure */
246 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
250 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
253 /* Get the urls to parse: must be non empty */
254 psz_urls = var_CreateGetNonEmptyString( p_filter, CFG_PREFIX "urls" );
257 msg_Err( p_filter, "The list of urls must not be empty" );
262 /* Fill the p_sys structure with the configuration */
263 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
264 p_sys->i_cur_feed = 0;
265 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
266 p_sys->i_cur_char = 0;
268 p_sys->p_feeds = NULL;
269 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
270 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
271 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
273 i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
275 p_sys->psz_marquee = malloc( p_sys->i_length + 1 );
276 if( p_sys->psz_marquee == NULL )
282 p_sys->psz_marquee[p_sys->i_length] = '\0';
284 p_sys->p_style = text_style_New();
285 if( p_sys->p_style == NULL )
288 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
289 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
290 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
291 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
292 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
293 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
295 if( p_sys->b_images && p_sys->p_style->i_font_size == -1 )
297 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus be displayed without being resized" );
301 if( ParseUrls( p_filter, psz_urls ) )
305 vlc_mutex_init( &p_sys->lock );
306 p_filter->pf_sub_source = Filter;
307 p_sys->last_date = (mtime_t)0;
308 p_sys->b_fetched = false;
310 /* Create and arm the timer */
311 if( vlc_timer_create( &p_sys->timer, Fetch, p_filter ) )
313 vlc_mutex_destroy( &p_sys->lock );
316 vlc_timer_schedule( p_sys->timer, false, 1,
317 (mtime_t)(i_ttl)*1000000 );
324 text_style_Delete( p_sys->p_style );
325 free( p_sys->psz_marquee );
330 /*****************************************************************************
331 * DestroyFilter: destroy RSS video filter
332 *****************************************************************************/
333 static void DestroyFilter( vlc_object_t *p_this )
335 filter_t *p_filter = (filter_t *)p_this;
336 filter_sys_t *p_sys = p_filter->p_sys;
338 vlc_timer_destroy( p_sys->timer );
339 vlc_mutex_destroy( &p_sys->lock );
341 text_style_Delete( p_sys->p_style );
342 free( p_sys->psz_marquee );
343 FreeRSS( p_sys->p_feeds, p_sys->i_feeds );
347 /****************************************************************************
348 * Filter: the whole thing
349 ****************************************************************************
350 * This function outputs subpictures at regular time intervals.
351 ****************************************************************************/
352 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
354 filter_sys_t *p_sys = p_filter->p_sys;
357 subpicture_region_t *p_region;
362 memset( &fmt, 0, sizeof(video_format_t) );
364 vlc_mutex_lock( &p_sys->lock );
366 /* Check if the feeds have been fetched and that we have some feeds */
367 /* TODO: check that we have items for each feeds */
368 if( !p_sys->b_fetched && p_sys->i_feeds > 0 )
370 vlc_mutex_unlock( &p_sys->lock );
375 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
376 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
377 * p_sys->i_speed > date )
379 vlc_mutex_unlock( &p_sys->lock );
383 p_sys->last_date = date;
385 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 )
387 p_sys->i_cur_char = 0;
389 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
391 if( p_sys->i_title == scroll_title )
392 p_sys->i_cur_item = -1;
394 p_sys->i_cur_item = 0;
395 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
399 p_spu = filter_NewSubpicture( p_filter );
402 vlc_mutex_unlock( &p_sys->lock );
406 fmt.i_chroma = VLC_CODEC_TEXT;
408 p_spu->p_region = subpicture_region_New( &fmt );
409 if( !p_spu->p_region )
411 p_filter->pf_sub_buffer_del( p_filter, p_spu );
412 vlc_mutex_unlock( &p_sys->lock );
416 /* Generate the string that will be displayed. This string is supposed to
417 be p_sys->i_length characters long. */
418 i_item = p_sys->i_cur_item;
419 i_feed = p_sys->i_cur_feed;
420 p_feed = &p_sys->p_feeds[i_feed];
422 if( ( p_feed->p_pic && p_sys->i_title == default_title )
423 || p_sys->i_title == hide_title )
425 /* Don't display the feed's title if we have an image */
426 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
427 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
428 +p_sys->i_cur_char );
430 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
431 || p_sys->i_title == prepend_title )
433 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
434 p_sys->p_feeds[i_feed].psz_title,
435 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
436 +p_sys->i_cur_char );
438 else /* scrolling title */
441 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
442 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
443 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
445 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
446 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
447 +p_sys->i_cur_char );
450 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
453 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
454 snprintf( strchr( p_sys->psz_marquee, 0 ),
455 p_sys->i_length - strlen( p_sys->psz_marquee ),
457 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
460 /* Calls to snprintf might split multibyte UTF8 chars ...
461 * which freetype doesn't like. */
463 char *a = strdup( p_sys->psz_marquee );
465 char *b = p_sys->psz_marquee;
466 EnsureUTF8( p_sys->psz_marquee );
467 /* we want to use ' ' instead of '?' for erroneous chars */
470 if( *b != *a ) *b = ' ';
476 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
477 if( p_sys->p_style->i_font_size > 0 )
478 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
479 p_spu->i_start = date;
481 p_spu->b_ephemer = true;
483 /* where to locate the string: */
484 if( p_sys->i_pos < 0 )
485 { /* set to an absolute xy */
486 p_spu->p_region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP;
487 p_spu->b_absolute = true;
490 { /* set to one of the 9 relative locations */
491 p_spu->p_region->i_align = p_sys->i_pos;
492 p_spu->b_absolute = false;
494 p_spu->p_region->i_x = p_sys->i_xoff;
495 p_spu->p_region->i_y = p_sys->i_yoff;
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_sar_num = fmt_out.i_sar_den = 1;
510 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
512 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
514 p_region = subpicture_region_New( &fmt_out );
517 msg_Err( p_filter, "cannot allocate SPU region" );
521 p_region->i_x = p_spu->p_region->i_x;
522 p_region->i_y = p_spu->p_region->i_y;
523 /* FIXME the copy is probably not needed anymore */
524 picture_Copy( p_region->p_picture, p_pic );
525 p_spu->p_region->p_next = p_region;
527 /* Offset text to display right next to the image */
528 p_spu->p_region->i_x += fmt_out.i_visible_width;
532 vlc_mutex_unlock( &p_sys->lock );
536 /****************************************************************************
537 * RSS related functions
538 ****************************************************************************
539 * You should always lock the p_filter mutex before using any of these
541 ***************************************************************************/
543 #undef LoadImage /* do not conflict with Win32 API */
545 /****************************************************************************
546 * download and resize image located at psz_url
547 ***************************************************************************/
548 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
550 filter_sys_t *p_sys = p_filter->p_sys;
551 video_format_t fmt_in;
552 video_format_t fmt_out;
554 picture_t *p_pic = NULL;
555 image_handler_t *p_handler = image_HandlerCreate( p_filter );
557 memset( &fmt_in, 0, sizeof(video_format_t) );
558 memset( &fmt_out, 0, sizeof(video_format_t) );
560 fmt_out.i_chroma = VLC_CODEC_YUVA;
561 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
565 msg_Warn( p_filter, "Unable to read image %s", psz_url );
567 else if( p_sys->p_style->i_font_size > 0 )
570 fmt_in.i_chroma = VLC_CODEC_YUVA;
571 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
572 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
573 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
574 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
575 fmt_out.i_height = p_sys->p_style->i_font_size;
577 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
578 picture_Release( p_orig );
581 msg_Warn( p_filter, "Error while converting %s", psz_url );
589 image_HandlerDelete( p_handler );
594 /****************************************************************************
595 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
597 ***************************************************************************/
598 static char *removeWhiteChars( const char *psz_src )
600 char *psz_src2,*psz_clean, *psz_clean2;
601 psz_src2 = psz_clean = strdup( psz_src );
604 while( ( *psz_clean == ' ' || *psz_clean == '\t'
605 || *psz_clean == '\n' || *psz_clean == '\r' )
606 && *psz_clean != '\0' )
610 i = strlen( psz_clean );
612 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
613 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
614 psz_clean[i+1] = '\0';
615 psz_clean2 = strdup( psz_clean );
621 /****************************************************************************
622 * Parse url list, psz_urls must be non empty (TODO: check it !)
623 ***************************************************************************/
624 static int ParseUrls( filter_t *p_filter, char *psz_urls )
626 filter_sys_t *p_sys = p_filter->p_sys;
627 char *psz_urls2 = psz_urls;
631 /* Count the number of feeds */
634 if( *psz_urls == '|' )
639 /* Allocate the structure */
640 p_sys->p_feeds = malloc( p_sys->i_feeds * sizeof( rss_feed_t ) );
641 if( !p_sys->p_feeds )
644 /* Loop on all urls and fill in the struct */
645 psz_urls = psz_urls2;
646 for( int i = 0; i < p_sys->i_feeds; i++ )
648 rss_feed_t* p_feed = p_sys->p_feeds + i;
651 if( i < p_sys->i_feeds - 1 )
653 psz_end = strchr( psz_urls, '|' );
660 p_feed->p_items = NULL;
661 p_feed->psz_title = NULL;
662 p_feed->psz_link = NULL;
663 p_feed->psz_description = NULL;
664 p_feed->psz_image = NULL;
665 p_feed->p_pic = NULL;
666 p_feed->psz_url = strdup( psz_urls );
668 psz_urls = psz_end + 1;
676 /****************************************************************************
678 ***************************************************************************/
679 static bool ParseFeed( filter_t *p_filter, xml_reader_t *p_xml_reader,
682 VLC_UNUSED(p_filter);
684 char *psz_eltname = NULL;
686 bool b_is_item = false;
687 bool b_is_image = false;
692 while( (type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
696 case XML_READER_STARTELEM:
698 msg_Dbg( p_filter, "element <%s>", node );
700 psz_eltname = strdup( node );
701 if( unlikely(!psz_eltname) )
705 if( !strcmp( node, "item" ) || !strcmp( node, "entry" ) )
709 p_feed->p_items = xrealloc( p_feed->p_items,
710 p_feed->i_items * sizeof( rss_item_t ) );
711 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
712 p_feed->p_items[p_feed->i_items-1].psz_description = NULL;
713 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
716 else if( !strcmp( node, "image" ) )
721 else if( !strcmp( node, "link" ) )
723 const char *name, *value;
724 char *psz_href = NULL;
725 char *psz_rel = NULL;
727 while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
729 if( !strcmp( name, "rel" ) )
732 psz_rel = strdup( value );
734 else if( !strcmp( name, "href" ) )
737 psz_href = strdup( value );
741 /* "rel" and "href" must be defined */
742 if( psz_rel && psz_href )
744 if( !strcmp( psz_rel, "alternate" ) && !b_is_item &&
745 !b_is_image && !p_feed->psz_link )
747 p_feed->psz_link = psz_href;
749 /* this isn't in the rfc but i found some ... */
750 else if( ( !strcmp( psz_rel, "logo" ) ||
751 !strcmp( psz_rel, "icon" ) )
752 && !b_is_item && !b_is_image
753 && !p_feed->psz_image )
755 p_feed->psz_image = psz_href;
770 case XML_READER_ENDELEM:
771 FREENULL( psz_eltname );
773 msg_Dbg( p_filter, "element end </%s>", node );
776 if( !strcmp( node, "item" ) || !strcmp( node, "entry" ) )
782 else if( !strcmp( node, "image" ) )
788 case XML_READER_TEXT:
793 char *psz_eltvalue = removeWhiteChars( node );
796 msg_Dbg( p_filter, " text : \"%s\"", psz_eltvalue );
798 /* Is it an item ? */
801 rss_item_t *p_item = p_feed->p_items+i_item;
803 if( !strcmp( psz_eltname, "title" ) && !p_item->psz_title )
805 p_item->psz_title = psz_eltvalue;
807 else if( !strcmp( psz_eltname, "link" ) /* rss */
808 && !p_item->psz_link )
810 p_item->psz_link = psz_eltvalue;
813 else if( ( !strcmp( psz_eltname, "description" ) ||
814 !strcmp( psz_eltname, "summary" ) )
815 && !p_item->psz_description )
817 p_item->psz_description = psz_eltvalue;
821 free( psz_eltvalue );
824 /* Is it an image ? */
825 else if( b_is_image )
827 if( !strcmp( psz_eltname, "url" ) && !p_feed->psz_image )
828 p_feed->psz_image = psz_eltvalue;
830 free( psz_eltvalue );
835 if( !strcmp( psz_eltname, "title" ) && !p_feed->psz_title )
837 p_feed->psz_title = psz_eltvalue;
840 else if( !strcmp( psz_eltname, "link" ) && !p_feed->psz_link )
842 p_feed->psz_link = psz_eltvalue;
845 else if( ( !strcmp( psz_eltname, "description" ) ||
846 !strcmp( psz_eltname, "subtitle" ) )
847 && !p_feed->psz_description )
849 p_feed->psz_description = psz_eltvalue;
852 else if( ( !strcmp( psz_eltname, "logo" ) ||
853 !strcmp( psz_eltname, "icon" ) )
854 && !p_feed->psz_image )
856 p_feed->psz_image = psz_eltvalue;
860 free( psz_eltvalue );
877 /****************************************************************************
878 * FetchRSS (or Atom) feeds
879 ***************************************************************************/
880 static rss_feed_t* FetchRSS( filter_t *p_filter )
882 filter_sys_t *p_sys = p_filter->p_sys;
886 xml_reader_t *p_xml_reader;
889 /* These data are not modified after the creation of the module so we don't
890 need to hold the lock */
891 int i_feeds = p_sys->i_feeds;
892 bool b_images = p_sys->b_images;
894 /* Allocate a new structure */
895 rss_feed_t *p_feeds = malloc( i_feeds * sizeof( rss_feed_t ) );
899 p_xml = xml_Create( p_filter );
902 msg_Err( p_filter, "Failed to open XML parser" );
907 /* Fetch all feeds and parse them */
908 for( i_feed = 0; i_feed < i_feeds; i_feed++ )
910 rss_feed_t *p_feed = p_feeds + i_feed;
911 rss_feed_t *p_old_feed = p_sys->p_feeds + i_feed;
913 /* Initialize the structure */
914 p_feed->psz_title = NULL;
915 p_feed->psz_description = NULL;
916 p_feed->psz_link = NULL;
917 p_feed->psz_image = NULL;
918 p_feed->p_pic = NULL;
920 p_feed->p_items = NULL;
922 p_feed->psz_url = strdup( p_old_feed->psz_url );
925 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", p_feed->psz_url );
927 p_stream = stream_UrlNew( p_filter, p_feed->psz_url );
930 msg_Err( p_filter, "Failed to open %s for reading", p_feed->psz_url );
935 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
938 msg_Err( p_filter, "Failed to open %s for parsing", p_feed->psz_url );
943 if( !ParseFeed( p_filter, p_xml_reader, p_feed ) )
946 /* If we have a image: load it if requiere */
947 if( b_images && p_feed->psz_image && !p_feed->p_pic )
949 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
952 msg_Dbg( p_filter, "done with %s RSS/Atom feed", p_feed->psz_url );
953 xml_ReaderDelete( p_xml_reader );
954 stream_Delete( p_stream );
961 FreeRSS( p_feeds, i_feed + 1 );
963 xml_ReaderDelete( p_xml_reader );
965 stream_Delete( p_stream );
972 /****************************************************************************
974 ***************************************************************************/
975 static void FreeRSS( rss_feed_t *p_feeds, int i_feeds )
977 for( int i_feed = 0; i_feed < i_feeds; i_feed++ )
979 rss_feed_t *p_feed = p_feeds+i_feed;
980 for( int i_item = 0; i_item < p_feed->i_items; i_item++ )
982 rss_item_t *p_item = p_feed->p_items+i_item;
983 free( p_item->psz_title );
984 free( p_item->psz_link );
985 free( p_item->psz_description );
987 free( p_feed->p_items );
988 free( p_feed->psz_title);
989 free( p_feed->psz_link );
990 free( p_feed->psz_description );
991 free( p_feed->psz_image );
992 if( p_feed->p_pic != NULL )
993 picture_Release( p_feed->p_pic );
994 free( p_feed->psz_url );
999 static void Fetch( void *p_data )
1001 filter_t *p_filter = p_data;
1002 filter_sys_t *p_sys = p_filter->p_sys;
1004 msg_Dbg( p_filter, "Updating the rss feeds" );
1005 rss_feed_t *p_feeds = FetchRSS( p_filter );
1008 msg_Err( p_filter, "Unable to fetch the feeds" );
1012 rss_feed_t *p_old_feeds = p_sys->p_feeds;
1014 vlc_mutex_lock( &p_sys->lock );
1015 /* Update the feeds */
1016 p_sys->p_feeds = p_feeds;
1017 p_sys->b_fetched = true;
1018 /* Set all current info to the original values */
1019 p_sys->i_cur_feed = 0;
1020 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
1021 p_sys->i_cur_char = 0;
1022 vlc_mutex_unlock( &p_sys->lock );
1025 FreeRSS( p_old_feeds, p_sys->i_feeds );