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" )
227 add_shortcut( "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_filter = 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);
683 char *psz_eltname = NULL;
685 bool b_is_item = false;
686 bool b_is_image = false;
690 while( xml_ReaderRead( p_xml_reader ) == 1 )
692 switch( xml_ReaderNodeType( p_xml_reader ) )
698 case XML_READER_STARTELEM:
700 psz_eltname = xml_ReaderName( p_xml_reader );
705 msg_Dbg( p_filter, "element name: %s", psz_eltname );
708 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
712 p_feed->p_items = xrealloc( p_feed->p_items,
713 p_feed->i_items * sizeof( rss_item_t ) );
714 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
715 p_feed->p_items[p_feed->i_items-1].psz_description = NULL;
716 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
719 else if( !strcmp( psz_eltname, "image" ) )
724 else if( !strcmp( psz_eltname, "link" ) )
726 char *psz_href = NULL;
727 char *psz_rel = NULL;
728 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
730 char *psz_name = xml_ReaderName( p_xml_reader );
731 char *psz_value = xml_ReaderValue( p_xml_reader );
732 if( !strcmp( psz_name, "rel" ) )
737 else if( !strcmp( psz_name, "href" ) )
740 psz_href = psz_value;
749 /* "rel" and "href" must be defined */
750 if( psz_rel && psz_href )
752 if( !strcmp( psz_rel, "alternate" ) && !b_is_item &&
753 !b_is_image && !p_feed->psz_link )
755 p_feed->psz_link = psz_href;
757 /* this isn't in the rfc but i found some ... */
758 else if( ( !strcmp( psz_rel, "logo" ) ||
759 !strcmp( psz_rel, "icon" ) )
760 && !b_is_item && !b_is_image
761 && !p_feed->psz_image )
763 p_feed->psz_image = psz_href;
778 case XML_READER_ENDELEM:
780 psz_eltname = xml_ReaderName( p_xml_reader );
785 msg_Dbg( p_filter, "element end : %s", psz_eltname );
788 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
794 else if( !strcmp( psz_eltname, "image" ) )
798 FREENULL( psz_eltname );
801 case XML_READER_TEXT:
805 char *psz_eltvalue = xml_ReaderValue( p_xml_reader );
809 char *psz_clean = removeWhiteChars( psz_eltvalue );
810 free( psz_eltvalue );
811 psz_eltvalue = psz_clean;
814 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
816 /* Is it an item ? */
819 rss_item_t *p_item = p_feed->p_items+i_item;
821 if( !strcmp( psz_eltname, "title" ) && !p_item->psz_title )
823 p_item->psz_title = psz_eltvalue;
825 else if( !strcmp( psz_eltname, "link" ) /* rss */
826 && !p_item->psz_link )
828 p_item->psz_link = psz_eltvalue;
831 else if( ( !strcmp( psz_eltname, "description" ) ||
832 !strcmp( psz_eltname, "summary" ) )
833 && !p_item->psz_description )
835 p_item->psz_description = psz_eltvalue;
839 free( psz_eltvalue );
842 /* Is it an image ? */
843 else if( b_is_image )
845 if( !strcmp( psz_eltname, "url" ) && !p_feed->psz_image )
846 p_feed->psz_image = psz_eltvalue;
848 free( psz_eltvalue );
853 if( !strcmp( psz_eltname, "title" ) && !p_feed->psz_title )
855 p_feed->psz_title = psz_eltvalue;
858 else if( !strcmp( psz_eltname, "link" ) && !p_feed->psz_link )
860 p_feed->psz_link = psz_eltvalue;
863 else if( ( !strcmp( psz_eltname, "description" ) ||
864 !strcmp( psz_eltname, "subtitle" ) )
865 && !p_feed->psz_description )
867 p_feed->psz_description = psz_eltvalue;
870 else if( ( !strcmp( psz_eltname, "logo" ) ||
871 !strcmp( psz_eltname, "icon" ) )
872 && !p_feed->psz_image )
874 p_feed->psz_image = psz_eltvalue;
878 free( psz_eltvalue );
895 /****************************************************************************
896 * FetchRSS (or Atom) feeds
897 ***************************************************************************/
898 static rss_feed_t* FetchRSS( filter_t *p_filter )
900 filter_sys_t *p_sys = p_filter->p_sys;
904 xml_reader_t *p_xml_reader;
907 /* These data are not modified after the creation of the module so we don't
908 need to hold the lock */
909 int i_feeds = p_sys->i_feeds;
910 bool b_images = p_sys->b_images;
912 /* Allocate a new structure */
913 rss_feed_t *p_feeds = malloc( i_feeds * sizeof( rss_feed_t ) );
917 p_xml = xml_Create( p_filter );
920 msg_Err( p_filter, "Failed to open XML parser" );
925 /* Fetch all feeds and parse them */
926 for( i_feed = 0; i_feed < i_feeds; i_feed++ )
928 rss_feed_t *p_feed = p_feeds + i_feed;
929 rss_feed_t *p_old_feed = p_sys->p_feeds + i_feed;
931 /* Initialize the structure */
932 p_feed->psz_title = NULL;
933 p_feed->psz_description = NULL;
934 p_feed->psz_link = NULL;
935 p_feed->psz_image = NULL;
936 p_feed->p_pic = NULL;
938 p_feed->p_items = NULL;
940 p_feed->psz_url = strdup( p_old_feed->psz_url );
943 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", p_feed->psz_url );
945 p_stream = stream_UrlNew( p_filter, p_feed->psz_url );
948 msg_Err( p_filter, "Failed to open %s for reading", p_feed->psz_url );
953 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
956 msg_Err( p_filter, "Failed to open %s for parsing", p_feed->psz_url );
961 if( !ParseFeed( p_filter, p_xml_reader, p_feed ) )
964 /* If we have a image: load it if requiere */
965 if( b_images && p_feed->psz_image && !p_feed->p_pic )
967 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
970 msg_Dbg( p_filter, "done with %s RSS/Atom feed", p_feed->psz_url );
971 xml_ReaderDelete( p_xml, p_xml_reader );
972 stream_Delete( p_stream );
979 FreeRSS( p_feeds, i_feed + 1 );
981 xml_ReaderDelete( p_xml, p_xml_reader );
983 stream_Delete( p_stream );
990 /****************************************************************************
992 ***************************************************************************/
993 static void FreeRSS( rss_feed_t *p_feeds, int i_feeds )
995 for( int i_feed = 0; i_feed < i_feeds; i_feed++ )
997 rss_feed_t *p_feed = p_feeds+i_feed;
998 for( int i_item = 0; i_item < p_feed->i_items; i_item++ )
1000 rss_item_t *p_item = p_feed->p_items+i_item;
1001 free( p_item->psz_title );
1002 free( p_item->psz_link );
1003 free( p_item->psz_description );
1005 free( p_feed->p_items );
1006 free( p_feed->psz_title);
1007 free( p_feed->psz_link );
1008 free( p_feed->psz_description );
1009 free( p_feed->psz_image );
1010 if( p_feed->p_pic != NULL )
1011 picture_Release( p_feed->p_pic );
1012 free( p_feed->psz_url );
1017 static void Fetch( void *p_data )
1019 filter_t *p_filter = p_data;
1020 filter_sys_t *p_sys = p_filter->p_sys;
1022 msg_Dbg( p_filter, "Updating the rss feeds" );
1023 rss_feed_t *p_feeds = FetchRSS( p_filter );
1026 msg_Err( p_filter, "Unable to fetch the feeds" );
1030 rss_feed_t *p_old_feeds = p_sys->p_feeds;
1032 vlc_mutex_lock( &p_sys->lock );
1033 /* Update the feeds */
1034 p_sys->p_feeds = p_feeds;
1035 p_sys->b_fetched = true;
1036 /* Set all current info to the original values */
1037 p_sys->i_cur_feed = 0;
1038 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
1039 p_sys->i_cur_char = 0;
1040 vlc_mutex_unlock( &p_sys->lock );
1043 FreeRSS( p_old_feeds, p_sys->i_feeds );