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 * );
63 static const int pi_color_values[] = {
64 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
65 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
66 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
67 0x00000080, 0x000000FF, 0x0000FFFF};
68 static const char *const ppsz_color_descriptions[] = {
69 N_("Default"), N_("Black"),
70 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
71 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
72 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
75 /*****************************************************************************
76 * filter_sys_t: rss filter descriptor
77 *****************************************************************************/
82 char *psz_description;
89 char *psz_description;
95 struct rss_item_t *p_items;
103 int i_xoff, i_yoff; /* offsets for the display string in the video window */
104 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
108 char *psz_marquee; /* marquee string */
110 text_style_t *p_style; /* font control */
116 struct rss_feed_t *p_feeds;
119 time_t t_last_update;
128 #define MSG_TEXT N_("Feed URLs")
129 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) separated URLs.")
130 #define SPEED_TEXT N_("Speed of feeds")
131 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
132 #define LENGTH_TEXT N_("Max length")
133 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
135 #define TTL_TEXT N_("Refresh time")
136 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
137 "of the feeds. 0 means that the feeds are never updated." )
138 #define IMAGE_TEXT N_("Feed images")
139 #define IMAGE_LONGTEXT N_("Display feed images if available.")
141 #define POSX_TEXT N_("X offset")
142 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
143 #define POSY_TEXT N_("Y offset")
144 #define POSY_LONGTEXT N_("Y offset, down from the top." )
145 #define OPACITY_TEXT N_("Opacity")
146 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
147 "overlay text. 0 = transparent, 255 = totally opaque." )
149 #define SIZE_TEXT N_("Font size, pixels")
150 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
153 #define COLOR_TEXT N_("Color")
154 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
155 "the video. This must be an hexadecimal (like HTML colors). The first two "\
156 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
157 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
159 #define POS_TEXT N_("Text position")
160 #define POS_LONGTEXT N_( \
161 "You can enforce the text position on the video " \
162 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
163 "also use combinations of these values, eg 6 = top-right).")
165 #define TITLE_TEXT N_("Title display mode")
166 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
168 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
169 static const char *const ppsz_pos_descriptions[] =
170 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
171 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
179 static const int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
180 static const char *const ppsz_title_modes[] =
181 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
183 #define CFG_PREFIX "rss-"
185 /*****************************************************************************
187 *****************************************************************************/
189 set_capability( "sub filter", 1 )
190 set_shortname( "RSS / Atom" )
191 set_callbacks( CreateFilter, DestroyFilter )
192 set_category( CAT_VIDEO )
193 set_subcategory( SUBCAT_VIDEO_SUBPIC )
194 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, false )
196 set_section( N_("Position"), NULL )
197 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, true )
198 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, true )
199 add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, false )
200 change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL )
202 set_section( N_("Font"), NULL )
203 /* 5 sets the default to top [1] left [4] */
204 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
205 OPACITY_TEXT, OPACITY_LONGTEXT, false )
206 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
208 change_integer_list( pi_color_values, ppsz_color_descriptions, NULL )
209 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, false )
211 set_section( N_("Misc"), NULL )
212 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
214 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
216 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, false )
217 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, false )
218 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, false )
219 change_integer_list( pi_title_modes, ppsz_title_modes, NULL )
221 set_description( N_("RSS and Atom feed display") )
222 add_shortcut( "rss" )
223 add_shortcut( "atom" )
226 static const char *const ppsz_filter_options[] = {
227 "urls", "x", "y", "position", "color", "size", "speed", "length",
228 "ttl", "images", "title", NULL
231 /*****************************************************************************
232 * CreateFilter: allocates RSS video filter
233 *****************************************************************************/
234 static int CreateFilter( vlc_object_t *p_this )
236 filter_t *p_filter = (filter_t *)p_this;
239 int i_ret = VLC_ENOMEM;
241 /* Allocate structure */
242 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
246 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
249 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
250 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
251 p_sys->i_cur_feed = 0;
252 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
253 p_sys->i_cur_char = 0;
255 p_sys->p_feeds = NULL;
256 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
257 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
258 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
259 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
261 p_sys->psz_marquee = malloc( p_sys->i_length + 1 );
262 if( p_sys->psz_marquee == NULL )
264 p_sys->psz_marquee[p_sys->i_length] = '\0';
266 p_sys->p_style = text_style_New();
267 if( p_sys->p_style == NULL )
270 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
271 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
272 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
273 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
274 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
275 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
277 if( p_sys->b_images == true && p_sys->p_style->i_font_size == -1 )
279 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus be displayed without being resized" );
282 if( FetchRSS( p_filter ) )
284 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
285 text_style_Delete( p_sys->p_style );
286 i_ret = VLC_EGENERIC;
289 p_sys->t_last_update = time( NULL );
291 if( p_sys->i_feeds == 0 )
293 text_style_Delete( p_sys->p_style );
294 i_ret = VLC_EGENERIC;
297 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
299 if( p_sys->p_feeds[i_feed].i_items == 0 )
301 DestroyFilter( p_this );
307 vlc_mutex_init( &p_sys->lock );
308 p_filter->pf_sub_filter = Filter;
309 p_sys->last_date = (mtime_t)0;
314 free( p_sys->psz_marquee );
315 free( p_sys->psz_urls );
319 /*****************************************************************************
320 * DestroyFilter: destroy RSS video filter
321 *****************************************************************************/
322 static void DestroyFilter( vlc_object_t *p_this )
324 filter_t *p_filter = (filter_t *)p_this;
325 filter_sys_t *p_sys = p_filter->p_sys;
327 text_style_Delete( p_sys->p_style );
328 free( p_sys->psz_marquee );
329 free( p_sys->psz_urls );
334 /****************************************************************************
335 * Filter: the whole thing
336 ****************************************************************************
337 * This function outputs subpictures at regular time intervals.
338 ****************************************************************************/
339 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
341 filter_sys_t *p_sys = p_filter->p_sys;
344 subpicture_region_t *p_region;
348 struct rss_feed_t *p_feed;
350 memset( &fmt, 0, sizeof(video_format_t) );
352 vlc_mutex_lock( &p_sys->lock );
355 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
356 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
357 * p_sys->i_speed > date )
359 vlc_mutex_unlock( &p_sys->lock );
363 /* Do we need to update the feeds ? */
365 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
367 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
368 if( FetchRSS( p_filter ) )
370 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
371 vlc_mutex_unlock( &p_sys->lock );
372 return NULL; /* FIXME : we most likely messed up all the data,
373 * so we might need to do something about it */
375 p_sys->t_last_update = time( NULL );
378 p_sys->last_date = date;
380 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 )
382 p_sys->i_cur_char = 0;
384 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
386 if( p_sys->i_title == scroll_title )
387 p_sys->i_cur_item = -1;
389 p_sys->i_cur_item = 0;
390 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
394 p_spu = filter_NewSubpicture( p_filter );
397 vlc_mutex_unlock( &p_sys->lock );
401 fmt.i_chroma = VLC_CODEC_TEXT;
403 p_spu->p_region = subpicture_region_New( &fmt );
404 if( !p_spu->p_region )
406 p_filter->pf_sub_buffer_del( p_filter, p_spu );
407 vlc_mutex_unlock( &p_sys->lock );
411 /* Generate the string that will be displayed. This string is supposed to
412 be p_sys->i_length characters long. */
413 i_item = p_sys->i_cur_item;
414 i_feed = p_sys->i_cur_feed;
415 p_feed = &p_sys->p_feeds[i_feed];
417 if( ( p_feed->p_pic && p_sys->i_title == default_title )
418 || p_sys->i_title == hide_title )
420 /* Don't display the feed's title if we have an image */
421 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
422 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
423 +p_sys->i_cur_char );
425 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
426 || p_sys->i_title == prepend_title )
428 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
429 p_sys->p_feeds[i_feed].psz_title,
430 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
431 +p_sys->i_cur_char );
433 else /* scrolling title */
436 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
437 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
438 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
440 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
441 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
442 +p_sys->i_cur_char );
445 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
448 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
449 snprintf( strchr( p_sys->psz_marquee, 0 ),
450 p_sys->i_length - strlen( p_sys->psz_marquee ),
452 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
455 /* Calls to snprintf might split multibyte UTF8 chars ...
456 * which freetype doesn't like. */
458 char *a = strdup( p_sys->psz_marquee );
460 char *b = p_sys->psz_marquee;
461 EnsureUTF8( p_sys->psz_marquee );
462 /* we want to use ' ' instead of '?' for erroneous chars */
465 if( *b != *a ) *b = ' ';
471 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
472 if( p_sys->p_style->i_font_size > 0 )
473 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
474 p_spu->i_start = date;
476 p_spu->b_ephemer = true;
478 /* where to locate the string: */
479 if( p_sys->i_pos < 0 )
480 { /* set to an absolute xy */
481 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
482 p_spu->b_absolute = true;
485 { /* set to one of the 9 relative locations */
486 p_spu->p_region->i_align = p_sys->i_pos;
487 p_spu->b_absolute = false;
490 p_spu->p_region->p_style = text_style_Duplicate( p_sys->p_style );
494 /* Display the feed's image */
495 picture_t *p_pic = p_feed->p_pic;
496 video_format_t fmt_out;
498 memset( &fmt_out, 0, sizeof(video_format_t) );
500 fmt_out.i_chroma = VLC_CODEC_YUVA;
501 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
502 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
504 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
506 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
508 p_region = subpicture_region_New( &fmt_out );
511 msg_Err( p_filter, "cannot allocate SPU region" );
515 p_region->i_x = p_sys->i_xoff;
516 p_region->i_y = p_sys->i_yoff;
517 /* FIXME the copy is probably not needed anymore */
518 picture_Copy( p_region->p_picture, p_pic );
519 p_spu->p_region->p_next = p_region;
522 /* Offset text to display right next to the image */
523 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
526 vlc_mutex_unlock( &p_sys->lock );
530 /****************************************************************************
531 * RSS related functions
532 ****************************************************************************
533 * You should always lock the p_filter mutex before using any of these
535 ***************************************************************************/
537 #undef LoadImage /* do not conflict with Win32 API */
539 /****************************************************************************
540 * download and resize image located at psz_url
541 ***************************************************************************/
542 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
544 filter_sys_t *p_sys = p_filter->p_sys;
545 video_format_t fmt_in;
546 video_format_t fmt_out;
548 picture_t *p_pic = NULL;
549 image_handler_t *p_handler = image_HandlerCreate( p_filter );
551 memset( &fmt_in, 0, sizeof(video_format_t) );
552 memset( &fmt_out, 0, sizeof(video_format_t) );
554 fmt_out.i_chroma = VLC_CODEC_YUVA;
555 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
559 msg_Warn( p_filter, "Unable to read image %s", psz_url );
561 else if( p_sys->p_style->i_font_size > 0 )
564 fmt_in.i_chroma = VLC_CODEC_YUVA;
565 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
566 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
567 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
568 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
569 fmt_out.i_height = p_sys->p_style->i_font_size;
571 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
572 picture_Release( p_orig );
575 msg_Warn( p_filter, "Error while converting %s", psz_url );
583 image_HandlerDelete( p_handler );
588 /****************************************************************************
589 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
591 ***************************************************************************/
592 static char *removeWhiteChars( const char *psz_src )
594 char *psz_src2,*psz_clean, *psz_clean2;
595 psz_src2 = psz_clean = strdup( psz_src );
598 while( ( *psz_clean == ' ' || *psz_clean == '\t'
599 || *psz_clean == '\n' || *psz_clean == '\r' )
600 && *psz_clean != '\0' )
604 i = strlen( psz_clean );
606 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
607 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
608 psz_clean[i+1] = '\0';
609 psz_clean2 = strdup( psz_clean );
614 /****************************************************************************
615 * FetchRSS (or Atom) feeds
616 ***************************************************************************/
617 static int FetchRSS( filter_t *p_filter)
619 filter_sys_t *p_sys = p_filter->p_sys;
621 stream_t *p_stream = NULL;
623 xml_reader_t *p_xml_reader = NULL;
625 char *psz_eltname = NULL;
626 char *psz_eltvalue = NULL;
627 char *psz_feed = NULL;
628 char *psz_buffer = NULL;
629 char *psz_buffer_2 = NULL;
640 while( p_sys->psz_urls[i_int] != 0 )
641 if( p_sys->psz_urls[i_int++] == '|' )
643 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
644 * sizeof( struct rss_feed_t ) );
646 p_xml = xml_Create( p_filter );
649 msg_Err( p_filter, "Failed to open XML parser" );
653 psz_buffer = strdup( p_sys->psz_urls );
654 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
655 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
657 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
659 if( psz_buffer == NULL ) break;
660 if( psz_buffer[0] == 0 ) psz_buffer++;
661 psz_feed = psz_buffer;
662 psz_buffer = strchr( psz_buffer, '|' );
663 if( psz_buffer != NULL ) psz_buffer[0] = 0;
665 p_feed->psz_title = NULL;
666 p_feed->psz_description = NULL;
667 p_feed->psz_link = NULL;
668 p_feed->psz_image = NULL;
669 p_feed->p_pic = NULL;
671 p_feed->p_items = NULL;
673 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
675 p_stream = stream_UrlNew( p_filter, psz_feed );
678 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
683 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
686 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
695 while( xml_ReaderRead( p_xml_reader ) == 1 )
697 switch( xml_ReaderNodeType( p_xml_reader ) )
703 case XML_READER_STARTELEM:
705 psz_eltname = xml_ReaderName( p_xml_reader );
711 msg_Dbg( p_filter, "element name: %s", psz_eltname );
713 if( !strcmp( psz_eltname, "item" ) /* rss */
714 || !strcmp( psz_eltname, "entry" ) ) /* atom */
718 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
719 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
720 p_feed->p_items[p_feed->i_items-1].psz_description
722 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
724 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
728 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
730 char *psz_href = NULL;
731 char *psz_rel = NULL;
732 while( xml_ReaderNextAttr( p_xml_reader )
735 char *psz_name = xml_ReaderName( p_xml_reader );
736 char *psz_value = xml_ReaderValue( p_xml_reader );
737 if( !strcmp( psz_name, "rel" ) )
741 msg_Dbg( p_filter, "\"rel\" attribute of link atom duplicated (last value: %s)", psz_value );
746 else if( !strcmp( psz_name, "href" ) )
750 msg_Dbg( p_filter, "\"href\" attribute of link atom duplicated (last value: %s)", psz_href );
753 psz_href = psz_value;
761 if( psz_rel && psz_href )
763 if( !strcmp( psz_rel, "alternate" )
764 && b_is_item == false
765 && b_is_image == false
766 && !p_feed->psz_link )
768 p_feed->psz_link = psz_href;
770 /* this isn't in the rfc but i found some ... */
771 else if( ( !strcmp( psz_rel, "logo" )
772 || !strcmp( psz_rel, "icon" ) )
773 && b_is_item == false
774 && b_is_image == false
775 && !p_feed->psz_image )
777 p_feed->psz_image = psz_href;
792 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 */
809 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
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 == 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 == 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 == 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 picture_Release( p_feed->p_pic );
953 free( p_sys->p_feeds );