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 int FetchRSS( filter_t * );
61 static void FreeRSS( filter_t * );
62 static int ParseUrls( filter_t *, char * );
64 static const int pi_color_values[] = {
65 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
66 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
67 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
68 0x00000080, 0x000000FF, 0x0000FFFF};
69 static const char *const ppsz_color_descriptions[] = {
70 N_("Default"), N_("Black"),
71 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
72 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
73 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
76 /*****************************************************************************
77 * filter_sys_t: rss filter descriptor
78 *****************************************************************************/
83 char *psz_description;
87 typedef struct rss_feed_t
91 char *psz_description;
97 struct rss_item_t *p_items;
105 int i_xoff, i_yoff; /* offsets for the display string in the video window */
106 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
110 char *psz_marquee; /* marquee string */
112 text_style_t *p_style; /* font control */
117 struct rss_feed_t *p_feeds;
120 time_t t_last_update;
129 #define MSG_TEXT N_("Feed URLs")
130 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) separated URLs.")
131 #define SPEED_TEXT N_("Speed of feeds")
132 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
133 #define LENGTH_TEXT N_("Max length")
134 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
136 #define TTL_TEXT N_("Refresh time")
137 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
138 "of the feeds. 0 means that the feeds are never updated." )
139 #define IMAGE_TEXT N_("Feed images")
140 #define IMAGE_LONGTEXT N_("Display feed images if available.")
142 #define POSX_TEXT N_("X offset")
143 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
144 #define POSY_TEXT N_("Y offset")
145 #define POSY_LONGTEXT N_("Y offset, down from the top." )
146 #define OPACITY_TEXT N_("Opacity")
147 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
148 "overlay text. 0 = transparent, 255 = totally opaque." )
150 #define SIZE_TEXT N_("Font size, pixels")
151 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
154 #define COLOR_TEXT N_("Color")
155 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
156 "the video. This must be an hexadecimal (like HTML colors). The first two "\
157 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
158 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
160 #define POS_TEXT N_("Text position")
161 #define POS_LONGTEXT N_( \
162 "You can enforce the text position on the video " \
163 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
164 "also use combinations of these values, eg 6 = top-right).")
166 #define TITLE_TEXT N_("Title display mode")
167 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
169 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
170 static const char *const ppsz_pos_descriptions[] =
171 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
172 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
180 static const int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
181 static const char *const ppsz_title_modes[] =
182 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
184 #define CFG_PREFIX "rss-"
186 /*****************************************************************************
188 *****************************************************************************/
190 set_capability( "sub filter", 1 )
191 set_shortname( "RSS / Atom" )
192 set_callbacks( CreateFilter, DestroyFilter )
193 set_category( CAT_VIDEO )
194 set_subcategory( SUBCAT_VIDEO_SUBPIC )
195 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, false )
197 set_section( N_("Position"), NULL )
198 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, true )
199 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, true )
200 add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, false )
201 change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL )
203 set_section( N_("Font"), NULL )
204 /* 5 sets the default to top [1] left [4] */
205 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
206 OPACITY_TEXT, OPACITY_LONGTEXT, false )
207 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
209 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
210 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, false )
212 set_section( N_("Misc"), NULL )
213 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
215 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
217 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, false )
218 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, false )
219 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, false )
220 change_integer_list( pi_title_modes, ppsz_title_modes, NULL )
222 set_description( N_("RSS and Atom feed display") )
223 add_shortcut( "rss" )
224 add_shortcut( "atom" )
227 static const char *const ppsz_filter_options[] = {
228 "urls", "x", "y", "position", "color", "size", "speed", "length",
229 "ttl", "images", "title", NULL
232 /*****************************************************************************
233 * CreateFilter: allocates RSS video filter
234 *****************************************************************************/
235 static int CreateFilter( vlc_object_t *p_this )
237 filter_t *p_filter = (filter_t *)p_this;
240 int i_ret = VLC_ENOMEM;
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 psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
252 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
253 p_sys->i_cur_feed = 0;
254 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
255 p_sys->i_cur_char = 0;
257 p_sys->p_feeds = NULL;
258 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
259 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
260 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
261 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
263 p_sys->psz_marquee = malloc( p_sys->i_length + 1 );
264 if( p_sys->psz_marquee == NULL )
266 p_sys->psz_marquee[p_sys->i_length] = '\0';
268 p_sys->p_style = text_style_New();
269 if( p_sys->p_style == NULL )
272 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
273 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
274 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
275 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
276 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
277 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
279 if( p_sys->b_images == true && p_sys->p_style->i_font_size == -1 )
281 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus be displayed without being resized" );
285 ParseUrls( p_filter, psz_urls );
288 if( FetchRSS( p_filter ) )
290 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
291 text_style_Delete( p_sys->p_style );
292 i_ret = VLC_EGENERIC;
295 p_sys->t_last_update = time( NULL );
297 if( p_sys->i_feeds == 0 )
299 text_style_Delete( p_sys->p_style );
300 i_ret = VLC_EGENERIC;
303 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
305 if( p_sys->p_feeds[i_feed].i_items == 0 )
307 DestroyFilter( p_this );
313 vlc_mutex_init( &p_sys->lock );
314 p_filter->pf_sub_filter = Filter;
315 p_sys->last_date = (mtime_t)0;
320 free( p_sys->psz_marquee );
324 /*****************************************************************************
325 * DestroyFilter: destroy RSS video filter
326 *****************************************************************************/
327 static void DestroyFilter( vlc_object_t *p_this )
329 filter_t *p_filter = (filter_t *)p_this;
330 filter_sys_t *p_sys = p_filter->p_sys;
332 text_style_Delete( p_sys->p_style );
333 free( p_sys->psz_marquee );
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 = filter_NewSubpicture( p_filter );
401 vlc_mutex_unlock( &p_sys->lock );
405 fmt.i_chroma = VLC_CODEC_TEXT;
407 p_spu->p_region = subpicture_region_New( &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 = true;
482 /* where to locate the string: */
483 if( p_sys->i_pos < 0 )
484 { /* set to an absolute xy */
485 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
486 p_spu->b_absolute = true;
489 { /* set to one of the 9 relative locations */
490 p_spu->p_region->i_align = p_sys->i_pos;
491 p_spu->b_absolute = false;
494 p_spu->p_region->p_style = text_style_Duplicate( p_sys->p_style );
498 /* Display the feed's image */
499 picture_t *p_pic = p_feed->p_pic;
500 video_format_t fmt_out;
502 memset( &fmt_out, 0, sizeof(video_format_t) );
504 fmt_out.i_chroma = VLC_CODEC_YUVA;
505 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
506 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
508 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
510 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
512 p_region = subpicture_region_New( &fmt_out );
515 msg_Err( p_filter, "cannot allocate SPU region" );
519 p_region->i_x = p_sys->i_xoff;
520 p_region->i_y = p_sys->i_yoff;
521 /* FIXME the copy is probably not needed anymore */
522 picture_Copy( p_region->p_picture, p_pic );
523 p_spu->p_region->p_next = p_region;
526 /* Offset text to display right next to the image */
527 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
530 vlc_mutex_unlock( &p_sys->lock );
534 /****************************************************************************
535 * RSS related functions
536 ****************************************************************************
537 * You should always lock the p_filter mutex before using any of these
539 ***************************************************************************/
541 #undef LoadImage /* do not conflict with Win32 API */
543 /****************************************************************************
544 * download and resize image located at psz_url
545 ***************************************************************************/
546 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
548 filter_sys_t *p_sys = p_filter->p_sys;
549 video_format_t fmt_in;
550 video_format_t fmt_out;
552 picture_t *p_pic = NULL;
553 image_handler_t *p_handler = image_HandlerCreate( p_filter );
555 memset( &fmt_in, 0, sizeof(video_format_t) );
556 memset( &fmt_out, 0, sizeof(video_format_t) );
558 fmt_out.i_chroma = VLC_CODEC_YUVA;
559 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
563 msg_Warn( p_filter, "Unable to read image %s", psz_url );
565 else if( p_sys->p_style->i_font_size > 0 )
568 fmt_in.i_chroma = VLC_CODEC_YUVA;
569 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
570 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
571 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
572 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
573 fmt_out.i_height = p_sys->p_style->i_font_size;
575 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
576 picture_Release( p_orig );
579 msg_Warn( p_filter, "Error while converting %s", psz_url );
587 image_HandlerDelete( p_handler );
592 /****************************************************************************
593 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
595 ***************************************************************************/
596 static char *removeWhiteChars( const char *psz_src )
598 char *psz_src2,*psz_clean, *psz_clean2;
599 psz_src2 = psz_clean = strdup( psz_src );
602 while( ( *psz_clean == ' ' || *psz_clean == '\t'
603 || *psz_clean == '\n' || *psz_clean == '\r' )
604 && *psz_clean != '\0' )
608 i = strlen( psz_clean );
610 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
611 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
612 psz_clean[i+1] = '\0';
613 psz_clean2 = strdup( psz_clean );
619 /****************************************************************************
620 * Parse url list, psz_urls must be non empty
621 ***************************************************************************/
622 static int ParseUrls( filter_t *p_filter, char *psz_urls )
624 filter_sys_t *p_sys = p_filter->p_sys;
625 char *psz_urls2 = psz_urls;
629 /* Count the number of feeds */
632 if( *psz_urls == '|' )
637 /* Allocate the structure */
638 p_sys->p_feeds = malloc( p_sys->i_feeds * sizeof( rss_feed_t ) );
639 if( !p_sys->p_feeds )
642 /* Loop on all urls and fill in the struct */
643 psz_urls = psz_urls2;
644 for( int i = 0; i < p_sys->i_feeds; i++ )
646 rss_feed_t* p_feed = p_sys->p_feeds + i;
649 if( i < p_sys->i_feeds - 1 )
651 psz_end = strchr( psz_urls, '|' );
658 p_feed->p_items = NULL;
659 p_feed->psz_title = NULL;
660 p_feed->psz_link = NULL;
661 p_feed->psz_description = NULL;
662 p_feed->psz_image = NULL;
663 p_feed->p_pic = NULL;
664 p_feed->psz_url = strdup( psz_urls );
666 psz_urls = psz_end + 1;
673 /****************************************************************************
674 * FetchRSS (or Atom) feeds
675 ***************************************************************************/
676 static int FetchRSS( filter_t *p_filter)
678 filter_sys_t *p_sys = p_filter->p_sys;
680 stream_t *p_stream = NULL;
682 xml_reader_t *p_xml_reader = NULL;
684 char *psz_eltname = NULL;
685 char *psz_eltvalue = NULL;
692 p_xml = xml_Create( p_filter );
695 msg_Err( p_filter, "Failed to open XML parser" );
699 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
701 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
703 FREENULL( p_feed->psz_title );
704 FREENULL( p_feed->psz_description );
705 FREENULL( p_feed->psz_link );
706 FREENULL( p_feed->psz_image );
709 picture_Release( p_feed->p_pic );
710 p_feed->p_pic = NULL;
712 for( int i = 0; i < p_feed->i_items; i++ )
714 struct rss_item_t *p_item = p_feed->p_items + i;
715 free( p_item->psz_title );
716 free( p_item->psz_link );
717 free( p_item->psz_description );
720 FREENULL( p_feed->p_items );
722 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", p_feed->psz_url );
724 p_stream = stream_UrlNew( p_filter, p_feed->psz_url );
727 msg_Err( p_filter, "Failed to open %s for reading", p_feed->psz_url );
732 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
735 msg_Err( p_filter, "Failed to open %s for parsing", p_feed->psz_url );
744 while( xml_ReaderRead( p_xml_reader ) == 1 )
746 switch( xml_ReaderNodeType( p_xml_reader ) )
752 case XML_READER_STARTELEM:
754 psz_eltname = xml_ReaderName( p_xml_reader );
760 msg_Dbg( p_filter, "element name: %s", psz_eltname );
762 if( !strcmp( psz_eltname, "item" ) /* rss */
763 || !strcmp( psz_eltname, "entry" ) ) /* atom */
767 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
768 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
769 p_feed->p_items[p_feed->i_items-1].psz_description
771 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
773 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
777 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
779 char *psz_href = NULL;
780 char *psz_rel = NULL;
781 while( xml_ReaderNextAttr( p_xml_reader )
784 char *psz_name = xml_ReaderName( p_xml_reader );
785 char *psz_value = xml_ReaderValue( p_xml_reader );
786 if( !strcmp( psz_name, "rel" ) )
790 msg_Dbg( p_filter, "\"rel\" attribute of link atom duplicated (last value: %s)", psz_value );
795 else if( !strcmp( psz_name, "href" ) )
799 msg_Dbg( p_filter, "\"href\" attribute of link atom duplicated (last value: %s)", psz_href );
802 psz_href = psz_value;
810 if( psz_rel && psz_href )
812 if( !strcmp( psz_rel, "alternate" )
813 && b_is_item == false
814 && b_is_image == false
815 && !p_feed->psz_link )
817 p_feed->psz_link = psz_href;
819 /* this isn't in the rfc but i found some ... */
820 else if( ( !strcmp( psz_rel, "logo" )
821 || !strcmp( psz_rel, "icon" ) )
822 && b_is_item == false
823 && b_is_image == false
824 && !p_feed->psz_image )
826 p_feed->psz_image = psz_href;
841 case XML_READER_ENDELEM:
844 psz_eltname = xml_ReaderName( p_xml_reader );
850 msg_Dbg( p_filter, "element end : %s", psz_eltname );
852 if( !strcmp( psz_eltname, "item" ) /* rss */
853 || !strcmp( psz_eltname, "entry" ) ) /* atom */
858 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
866 case XML_READER_TEXT:
867 if( !psz_eltname ) break;
868 psz_eltvalue = xml_ReaderValue( p_xml_reader );
876 psz_clean = removeWhiteChars( psz_eltvalue );
877 free( psz_eltvalue ); psz_eltvalue = psz_clean;
880 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
882 if( b_is_item == true )
884 struct rss_item_t *p_item;
885 p_item = p_feed->p_items+i_item;
886 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
887 && !p_item->psz_title )
889 p_item->psz_title = psz_eltvalue;
891 else if( !strcmp( psz_eltname, "link" ) /* rss */
892 && !p_item->psz_link )
894 p_item->psz_link = psz_eltvalue;
896 else if((!strcmp( psz_eltname, "description" ) /* rss */
897 || !strcmp( psz_eltname, "summary" ) ) /* atom */
898 && !p_item->psz_description )
900 p_item->psz_description = psz_eltvalue;
904 free( psz_eltvalue );
908 else if( b_is_image == true )
910 if( !strcmp( psz_eltname, "url" ) /* rss */
911 && !p_feed->psz_image )
913 p_feed->psz_image = psz_eltvalue;
917 free( psz_eltvalue );
923 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
924 && !p_feed->psz_title )
926 p_feed->psz_title = psz_eltvalue;
928 else if( !strcmp( psz_eltname, "link" ) /* rss */
929 && !p_feed->psz_link )
931 p_feed->psz_link = psz_eltvalue;
933 else if((!strcmp( psz_eltname, "description" ) /* rss */
934 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
935 && !p_feed->psz_description )
937 p_feed->psz_description = psz_eltvalue;
939 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
940 || !strcmp( psz_eltname, "icon" ) ) /* atom */
941 && !p_feed->psz_image )
943 p_feed->psz_image = psz_eltvalue;
947 free( psz_eltvalue );
955 if( p_sys->b_images == true
956 && p_feed->psz_image && !p_feed->p_pic )
958 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
961 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
962 if( p_stream ) stream_Delete( p_stream );
963 msg_Dbg( p_filter, "done with %s RSS/Atom feed", p_feed->psz_url );
965 if( p_xml ) xml_Delete( p_xml );
970 /****************************************************************************
972 ***************************************************************************/
973 static void FreeRSS( filter_t *p_filter)
975 filter_sys_t *p_sys = p_filter->p_sys;
977 struct rss_item_t *p_item;
978 struct rss_feed_t *p_feed;
983 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
985 p_feed = p_sys->p_feeds+i_feed;
986 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
988 p_item = p_feed->p_items+i_item;
989 free( p_item->psz_title );
990 free( p_item->psz_link );
991 free( p_item->psz_description );
993 free( p_feed->p_items );
994 free( p_feed->psz_title);
995 free( p_feed->psz_link );
996 free( p_feed->psz_description );
997 free( p_feed->psz_image );
998 if( p_feed->p_pic != NULL )
999 picture_Release( p_feed->p_pic );
1000 free( p_feed->psz_url );
1002 free( p_sys->p_feeds );