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", NULL, 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;
243 /* Allocate structure */
244 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
248 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
251 /* Get the urls to parse: must be non empty */
252 psz_urls = var_CreateGetNonEmptyString( p_filter, CFG_PREFIX "urls" );
255 msg_Err( p_filter, "The list of urls must not be empty" );
260 /* Fill the p_sys structure with the configuration */
261 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
262 p_sys->i_cur_feed = 0;
263 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
264 p_sys->i_cur_char = 0;
266 p_sys->p_feeds = NULL;
267 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
268 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
269 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
271 i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
273 p_sys->psz_marquee = malloc( p_sys->i_length + 1 );
274 if( p_sys->psz_marquee == NULL )
280 p_sys->psz_marquee[p_sys->i_length] = '\0';
282 p_sys->p_style = text_style_New();
283 if( p_sys->p_style == NULL )
286 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
287 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
288 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
289 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
290 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
291 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
293 if( p_sys->b_images && p_sys->p_style->i_font_size == -1 )
295 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus be displayed without being resized" );
299 if( ParseUrls( p_filter, psz_urls ) )
303 vlc_mutex_init( &p_sys->lock );
304 p_filter->pf_sub_filter = Filter;
305 p_sys->last_date = (mtime_t)0;
306 p_sys->b_fetched = false;
308 /* Create and arm the timer */
309 if( vlc_timer_create( &p_sys->timer, Fetch, p_filter ) )
311 vlc_mutex_destroy( &p_sys->lock );
314 vlc_timer_schedule( p_sys->timer, false, 1,
315 (mtime_t)(i_ttl)*1000000 );
322 text_style_Delete( p_sys->p_style );
323 free( p_sys->psz_marquee );
328 /*****************************************************************************
329 * DestroyFilter: destroy RSS video filter
330 *****************************************************************************/
331 static void DestroyFilter( vlc_object_t *p_this )
333 filter_t *p_filter = (filter_t *)p_this;
334 filter_sys_t *p_sys = p_filter->p_sys;
336 vlc_timer_destroy( p_sys->timer );
337 vlc_mutex_destroy( &p_sys->lock );
339 text_style_Delete( p_sys->p_style );
340 free( p_sys->psz_marquee );
341 FreeRSS( p_sys->p_feeds, p_sys->i_feeds );
345 /****************************************************************************
346 * Filter: the whole thing
347 ****************************************************************************
348 * This function outputs subpictures at regular time intervals.
349 ****************************************************************************/
350 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
352 filter_sys_t *p_sys = p_filter->p_sys;
355 subpicture_region_t *p_region;
360 memset( &fmt, 0, sizeof(video_format_t) );
362 vlc_mutex_lock( &p_sys->lock );
364 /* Check if the feeds have been fetched and that we have some feeds */
365 /* TODO: check that we have items for each feeds */
366 if( !p_sys->b_fetched && p_sys->i_feeds > 0 )
368 vlc_mutex_unlock( &p_sys->lock );
373 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
374 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
375 * p_sys->i_speed > date )
377 vlc_mutex_unlock( &p_sys->lock );
381 p_sys->last_date = date;
383 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 )
385 p_sys->i_cur_char = 0;
387 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
389 if( p_sys->i_title == scroll_title )
390 p_sys->i_cur_item = -1;
392 p_sys->i_cur_item = 0;
393 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
397 p_spu = filter_NewSubpicture( p_filter );
400 vlc_mutex_unlock( &p_sys->lock );
404 fmt.i_chroma = VLC_CODEC_TEXT;
406 p_spu->p_region = subpicture_region_New( &fmt );
407 if( !p_spu->p_region )
409 p_filter->pf_sub_buffer_del( p_filter, p_spu );
410 vlc_mutex_unlock( &p_sys->lock );
414 /* Generate the string that will be displayed. This string is supposed to
415 be p_sys->i_length characters long. */
416 i_item = p_sys->i_cur_item;
417 i_feed = p_sys->i_cur_feed;
418 p_feed = &p_sys->p_feeds[i_feed];
420 if( ( p_feed->p_pic && p_sys->i_title == default_title )
421 || p_sys->i_title == hide_title )
423 /* Don't display the feed's title if we have an image */
424 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
425 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
426 +p_sys->i_cur_char );
428 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
429 || p_sys->i_title == prepend_title )
431 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
432 p_sys->p_feeds[i_feed].psz_title,
433 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
434 +p_sys->i_cur_char );
436 else /* scrolling title */
439 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
440 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
441 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
443 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
444 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
445 +p_sys->i_cur_char );
448 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
451 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
452 snprintf( strchr( p_sys->psz_marquee, 0 ),
453 p_sys->i_length - strlen( p_sys->psz_marquee ),
455 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
458 /* Calls to snprintf might split multibyte UTF8 chars ...
459 * which freetype doesn't like. */
461 char *a = strdup( p_sys->psz_marquee );
463 char *b = p_sys->psz_marquee;
464 EnsureUTF8( p_sys->psz_marquee );
465 /* we want to use ' ' instead of '?' for erroneous chars */
468 if( *b != *a ) *b = ' ';
474 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
475 if( p_sys->p_style->i_font_size > 0 )
476 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
477 p_spu->i_start = date;
479 p_spu->b_ephemer = true;
481 /* where to locate the string: */
482 if( p_sys->i_pos < 0 )
483 { /* set to an absolute xy */
484 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
485 p_spu->b_absolute = true;
488 { /* set to one of the 9 relative locations */
489 p_spu->p_region->i_align = p_sys->i_pos;
490 p_spu->b_absolute = false;
493 p_spu->p_region->p_style = text_style_Duplicate( p_sys->p_style );
497 /* Display the feed's image */
498 picture_t *p_pic = p_feed->p_pic;
499 video_format_t fmt_out;
501 memset( &fmt_out, 0, sizeof(video_format_t) );
503 fmt_out.i_chroma = VLC_CODEC_YUVA;
504 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
505 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
507 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
509 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
511 p_region = subpicture_region_New( &fmt_out );
514 msg_Err( p_filter, "cannot allocate SPU region" );
518 p_region->i_x = p_sys->i_xoff;
519 p_region->i_y = p_sys->i_yoff;
520 /* FIXME the copy is probably not needed anymore */
521 picture_Copy( p_region->p_picture, p_pic );
522 p_spu->p_region->p_next = p_region;
525 /* Offset text to display right next to the image */
526 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
529 vlc_mutex_unlock( &p_sys->lock );
533 /****************************************************************************
534 * RSS related functions
535 ****************************************************************************
536 * You should always lock the p_filter mutex before using any of these
538 ***************************************************************************/
540 #undef LoadImage /* do not conflict with Win32 API */
542 /****************************************************************************
543 * download and resize image located at psz_url
544 ***************************************************************************/
545 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
547 filter_sys_t *p_sys = p_filter->p_sys;
548 video_format_t fmt_in;
549 video_format_t fmt_out;
551 picture_t *p_pic = NULL;
552 image_handler_t *p_handler = image_HandlerCreate( p_filter );
554 memset( &fmt_in, 0, sizeof(video_format_t) );
555 memset( &fmt_out, 0, sizeof(video_format_t) );
557 fmt_out.i_chroma = VLC_CODEC_YUVA;
558 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
562 msg_Warn( p_filter, "Unable to read image %s", psz_url );
564 else if( p_sys->p_style->i_font_size > 0 )
567 fmt_in.i_chroma = VLC_CODEC_YUVA;
568 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
569 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
570 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
571 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
572 fmt_out.i_height = p_sys->p_style->i_font_size;
574 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
575 picture_Release( p_orig );
578 msg_Warn( p_filter, "Error while converting %s", psz_url );
586 image_HandlerDelete( p_handler );
591 /****************************************************************************
592 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
594 ***************************************************************************/
595 static char *removeWhiteChars( const char *psz_src )
597 char *psz_src2,*psz_clean, *psz_clean2;
598 psz_src2 = psz_clean = strdup( psz_src );
601 while( ( *psz_clean == ' ' || *psz_clean == '\t'
602 || *psz_clean == '\n' || *psz_clean == '\r' )
603 && *psz_clean != '\0' )
607 i = strlen( psz_clean );
609 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
610 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
611 psz_clean[i+1] = '\0';
612 psz_clean2 = strdup( psz_clean );
618 /****************************************************************************
619 * Parse url list, psz_urls must be non empty (TODO: check it !)
620 ***************************************************************************/
621 static int ParseUrls( filter_t *p_filter, char *psz_urls )
623 filter_sys_t *p_sys = p_filter->p_sys;
624 char *psz_urls2 = psz_urls;
628 /* Count the number of feeds */
631 if( *psz_urls == '|' )
636 /* Allocate the structure */
637 p_sys->p_feeds = malloc( p_sys->i_feeds * sizeof( rss_feed_t ) );
638 if( !p_sys->p_feeds )
641 /* Loop on all urls and fill in the struct */
642 psz_urls = psz_urls2;
643 for( int i = 0; i < p_sys->i_feeds; i++ )
645 rss_feed_t* p_feed = p_sys->p_feeds + i;
648 if( i < p_sys->i_feeds - 1 )
650 psz_end = strchr( psz_urls, '|' );
657 p_feed->p_items = NULL;
658 p_feed->psz_title = NULL;
659 p_feed->psz_link = NULL;
660 p_feed->psz_description = NULL;
661 p_feed->psz_image = NULL;
662 p_feed->p_pic = NULL;
663 p_feed->psz_url = strdup( psz_urls );
665 psz_urls = psz_end + 1;
673 /****************************************************************************
675 ***************************************************************************/
676 static bool ParseFeed( filter_t *p_filter, xml_reader_t *p_xml_reader,
679 VLC_UNUSED(p_filter);
680 char *psz_eltname = NULL;
682 bool b_is_item = false;
683 bool b_is_image = false;
687 while( xml_ReaderRead( p_xml_reader ) == 1 )
689 switch( xml_ReaderNodeType( p_xml_reader ) )
695 case XML_READER_STARTELEM:
697 psz_eltname = xml_ReaderName( p_xml_reader );
702 msg_Dbg( p_filter, "element name: %s", psz_eltname );
705 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
709 p_feed->p_items = realloc( p_feed->p_items,
710 p_feed->i_items * sizeof( rss_item_t ) );
711 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
712 p_feed->p_items[p_feed->i_items-1].psz_description = NULL;
713 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
716 else if( !strcmp( psz_eltname, "image" ) )
721 else if( !strcmp( psz_eltname, "link" ) )
723 char *psz_href = NULL;
724 char *psz_rel = NULL;
725 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
727 char *psz_name = xml_ReaderName( p_xml_reader );
728 char *psz_value = xml_ReaderValue( p_xml_reader );
729 if( !strcmp( psz_name, "rel" ) )
734 else if( !strcmp( psz_name, "href" ) )
737 psz_href = psz_value;
746 /* "rel" and "href" must be defined */
747 if( psz_rel && psz_href )
749 if( !strcmp( psz_rel, "alternate" ) && !b_is_item &&
750 !b_is_image && !p_feed->psz_link )
752 p_feed->psz_link = psz_href;
754 /* this isn't in the rfc but i found some ... */
755 else if( ( !strcmp( psz_rel, "logo" ) ||
756 !strcmp( psz_rel, "icon" ) )
757 && !b_is_item && !b_is_image
758 && !p_feed->psz_image )
760 p_feed->psz_image = psz_href;
775 case XML_READER_ENDELEM:
777 psz_eltname = xml_ReaderName( p_xml_reader );
782 msg_Dbg( p_filter, "element end : %s", psz_eltname );
785 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
791 else if( !strcmp( psz_eltname, "image" ) )
795 FREENULL( psz_eltname );
798 case XML_READER_TEXT:
802 char *psz_eltvalue = xml_ReaderValue( p_xml_reader );
806 char *psz_clean = removeWhiteChars( psz_eltvalue );
807 free( psz_eltvalue );
808 psz_eltvalue = psz_clean;
811 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
813 /* Is it an item ? */
816 rss_item_t *p_item = p_feed->p_items+i_item;
818 if( !strcmp( psz_eltname, "title" ) && !p_item->psz_title )
820 p_item->psz_title = psz_eltvalue;
822 else if( !strcmp( psz_eltname, "link" ) /* rss */
823 && !p_item->psz_link )
825 p_item->psz_link = psz_eltvalue;
828 else if( ( !strcmp( psz_eltname, "description" ) ||
829 !strcmp( psz_eltname, "summary" ) )
830 && !p_item->psz_description )
832 p_item->psz_description = psz_eltvalue;
836 free( psz_eltvalue );
839 /* Is it an image ? */
840 else if( b_is_image )
842 if( !strcmp( psz_eltname, "url" ) && !p_feed->psz_image )
843 p_feed->psz_image = psz_eltvalue;
845 free( psz_eltvalue );
850 if( !strcmp( psz_eltname, "title" ) && !p_feed->psz_title )
852 p_feed->psz_title = psz_eltvalue;
855 else if( !strcmp( psz_eltname, "link" ) && !p_feed->psz_link )
857 p_feed->psz_link = psz_eltvalue;
860 else if( ( !strcmp( psz_eltname, "description" ) ||
861 !strcmp( psz_eltname, "subtitle" ) )
862 && !p_feed->psz_description )
864 p_feed->psz_description = psz_eltvalue;
867 else if( ( !strcmp( psz_eltname, "logo" ) ||
868 !strcmp( psz_eltname, "icon" ) )
869 && !p_feed->psz_image )
871 p_feed->psz_image = psz_eltvalue;
875 free( psz_eltvalue );
892 /****************************************************************************
893 * FetchRSS (or Atom) feeds
894 ***************************************************************************/
895 static rss_feed_t* FetchRSS( filter_t *p_filter )
897 filter_sys_t *p_sys = p_filter->p_sys;
901 xml_reader_t *p_xml_reader;
903 /* These data are not modified after the creation of the module so we don't
904 need to hold the lock */
905 int i_feeds = p_sys->i_feeds;
906 bool b_images = p_sys->b_images;
908 /* Allocate a new structure */
909 rss_feed_t *p_feeds = malloc( i_feeds * sizeof( rss_feed_t ) );
913 p_xml = xml_Create( p_filter );
916 msg_Err( p_filter, "Failed to open XML parser" );
921 /* Fetch all feeds and parse them */
922 for( int i_feed = 0; i_feed < i_feeds; i_feed++ )
924 rss_feed_t *p_feed = p_feeds + i_feed;
925 rss_feed_t *p_old_feed = p_sys->p_feeds + i_feed;
927 /* Initialize the structure */
928 p_feed->psz_title = NULL;
929 p_feed->psz_description = NULL;
930 p_feed->psz_link = NULL;
931 p_feed->psz_image = NULL;
932 p_feed->p_pic = NULL;
934 p_feed->p_items = NULL;
936 p_feed->psz_url = strdup( p_old_feed->psz_url );
939 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", p_feed->psz_url );
941 p_stream = stream_UrlNew( p_filter, p_feed->psz_url );
944 msg_Err( p_filter, "Failed to open %s for reading", p_feed->psz_url );
949 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
952 msg_Err( p_filter, "Failed to open %s for parsing", p_feed->psz_url );
957 if( !ParseFeed( p_filter, p_xml_reader, p_feed ) )
960 /* If we have a image: load it if requiere */
961 if( b_images && p_feed->psz_image && !p_feed->p_pic )
963 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
966 msg_Dbg( p_filter, "done with %s RSS/Atom feed", p_feed->psz_url );
967 xml_ReaderDelete( p_xml, p_xml_reader );
968 stream_Delete( p_stream );
976 /*TODO: still a memleak */
978 xml_ReaderDelete( p_xml, p_xml_reader );
980 stream_Delete( p_stream );
987 /****************************************************************************
989 ***************************************************************************/
990 static void FreeRSS( rss_feed_t *p_feeds, int i_feeds )
992 for( int i_feed = 0; i_feed < i_feeds; i_feed++ )
994 rss_feed_t *p_feed = p_feeds+i_feed;
995 for( int i_item = 0; i_item < p_feed->i_items; i_item++ )
997 rss_item_t *p_item = p_feed->p_items+i_item;
998 free( p_item->psz_title );
999 free( p_item->psz_link );
1000 free( p_item->psz_description );
1002 free( p_feed->p_items );
1003 free( p_feed->psz_title);
1004 free( p_feed->psz_link );
1005 free( p_feed->psz_description );
1006 free( p_feed->psz_image );
1007 if( p_feed->p_pic != NULL )
1008 picture_Release( p_feed->p_pic );
1009 free( p_feed->psz_url );
1014 static void Fetch( void *p_data )
1016 filter_t *p_filter = p_data;
1017 filter_sys_t *p_sys = p_filter->p_sys;
1019 rss_feed_t *p_feeds = FetchRSS( p_filter );
1020 rss_feed_t *p_old_feeds = p_sys->p_feeds;
1025 vlc_mutex_lock( &p_sys->lock );
1026 p_sys->p_feeds = p_feeds;
1027 p_sys->b_fetched = true;
1028 vlc_mutex_unlock( &p_sys->lock );
1031 FreeRSS( p_old_feeds, p_sys->i_feeds );