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>
45 #include <vlc_stream.h>
47 #include <vlc_charset.h>
49 #include <vlc_image.h>
53 /*****************************************************************************
55 *****************************************************************************/
56 static int CreateFilter ( vlc_object_t * );
57 static void DestroyFilter( vlc_object_t * );
58 static subpicture_t *Filter( filter_t *, mtime_t );
60 static struct rss_feed_t *FetchRSS( filter_t * );
61 static void FreeRSS( struct rss_feed_t *, int );
62 static int ParseUrls( filter_t *, char * );
64 static void Fetch( void * );
66 static const int pi_color_values[] = {
67 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
68 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
69 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
70 0x00000080, 0x000000FF, 0x0000FFFF};
71 static const char *const ppsz_color_descriptions[] = {
72 N_("Default"), N_("Black"),
73 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
74 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
75 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
78 /*****************************************************************************
79 * filter_sys_t: rss filter descriptor
80 *****************************************************************************/
82 typedef struct rss_item_t
85 char *psz_description;
89 typedef struct rss_feed_t
93 char *psz_description;
105 vlc_timer_t timer; /* Timer to refresh the rss feeds */
108 int i_xoff, i_yoff; /* offsets for the display string in the video window */
109 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
113 char *psz_marquee; /* marquee string */
115 text_style_t *p_style; /* font control */
130 #define MSG_TEXT N_("Feed URLs")
131 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) separated URLs.")
132 #define SPEED_TEXT N_("Speed of feeds")
133 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
134 #define LENGTH_TEXT N_("Max length")
135 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
137 #define TTL_TEXT N_("Refresh time")
138 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
139 "of the feeds. 0 means that the feeds are never updated." )
140 #define IMAGE_TEXT N_("Feed images")
141 #define IMAGE_LONGTEXT N_("Display feed images if available.")
143 #define POSX_TEXT N_("X offset")
144 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
145 #define POSY_TEXT N_("Y offset")
146 #define POSY_LONGTEXT N_("Y offset, down from the top." )
147 #define OPACITY_TEXT N_("Opacity")
148 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
149 "overlay text. 0 = transparent, 255 = totally opaque." )
151 #define SIZE_TEXT N_("Font size, pixels")
152 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
155 #define COLOR_TEXT N_("Color")
156 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
157 "the video. This must be an hexadecimal (like HTML colors). The first two "\
158 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
159 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
161 #define POS_TEXT N_("Text position")
162 #define POS_LONGTEXT N_( \
163 "You can enforce the text position on the video " \
164 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
165 "also use combinations of these values, eg 6 = top-right).")
167 #define TITLE_TEXT N_("Title display mode")
168 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
170 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
171 static const char *const ppsz_pos_descriptions[] =
172 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
173 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
181 static const int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
182 static const char *const ppsz_title_modes[] =
183 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
185 #define CFG_PREFIX "rss-"
187 /*****************************************************************************
189 *****************************************************************************/
191 set_capability( "sub filter", 1 )
192 set_shortname( "RSS / Atom" )
193 set_callbacks( CreateFilter, DestroyFilter )
194 set_category( CAT_VIDEO )
195 set_subcategory( SUBCAT_VIDEO_SUBPIC )
196 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, false )
198 set_section( N_("Position"), NULL )
199 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, true )
200 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, true )
201 add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, false )
202 change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL )
204 set_section( N_("Font"), NULL )
205 /* 5 sets the default to top [1] left [4] */
206 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
207 OPACITY_TEXT, OPACITY_LONGTEXT, false )
208 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
210 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
211 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, false )
213 set_section( N_("Misc"), NULL )
214 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
216 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
218 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, false )
219 add_bool( CFG_PREFIX "images", true, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, false )
220 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, false )
221 change_integer_list( pi_title_modes, ppsz_title_modes, NULL )
223 set_description( N_("RSS and Atom feed display") )
224 add_shortcut( "rss" )
225 add_shortcut( "atom" )
228 static const char *const ppsz_filter_options[] = {
229 "urls", "x", "y", "position", "color", "size", "speed", "length",
230 "ttl", "images", "title", NULL
233 /*****************************************************************************
234 * CreateFilter: allocates RSS video filter
235 *****************************************************************************/
236 static int CreateFilter( vlc_object_t *p_this )
238 filter_t *p_filter = (filter_t *)p_this;
240 int i_ret = VLC_ENOMEM;
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 /* Fill the p_sys structure with the configuration */
253 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
254 p_sys->i_cur_feed = 0;
255 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
256 p_sys->i_cur_char = 0;
258 p_sys->p_feeds = NULL;
259 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
260 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
261 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
263 i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
264 psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
266 p_sys->psz_marquee = malloc( p_sys->i_length + 1 );
267 if( p_sys->psz_marquee == NULL )
269 p_sys->psz_marquee[p_sys->i_length] = '\0';
271 p_sys->p_style = text_style_New();
272 if( p_sys->p_style == NULL )
275 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
276 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
277 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
278 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
279 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
280 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
282 if( p_sys->b_images && p_sys->p_style->i_font_size == -1 )
284 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus be displayed without being resized" );
288 if( ParseUrls( p_filter, psz_urls ) )
296 vlc_mutex_init( &p_sys->lock );
297 p_filter->pf_sub_filter = Filter;
298 p_sys->last_date = (mtime_t)0;
299 p_sys->b_fetched = false;
301 /* Create and arm the timer */
302 if( vlc_timer_create( &p_sys->timer, Fetch, p_filter ) )
304 vlc_mutex_destroy( &p_sys->lock );
307 vlc_timer_schedule( p_sys->timer, false, 1,
308 (mtime_t)(i_ttl)*1000000 );
313 free( p_sys->psz_marquee );
317 /*****************************************************************************
318 * DestroyFilter: destroy RSS video filter
319 *****************************************************************************/
320 static void DestroyFilter( vlc_object_t *p_this )
322 filter_t *p_filter = (filter_t *)p_this;
323 filter_sys_t *p_sys = p_filter->p_sys;
325 vlc_timer_destroy( p_sys->timer );
326 vlc_mutex_destroy( &p_sys->lock );
328 text_style_Delete( p_sys->p_style );
329 free( p_sys->psz_marquee );
330 FreeRSS( p_sys->p_feeds, p_sys->i_feeds );
334 /****************************************************************************
335 * Filter: the whole thing
336 ****************************************************************************
337 * This function outputs subpictures at regular time intervals.
338 ****************************************************************************/
339 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
341 filter_sys_t *p_sys = p_filter->p_sys;
344 subpicture_region_t *p_region;
349 memset( &fmt, 0, sizeof(video_format_t) );
351 vlc_mutex_lock( &p_sys->lock );
353 /* Check if the feeds have been fetched and that we have some feeds */
354 /* TODO: check that we have items for each feeds */
355 if( !p_sys->b_fetched && p_sys->i_feeds > 0 )
357 vlc_mutex_unlock( &p_sys->lock );
362 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
363 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
364 * p_sys->i_speed > date )
366 vlc_mutex_unlock( &p_sys->lock );
370 p_sys->last_date = date;
372 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 )
374 p_sys->i_cur_char = 0;
376 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
378 if( p_sys->i_title == scroll_title )
379 p_sys->i_cur_item = -1;
381 p_sys->i_cur_item = 0;
382 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
386 p_spu = filter_NewSubpicture( p_filter );
389 vlc_mutex_unlock( &p_sys->lock );
393 fmt.i_chroma = VLC_CODEC_TEXT;
395 p_spu->p_region = subpicture_region_New( &fmt );
396 if( !p_spu->p_region )
398 p_filter->pf_sub_buffer_del( p_filter, p_spu );
399 vlc_mutex_unlock( &p_sys->lock );
403 /* Generate the string that will be displayed. This string is supposed to
404 be p_sys->i_length characters long. */
405 i_item = p_sys->i_cur_item;
406 i_feed = p_sys->i_cur_feed;
407 p_feed = &p_sys->p_feeds[i_feed];
409 if( ( p_feed->p_pic && p_sys->i_title == default_title )
410 || p_sys->i_title == hide_title )
412 /* Don't display the feed's title if we have an image */
413 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
414 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
415 +p_sys->i_cur_char );
417 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
418 || p_sys->i_title == prepend_title )
420 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
421 p_sys->p_feeds[i_feed].psz_title,
422 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
423 +p_sys->i_cur_char );
425 else /* scrolling title */
428 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
429 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
430 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
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 );
437 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
440 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
441 snprintf( strchr( p_sys->psz_marquee, 0 ),
442 p_sys->i_length - strlen( p_sys->psz_marquee ),
444 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
447 /* Calls to snprintf might split multibyte UTF8 chars ...
448 * which freetype doesn't like. */
450 char *a = strdup( p_sys->psz_marquee );
452 char *b = p_sys->psz_marquee;
453 EnsureUTF8( p_sys->psz_marquee );
454 /* we want to use ' ' instead of '?' for erroneous chars */
457 if( *b != *a ) *b = ' ';
463 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
464 if( p_sys->p_style->i_font_size > 0 )
465 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
466 p_spu->i_start = date;
468 p_spu->b_ephemer = true;
470 /* where to locate the string: */
471 if( p_sys->i_pos < 0 )
472 { /* set to an absolute xy */
473 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
474 p_spu->b_absolute = true;
477 { /* set to one of the 9 relative locations */
478 p_spu->p_region->i_align = p_sys->i_pos;
479 p_spu->b_absolute = false;
482 p_spu->p_region->p_style = text_style_Duplicate( p_sys->p_style );
486 /* Display the feed's image */
487 picture_t *p_pic = p_feed->p_pic;
488 video_format_t fmt_out;
490 memset( &fmt_out, 0, sizeof(video_format_t) );
492 fmt_out.i_chroma = VLC_CODEC_YUVA;
493 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
494 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
496 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
498 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
500 p_region = subpicture_region_New( &fmt_out );
503 msg_Err( p_filter, "cannot allocate SPU region" );
507 p_region->i_x = p_sys->i_xoff;
508 p_region->i_y = p_sys->i_yoff;
509 /* FIXME the copy is probably not needed anymore */
510 picture_Copy( p_region->p_picture, p_pic );
511 p_spu->p_region->p_next = p_region;
514 /* Offset text to display right next to the image */
515 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
518 vlc_mutex_unlock( &p_sys->lock );
522 /****************************************************************************
523 * RSS related functions
524 ****************************************************************************
525 * You should always lock the p_filter mutex before using any of these
527 ***************************************************************************/
529 #undef LoadImage /* do not conflict with Win32 API */
531 /****************************************************************************
532 * download and resize image located at psz_url
533 ***************************************************************************/
534 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
536 filter_sys_t *p_sys = p_filter->p_sys;
537 video_format_t fmt_in;
538 video_format_t fmt_out;
540 picture_t *p_pic = NULL;
541 image_handler_t *p_handler = image_HandlerCreate( p_filter );
543 memset( &fmt_in, 0, sizeof(video_format_t) );
544 memset( &fmt_out, 0, sizeof(video_format_t) );
546 fmt_out.i_chroma = VLC_CODEC_YUVA;
547 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
551 msg_Warn( p_filter, "Unable to read image %s", psz_url );
553 else if( p_sys->p_style->i_font_size > 0 )
556 fmt_in.i_chroma = VLC_CODEC_YUVA;
557 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
558 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
559 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
560 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
561 fmt_out.i_height = p_sys->p_style->i_font_size;
563 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
564 picture_Release( p_orig );
567 msg_Warn( p_filter, "Error while converting %s", psz_url );
575 image_HandlerDelete( p_handler );
580 /****************************************************************************
581 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
583 ***************************************************************************/
584 static char *removeWhiteChars( const char *psz_src )
586 char *psz_src2,*psz_clean, *psz_clean2;
587 psz_src2 = psz_clean = strdup( psz_src );
590 while( ( *psz_clean == ' ' || *psz_clean == '\t'
591 || *psz_clean == '\n' || *psz_clean == '\r' )
592 && *psz_clean != '\0' )
596 i = strlen( psz_clean );
598 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
599 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
600 psz_clean[i+1] = '\0';
601 psz_clean2 = strdup( psz_clean );
607 /****************************************************************************
608 * Parse url list, psz_urls must be non empty (TODO: check it !)
609 ***************************************************************************/
610 static int ParseUrls( filter_t *p_filter, char *psz_urls )
612 filter_sys_t *p_sys = p_filter->p_sys;
613 char *psz_urls2 = psz_urls;
617 /* Count the number of feeds */
620 if( *psz_urls == '|' )
625 /* Allocate the structure */
626 p_sys->p_feeds = malloc( p_sys->i_feeds * sizeof( rss_feed_t ) );
627 if( !p_sys->p_feeds )
630 /* Loop on all urls and fill in the struct */
631 psz_urls = psz_urls2;
632 for( int i = 0; i < p_sys->i_feeds; i++ )
634 rss_feed_t* p_feed = p_sys->p_feeds + i;
637 if( i < p_sys->i_feeds - 1 )
639 psz_end = strchr( psz_urls, '|' );
646 p_feed->p_items = NULL;
647 p_feed->psz_title = NULL;
648 p_feed->psz_link = NULL;
649 p_feed->psz_description = NULL;
650 p_feed->psz_image = NULL;
651 p_feed->p_pic = NULL;
652 p_feed->psz_url = strdup( psz_urls );
654 psz_urls = psz_end + 1;
662 /****************************************************************************
664 ***************************************************************************/
665 static bool ParseFeed( filter_t *p_filter, xml_reader_t *p_xml_reader,
668 VLC_UNUSED(p_filter);
669 char *psz_eltname = NULL;
671 bool b_is_item = false;
672 bool b_is_image = false;
676 while( xml_ReaderRead( p_xml_reader ) == 1 )
678 switch( xml_ReaderNodeType( p_xml_reader ) )
684 case XML_READER_STARTELEM:
686 psz_eltname = xml_ReaderName( p_xml_reader );
691 msg_Dbg( p_filter, "element name: %s", psz_eltname );
694 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
698 p_feed->p_items = realloc( p_feed->p_items,
699 p_feed->i_items * sizeof( rss_item_t ) );
700 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
701 p_feed->p_items[p_feed->i_items-1].psz_description = NULL;
702 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
705 else if( !strcmp( psz_eltname, "image" ) )
710 else if( !strcmp( psz_eltname, "link" ) )
712 char *psz_href = NULL;
713 char *psz_rel = NULL;
714 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
716 char *psz_name = xml_ReaderName( p_xml_reader );
717 char *psz_value = xml_ReaderValue( p_xml_reader );
718 if( !strcmp( psz_name, "rel" ) )
723 else if( !strcmp( psz_name, "href" ) )
726 psz_href = psz_value;
735 /* "rel" and "href" must be defined */
736 if( psz_rel && psz_href )
738 if( !strcmp( psz_rel, "alternate" ) && !b_is_item &&
739 !b_is_image && !p_feed->psz_link )
741 p_feed->psz_link = psz_href;
743 /* this isn't in the rfc but i found some ... */
744 else if( ( !strcmp( psz_rel, "logo" ) ||
745 !strcmp( psz_rel, "icon" ) )
746 && !b_is_item && !b_is_image
747 && !p_feed->psz_image )
749 p_feed->psz_image = psz_href;
764 case XML_READER_ENDELEM:
766 psz_eltname = xml_ReaderName( p_xml_reader );
771 msg_Dbg( p_filter, "element end : %s", psz_eltname );
774 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
780 else if( !strcmp( psz_eltname, "image" ) )
784 FREENULL( psz_eltname );
787 case XML_READER_TEXT:
791 char *psz_eltvalue = xml_ReaderValue( p_xml_reader );
795 char *psz_clean = removeWhiteChars( psz_eltvalue );
796 free( psz_eltvalue );
797 psz_eltvalue = psz_clean;
800 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
802 /* Is it an item ? */
805 rss_item_t *p_item = p_feed->p_items+i_item;
807 if( !strcmp( psz_eltname, "title" ) && !p_item->psz_title )
809 p_item->psz_title = psz_eltvalue;
811 else if( !strcmp( psz_eltname, "link" ) /* rss */
812 && !p_item->psz_link )
814 p_item->psz_link = psz_eltvalue;
817 else if( ( !strcmp( psz_eltname, "description" ) ||
818 !strcmp( psz_eltname, "summary" ) )
819 && !p_item->psz_description )
821 p_item->psz_description = psz_eltvalue;
825 free( psz_eltvalue );
828 /* Is it an image ? */
829 else if( b_is_image )
831 if( !strcmp( psz_eltname, "url" ) && !p_feed->psz_image )
832 p_feed->psz_image = psz_eltvalue;
834 free( psz_eltvalue );
839 if( !strcmp( psz_eltname, "title" ) && !p_feed->psz_title )
841 p_feed->psz_title = psz_eltvalue;
844 else if( !strcmp( psz_eltname, "link" ) && !p_feed->psz_link )
846 p_feed->psz_link = psz_eltvalue;
849 else if( ( !strcmp( psz_eltname, "description" ) ||
850 !strcmp( psz_eltname, "subtitle" ) )
851 && !p_feed->psz_description )
853 p_feed->psz_description = psz_eltvalue;
856 else if( ( !strcmp( psz_eltname, "logo" ) ||
857 !strcmp( psz_eltname, "icon" ) )
858 && !p_feed->psz_image )
860 p_feed->psz_image = psz_eltvalue;
864 free( psz_eltvalue );
881 /****************************************************************************
882 * FetchRSS (or Atom) feeds
883 ***************************************************************************/
884 static rss_feed_t* FetchRSS( filter_t *p_filter )
886 filter_sys_t *p_sys = p_filter->p_sys;
890 xml_reader_t *p_xml_reader;
892 /* These data are not modified after the creation of the module so we don't
893 need to hold the lock */
894 int i_feeds = p_sys->i_feeds;
895 bool b_images = p_sys->b_images;
897 /* Allocate a new structure */
898 rss_feed_t *p_feeds = malloc( i_feeds * sizeof( rss_feed_t ) );
902 p_xml = xml_Create( p_filter );
905 msg_Err( p_filter, "Failed to open XML parser" );
910 /* Fetch all feeds and parse them */
911 for( int i_feed = 0; i_feed < i_feeds; i_feed++ )
913 rss_feed_t *p_feed = p_feeds + i_feed;
914 rss_feed_t *p_old_feed = p_sys->p_feeds + i_feed;
916 /* Initialize the structure */
917 p_feed->psz_title = NULL;
918 p_feed->psz_description = NULL;
919 p_feed->psz_link = NULL;
920 p_feed->psz_image = NULL;
921 p_feed->p_pic = NULL;
923 p_feed->p_items = NULL;
925 p_feed->psz_url = strdup( p_old_feed->psz_url );
928 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", p_feed->psz_url );
930 p_stream = stream_UrlNew( p_filter, p_feed->psz_url );
933 msg_Err( p_filter, "Failed to open %s for reading", p_feed->psz_url );
938 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
941 msg_Err( p_filter, "Failed to open %s for parsing", p_feed->psz_url );
946 if( !ParseFeed( p_filter, p_xml_reader, p_feed ) )
949 /* If we have a image: load it if requiere */
950 if( b_images && p_feed->psz_image && !p_feed->p_pic )
952 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
955 msg_Dbg( p_filter, "done with %s RSS/Atom feed", p_feed->psz_url );
956 xml_ReaderDelete( p_xml, p_xml_reader );
957 stream_Delete( p_stream );
965 /*TODO: still a memleak */
967 xml_ReaderDelete( p_xml, p_xml_reader );
969 stream_Delete( p_stream );
976 /****************************************************************************
978 ***************************************************************************/
979 static void FreeRSS( rss_feed_t *p_feeds, int i_feeds )
981 for( int i_feed = 0; i_feed < i_feeds; i_feed++ )
983 rss_feed_t *p_feed = p_feeds+i_feed;
984 for( int i_item = 0; i_item < p_feed->i_items; i_item++ )
986 rss_item_t *p_item = p_feed->p_items+i_item;
987 free( p_item->psz_title );
988 free( p_item->psz_link );
989 free( p_item->psz_description );
991 free( p_feed->p_items );
992 free( p_feed->psz_title);
993 free( p_feed->psz_link );
994 free( p_feed->psz_description );
995 free( p_feed->psz_image );
996 if( p_feed->p_pic != NULL )
997 picture_Release( p_feed->p_pic );
998 free( p_feed->psz_url );
1003 static void Fetch( void *p_data )
1005 filter_t *p_filter = p_data;
1006 filter_sys_t *p_sys = p_filter->p_sys;
1008 rss_feed_t *p_feeds = FetchRSS( p_filter );
1009 rss_feed_t *p_old_feeds = p_sys->p_feeds;
1014 vlc_mutex_lock( &p_sys->lock );
1015 p_sys->p_feeds = p_feeds;
1016 p_sys->b_fetched = true;
1017 vlc_mutex_unlock( &p_sys->lock );
1020 FreeRSS( p_old_feeds, p_sys->i_feeds );