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 *****************************************************************************/
32 #include <stdlib.h> /* malloc(), free() */
38 #include "vlc_filter.h"
39 #include "vlc_block.h"
42 #include "vlc_block.h"
43 #include "vlc_stream.h"
45 #include <vlc_charset.h>
47 #include "vlc_image.h"
49 /*****************************************************************************
51 *****************************************************************************/
52 static int CreateFilter ( vlc_object_t * );
53 static void DestroyFilter( vlc_object_t * );
54 static subpicture_t *Filter( filter_t *, mtime_t );
56 static int FetchRSS( filter_t * );
57 static void FreeRSS( filter_t * );
59 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
60 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
61 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
62 0x00000080, 0x000000FF, 0x0000FFFF};
63 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
64 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
65 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
66 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
69 /*****************************************************************************
70 * filter_sys_t: rss filter descriptor
71 *****************************************************************************/
76 char *psz_description;
83 char *psz_description;
89 struct rss_item_t *p_items;
97 int i_xoff, i_yoff; /* offsets for the display string in the video window */
98 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
102 char *psz_marquee; /* marquee string */
104 text_style_t *p_style; /* font control */
110 struct rss_feed_t *p_feeds;
113 time_t t_last_update;
122 #define MSG_TEXT N_("Feed URLs")
123 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) seperated URLs.")
124 #define SPEED_TEXT N_("Speed of feeds")
125 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
126 #define LENGTH_TEXT N_("Max length")
127 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
129 #define TTL_TEXT N_("Refresh time")
130 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
131 "of the feeds. 0 means that the feeds are never updated." )
132 #define IMAGE_TEXT N_("Feed images")
133 #define IMAGE_LONGTEXT N_("Display feed images if available.")
135 #define POSX_TEXT N_("X offset")
136 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
137 #define POSY_TEXT N_("Y offset")
138 #define POSY_LONGTEXT N_("Y offset, down from the top." )
139 #define OPACITY_TEXT N_("Opacity")
140 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
141 "overlay text. 0 = transparent, 255 = totally opaque." )
143 #define SIZE_TEXT N_("Font size, pixels")
144 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
147 #define COLOR_TEXT N_("Color")
148 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
149 "the video. This must be an hexadecimal (like HTML colors). The first two "\
150 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
151 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
153 #define POS_TEXT N_("Text position")
154 #define POS_LONGTEXT N_( \
155 "You can enforce the text position on the video " \
156 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
157 "also use combinations of these values, eg 6 = top-right).")
159 #define TITLE_TEXT N_("Title display mode")
160 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
162 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
163 static const char *ppsz_pos_descriptions[] =
164 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
165 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
173 static int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
174 static const char *ppsz_title_modes[] =
175 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
177 #define CFG_PREFIX "rss-"
179 /*****************************************************************************
181 *****************************************************************************/
183 set_capability( "sub filter", 0 );
184 set_shortname( "RSS / Atom" );
185 set_callbacks( CreateFilter, DestroyFilter );
186 set_category( CAT_VIDEO );
187 set_subcategory( SUBCAT_VIDEO_SUBPIC );
188 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, VLC_FALSE );
190 set_section( N_("Position"), NULL );
191 add_integer( CFG_PREFIX "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
192 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
193 add_integer( CFG_PREFIX "position", 5, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
194 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
196 set_section( N_("Font"), NULL );
197 /* 5 sets the default to top [1] left [4] */
198 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
199 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
200 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
202 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
203 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
205 set_section( N_("Misc"), NULL );
206 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
208 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
210 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_FALSE );
211 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, VLC_FALSE );
212 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, VLC_FALSE );
213 change_integer_list( pi_title_modes, ppsz_title_modes, 0 );
215 set_description( _("RSS and Atom feed display") );
216 add_shortcut( "rss" );
217 add_shortcut( "atom" );
220 static const char *ppsz_filter_options[] = {
221 "urls", "x", "y", "position", "color", "size", "speed", "length",
222 "ttl", "images", "title", NULL
225 /*****************************************************************************
226 * CreateFilter: allocates RSS video filter
227 *****************************************************************************/
228 static int CreateFilter( vlc_object_t *p_this )
230 filter_t *p_filter = (filter_t *)p_this;
234 /* Allocate structure */
235 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
238 msg_Err( p_filter, "out of memory" );
242 vlc_mutex_init( p_filter, &p_sys->lock );
243 vlc_mutex_lock( &p_sys->lock );
245 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
248 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
249 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
250 p_sys->i_cur_feed = 0;
251 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
252 p_sys->i_cur_char = 0;
254 p_sys->p_feeds = NULL;
255 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
256 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
257 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
258 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
259 p_sys->psz_marquee = (char *)malloc( p_sys->i_length + 1 );
260 p_sys->psz_marquee[p_sys->i_length] = '\0';
262 p_sys->p_style = malloc( sizeof( text_style_t ));
263 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
265 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
266 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
267 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
268 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
269 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
270 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
272 if( p_sys->b_images == VLC_TRUE && p_sys->p_style->i_font_size == -1 )
274 msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
277 if( FetchRSS( p_filter ) )
279 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
280 vlc_mutex_unlock( &p_sys->lock );
283 p_sys->t_last_update = time( NULL );
285 if( p_sys->i_feeds == 0 )
287 vlc_mutex_unlock( &p_sys->lock );
290 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
291 if( p_sys->p_feeds[i_feed].i_items == 0 )
293 vlc_mutex_unlock( &p_sys->lock );
298 p_filter->pf_sub_filter = Filter;
299 p_sys->last_date = (mtime_t)0;
301 vlc_mutex_unlock( &p_sys->lock );
305 /*****************************************************************************
306 * DestroyFilter: destroy RSS video filter
307 *****************************************************************************/
308 static void DestroyFilter( vlc_object_t *p_this )
310 filter_t *p_filter = (filter_t *)p_this;
311 filter_sys_t *p_sys = p_filter->p_sys;
313 vlc_mutex_lock( &p_sys->lock );
315 if( p_sys->p_style ) free( p_sys->p_style );
316 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
317 free( p_sys->psz_urls );
319 vlc_mutex_unlock( &p_sys->lock );
320 vlc_mutex_destroy( &p_sys->lock );
323 /* Delete the RSS variables */
324 var_Destroy( p_filter, CFG_PREFIX "urls" );
325 var_Destroy( p_filter, CFG_PREFIX "speed" );
326 var_Destroy( p_filter, CFG_PREFIX "length" );
327 var_Destroy( p_filter, CFG_PREFIX "ttl" );
328 var_Destroy( p_filter, CFG_PREFIX "images" );
329 var_Destroy( p_filter, CFG_PREFIX "x" );
330 var_Destroy( p_filter, CFG_PREFIX "y" );
331 var_Destroy( p_filter, CFG_PREFIX "position" );
332 var_Destroy( p_filter, CFG_PREFIX "color");
333 var_Destroy( p_filter, CFG_PREFIX "opacity");
334 var_Destroy( p_filter, CFG_PREFIX "size");
335 var_Destroy( p_filter, CFG_PREFIX "title" );
338 /****************************************************************************
339 * Filter: the whole thing
340 ****************************************************************************
341 * This function outputs subpictures at regular time intervals.
342 ****************************************************************************/
343 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
345 filter_sys_t *p_sys = p_filter->p_sys;
348 subpicture_region_t *p_region;
352 struct rss_feed_t *p_feed;
354 memset( &fmt, 0, sizeof(video_format_t) );
356 vlc_mutex_lock( &p_sys->lock );
359 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
360 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
361 * p_sys->i_speed > date )
363 vlc_mutex_unlock( &p_sys->lock );
367 /* Do we need to update the feeds ? */
369 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
371 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
372 if( FetchRSS( p_filter ) )
374 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
375 vlc_mutex_unlock( &p_sys->lock );
376 return NULL; /* FIXME : we most likely messed up all the data,
377 * so we might need to do something about it */
379 p_sys->t_last_update = time( NULL );
382 p_sys->last_date = date;
384 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 )
386 p_sys->i_cur_char = 0;
388 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
390 if( p_sys->i_title == scroll_title )
391 p_sys->i_cur_item = -1;
393 p_sys->i_cur_item = 0;
394 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
398 p_spu = p_filter->pf_sub_buffer_new( p_filter );
401 vlc_mutex_unlock( &p_sys->lock );
405 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
407 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
408 if( !p_spu->p_region )
410 p_filter->pf_sub_buffer_del( p_filter, p_spu );
411 vlc_mutex_unlock( &p_sys->lock );
415 /* Generate the string that will be displayed. This string is supposed to
416 be p_sys->i_length characters long. */
417 i_item = p_sys->i_cur_item;
418 i_feed = p_sys->i_cur_feed;
419 p_feed = &p_sys->p_feeds[i_feed];
421 if( ( p_feed->p_pic && p_sys->i_title == default_title )
422 || p_sys->i_title == hide_title )
424 /* Don't display the feed's title if we have an image */
425 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
426 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
427 +p_sys->i_cur_char );
429 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
430 || p_sys->i_title == prepend_title )
432 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
433 p_sys->p_feeds[i_feed].psz_title,
434 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
435 +p_sys->i_cur_char );
437 else /* scrolling title */
440 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
441 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
442 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
444 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
445 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
446 +p_sys->i_cur_char );
449 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
452 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
453 snprintf( strchr( p_sys->psz_marquee, 0 ),
454 p_sys->i_length - strlen( p_sys->psz_marquee ),
456 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
459 /* Calls to snprintf might split multibyte UTF8 chars ...
460 * which freetype doesn't like. */
462 char *a = strdup( p_sys->psz_marquee );
464 char *b = p_sys->psz_marquee;
465 EnsureUTF8( p_sys->psz_marquee );
466 /* we want to use ' ' instead of '?' for erroneous chars */
469 if( *b != *a ) *b = ' ';
475 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
476 if( p_sys->p_style->i_font_size > 0 )
477 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
478 p_spu->i_start = date;
480 p_spu->b_ephemer = VLC_TRUE;
482 /* where to locate the string: */
483 if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
484 { /* set to one of the 9 relative locations */
485 p_spu->p_region->i_align = p_sys->i_pos;
488 p_spu->b_absolute = VLC_FALSE;
491 { /* set to an absolute xy, referenced to upper left corner */
492 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
493 p_spu->i_x = p_sys->i_xoff;
494 p_spu->i_y = p_sys->i_yoff;
495 p_spu->b_absolute = VLC_TRUE;
499 p_spu->p_region->p_style = p_sys->p_style;
503 /* Display the feed's image */
504 picture_t *p_pic = p_feed->p_pic;
505 video_format_t fmt_out;
507 memset( &fmt_out, 0, sizeof(video_format_t) );
509 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
510 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
511 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
513 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
515 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
517 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
520 msg_Err( p_filter, "cannot allocate SPU region" );
524 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
525 p_spu->p_region->p_next = p_region;
528 /* Offset text to display right next to the image */
529 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
532 vlc_mutex_unlock( &p_sys->lock );
536 /****************************************************************************
537 * RSS related functions
538 ****************************************************************************
539 * You should always lock the p_filter mutex before using any of these
541 ***************************************************************************/
543 #undef LoadImage /* do not conflict with Win32 API */
545 /****************************************************************************
546 * download and resize image located at psz_url
547 ***************************************************************************/
548 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
550 filter_sys_t *p_sys = p_filter->p_sys;
551 video_format_t fmt_in;
552 video_format_t fmt_out;
554 picture_t *p_pic = NULL;
555 image_handler_t *p_handler = image_HandlerCreate( p_filter );
557 memset( &fmt_in, 0, sizeof(video_format_t) );
558 memset( &fmt_out, 0, sizeof(video_format_t) );
560 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
561 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
565 msg_Warn( p_filter, "Unable to read image %s", psz_url );
567 else if( p_sys->p_style->i_font_size > 0 )
570 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
571 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
572 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
573 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
574 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
575 fmt_out.i_height = p_sys->p_style->i_font_size;
577 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
578 p_orig->pf_release( p_orig );
581 msg_Warn( p_filter, "Error while converting %s", psz_url );
589 image_HandlerDelete( p_handler );
594 /****************************************************************************
595 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
597 ***************************************************************************/
598 static char *removeWhiteChars( char *psz_src )
600 char *psz_src2 = strdup( psz_src );
601 char *psz_clean = strdup( psz_src2 );
604 while( ( *psz_clean == ' ' || *psz_clean == '\t'
605 || *psz_clean == '\n' || *psz_clean == '\r' )
606 && *psz_clean != '\0' )
610 i = strlen( psz_clean );
612 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
613 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
614 psz_clean[i+1] = '\0';
615 psz_clean2 = strdup( psz_clean );
620 /****************************************************************************
621 * FetchRSS (or Atom) feeds
622 ***************************************************************************/
623 static int FetchRSS( filter_t *p_filter)
625 filter_sys_t *p_sys = p_filter->p_sys;
627 stream_t *p_stream = NULL;
629 xml_reader_t *p_xml_reader = NULL;
631 char *psz_eltname = NULL;
632 char *psz_eltvalue = NULL;
633 char *psz_feed = NULL;
634 char *psz_buffer = NULL;
635 char *psz_buffer_2 = NULL;
639 vlc_bool_t b_is_item;
640 vlc_bool_t b_is_image;
646 while( p_sys->psz_urls[i_int] != 0 )
647 if( p_sys->psz_urls[i_int++] == '|' )
649 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
650 * sizeof( struct rss_feed_t ) );
652 p_xml = xml_Create( p_filter );
655 msg_Err( p_filter, "Failed to open XML parser" );
659 psz_buffer = strdup( p_sys->psz_urls );
660 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
661 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
663 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
665 if( psz_buffer == NULL ) break;
666 if( psz_buffer[0] == 0 ) psz_buffer++;
667 psz_feed = psz_buffer;
668 psz_buffer = strchr( psz_buffer, '|' );
669 if( psz_buffer != NULL ) psz_buffer[0] = 0;
671 p_feed->psz_title = NULL;
672 p_feed->psz_description = NULL;
673 p_feed->psz_link = NULL;
674 p_feed->psz_image = NULL;
675 p_feed->p_pic = NULL;
677 p_feed->p_items = NULL;
679 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
681 p_stream = stream_UrlNew( p_filter, psz_feed );
684 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
688 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
691 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
696 b_is_item = VLC_FALSE;
697 b_is_image = VLC_FALSE;
699 while( xml_ReaderRead( p_xml_reader ) == 1 )
701 switch( xml_ReaderNodeType( p_xml_reader ) )
707 case XML_READER_STARTELEM:
713 psz_eltname = xml_ReaderName( p_xml_reader );
719 msg_Dbg( p_filter, "element name: %s", psz_eltname );
721 if( !strcmp( psz_eltname, "item" ) /* rss */
722 || !strcmp( psz_eltname, "entry" ) ) /* atom */
724 b_is_item = VLC_TRUE;
726 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
727 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
728 p_feed->p_items[p_feed->i_items-1].psz_description
730 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
732 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
734 b_is_image = VLC_TRUE;
736 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
738 char *psz_href = NULL;
739 char *psz_rel = NULL;
740 while( xml_ReaderNextAttr( p_xml_reader )
743 char *psz_name = xml_ReaderName( p_xml_reader );
744 char *psz_value = xml_ReaderValue( p_xml_reader );
745 if( !strcmp( psz_name, "rel" ) )
749 else if( !strcmp( psz_name, "href" ) )
751 psz_href = psz_value;
759 if( psz_rel && psz_href )
761 if( !strcmp( psz_rel, "alternate" )
762 && b_is_item == VLC_FALSE
763 && b_is_image == VLC_FALSE
764 && !p_feed->psz_link )
766 p_feed->psz_link = psz_href;
768 /* this isn't in the rfc but i found some ... */
769 else if( ( !strcmp( psz_rel, "logo" )
770 || !strcmp( psz_rel, "icon" ) )
771 && b_is_item == VLC_FALSE
772 && b_is_image == VLC_FALSE
773 && !p_feed->psz_image )
775 p_feed->psz_image = psz_href;
784 if( psz_href ) free( psz_href );
786 if( psz_rel ) free( psz_rel );
790 case XML_READER_ENDELEM:
796 psz_eltname = xml_ReaderName( p_xml_reader );
802 msg_Dbg( p_filter, "element end : %s", psz_eltname );
804 if( !strcmp( psz_eltname, "item" ) /* rss */
805 || !strcmp( psz_eltname, "entry" ) ) /* atom */
807 b_is_item = VLC_FALSE;
810 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
812 b_is_image = VLC_FALSE;
818 case XML_READER_TEXT:
819 if( !psz_eltname ) break;
820 psz_eltvalue = xml_ReaderValue( p_xml_reader );
828 psz_clean = removeWhiteChars( psz_eltvalue );
829 free( psz_eltvalue ); psz_eltvalue = psz_clean;
832 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
834 if( b_is_item == VLC_TRUE )
836 struct rss_item_t *p_item;
837 p_item = p_feed->p_items+i_item;
838 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
839 && !p_item->psz_title )
841 p_item->psz_title = psz_eltvalue;
843 else if( !strcmp( psz_eltname, "link" ) /* rss */
844 && !p_item->psz_link )
846 p_item->psz_link = psz_eltvalue;
848 else if((!strcmp( psz_eltname, "description" ) /* rss */
849 || !strcmp( psz_eltname, "summary" ) ) /* atom */
850 && !p_item->psz_description )
852 p_item->psz_description = psz_eltvalue;
856 free( psz_eltvalue );
860 else if( b_is_image == VLC_TRUE )
862 if( !strcmp( psz_eltname, "url" ) /* rss */
863 && !p_feed->psz_image )
865 p_feed->psz_image = psz_eltvalue;
869 free( psz_eltvalue );
875 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
876 && !p_feed->psz_title )
878 p_feed->psz_title = psz_eltvalue;
880 else if( !strcmp( psz_eltname, "link" ) /* rss */
881 && !p_feed->psz_link )
883 p_feed->psz_link = psz_eltvalue;
885 else if((!strcmp( psz_eltname, "description" ) /* rss */
886 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
887 && !p_feed->psz_description )
889 p_feed->psz_description = psz_eltvalue;
891 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
892 || !strcmp( psz_eltname, "icon" ) ) /* atom */
893 && !p_feed->psz_image )
895 p_feed->psz_image = psz_eltvalue;
899 free( psz_eltvalue );
907 if( p_sys->b_images == VLC_TRUE
908 && p_feed->psz_image && !p_feed->p_pic )
910 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
913 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
914 if( p_stream ) stream_Delete( p_stream );
915 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
917 free( psz_buffer_2 );
918 if( p_xml ) xml_Delete( p_xml );
923 /****************************************************************************
925 ***************************************************************************/
926 static void FreeRSS( filter_t *p_filter)
928 filter_sys_t *p_sys = p_filter->p_sys;
930 struct rss_item_t *p_item;
931 struct rss_feed_t *p_feed;
936 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
938 p_feed = p_sys->p_feeds+i_feed;
939 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
941 p_item = p_feed->p_items+i_item;
942 free( p_item->psz_title );
943 free( p_item->psz_link );
944 free( p_item->psz_description );
946 free( p_feed->p_items );
947 free( p_feed->psz_title);
948 free( p_feed->psz_link );
949 free( p_feed->psz_description );
950 free( p_feed->psz_image );
951 if( p_feed->p_pic != NULL )
952 p_feed->p_pic->pf_release( p_feed->p_pic );
954 free( p_sys->p_feeds );