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_filter.h"
38 #include "vlc_block.h"
41 #include "vlc_block.h"
42 #include "vlc_stream.h"
44 #include <vlc_charset.h>
46 #include "vlc_image.h"
48 /*****************************************************************************
50 *****************************************************************************/
51 static int CreateFilter ( vlc_object_t * );
52 static void DestroyFilter( vlc_object_t * );
53 static subpicture_t *Filter( filter_t *, mtime_t );
55 static int FetchRSS( filter_t * );
56 static void FreeRSS( filter_t * );
58 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
59 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
60 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
61 0x00000080, 0x000000FF, 0x0000FFFF};
62 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
63 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
64 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
65 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
68 /*****************************************************************************
69 * filter_sys_t: rss filter descriptor
70 *****************************************************************************/
75 char *psz_description;
82 char *psz_description;
88 struct rss_item_t *p_items;
96 int i_xoff, i_yoff; /* offsets for the display string in the video window */
97 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
101 char *psz_marquee; /* marquee string */
103 text_style_t *p_style; /* font control */
109 struct rss_feed_t *p_feeds;
112 time_t t_last_update;
121 #define MSG_TEXT N_("Feed URLs")
122 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) seperated URLs.")
123 #define SPEED_TEXT N_("Speed of feeds")
124 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
125 #define LENGTH_TEXT N_("Max length")
126 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
128 #define TTL_TEXT N_("Refresh time")
129 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
130 "of the feeds. 0 means that the feeds are never updated." )
131 #define IMAGE_TEXT N_("Feed images")
132 #define IMAGE_LONGTEXT N_("Display feed images if available.")
134 #define POSX_TEXT N_("X offset")
135 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
136 #define POSY_TEXT N_("Y offset")
137 #define POSY_LONGTEXT N_("Y offset, down from the top." )
138 #define OPACITY_TEXT N_("Opacity")
139 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
140 "overlay text. 0 = transparent, 255 = totally opaque." )
142 #define SIZE_TEXT N_("Font size, pixels")
143 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
146 #define COLOR_TEXT N_("Color")
147 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
148 "the video. This must be an hexadecimal (like HTML colors). The first two "\
149 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
150 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
152 #define POS_TEXT N_("Text position")
153 #define POS_LONGTEXT N_( \
154 "You can enforce the text position on the video " \
155 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
156 "also use combinations of these values, eg 6 = top-right).")
158 #define TITLE_TEXT N_("Title display mode")
159 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
161 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
162 static const char *ppsz_pos_descriptions[] =
163 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
164 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
172 static int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
173 static const char *ppsz_title_modes[] =
174 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
176 #define CFG_PREFIX "rss-"
178 /*****************************************************************************
180 *****************************************************************************/
182 set_capability( "sub filter", 0 );
183 set_shortname( "RSS / Atom" );
184 set_callbacks( CreateFilter, DestroyFilter );
185 set_category( CAT_VIDEO );
186 set_subcategory( SUBCAT_VIDEO_SUBPIC );
187 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, VLC_FALSE );
189 set_section( N_("Position"), NULL );
190 add_integer( CFG_PREFIX "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
191 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
192 add_integer( CFG_PREFIX "position", 5, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
193 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
195 set_section( N_("Font"), NULL );
196 /* 5 sets the default to top [1] left [4] */
197 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
198 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
199 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
201 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
202 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
204 set_section( N_("Misc"), NULL );
205 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
207 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
209 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_FALSE );
210 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, VLC_FALSE );
211 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, VLC_FALSE );
212 change_integer_list( pi_title_modes, ppsz_title_modes, 0 );
214 set_description( _("RSS and Atom feed display") );
215 add_shortcut( "rss" );
216 add_shortcut( "atom" );
219 static const char *ppsz_filter_options[] = {
220 "urls", "x", "y", "position", "color", "size", "speed", "length",
221 "ttl", "images", "title", NULL
224 /*****************************************************************************
225 * CreateFilter: allocates RSS video filter
226 *****************************************************************************/
227 static int CreateFilter( vlc_object_t *p_this )
229 filter_t *p_filter = (filter_t *)p_this;
233 /* Allocate structure */
234 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
237 msg_Err( p_filter, "out of memory" );
241 vlc_mutex_init( p_filter, &p_sys->lock );
242 vlc_mutex_lock( &p_sys->lock );
244 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
247 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
248 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
249 p_sys->i_cur_feed = 0;
250 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
251 p_sys->i_cur_char = 0;
253 p_sys->p_feeds = NULL;
254 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
255 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
256 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
257 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
258 p_sys->psz_marquee = (char *)malloc( p_sys->i_length + 1 );
259 p_sys->psz_marquee[p_sys->i_length] = '\0';
261 p_sys->p_style = malloc( sizeof( text_style_t ));
262 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
264 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
265 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
266 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
267 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
268 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
269 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
271 if( p_sys->b_images == VLC_TRUE && p_sys->p_style->i_font_size == -1 )
273 msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
276 if( FetchRSS( p_filter ) )
278 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
279 vlc_mutex_unlock( &p_sys->lock );
282 p_sys->t_last_update = time( NULL );
284 if( p_sys->i_feeds == 0 )
286 vlc_mutex_unlock( &p_sys->lock );
289 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
290 if( p_sys->p_feeds[i_feed].i_items == 0 )
292 vlc_mutex_unlock( &p_sys->lock );
297 p_filter->pf_sub_filter = Filter;
298 p_sys->last_date = (mtime_t)0;
300 vlc_mutex_unlock( &p_sys->lock );
304 /*****************************************************************************
305 * DestroyFilter: destroy RSS video filter
306 *****************************************************************************/
307 static void DestroyFilter( vlc_object_t *p_this )
309 filter_t *p_filter = (filter_t *)p_this;
310 filter_sys_t *p_sys = p_filter->p_sys;
312 vlc_mutex_lock( &p_sys->lock );
314 if( p_sys->p_style ) free( p_sys->p_style );
315 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
316 free( p_sys->psz_urls );
318 vlc_mutex_unlock( &p_sys->lock );
319 vlc_mutex_destroy( &p_sys->lock );
322 /* Delete the RSS variables */
323 var_Destroy( p_filter, CFG_PREFIX "urls" );
324 var_Destroy( p_filter, CFG_PREFIX "speed" );
325 var_Destroy( p_filter, CFG_PREFIX "length" );
326 var_Destroy( p_filter, CFG_PREFIX "ttl" );
327 var_Destroy( p_filter, CFG_PREFIX "images" );
328 var_Destroy( p_filter, CFG_PREFIX "x" );
329 var_Destroy( p_filter, CFG_PREFIX "y" );
330 var_Destroy( p_filter, CFG_PREFIX "position" );
331 var_Destroy( p_filter, CFG_PREFIX "color");
332 var_Destroy( p_filter, CFG_PREFIX "opacity");
333 var_Destroy( p_filter, CFG_PREFIX "size");
334 var_Destroy( p_filter, CFG_PREFIX "title" );
337 /****************************************************************************
338 * Filter: the whole thing
339 ****************************************************************************
340 * This function outputs subpictures at regular time intervals.
341 ****************************************************************************/
342 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
344 filter_sys_t *p_sys = p_filter->p_sys;
347 subpicture_region_t *p_region;
351 struct rss_feed_t *p_feed;
353 memset( &fmt, 0, sizeof(video_format_t) );
355 vlc_mutex_lock( &p_sys->lock );
358 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
359 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
360 * p_sys->i_speed > date )
362 vlc_mutex_unlock( &p_sys->lock );
366 /* Do we need to update the feeds ? */
368 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
370 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
371 if( FetchRSS( p_filter ) )
373 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
374 vlc_mutex_unlock( &p_sys->lock );
375 return NULL; /* FIXME : we most likely messed up all the data,
376 * so we might need to do something about it */
378 p_sys->t_last_update = time( NULL );
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 = p_filter->pf_sub_buffer_new( p_filter );
400 vlc_mutex_unlock( &p_sys->lock );
404 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
406 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &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 = VLC_TRUE;
481 /* where to locate the string: */
482 if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
483 { /* set to one of the 9 relative locations */
484 p_spu->p_region->i_align = p_sys->i_pos;
487 p_spu->b_absolute = VLC_FALSE;
490 { /* set to an absolute xy, referenced to upper left corner */
491 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
492 p_spu->i_x = p_sys->i_xoff;
493 p_spu->i_y = p_sys->i_yoff;
494 p_spu->b_absolute = VLC_TRUE;
498 p_spu->p_region->p_style = p_sys->p_style;
502 /* Display the feed's image */
503 picture_t *p_pic = p_feed->p_pic;
504 video_format_t fmt_out;
506 memset( &fmt_out, 0, sizeof(video_format_t) );
508 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
509 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
510 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
512 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
514 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
516 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
519 msg_Err( p_filter, "cannot allocate SPU region" );
523 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
524 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 = p_pic->p[Y_PLANE].i_visible_pitch;
531 vlc_mutex_unlock( &p_sys->lock );
535 /****************************************************************************
536 * RSS related functions
537 ****************************************************************************
538 * You should always lock the p_filter mutex before using any of these
540 ***************************************************************************/
542 #undef LoadImage /* do not conflict with Win32 API */
544 /****************************************************************************
545 * download and resize image located at psz_url
546 ***************************************************************************/
547 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
549 filter_sys_t *p_sys = p_filter->p_sys;
550 video_format_t fmt_in;
551 video_format_t fmt_out;
553 picture_t *p_pic = NULL;
554 image_handler_t *p_handler = image_HandlerCreate( p_filter );
556 memset( &fmt_in, 0, sizeof(video_format_t) );
557 memset( &fmt_out, 0, sizeof(video_format_t) );
559 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
560 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
564 msg_Warn( p_filter, "Unable to read image %s", psz_url );
566 else if( p_sys->p_style->i_font_size > 0 )
569 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
570 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
571 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
572 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
573 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
574 fmt_out.i_height = p_sys->p_style->i_font_size;
576 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
577 p_orig->pf_release( p_orig );
580 msg_Warn( p_filter, "Error while converting %s", psz_url );
588 image_HandlerDelete( p_handler );
593 /****************************************************************************
594 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
596 ***************************************************************************/
597 static char *removeWhiteChars( char *psz_src )
599 char *psz_src2 = strdup( psz_src );
600 char *psz_clean = strdup( psz_src2 );
603 while( ( *psz_clean == ' ' || *psz_clean == '\t'
604 || *psz_clean == '\n' || *psz_clean == '\r' )
605 && *psz_clean != '\0' )
609 i = strlen( psz_clean );
611 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
612 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
613 psz_clean[i+1] = '\0';
614 psz_clean2 = strdup( psz_clean );
619 /****************************************************************************
620 * FetchRSS (or Atom) feeds
621 ***************************************************************************/
622 static int FetchRSS( filter_t *p_filter)
624 filter_sys_t *p_sys = p_filter->p_sys;
626 stream_t *p_stream = NULL;
628 xml_reader_t *p_xml_reader = NULL;
630 char *psz_eltname = NULL;
631 char *psz_eltvalue = NULL;
632 char *psz_feed = NULL;
633 char *psz_buffer = NULL;
634 char *psz_buffer_2 = NULL;
638 vlc_bool_t b_is_item;
639 vlc_bool_t b_is_image;
645 while( p_sys->psz_urls[i_int] != 0 )
646 if( p_sys->psz_urls[i_int++] == '|' )
648 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
649 * sizeof( struct rss_feed_t ) );
651 p_xml = xml_Create( p_filter );
654 msg_Err( p_filter, "Failed to open XML parser" );
658 psz_buffer = strdup( p_sys->psz_urls );
659 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
660 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
662 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
664 if( psz_buffer == NULL ) break;
665 if( psz_buffer[0] == 0 ) psz_buffer++;
666 psz_feed = psz_buffer;
667 psz_buffer = strchr( psz_buffer, '|' );
668 if( psz_buffer != NULL ) psz_buffer[0] = 0;
670 p_feed->psz_title = NULL;
671 p_feed->psz_description = NULL;
672 p_feed->psz_link = NULL;
673 p_feed->psz_image = NULL;
674 p_feed->p_pic = NULL;
676 p_feed->p_items = NULL;
678 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
680 p_stream = stream_UrlNew( p_filter, psz_feed );
683 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
687 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
690 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
695 b_is_item = VLC_FALSE;
696 b_is_image = VLC_FALSE;
698 while( xml_ReaderRead( p_xml_reader ) == 1 )
700 switch( xml_ReaderNodeType( p_xml_reader ) )
706 case XML_READER_STARTELEM:
712 psz_eltname = xml_ReaderName( p_xml_reader );
718 msg_Dbg( p_filter, "element name: %s", psz_eltname );
720 if( !strcmp( psz_eltname, "item" ) /* rss */
721 || !strcmp( psz_eltname, "entry" ) ) /* atom */
723 b_is_item = VLC_TRUE;
725 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
726 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
727 p_feed->p_items[p_feed->i_items-1].psz_description
729 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
731 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
733 b_is_image = VLC_TRUE;
735 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
737 char *psz_href = NULL;
738 char *psz_rel = NULL;
739 while( xml_ReaderNextAttr( p_xml_reader )
742 char *psz_name = xml_ReaderName( p_xml_reader );
743 char *psz_value = xml_ReaderValue( p_xml_reader );
744 if( !strcmp( psz_name, "rel" ) )
748 else if( !strcmp( psz_name, "href" ) )
750 psz_href = psz_value;
758 if( psz_rel && psz_href )
760 if( !strcmp( psz_rel, "alternate" )
761 && b_is_item == VLC_FALSE
762 && b_is_image == VLC_FALSE
763 && !p_feed->psz_link )
765 p_feed->psz_link = psz_href;
767 /* this isn't in the rfc but i found some ... */
768 else if( ( !strcmp( psz_rel, "logo" )
769 || !strcmp( psz_rel, "icon" ) )
770 && b_is_item == VLC_FALSE
771 && b_is_image == VLC_FALSE
772 && !p_feed->psz_image )
774 p_feed->psz_image = psz_href;
783 if( psz_href ) free( psz_href );
785 if( psz_rel ) free( psz_rel );
789 case XML_READER_ENDELEM:
795 psz_eltname = xml_ReaderName( p_xml_reader );
801 msg_Dbg( p_filter, "element end : %s", psz_eltname );
803 if( !strcmp( psz_eltname, "item" ) /* rss */
804 || !strcmp( psz_eltname, "entry" ) ) /* atom */
806 b_is_item = VLC_FALSE;
809 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
811 b_is_image = VLC_FALSE;
817 case XML_READER_TEXT:
818 if( !psz_eltname ) break;
819 psz_eltvalue = xml_ReaderValue( p_xml_reader );
827 psz_clean = removeWhiteChars( psz_eltvalue );
828 free( psz_eltvalue ); psz_eltvalue = psz_clean;
831 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
833 if( b_is_item == VLC_TRUE )
835 struct rss_item_t *p_item;
836 p_item = p_feed->p_items+i_item;
837 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
838 && !p_item->psz_title )
840 p_item->psz_title = psz_eltvalue;
842 else if( !strcmp( psz_eltname, "link" ) /* rss */
843 && !p_item->psz_link )
845 p_item->psz_link = psz_eltvalue;
847 else if((!strcmp( psz_eltname, "description" ) /* rss */
848 || !strcmp( psz_eltname, "summary" ) ) /* atom */
849 && !p_item->psz_description )
851 p_item->psz_description = psz_eltvalue;
855 free( psz_eltvalue );
859 else if( b_is_image == VLC_TRUE )
861 if( !strcmp( psz_eltname, "url" ) /* rss */
862 && !p_feed->psz_image )
864 p_feed->psz_image = psz_eltvalue;
868 free( psz_eltvalue );
874 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
875 && !p_feed->psz_title )
877 p_feed->psz_title = psz_eltvalue;
879 else if( !strcmp( psz_eltname, "link" ) /* rss */
880 && !p_feed->psz_link )
882 p_feed->psz_link = psz_eltvalue;
884 else if((!strcmp( psz_eltname, "description" ) /* rss */
885 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
886 && !p_feed->psz_description )
888 p_feed->psz_description = psz_eltvalue;
890 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
891 || !strcmp( psz_eltname, "icon" ) ) /* atom */
892 && !p_feed->psz_image )
894 p_feed->psz_image = psz_eltvalue;
898 free( psz_eltvalue );
906 if( p_sys->b_images == VLC_TRUE
907 && p_feed->psz_image && !p_feed->p_pic )
909 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
912 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
913 if( p_stream ) stream_Delete( p_stream );
914 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
916 free( psz_buffer_2 );
917 if( p_xml ) xml_Delete( p_xml );
922 /****************************************************************************
924 ***************************************************************************/
925 static void FreeRSS( filter_t *p_filter)
927 filter_sys_t *p_sys = p_filter->p_sys;
929 struct rss_item_t *p_item;
930 struct rss_feed_t *p_feed;
935 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
937 p_feed = p_sys->p_feeds+i_feed;
938 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
940 p_item = p_feed->p_items+i_item;
941 free( p_item->psz_title );
942 free( p_item->psz_link );
943 free( p_item->psz_description );
945 free( p_feed->p_items );
946 free( p_feed->psz_title);
947 free( p_feed->psz_link );
948 free( p_feed->psz_description );
949 free( p_feed->psz_image );
950 if( p_feed->p_pic != NULL )
951 p_feed->p_pic->pf_release( p_feed->p_pic );
953 free( p_sys->p_feeds );