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>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Atom : http://www.ietf.org/rfc/rfc4287.txt
26 * RSS : http://www.rssboard.org/rss-specification
27 *****************************************************************************/
29 /*****************************************************************************
31 *****************************************************************************/
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
40 #include <vlc_filter.h>
41 #include <vlc_block.h>
44 #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 */
131 #define MSG_TEXT N_("Feed URLs")
132 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) separated URLs.")
133 #define SPEED_TEXT N_("Speed of feeds")
134 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
135 #define LENGTH_TEXT N_("Max length")
136 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
138 #define TTL_TEXT N_("Refresh time")
139 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
140 "of the feeds. 0 means that the feeds are never updated." )
141 #define IMAGE_TEXT N_("Feed images")
142 #define IMAGE_LONGTEXT N_("Display feed images if available.")
144 #define POSX_TEXT N_("X offset")
145 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
146 #define POSY_TEXT N_("Y offset")
147 #define POSY_LONGTEXT N_("Y offset, down from the top." )
148 #define OPACITY_TEXT N_("Opacity")
149 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
150 "overlay text. 0 = transparent, 255 = totally opaque." )
152 #define SIZE_TEXT N_("Font size, pixels")
153 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
156 #define COLOR_TEXT N_("Color")
157 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
158 "the video. This must be an hexadecimal (like HTML colors). The first two "\
159 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
160 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
162 #define POS_TEXT N_("Text position")
163 #define POS_LONGTEXT N_( \
164 "You can enforce the text position on the video " \
165 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
166 "also use combinations of these values, eg 6 = top-right).")
168 #define TITLE_TEXT N_("Title display mode")
169 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
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( "RSS / Atom" )
194 set_callbacks( CreateFilter, DestroyFilter )
195 set_category( CAT_VIDEO )
196 set_subcategory( SUBCAT_VIDEO_SUBPIC )
197 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, false )
199 set_section( N_("Position"), NULL )
200 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, true )
201 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, true )
202 add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, false )
203 change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL )
205 set_section( N_("Font"), NULL )
206 /* 5 sets the default to top [1] left [4] */
207 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
208 OPACITY_TEXT, OPACITY_LONGTEXT, false )
209 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
211 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
212 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, false )
214 set_section( N_("Misc"), NULL )
215 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
217 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
219 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, false )
220 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, false )
221 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, false )
222 change_integer_list( pi_title_modes, ppsz_title_modes, NULL )
224 set_description( N_("RSS and Atom feed display") )
225 add_shortcut( "rss" )
226 add_shortcut( "atom" )
229 static const char *const ppsz_filter_options[] = {
230 "urls", "x", "y", "position", "color", "size", "speed", "length",
231 "ttl", "images", "title", NULL
234 /*****************************************************************************
235 * CreateFilter: allocates RSS video filter
236 *****************************************************************************/
237 static int CreateFilter( vlc_object_t *p_this )
239 filter_t *p_filter = (filter_t *)p_this;
241 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 psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
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->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
262 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
264 p_sys->psz_marquee = malloc( p_sys->i_length + 1 );
265 if( p_sys->psz_marquee == NULL )
267 p_sys->psz_marquee[p_sys->i_length] = '\0';
269 p_sys->p_style = text_style_New();
270 if( p_sys->p_style == NULL )
273 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
274 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
275 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
276 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
277 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
278 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
280 if( p_sys->b_images == true && p_sys->p_style->i_font_size == -1 )
282 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus be displayed without being resized" );
286 if( ParseUrls( p_filter, psz_urls ) )
294 vlc_mutex_init( &p_sys->lock );
295 p_filter->pf_sub_filter = Filter;
296 p_sys->last_date = (mtime_t)0;
297 p_sys->b_fetched = false;
299 /* Create and arm the timer */
300 if( vlc_timer_create( &p_sys->timer, Fetch, p_filter ) )
302 vlc_mutex_destroy( &p_sys->lock );
305 vlc_timer_schedule( p_sys->timer, false, 1,
306 (mtime_t)(p_sys->i_ttl)*1000000 );
311 free( p_sys->psz_marquee );
315 /*****************************************************************************
316 * DestroyFilter: destroy RSS video filter
317 *****************************************************************************/
318 static void DestroyFilter( vlc_object_t *p_this )
320 filter_t *p_filter = (filter_t *)p_this;
321 filter_sys_t *p_sys = p_filter->p_sys;
323 vlc_timer_destroy( p_sys->timer );
324 vlc_mutex_destroy( &p_sys->lock );
326 text_style_Delete( p_sys->p_style );
327 free( p_sys->psz_marquee );
328 FreeRSS( p_sys->p_feeds, p_sys->i_feeds );
332 /****************************************************************************
333 * Filter: the whole thing
334 ****************************************************************************
335 * This function outputs subpictures at regular time intervals.
336 ****************************************************************************/
337 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
339 filter_sys_t *p_sys = p_filter->p_sys;
342 subpicture_region_t *p_region;
347 memset( &fmt, 0, sizeof(video_format_t) );
349 vlc_mutex_lock( &p_sys->lock );
351 /* Check if the feeds have been fetched and that we have some feeds */
352 /* TODO: check that we have items for each feeds */
353 if( !p_sys->b_fetched && p_sys->i_feeds > 0 )
355 vlc_mutex_unlock( &p_sys->lock );
360 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
361 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
362 * p_sys->i_speed > date )
364 vlc_mutex_unlock( &p_sys->lock );
368 p_sys->last_date = date;
370 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 )
372 p_sys->i_cur_char = 0;
374 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
376 if( p_sys->i_title == scroll_title )
377 p_sys->i_cur_item = -1;
379 p_sys->i_cur_item = 0;
380 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
384 p_spu = filter_NewSubpicture( p_filter );
387 vlc_mutex_unlock( &p_sys->lock );
391 fmt.i_chroma = VLC_CODEC_TEXT;
393 p_spu->p_region = subpicture_region_New( &fmt );
394 if( !p_spu->p_region )
396 p_filter->pf_sub_buffer_del( p_filter, p_spu );
397 vlc_mutex_unlock( &p_sys->lock );
401 /* Generate the string that will be displayed. This string is supposed to
402 be p_sys->i_length characters long. */
403 i_item = p_sys->i_cur_item;
404 i_feed = p_sys->i_cur_feed;
405 p_feed = &p_sys->p_feeds[i_feed];
407 if( ( p_feed->p_pic && p_sys->i_title == default_title )
408 || p_sys->i_title == hide_title )
410 /* Don't display the feed's title if we have an image */
411 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
412 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
413 +p_sys->i_cur_char );
415 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
416 || p_sys->i_title == prepend_title )
418 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
419 p_sys->p_feeds[i_feed].psz_title,
420 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
421 +p_sys->i_cur_char );
423 else /* scrolling title */
426 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
427 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
428 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
430 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
431 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
432 +p_sys->i_cur_char );
435 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
438 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
439 snprintf( strchr( p_sys->psz_marquee, 0 ),
440 p_sys->i_length - strlen( p_sys->psz_marquee ),
442 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
445 /* Calls to snprintf might split multibyte UTF8 chars ...
446 * which freetype doesn't like. */
448 char *a = strdup( p_sys->psz_marquee );
450 char *b = p_sys->psz_marquee;
451 EnsureUTF8( p_sys->psz_marquee );
452 /* we want to use ' ' instead of '?' for erroneous chars */
455 if( *b != *a ) *b = ' ';
461 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
462 if( p_sys->p_style->i_font_size > 0 )
463 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
464 p_spu->i_start = date;
466 p_spu->b_ephemer = true;
468 /* where to locate the string: */
469 if( p_sys->i_pos < 0 )
470 { /* set to an absolute xy */
471 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
472 p_spu->b_absolute = true;
475 { /* set to one of the 9 relative locations */
476 p_spu->p_region->i_align = p_sys->i_pos;
477 p_spu->b_absolute = false;
480 p_spu->p_region->p_style = text_style_Duplicate( p_sys->p_style );
484 /* Display the feed's image */
485 picture_t *p_pic = p_feed->p_pic;
486 video_format_t fmt_out;
488 memset( &fmt_out, 0, sizeof(video_format_t) );
490 fmt_out.i_chroma = VLC_CODEC_YUVA;
491 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
492 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
494 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
496 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
498 p_region = subpicture_region_New( &fmt_out );
501 msg_Err( p_filter, "cannot allocate SPU region" );
505 p_region->i_x = p_sys->i_xoff;
506 p_region->i_y = p_sys->i_yoff;
507 /* FIXME the copy is probably not needed anymore */
508 picture_Copy( p_region->p_picture, p_pic );
509 p_spu->p_region->p_next = p_region;
512 /* Offset text to display right next to the image */
513 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
516 vlc_mutex_unlock( &p_sys->lock );
520 /****************************************************************************
521 * RSS related functions
522 ****************************************************************************
523 * You should always lock the p_filter mutex before using any of these
525 ***************************************************************************/
527 #undef LoadImage /* do not conflict with Win32 API */
529 /****************************************************************************
530 * download and resize image located at psz_url
531 ***************************************************************************/
532 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
534 filter_sys_t *p_sys = p_filter->p_sys;
535 video_format_t fmt_in;
536 video_format_t fmt_out;
538 picture_t *p_pic = NULL;
539 image_handler_t *p_handler = image_HandlerCreate( p_filter );
541 memset( &fmt_in, 0, sizeof(video_format_t) );
542 memset( &fmt_out, 0, sizeof(video_format_t) );
544 fmt_out.i_chroma = VLC_CODEC_YUVA;
545 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
549 msg_Warn( p_filter, "Unable to read image %s", psz_url );
551 else if( p_sys->p_style->i_font_size > 0 )
554 fmt_in.i_chroma = VLC_CODEC_YUVA;
555 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
556 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
557 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
558 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
559 fmt_out.i_height = p_sys->p_style->i_font_size;
561 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
562 picture_Release( p_orig );
565 msg_Warn( p_filter, "Error while converting %s", psz_url );
573 image_HandlerDelete( p_handler );
578 /****************************************************************************
579 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
581 ***************************************************************************/
582 static char *removeWhiteChars( const char *psz_src )
584 char *psz_src2,*psz_clean, *psz_clean2;
585 psz_src2 = psz_clean = strdup( psz_src );
588 while( ( *psz_clean == ' ' || *psz_clean == '\t'
589 || *psz_clean == '\n' || *psz_clean == '\r' )
590 && *psz_clean != '\0' )
594 i = strlen( psz_clean );
596 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
597 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
598 psz_clean[i+1] = '\0';
599 psz_clean2 = strdup( psz_clean );
605 /****************************************************************************
606 * Parse url list, psz_urls must be non empty (TODO: check it !)
607 ***************************************************************************/
608 static int ParseUrls( filter_t *p_filter, char *psz_urls )
610 filter_sys_t *p_sys = p_filter->p_sys;
611 char *psz_urls2 = psz_urls;
615 /* Count the number of feeds */
618 if( *psz_urls == '|' )
623 /* Allocate the structure */
624 p_sys->p_feeds = malloc( p_sys->i_feeds * sizeof( rss_feed_t ) );
625 if( !p_sys->p_feeds )
628 /* Loop on all urls and fill in the struct */
629 psz_urls = psz_urls2;
630 for( int i = 0; i < p_sys->i_feeds; i++ )
632 rss_feed_t* p_feed = p_sys->p_feeds + i;
635 if( i < p_sys->i_feeds - 1 )
637 psz_end = strchr( psz_urls, '|' );
644 p_feed->p_items = NULL;
645 p_feed->psz_title = NULL;
646 p_feed->psz_link = NULL;
647 p_feed->psz_description = NULL;
648 p_feed->psz_image = NULL;
649 p_feed->p_pic = NULL;
650 p_feed->psz_url = strdup( psz_urls );
652 psz_urls = psz_end + 1;
660 /****************************************************************************
662 ***************************************************************************/
663 static bool ParseFeed( filter_t *p_filter, xml_reader_t *p_xml_reader,
666 VLC_UNUSED(p_filter);
667 char *psz_eltname = NULL;
669 bool b_is_item = false;
670 bool b_is_image = false;
674 while( xml_ReaderRead( p_xml_reader ) == 1 )
676 switch( xml_ReaderNodeType( p_xml_reader ) )
682 case XML_READER_STARTELEM:
684 psz_eltname = xml_ReaderName( p_xml_reader );
689 msg_Dbg( p_filter, "element name: %s", psz_eltname );
692 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
696 p_feed->p_items = realloc( p_feed->p_items,
697 p_feed->i_items * sizeof( rss_item_t ) );
698 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
699 p_feed->p_items[p_feed->i_items-1].psz_description = NULL;
700 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
703 else if( !strcmp( psz_eltname, "image" ) )
708 else if( !strcmp( psz_eltname, "link" ) )
710 char *psz_href = NULL;
711 char *psz_rel = NULL;
712 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
714 char *psz_name = xml_ReaderName( p_xml_reader );
715 char *psz_value = xml_ReaderValue( p_xml_reader );
716 if( !strcmp( psz_name, "rel" ) )
721 else if( !strcmp( psz_name, "href" ) )
724 psz_href = psz_value;
733 /* "rel" and "href" must be defined */
734 if( psz_rel && psz_href )
736 if( !strcmp( psz_rel, "alternate" ) && !b_is_item &&
737 !b_is_image && !p_feed->psz_link )
739 p_feed->psz_link = psz_href;
741 /* this isn't in the rfc but i found some ... */
742 else if( ( !strcmp( psz_rel, "logo" ) ||
743 !strcmp( psz_rel, "icon" ) )
744 && !b_is_item && !b_is_image
745 && !p_feed->psz_image )
747 p_feed->psz_image = psz_href;
762 case XML_READER_ENDELEM:
764 psz_eltname = xml_ReaderName( p_xml_reader );
769 msg_Dbg( p_filter, "element end : %s", psz_eltname );
772 if( !strcmp( psz_eltname, "item" ) || !strcmp( psz_eltname, "entry" ) )
778 else if( !strcmp( psz_eltname, "image" ) )
782 FREENULL( psz_eltname );
785 case XML_READER_TEXT:
789 char *psz_eltvalue = xml_ReaderValue( p_xml_reader );
793 char *psz_clean = removeWhiteChars( psz_eltvalue );
794 free( psz_eltvalue );
795 psz_eltvalue = psz_clean;
798 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
800 /* Is it an item ? */
803 rss_item_t *p_item = p_feed->p_items+i_item;
805 if( !strcmp( psz_eltname, "title" ) && !p_item->psz_title )
807 p_item->psz_title = psz_eltvalue;
809 else if( !strcmp( psz_eltname, "link" ) /* rss */
810 && !p_item->psz_link )
812 p_item->psz_link = psz_eltvalue;
815 else if( ( !strcmp( psz_eltname, "description" ) ||
816 !strcmp( psz_eltname, "summary" ) )
817 && !p_item->psz_description )
819 p_item->psz_description = psz_eltvalue;
823 free( psz_eltvalue );
826 /* Is it an image ? */
827 else if( b_is_image )
829 if( !strcmp( psz_eltname, "url" ) && !p_feed->psz_image )
830 p_feed->psz_image = psz_eltvalue;
832 free( psz_eltvalue );
837 if( !strcmp( psz_eltname, "title" ) && !p_feed->psz_title )
839 p_feed->psz_title = psz_eltvalue;
842 else if( !strcmp( psz_eltname, "link" ) && !p_feed->psz_link )
844 p_feed->psz_link = psz_eltvalue;
847 else if( ( !strcmp( psz_eltname, "description" ) ||
848 !strcmp( psz_eltname, "subtitle" ) )
849 && !p_feed->psz_description )
851 p_feed->psz_description = psz_eltvalue;
854 else if( ( !strcmp( psz_eltname, "logo" ) ||
855 !strcmp( psz_eltname, "icon" ) )
856 && !p_feed->psz_image )
858 p_feed->psz_image = psz_eltvalue;
862 free( psz_eltvalue );
879 /****************************************************************************
880 * FetchRSS (or Atom) feeds
881 ***************************************************************************/
882 static rss_feed_t* FetchRSS( filter_t *p_filter )
884 filter_sys_t *p_sys = p_filter->p_sys;
888 xml_reader_t *p_xml_reader;
890 /* These data are not modified after the creation of the module so we don't
891 need to hold the lock */
892 int i_feeds = p_sys->i_feeds;
893 bool b_images = p_sys->b_images;
895 /* Allocate a new structure */
896 rss_feed_t *p_feeds = malloc( i_feeds * sizeof( rss_feed_t ) );
900 p_xml = xml_Create( p_filter );
903 msg_Err( p_filter, "Failed to open XML parser" );
908 /* Fetch all feeds and parse them */
909 for( int i_feed = 0; i_feed < i_feeds; i_feed++ )
911 rss_feed_t *p_feed = p_feeds + i_feed;
912 rss_feed_t *p_old_feed = p_sys->p_feeds + i_feed;
914 /* Initialize the structure */
915 p_feed->psz_title = NULL;
916 p_feed->psz_description = NULL;
917 p_feed->psz_link = NULL;
918 p_feed->psz_image = NULL;
919 p_feed->p_pic = NULL;
921 p_feed->p_items = NULL;
923 p_feed->psz_url = strdup( p_old_feed->psz_url );
926 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", p_feed->psz_url );
928 p_stream = stream_UrlNew( p_filter, p_feed->psz_url );
931 msg_Err( p_filter, "Failed to open %s for reading", p_feed->psz_url );
936 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
939 msg_Err( p_filter, "Failed to open %s for parsing", p_feed->psz_url );
944 if( !ParseFeed( p_filter, p_xml_reader, p_feed ) )
947 /* If we have a image: load it if requiere */
948 if( b_images && p_feed->psz_image && !p_feed->p_pic )
950 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
953 msg_Dbg( p_filter, "done with %s RSS/Atom feed", p_feed->psz_url );
954 xml_ReaderDelete( p_xml, p_xml_reader );
955 stream_Delete( p_stream );
963 /*TODO: still a memleak */
965 xml_ReaderDelete( p_xml, p_xml_reader );
967 stream_Delete( p_stream );
974 /****************************************************************************
976 ***************************************************************************/
977 static void FreeRSS( rss_feed_t *p_feeds, int i_feeds )
979 for( int i_feed = 0; i_feed < i_feeds; i_feed++ )
981 rss_feed_t *p_feed = p_feeds+i_feed;
982 for( int i_item = 0; i_item < p_feed->i_items; i_item++ )
984 rss_item_t *p_item = p_feed->p_items+i_item;
985 free( p_item->psz_title );
986 free( p_item->psz_link );
987 free( p_item->psz_description );
989 free( p_feed->p_items );
990 free( p_feed->psz_title);
991 free( p_feed->psz_link );
992 free( p_feed->psz_description );
993 free( p_feed->psz_image );
994 if( p_feed->p_pic != NULL )
995 picture_Release( p_feed->p_pic );
996 free( p_feed->psz_url );
1001 static void Fetch( void *p_data )
1003 filter_t *p_filter = p_data;
1004 filter_sys_t *p_sys = p_filter->p_sys;
1006 rss_feed_t *p_feeds = FetchRSS( p_filter );
1007 rss_feed_t *p_old_feeds = p_sys->p_feeds;
1012 vlc_mutex_lock( &p_sys->lock );
1013 p_sys->p_feeds = p_feeds;
1014 p_sys->b_fetched = true;
1015 vlc_mutex_unlock( &p_sys->lock );
1018 FreeRSS( p_old_feeds, p_sys->i_feeds );