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,
225 change_integer_list( pi_title_modes, ppsz_title_modes )
227 set_description( N_("RSS and Atom feed display") )
228 add_shortcut( "rss", "atom" )
231 static const char *const ppsz_filter_options[] = {
232 "urls", "x", "y", "position", "opacity", "color", "size", "speed", "length",
233 "ttl", "images", "title", NULL
236 /*****************************************************************************
237 * CreateFilter: allocates RSS video filter
238 *****************************************************************************/
239 static int CreateFilter( vlc_object_t *p_this )
241 filter_t *p_filter = (filter_t *)p_this;
246 /* Allocate structure */
247 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
251 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
254 /* Get the urls to parse: must be non empty */
255 psz_urls = var_CreateGetNonEmptyString( p_filter, CFG_PREFIX "urls" );
258 msg_Err( p_filter, "The list of urls must not be empty" );
263 /* Fill the p_sys structure with the configuration */
264 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
265 p_sys->i_cur_feed = 0;
266 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
267 p_sys->i_cur_char = 0;
269 p_sys->p_feeds = NULL;
270 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
271 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
272 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
274 i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
276 p_sys->psz_marquee = malloc( p_sys->i_length + 1 );
277 if( p_sys->psz_marquee == NULL )
283 p_sys->psz_marquee[p_sys->i_length] = '\0';
285 p_sys->p_style = text_style_New();
286 if( p_sys->p_style == NULL )
289 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
290 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
291 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
292 p_sys->p_style->i_font_alpha = var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
293 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
294 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
296 if( p_sys->b_images && p_sys->p_style->i_font_size == -1 )
298 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus "
299 "be displayed without being resized" );
303 if( ParseUrls( p_filter, psz_urls ) )
307 vlc_mutex_init( &p_sys->lock );
308 p_filter->pf_sub_source = 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 &&
378 p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
379 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
380 * p_sys->i_speed > date )
382 vlc_mutex_unlock( &p_sys->lock );
386 p_sys->last_date = date;
389 if( p_sys->i_cur_item == -1 ?
390 p_sys->p_feeds[p_sys->i_cur_feed].psz_title[p_sys->i_cur_char] == 0 :
391 p_sys->p_feeds[p_sys->i_cur_feed].p_items[p_sys->i_cur_item].psz_title[p_sys->i_cur_char] == 0 )
393 p_sys->i_cur_char = 0;
395 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
397 if( p_sys->i_title == scroll_title )
398 p_sys->i_cur_item = -1;
400 p_sys->i_cur_item = 0;
401 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
405 p_spu = filter_NewSubpicture( p_filter );
408 vlc_mutex_unlock( &p_sys->lock );
412 fmt.i_chroma = VLC_CODEC_TEXT;
414 p_spu->p_region = subpicture_region_New( &fmt );
415 if( !p_spu->p_region )
417 subpicture_Delete( p_spu );
418 vlc_mutex_unlock( &p_sys->lock );
422 /* Generate the string that will be displayed. This string is supposed to
423 be p_sys->i_length characters long. */
424 i_item = p_sys->i_cur_item;
425 i_feed = p_sys->i_cur_feed;
426 p_feed = &p_sys->p_feeds[i_feed];
428 if( ( p_feed->p_pic && p_sys->i_title == default_title )
429 || p_sys->i_title == hide_title )
431 /* Don't display the feed's title if we have an image */
432 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
433 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
434 +p_sys->i_cur_char );
436 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
437 || p_sys->i_title == prepend_title )
439 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
440 p_sys->p_feeds[i_feed].psz_title,
441 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
442 +p_sys->i_cur_char );
444 else /* scrolling title */
447 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
448 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
449 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
451 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
452 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
453 +p_sys->i_cur_char );
456 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
459 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
460 snprintf( strchr( p_sys->psz_marquee, 0 ),
461 p_sys->i_length - strlen( p_sys->psz_marquee ),
463 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
466 /* Calls to snprintf might split multibyte UTF8 chars ...
467 * which freetype doesn't like. */
469 char *a = strdup( p_sys->psz_marquee );
471 char *b = p_sys->psz_marquee;
472 EnsureUTF8( p_sys->psz_marquee );
473 /* we want to use ' ' instead of '?' for erroneous chars */
476 if( *b != *a ) *b = ' ';
482 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
483 if( p_sys->p_style->i_font_size > 0 )
484 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
485 p_spu->i_start = date;
487 p_spu->b_ephemer = true;
489 /* where to locate the string: */
490 if( p_sys->i_pos < 0 )
491 { /* set to an absolute xy */
492 p_spu->p_region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP;
493 p_spu->b_absolute = true;
496 { /* set to one of the 9 relative locations */
497 p_spu->p_region->i_align = p_sys->i_pos;
498 p_spu->b_absolute = false;
500 p_spu->p_region->i_x = p_sys->i_xoff;
501 p_spu->p_region->i_y = p_sys->i_yoff;
503 p_spu->p_region->p_style = text_style_Duplicate( p_sys->p_style );
507 /* Display the feed's image */
508 picture_t *p_pic = p_feed->p_pic;
509 video_format_t fmt_out;
511 memset( &fmt_out, 0, sizeof(video_format_t) );
513 fmt_out.i_chroma = VLC_CODEC_YUVA;
514 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
516 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
518 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
520 p_region = subpicture_region_New( &fmt_out );
523 msg_Err( p_filter, "cannot allocate SPU region" );
527 p_region->i_x = p_spu->p_region->i_x;
528 p_region->i_y = p_spu->p_region->i_y;
529 /* FIXME the copy is probably not needed anymore */
530 picture_Copy( p_region->p_picture, p_pic );
531 p_spu->p_region->p_next = p_region;
533 /* Offset text to display right next to the image */
534 p_spu->p_region->i_x += fmt_out.i_visible_width;
538 vlc_mutex_unlock( &p_sys->lock );
542 /****************************************************************************
543 * RSS related functions
544 ****************************************************************************
545 * You should always lock the p_filter mutex before using any of these
547 ***************************************************************************/
549 #undef LoadImage /* do not conflict with Win32 API */
551 /****************************************************************************
552 * download and resize image located at psz_url
553 ***************************************************************************/
554 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
556 filter_sys_t *p_sys = p_filter->p_sys;
557 video_format_t fmt_in;
558 video_format_t fmt_out;
560 picture_t *p_pic = NULL;
561 image_handler_t *p_handler = image_HandlerCreate( p_filter );
563 memset( &fmt_in, 0, sizeof(video_format_t) );
564 memset( &fmt_out, 0, sizeof(video_format_t) );
566 fmt_out.i_chroma = VLC_CODEC_YUVA;
567 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
571 msg_Warn( p_filter, "Unable to read image %s", psz_url );
573 else if( p_sys->p_style->i_font_size > 0 )
576 fmt_in.i_chroma = VLC_CODEC_YUVA;
577 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
578 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
579 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
580 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
581 fmt_out.i_height = p_sys->p_style->i_font_size;
583 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
584 picture_Release( p_orig );
587 msg_Warn( p_filter, "Error while converting %s", psz_url );
595 image_HandlerDelete( p_handler );
600 /****************************************************************************
601 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
603 ***************************************************************************/
604 static char *removeWhiteChars( const char *psz_src )
606 char *psz_src2,*psz_clean, *psz_clean2;
607 psz_src2 = psz_clean = strdup( psz_src );
610 while( ( *psz_clean == ' ' || *psz_clean == '\t'
611 || *psz_clean == '\n' || *psz_clean == '\r' )
612 && *psz_clean != '\0' )
616 i = strlen( psz_clean );
618 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
619 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
620 psz_clean[i+1] = '\0';
621 psz_clean2 = strdup( psz_clean );
627 /****************************************************************************
628 * Parse url list, psz_urls must be non empty (TODO: check it !)
629 ***************************************************************************/
630 static int ParseUrls( filter_t *p_filter, char *psz_urls )
632 filter_sys_t *p_sys = p_filter->p_sys;
633 char *psz_urls2 = psz_urls;
637 /* Count the number of feeds */
640 if( *psz_urls == '|' )
645 /* Allocate the structure */
646 p_sys->p_feeds = malloc( p_sys->i_feeds * sizeof( rss_feed_t ) );
647 if( !p_sys->p_feeds )
650 /* Loop on all urls and fill in the struct */
651 psz_urls = psz_urls2;
652 for( int i = 0; i < p_sys->i_feeds; i++ )
654 rss_feed_t* p_feed = p_sys->p_feeds + i;
657 if( i < p_sys->i_feeds - 1 )
659 psz_end = strchr( psz_urls, '|' );
666 p_feed->p_items = NULL;
667 p_feed->psz_title = NULL;
668 p_feed->psz_link = NULL;
669 p_feed->psz_description = NULL;
670 p_feed->psz_image = NULL;
671 p_feed->p_pic = NULL;
672 p_feed->psz_url = strdup( psz_urls );
674 psz_urls = psz_end + 1;
682 /****************************************************************************
684 ***************************************************************************/
685 static bool ParseFeed( filter_t *p_filter, xml_reader_t *p_xml_reader,
688 VLC_UNUSED(p_filter);
690 char *psz_eltname = NULL;
692 bool b_is_item = false;
693 bool b_is_image = false;
698 while( (type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
702 case XML_READER_STARTELEM:
704 msg_Dbg( p_filter, "element <%s>", node );
706 psz_eltname = strdup( node );
707 if( unlikely(!psz_eltname) )
711 if( !strcmp( node, "item" ) || !strcmp( node, "entry" ) )
715 p_feed->p_items = xrealloc( p_feed->p_items,
716 p_feed->i_items * sizeof( rss_item_t ) );
717 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
718 p_feed->p_items[p_feed->i_items-1].psz_description = NULL;
719 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
722 else if( !strcmp( node, "image" ) )
727 else if( !strcmp( node, "link" ) )
729 const char *name, *value;
730 char *psz_href = NULL;
731 char *psz_rel = NULL;
733 while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) != NULL )
735 if( !strcmp( name, "rel" ) )
738 psz_rel = strdup( value );
740 else if( !strcmp( name, "href" ) )
743 psz_href = strdup( value );
747 /* "rel" and "href" must be defined */
748 if( psz_rel && psz_href )
750 if( !strcmp( psz_rel, "alternate" ) && !b_is_item &&
751 !b_is_image && !p_feed->psz_link )
753 p_feed->psz_link = psz_href;
755 /* this isn't in the rfc but i found some ... */
756 else if( ( !strcmp( psz_rel, "logo" ) ||
757 !strcmp( psz_rel, "icon" ) )
758 && !b_is_item && !b_is_image
759 && !p_feed->psz_image )
761 p_feed->psz_image = psz_href;
776 case XML_READER_ENDELEM:
777 FREENULL( psz_eltname );
779 msg_Dbg( p_filter, "element end </%s>", node );
782 if( !strcmp( node, "item" ) || !strcmp( node, "entry" ) )
788 else if( !strcmp( node, "image" ) )
794 case XML_READER_TEXT:
799 char *psz_eltvalue = removeWhiteChars( node );
802 msg_Dbg( p_filter, " text : \"%s\"", psz_eltvalue );
804 /* Is it an item ? */
807 rss_item_t *p_item = p_feed->p_items+i_item;
809 if( !strcmp( psz_eltname, "title" ) && !p_item->psz_title )
811 p_item->psz_title = psz_eltvalue;
813 else if( !strcmp( psz_eltname, "link" ) /* rss */
814 && !p_item->psz_link )
816 p_item->psz_link = psz_eltvalue;
819 else if( ( !strcmp( psz_eltname, "description" ) ||
820 !strcmp( psz_eltname, "summary" ) )
821 && !p_item->psz_description )
823 p_item->psz_description = psz_eltvalue;
827 free( psz_eltvalue );
830 /* Is it an image ? */
831 else if( b_is_image )
833 if( !strcmp( psz_eltname, "url" ) && !p_feed->psz_image )
834 p_feed->psz_image = psz_eltvalue;
836 free( psz_eltvalue );
841 if( !strcmp( psz_eltname, "title" ) && !p_feed->psz_title )
843 p_feed->psz_title = psz_eltvalue;
846 else if( !strcmp( psz_eltname, "link" ) && !p_feed->psz_link )
848 p_feed->psz_link = psz_eltvalue;
851 else if( ( !strcmp( psz_eltname, "description" ) ||
852 !strcmp( psz_eltname, "subtitle" ) )
853 && !p_feed->psz_description )
855 p_feed->psz_description = psz_eltvalue;
858 else if( ( !strcmp( psz_eltname, "logo" ) ||
859 !strcmp( psz_eltname, "icon" ) )
860 && !p_feed->psz_image )
862 p_feed->psz_image = psz_eltvalue;
866 free( psz_eltvalue );
882 /****************************************************************************
883 * FetchRSS (or Atom) feeds
884 ***************************************************************************/
885 static rss_feed_t* FetchRSS( filter_t *p_filter )
887 filter_sys_t *p_sys = p_filter->p_sys;
891 xml_reader_t *p_xml_reader;
894 /* These data are not modified after the creation of the module so we don't
895 need to hold the lock */
896 int i_feeds = p_sys->i_feeds;
897 bool b_images = p_sys->b_images;
899 /* Allocate a new structure */
900 rss_feed_t *p_feeds = malloc( i_feeds * sizeof( rss_feed_t ) );
904 p_xml = xml_Create( p_filter );
907 msg_Err( p_filter, "Failed to open XML parser" );
912 /* Fetch all feeds and parse them */
913 for( i_feed = 0; i_feed < i_feeds; i_feed++ )
915 rss_feed_t *p_feed = p_feeds + i_feed;
916 rss_feed_t *p_old_feed = p_sys->p_feeds + i_feed;
918 /* Initialize the structure */
919 p_feed->psz_title = NULL;
920 p_feed->psz_description = NULL;
921 p_feed->psz_link = NULL;
922 p_feed->psz_image = NULL;
923 p_feed->p_pic = NULL;
925 p_feed->p_items = NULL;
927 p_feed->psz_url = strdup( p_old_feed->psz_url );
930 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", p_feed->psz_url );
932 p_stream = stream_UrlNew( p_filter, p_feed->psz_url );
935 msg_Err( p_filter, "Failed to open %s for reading", p_feed->psz_url );
940 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
943 msg_Err( p_filter, "Failed to open %s for parsing", p_feed->psz_url );
948 if( !ParseFeed( p_filter, p_xml_reader, p_feed ) )
951 /* If we have a image: load it if requiere */
952 if( b_images && p_feed->psz_image && !p_feed->p_pic )
954 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
957 msg_Dbg( p_filter, "done with %s RSS/Atom feed", p_feed->psz_url );
958 xml_ReaderDelete( p_xml_reader );
959 stream_Delete( p_stream );
966 FreeRSS( p_feeds, i_feed + 1 );
968 xml_ReaderDelete( p_xml_reader );
970 stream_Delete( p_stream );
977 /****************************************************************************
979 ***************************************************************************/
980 static void FreeRSS( rss_feed_t *p_feeds, int i_feeds )
982 for( int i_feed = 0; i_feed < i_feeds; i_feed++ )
984 rss_feed_t *p_feed = p_feeds+i_feed;
985 for( int i_item = 0; i_item < p_feed->i_items; i_item++ )
987 rss_item_t *p_item = p_feed->p_items+i_item;
988 free( p_item->psz_title );
989 free( p_item->psz_link );
990 free( p_item->psz_description );
992 free( p_feed->p_items );
993 free( p_feed->psz_title);
994 free( p_feed->psz_link );
995 free( p_feed->psz_description );
996 free( p_feed->psz_image );
997 if( p_feed->p_pic != NULL )
998 picture_Release( p_feed->p_pic );
999 free( p_feed->psz_url );
1004 static void Fetch( void *p_data )
1006 filter_t *p_filter = p_data;
1007 filter_sys_t *p_sys = p_filter->p_sys;
1009 msg_Dbg( p_filter, "Updating the rss feeds" );
1010 rss_feed_t *p_feeds = FetchRSS( p_filter );
1013 msg_Err( p_filter, "Unable to fetch the feeds" );
1017 rss_feed_t *p_old_feeds = p_sys->p_feeds;
1019 vlc_mutex_lock( &p_sys->lock );
1020 /* Update the feeds */
1021 p_sys->p_feeds = p_feeds;
1022 p_sys->b_fetched = true;
1023 /* Set all current info to the original values */
1024 p_sys->i_cur_feed = 0;
1025 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
1026 p_sys->i_cur_char = 0;
1027 vlc_mutex_unlock( &p_sys->lock );
1030 FreeRSS( p_old_feeds, p_sys->i_feeds );