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 *****************************************************************************/
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 filter", 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, NULL, MSG_TEXT, MSG_LONGTEXT, false )
200 set_section( N_("Position"), NULL )
201 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, true )
202 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, true )
203 add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, false )
204 change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL )
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, NULL,
209 OPACITY_TEXT, OPACITY_LONGTEXT, false )
210 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
212 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
213 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, false )
215 set_section( N_("Misc"), NULL )
216 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
218 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
220 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, false )
221 add_bool( CFG_PREFIX "images", true, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, false )
222 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, false )
223 change_integer_list( pi_title_modes, ppsz_title_modes, NULL )
225 set_description( N_("RSS and Atom feed display") )
226 add_shortcut( "rss", "atom" )
229 static const char *const ppsz_filter_options[] = {
230 "urls", "x", "y", "position", "color", "size", "speed", "length",
231 "ttl", "images", "title", NULL
234 /*****************************************************************************
235 * CreateFilter: allocates RSS video filter
236 *****************************************************************************/
237 static int CreateFilter( vlc_object_t *p_this )
239 filter_t *p_filter = (filter_t *)p_this;
244 /* Allocate structure */
245 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
249 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
252 /* Get the urls to parse: must be non empty */
253 psz_urls = var_CreateGetNonEmptyString( p_filter, CFG_PREFIX "urls" );
256 msg_Err( p_filter, "The list of urls must not be empty" );
261 /* Fill the p_sys structure with the configuration */
262 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
263 p_sys->i_cur_feed = 0;
264 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
265 p_sys->i_cur_char = 0;
267 p_sys->p_feeds = NULL;
268 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
269 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
270 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
272 i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
274 p_sys->psz_marquee = malloc( p_sys->i_length + 1 );
275 if( p_sys->psz_marquee == NULL )
281 p_sys->psz_marquee[p_sys->i_length] = '\0';
283 p_sys->p_style = text_style_New();
284 if( p_sys->p_style == NULL )
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 && p_sys->p_style->i_font_size == -1 )
296 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus be displayed without being resized" );
300 if( ParseUrls( p_filter, psz_urls ) )
304 vlc_mutex_init( &p_sys->lock );
305 p_filter->pf_sub_filter = Filter;
306 p_sys->last_date = (mtime_t)0;
307 p_sys->b_fetched = false;
309 /* Create and arm the timer */
310 if( vlc_timer_create( &p_sys->timer, Fetch, p_filter ) )
312 vlc_mutex_destroy( &p_sys->lock );
315 vlc_timer_schedule( p_sys->timer, false, 1,
316 (mtime_t)(i_ttl)*1000000 );
323 text_style_Delete( p_sys->p_style );
324 free( p_sys->psz_marquee );
329 /*****************************************************************************
330 * DestroyFilter: destroy RSS video filter
331 *****************************************************************************/
332 static void DestroyFilter( vlc_object_t *p_this )
334 filter_t *p_filter = (filter_t *)p_this;
335 filter_sys_t *p_sys = p_filter->p_sys;
337 vlc_timer_destroy( p_sys->timer );
338 vlc_mutex_destroy( &p_sys->lock );
340 text_style_Delete( p_sys->p_style );
341 free( p_sys->psz_marquee );
342 FreeRSS( p_sys->p_feeds, p_sys->i_feeds );
346 /****************************************************************************
347 * Filter: the whole thing
348 ****************************************************************************
349 * This function outputs subpictures at regular time intervals.
350 ****************************************************************************/
351 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
353 filter_sys_t *p_sys = p_filter->p_sys;
356 subpicture_region_t *p_region;
361 memset( &fmt, 0, sizeof(video_format_t) );
363 vlc_mutex_lock( &p_sys->lock );
365 /* Check if the feeds have been fetched and that we have some feeds */
366 /* TODO: check that we have items for each feeds */
367 if( !p_sys->b_fetched && p_sys->i_feeds > 0 )
369 vlc_mutex_unlock( &p_sys->lock );
374 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
375 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
376 * p_sys->i_speed > date )
378 vlc_mutex_unlock( &p_sys->lock );
382 p_sys->last_date = date;
384 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 )
386 p_sys->i_cur_char = 0;
388 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
390 if( p_sys->i_title == scroll_title )
391 p_sys->i_cur_item = -1;
393 p_sys->i_cur_item = 0;
394 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
398 p_spu = filter_NewSubpicture( p_filter );
401 vlc_mutex_unlock( &p_sys->lock );
405 fmt.i_chroma = VLC_CODEC_TEXT;
407 p_spu->p_region = subpicture_region_New( &fmt );
408 if( !p_spu->p_region )
410 p_filter->pf_sub_buffer_del( p_filter, p_spu );
411 vlc_mutex_unlock( &p_sys->lock );
415 /* Generate the string that will be displayed. This string is supposed to
416 be p_sys->i_length characters long. */
417 i_item = p_sys->i_cur_item;
418 i_feed = p_sys->i_cur_feed;
419 p_feed = &p_sys->p_feeds[i_feed];
421 if( ( p_feed->p_pic && p_sys->i_title == default_title )
422 || p_sys->i_title == hide_title )
424 /* Don't display the feed's title if we have an image */
425 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
426 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
427 +p_sys->i_cur_char );
429 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
430 || p_sys->i_title == prepend_title )
432 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
433 p_sys->p_feeds[i_feed].psz_title,
434 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
435 +p_sys->i_cur_char );
437 else /* scrolling title */
440 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
441 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
442 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
444 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
445 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
446 +p_sys->i_cur_char );
449 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
452 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
453 snprintf( strchr( p_sys->psz_marquee, 0 ),
454 p_sys->i_length - strlen( p_sys->psz_marquee ),
456 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
459 /* Calls to snprintf might split multibyte UTF8 chars ...
460 * which freetype doesn't like. */
462 char *a = strdup( p_sys->psz_marquee );
464 char *b = p_sys->psz_marquee;
465 EnsureUTF8( p_sys->psz_marquee );
466 /* we want to use ' ' instead of '?' for erroneous chars */
469 if( *b != *a ) *b = ' ';
475 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
476 if( p_sys->p_style->i_font_size > 0 )
477 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
478 p_spu->i_start = date;
480 p_spu->b_ephemer = true;
482 /* where to locate the string: */
483 if( p_sys->i_pos < 0 )
484 { /* set to an absolute xy */
485 p_spu->p_region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP;
486 p_spu->b_absolute = true;
489 { /* set to one of the 9 relative locations */
490 p_spu->p_region->i_align = p_sys->i_pos;
491 p_spu->b_absolute = false;
493 p_spu->p_region->i_x = p_sys->i_xoff;
494 p_spu->p_region->i_y = p_sys->i_yoff;
496 p_spu->p_region->p_style = text_style_Duplicate( p_sys->p_style );
500 /* Display the feed's image */
501 picture_t *p_pic = p_feed->p_pic;
502 video_format_t fmt_out;
504 memset( &fmt_out, 0, sizeof(video_format_t) );
506 fmt_out.i_chroma = VLC_CODEC_YUVA;
507 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
509 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
511 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
513 p_region = subpicture_region_New( &fmt_out );
516 msg_Err( p_filter, "cannot allocate SPU region" );
520 p_region->i_x = p_spu->p_region->i_x;
521 p_region->i_y = p_spu->p_region->i_y;
522 /* FIXME the copy is probably not needed anymore */
523 picture_Copy( p_region->p_picture, p_pic );
524 p_spu->p_region->p_next = p_region;
526 /* Offset text to display right next to the image */
527 p_spu->p_region->i_x += fmt_out.i_visible_width;
531 vlc_mutex_unlock( &p_sys->lock );
535 /****************************************************************************
536 * RSS related functions
537 ****************************************************************************
538 * You should always lock the p_filter mutex before using any of these
540 ***************************************************************************/
542 #undef LoadImage /* do not conflict with Win32 API */
544 /****************************************************************************
545 * download and resize image located at psz_url
546 ***************************************************************************/
547 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
549 filter_sys_t *p_sys = p_filter->p_sys;
550 video_format_t fmt_in;
551 video_format_t fmt_out;
553 picture_t *p_pic = NULL;
554 image_handler_t *p_handler = image_HandlerCreate( p_filter );
556 memset( &fmt_in, 0, sizeof(video_format_t) );
557 memset( &fmt_out, 0, sizeof(video_format_t) );
559 fmt_out.i_chroma = VLC_CODEC_YUVA;
560 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
564 msg_Warn( p_filter, "Unable to read image %s", psz_url );
566 else if( p_sys->p_style->i_font_size > 0 )
569 fmt_in.i_chroma = VLC_CODEC_YUVA;
570 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
571 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
572 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
573 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
574 fmt_out.i_height = p_sys->p_style->i_font_size;
576 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
577 picture_Release( p_orig );
580 msg_Warn( p_filter, "Error while converting %s", psz_url );
588 image_HandlerDelete( p_handler );
593 /****************************************************************************
594 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
596 ***************************************************************************/
597 static char *removeWhiteChars( const char *psz_src )
599 char *psz_src2,*psz_clean, *psz_clean2;
600 psz_src2 = psz_clean = strdup( psz_src );
603 while( ( *psz_clean == ' ' || *psz_clean == '\t'
604 || *psz_clean == '\n' || *psz_clean == '\r' )
605 && *psz_clean != '\0' )
609 i = strlen( psz_clean );
611 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
612 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
613 psz_clean[i+1] = '\0';
614 psz_clean2 = strdup( psz_clean );
620 /****************************************************************************
621 * Parse url list, psz_urls must be non empty (TODO: check it !)
622 ***************************************************************************/
623 static int ParseUrls( filter_t *p_filter, char *psz_urls )
625 filter_sys_t *p_sys = p_filter->p_sys;
626 char *psz_urls2 = psz_urls;
630 /* Count the number of feeds */
633 if( *psz_urls == '|' )
638 /* Allocate the structure */
639 p_sys->p_feeds = malloc( p_sys->i_feeds * sizeof( rss_feed_t ) );
640 if( !p_sys->p_feeds )
643 /* Loop on all urls and fill in the struct */
644 psz_urls = psz_urls2;
645 for( int i = 0; i < p_sys->i_feeds; i++ )
647 rss_feed_t* p_feed = p_sys->p_feeds + i;
650 if( i < p_sys->i_feeds - 1 )
652 psz_end = strchr( psz_urls, '|' );
659 p_feed->p_items = NULL;
660 p_feed->psz_title = NULL;
661 p_feed->psz_link = NULL;
662 p_feed->psz_description = NULL;
663 p_feed->psz_image = NULL;
664 p_feed->p_pic = NULL;
665 p_feed->psz_url = strdup( psz_urls );
667 psz_urls = psz_end + 1;
675 /****************************************************************************
677 ***************************************************************************/
678 static bool ParseFeed( filter_t *p_filter, xml_reader_t *p_xml_reader,
681 VLC_UNUSED(p_filter);
682 char *psz_eltname = NULL;
684 bool b_is_item = false;
685 bool b_is_image = false;
689 while( xml_ReaderRead( p_xml_reader ) == 1 )
691 switch( xml_ReaderNodeType( p_xml_reader ) )
697 case XML_READER_STARTELEM:
699 psz_eltname = xml_ReaderName( p_xml_reader );
704 msg_Dbg( p_filter, "element name: %s", psz_eltname );
707 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
711 p_feed->p_items = xrealloc( p_feed->p_items,
712 p_feed->i_items * sizeof( rss_item_t ) );
713 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
714 p_feed->p_items[p_feed->i_items-1].psz_description = NULL;
715 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
718 else if( !strcmp( psz_eltname, "image" ) )
723 else if( !strcmp( psz_eltname, "link" ) )
725 char *psz_href = NULL;
726 char *psz_rel = NULL;
727 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
729 char *psz_name = xml_ReaderName( p_xml_reader );
730 char *psz_value = xml_ReaderValue( p_xml_reader );
731 if( !strcmp( psz_name, "rel" ) )
736 else if( !strcmp( psz_name, "href" ) )
739 psz_href = psz_value;
748 /* "rel" and "href" must be defined */
749 if( psz_rel && psz_href )
751 if( !strcmp( psz_rel, "alternate" ) && !b_is_item &&
752 !b_is_image && !p_feed->psz_link )
754 p_feed->psz_link = psz_href;
756 /* this isn't in the rfc but i found some ... */
757 else if( ( !strcmp( psz_rel, "logo" ) ||
758 !strcmp( psz_rel, "icon" ) )
759 && !b_is_item && !b_is_image
760 && !p_feed->psz_image )
762 p_feed->psz_image = psz_href;
777 case XML_READER_ENDELEM:
779 psz_eltname = xml_ReaderName( p_xml_reader );
784 msg_Dbg( p_filter, "element end : %s", psz_eltname );
787 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
793 else if( !strcmp( psz_eltname, "image" ) )
797 FREENULL( psz_eltname );
800 case XML_READER_TEXT:
804 char *psz_eltvalue = xml_ReaderValue( p_xml_reader );
808 char *psz_clean = removeWhiteChars( psz_eltvalue );
809 free( psz_eltvalue );
810 psz_eltvalue = psz_clean;
813 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
815 /* Is it an item ? */
818 rss_item_t *p_item = p_feed->p_items+i_item;
820 if( !strcmp( psz_eltname, "title" ) && !p_item->psz_title )
822 p_item->psz_title = psz_eltvalue;
824 else if( !strcmp( psz_eltname, "link" ) /* rss */
825 && !p_item->psz_link )
827 p_item->psz_link = psz_eltvalue;
830 else if( ( !strcmp( psz_eltname, "description" ) ||
831 !strcmp( psz_eltname, "summary" ) )
832 && !p_item->psz_description )
834 p_item->psz_description = psz_eltvalue;
838 free( psz_eltvalue );
841 /* Is it an image ? */
842 else if( b_is_image )
844 if( !strcmp( psz_eltname, "url" ) && !p_feed->psz_image )
845 p_feed->psz_image = psz_eltvalue;
847 free( psz_eltvalue );
852 if( !strcmp( psz_eltname, "title" ) && !p_feed->psz_title )
854 p_feed->psz_title = psz_eltvalue;
857 else if( !strcmp( psz_eltname, "link" ) && !p_feed->psz_link )
859 p_feed->psz_link = psz_eltvalue;
862 else if( ( !strcmp( psz_eltname, "description" ) ||
863 !strcmp( psz_eltname, "subtitle" ) )
864 && !p_feed->psz_description )
866 p_feed->psz_description = psz_eltvalue;
869 else if( ( !strcmp( psz_eltname, "logo" ) ||
870 !strcmp( psz_eltname, "icon" ) )
871 && !p_feed->psz_image )
873 p_feed->psz_image = psz_eltvalue;
877 free( psz_eltvalue );
894 /****************************************************************************
895 * FetchRSS (or Atom) feeds
896 ***************************************************************************/
897 static rss_feed_t* FetchRSS( filter_t *p_filter )
899 filter_sys_t *p_sys = p_filter->p_sys;
903 xml_reader_t *p_xml_reader;
906 /* These data are not modified after the creation of the module so we don't
907 need to hold the lock */
908 int i_feeds = p_sys->i_feeds;
909 bool b_images = p_sys->b_images;
911 /* Allocate a new structure */
912 rss_feed_t *p_feeds = malloc( i_feeds * sizeof( rss_feed_t ) );
916 p_xml = xml_Create( p_filter );
919 msg_Err( p_filter, "Failed to open XML parser" );
924 /* Fetch all feeds and parse them */
925 for( i_feed = 0; i_feed < i_feeds; i_feed++ )
927 rss_feed_t *p_feed = p_feeds + i_feed;
928 rss_feed_t *p_old_feed = p_sys->p_feeds + i_feed;
930 /* Initialize the structure */
931 p_feed->psz_title = NULL;
932 p_feed->psz_description = NULL;
933 p_feed->psz_link = NULL;
934 p_feed->psz_image = NULL;
935 p_feed->p_pic = NULL;
937 p_feed->p_items = NULL;
939 p_feed->psz_url = strdup( p_old_feed->psz_url );
942 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", p_feed->psz_url );
944 p_stream = stream_UrlNew( p_filter, p_feed->psz_url );
947 msg_Err( p_filter, "Failed to open %s for reading", p_feed->psz_url );
952 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
955 msg_Err( p_filter, "Failed to open %s for parsing", p_feed->psz_url );
960 if( !ParseFeed( p_filter, p_xml_reader, p_feed ) )
963 /* If we have a image: load it if requiere */
964 if( b_images && p_feed->psz_image && !p_feed->p_pic )
966 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
969 msg_Dbg( p_filter, "done with %s RSS/Atom feed", p_feed->psz_url );
970 xml_ReaderDelete( p_xml, p_xml_reader );
971 stream_Delete( p_stream );
978 FreeRSS( p_feeds, i_feed + 1 );
980 xml_ReaderDelete( p_xml, p_xml_reader );
982 stream_Delete( p_stream );
989 /****************************************************************************
991 ***************************************************************************/
992 static void FreeRSS( rss_feed_t *p_feeds, int i_feeds )
994 for( int i_feed = 0; i_feed < i_feeds; i_feed++ )
996 rss_feed_t *p_feed = p_feeds+i_feed;
997 for( int i_item = 0; i_item < p_feed->i_items; i_item++ )
999 rss_item_t *p_item = p_feed->p_items+i_item;
1000 free( p_item->psz_title );
1001 free( p_item->psz_link );
1002 free( p_item->psz_description );
1004 free( p_feed->p_items );
1005 free( p_feed->psz_title);
1006 free( p_feed->psz_link );
1007 free( p_feed->psz_description );
1008 free( p_feed->psz_image );
1009 if( p_feed->p_pic != NULL )
1010 picture_Release( p_feed->p_pic );
1011 free( p_feed->psz_url );
1016 static void Fetch( void *p_data )
1018 filter_t *p_filter = p_data;
1019 filter_sys_t *p_sys = p_filter->p_sys;
1021 msg_Dbg( p_filter, "Updating the rss feeds" );
1022 rss_feed_t *p_feeds = FetchRSS( p_filter );
1025 msg_Err( p_filter, "Unable to fetch the feeds" );
1029 rss_feed_t *p_old_feeds = p_sys->p_feeds;
1031 vlc_mutex_lock( &p_sys->lock );
1032 /* Update the feeds */
1033 p_sys->p_feeds = p_feeds;
1034 p_sys->b_fetched = true;
1035 /* Set all current info to the original values */
1036 p_sys->i_cur_feed = 0;
1037 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
1038 p_sys->i_cur_char = 0;
1039 vlc_mutex_unlock( &p_sys->lock );
1042 FreeRSS( p_old_feeds, p_sys->i_feeds );