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) seperated 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;
240 /* Allocate structure */
241 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
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" );
260 p_sys->psz_marquee = (char *)malloc( p_sys->i_length + 1 );
261 if( p_sys->psz_marquee == NULL )
263 free( p_sys->psz_urls );
267 p_sys->psz_marquee[p_sys->i_length] = '\0';
269 p_sys->p_style = text_style_New();
270 if( p_sys->p_style == NULL )
272 free( p_sys->psz_marquee );
273 free( p_sys->psz_urls );
278 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
279 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
280 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
281 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
282 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
283 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
285 if( p_sys->b_images == true && p_sys->p_style->i_font_size == -1 )
287 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus be displayed without being resized" );
290 if( FetchRSS( p_filter ) )
292 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
293 text_style_Delete( p_sys->p_style );
294 free( p_sys->psz_marquee );
295 free( p_sys->psz_urls );
299 p_sys->t_last_update = time( NULL );
301 if( p_sys->i_feeds == 0 )
303 text_style_Delete( p_sys->p_style );
304 free( p_sys->psz_marquee );
305 free( p_sys->psz_urls );
309 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
311 if( p_sys->p_feeds[i_feed].i_items == 0 )
313 text_style_Delete( p_sys->p_style );
314 free( p_sys->psz_marquee );
316 free( p_sys->psz_urls );
322 vlc_mutex_init( &p_sys->lock );
323 p_filter->pf_sub_filter = Filter;
324 p_sys->last_date = (mtime_t)0;
328 /*****************************************************************************
329 * DestroyFilter: destroy RSS video filter
330 *****************************************************************************/
331 static void DestroyFilter( vlc_object_t *p_this )
333 filter_t *p_filter = (filter_t *)p_this;
334 filter_sys_t *p_sys = p_filter->p_sys;
336 text_style_Delete( p_sys->p_style );
337 free( p_sys->psz_marquee );
338 free( p_sys->psz_urls );
343 /****************************************************************************
344 * Filter: the whole thing
345 ****************************************************************************
346 * This function outputs subpictures at regular time intervals.
347 ****************************************************************************/
348 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
350 filter_sys_t *p_sys = p_filter->p_sys;
353 subpicture_region_t *p_region;
357 struct rss_feed_t *p_feed;
359 memset( &fmt, 0, sizeof(video_format_t) );
361 vlc_mutex_lock( &p_sys->lock );
364 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
365 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
366 * p_sys->i_speed > date )
368 vlc_mutex_unlock( &p_sys->lock );
372 /* Do we need to update the feeds ? */
374 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
376 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
377 if( FetchRSS( p_filter ) )
379 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
380 vlc_mutex_unlock( &p_sys->lock );
381 return NULL; /* FIXME : we most likely messed up all the data,
382 * so we might need to do something about it */
384 p_sys->t_last_update = time( NULL );
387 p_sys->last_date = date;
389 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 )
391 p_sys->i_cur_char = 0;
393 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
395 if( p_sys->i_title == scroll_title )
396 p_sys->i_cur_item = -1;
398 p_sys->i_cur_item = 0;
399 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
403 p_spu = filter_NewSubpicture( p_filter );
406 vlc_mutex_unlock( &p_sys->lock );
410 fmt.i_chroma = VLC_CODEC_TEXT;
412 p_spu->p_region = subpicture_region_New( &fmt );
413 if( !p_spu->p_region )
415 p_filter->pf_sub_buffer_del( p_filter, p_spu );
416 vlc_mutex_unlock( &p_sys->lock );
420 /* Generate the string that will be displayed. This string is supposed to
421 be p_sys->i_length characters long. */
422 i_item = p_sys->i_cur_item;
423 i_feed = p_sys->i_cur_feed;
424 p_feed = &p_sys->p_feeds[i_feed];
426 if( ( p_feed->p_pic && p_sys->i_title == default_title )
427 || p_sys->i_title == hide_title )
429 /* Don't display the feed's title if we have an image */
430 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
431 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
432 +p_sys->i_cur_char );
434 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
435 || p_sys->i_title == prepend_title )
437 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
438 p_sys->p_feeds[i_feed].psz_title,
439 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
440 +p_sys->i_cur_char );
442 else /* scrolling title */
445 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
446 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
447 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
449 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
450 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
451 +p_sys->i_cur_char );
454 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
457 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
458 snprintf( strchr( p_sys->psz_marquee, 0 ),
459 p_sys->i_length - strlen( p_sys->psz_marquee ),
461 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
464 /* Calls to snprintf might split multibyte UTF8 chars ...
465 * which freetype doesn't like. */
467 char *a = strdup( p_sys->psz_marquee );
469 char *b = p_sys->psz_marquee;
470 EnsureUTF8( p_sys->psz_marquee );
471 /* we want to use ' ' instead of '?' for erroneous chars */
474 if( *b != *a ) *b = ' ';
480 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
481 if( p_sys->p_style->i_font_size > 0 )
482 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
483 p_spu->i_start = date;
485 p_spu->b_ephemer = true;
487 /* where to locate the string: */
488 if( p_sys->i_pos < 0 )
489 { /* set to an absolute xy */
490 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
491 p_spu->b_absolute = true;
494 { /* set to one of the 9 relative locations */
495 p_spu->p_region->i_align = p_sys->i_pos;
496 p_spu->b_absolute = false;
499 p_spu->p_region->p_style = text_style_Duplicate( 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_CODEC_YUVA;
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 = subpicture_region_New( &fmt_out );
520 msg_Err( p_filter, "cannot allocate SPU region" );
524 p_region->i_x = p_sys->i_xoff;
525 p_region->i_y = p_sys->i_yoff;
526 /* FIXME the copy is probably not needed anymore */
527 picture_Copy( p_region->p_picture, p_pic );
528 p_spu->p_region->p_next = p_region;
531 /* Offset text to display right next to the image */
532 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
535 vlc_mutex_unlock( &p_sys->lock );
539 /****************************************************************************
540 * RSS related functions
541 ****************************************************************************
542 * You should always lock the p_filter mutex before using any of these
544 ***************************************************************************/
546 #undef LoadImage /* do not conflict with Win32 API */
548 /****************************************************************************
549 * download and resize image located at psz_url
550 ***************************************************************************/
551 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
553 filter_sys_t *p_sys = p_filter->p_sys;
554 video_format_t fmt_in;
555 video_format_t fmt_out;
557 picture_t *p_pic = NULL;
558 image_handler_t *p_handler = image_HandlerCreate( p_filter );
560 memset( &fmt_in, 0, sizeof(video_format_t) );
561 memset( &fmt_out, 0, sizeof(video_format_t) );
563 fmt_out.i_chroma = VLC_CODEC_YUVA;
564 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
568 msg_Warn( p_filter, "Unable to read image %s", psz_url );
570 else if( p_sys->p_style->i_font_size > 0 )
573 fmt_in.i_chroma = VLC_CODEC_YUVA;
574 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
575 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
576 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
577 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
578 fmt_out.i_height = p_sys->p_style->i_font_size;
580 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
581 picture_Release( p_orig );
584 msg_Warn( p_filter, "Error while converting %s", psz_url );
592 image_HandlerDelete( p_handler );
597 /****************************************************************************
598 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
600 ***************************************************************************/
601 static char *removeWhiteChars( const char *psz_src )
603 char *psz_src2,*psz_clean, *psz_clean2;
604 psz_src2 = psz_clean = strdup( psz_src );
607 while( ( *psz_clean == ' ' || *psz_clean == '\t'
608 || *psz_clean == '\n' || *psz_clean == '\r' )
609 && *psz_clean != '\0' )
613 i = strlen( psz_clean );
615 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
616 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
617 psz_clean[i+1] = '\0';
618 psz_clean2 = strdup( psz_clean );
623 /****************************************************************************
624 * FetchRSS (or Atom) feeds
625 ***************************************************************************/
626 static int FetchRSS( filter_t *p_filter)
628 filter_sys_t *p_sys = p_filter->p_sys;
630 stream_t *p_stream = NULL;
632 xml_reader_t *p_xml_reader = NULL;
634 char *psz_eltname = NULL;
635 char *psz_eltvalue = NULL;
636 char *psz_feed = NULL;
637 char *psz_buffer = NULL;
638 char *psz_buffer_2 = NULL;
649 while( p_sys->psz_urls[i_int] != 0 )
650 if( p_sys->psz_urls[i_int++] == '|' )
652 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
653 * sizeof( struct rss_feed_t ) );
655 p_xml = xml_Create( p_filter );
658 msg_Err( p_filter, "Failed to open XML parser" );
662 psz_buffer = strdup( p_sys->psz_urls );
663 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
664 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
666 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
668 if( psz_buffer == NULL ) break;
669 if( psz_buffer[0] == 0 ) psz_buffer++;
670 psz_feed = psz_buffer;
671 psz_buffer = strchr( psz_buffer, '|' );
672 if( psz_buffer != NULL ) psz_buffer[0] = 0;
674 p_feed->psz_title = NULL;
675 p_feed->psz_description = NULL;
676 p_feed->psz_link = NULL;
677 p_feed->psz_image = NULL;
678 p_feed->p_pic = NULL;
680 p_feed->p_items = NULL;
682 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
684 p_stream = stream_UrlNew( p_filter, psz_feed );
687 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
692 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
695 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
704 while( xml_ReaderRead( p_xml_reader ) == 1 )
706 switch( xml_ReaderNodeType( p_xml_reader ) )
712 case XML_READER_STARTELEM:
714 psz_eltname = xml_ReaderName( p_xml_reader );
720 msg_Dbg( p_filter, "element name: %s", psz_eltname );
722 if( !strcmp( psz_eltname, "item" ) /* rss */
723 || !strcmp( psz_eltname, "entry" ) ) /* atom */
727 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
728 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
729 p_feed->p_items[p_feed->i_items-1].psz_description
731 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
733 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
737 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
739 char *psz_href = NULL;
740 char *psz_rel = NULL;
741 while( xml_ReaderNextAttr( p_xml_reader )
744 char *psz_name = xml_ReaderName( p_xml_reader );
745 char *psz_value = xml_ReaderValue( p_xml_reader );
746 if( !strcmp( psz_name, "rel" ) )
750 msg_Dbg( p_filter, "\"rel\" attribute of link atom duplicated (last value: %s)", psz_value );
755 else if( !strcmp( psz_name, "href" ) )
759 msg_Dbg( p_filter, "\"href\" attribute of link atom duplicated (last value: %s)", psz_href );
762 psz_href = psz_value;
770 if( psz_rel && psz_href )
772 if( !strcmp( psz_rel, "alternate" )
773 && b_is_item == false
774 && b_is_image == false
775 && !p_feed->psz_link )
777 p_feed->psz_link = psz_href;
779 /* this isn't in the rfc but i found some ... */
780 else if( ( !strcmp( psz_rel, "logo" )
781 || !strcmp( psz_rel, "icon" ) )
782 && b_is_item == false
783 && b_is_image == false
784 && !p_feed->psz_image )
786 p_feed->psz_image = psz_href;
801 case XML_READER_ENDELEM:
804 psz_eltname = xml_ReaderName( p_xml_reader );
810 msg_Dbg( p_filter, "element end : %s", psz_eltname );
812 if( !strcmp( psz_eltname, "item" ) /* rss */
813 || !strcmp( psz_eltname, "entry" ) ) /* atom */
818 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
826 case XML_READER_TEXT:
827 if( !psz_eltname ) break;
828 psz_eltvalue = xml_ReaderValue( p_xml_reader );
836 psz_clean = removeWhiteChars( psz_eltvalue );
837 free( psz_eltvalue ); psz_eltvalue = psz_clean;
840 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
842 if( b_is_item == true )
844 struct rss_item_t *p_item;
845 p_item = p_feed->p_items+i_item;
846 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
847 && !p_item->psz_title )
849 p_item->psz_title = psz_eltvalue;
851 else if( !strcmp( psz_eltname, "link" ) /* rss */
852 && !p_item->psz_link )
854 p_item->psz_link = psz_eltvalue;
856 else if((!strcmp( psz_eltname, "description" ) /* rss */
857 || !strcmp( psz_eltname, "summary" ) ) /* atom */
858 && !p_item->psz_description )
860 p_item->psz_description = psz_eltvalue;
864 free( psz_eltvalue );
868 else if( b_is_image == true )
870 if( !strcmp( psz_eltname, "url" ) /* rss */
871 && !p_feed->psz_image )
873 p_feed->psz_image = psz_eltvalue;
877 free( psz_eltvalue );
883 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
884 && !p_feed->psz_title )
886 p_feed->psz_title = psz_eltvalue;
888 else if( !strcmp( psz_eltname, "link" ) /* rss */
889 && !p_feed->psz_link )
891 p_feed->psz_link = psz_eltvalue;
893 else if((!strcmp( psz_eltname, "description" ) /* rss */
894 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
895 && !p_feed->psz_description )
897 p_feed->psz_description = psz_eltvalue;
899 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
900 || !strcmp( psz_eltname, "icon" ) ) /* atom */
901 && !p_feed->psz_image )
903 p_feed->psz_image = psz_eltvalue;
907 free( psz_eltvalue );
915 if( p_sys->b_images == true
916 && p_feed->psz_image && !p_feed->p_pic )
918 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
921 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
922 if( p_stream ) stream_Delete( p_stream );
923 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
925 free( psz_buffer_2 );
926 if( p_xml ) xml_Delete( p_xml );
931 /****************************************************************************
933 ***************************************************************************/
934 static void FreeRSS( filter_t *p_filter)
936 filter_sys_t *p_sys = p_filter->p_sys;
938 struct rss_item_t *p_item;
939 struct rss_feed_t *p_feed;
944 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
946 p_feed = p_sys->p_feeds+i_feed;
947 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
949 p_item = p_feed->p_items+i_item;
950 free( p_item->psz_title );
951 free( p_item->psz_link );
952 free( p_item->psz_description );
954 free( p_feed->p_items );
955 free( p_feed->psz_title);
956 free( p_feed->psz_link );
957 free( p_feed->psz_description );
958 free( p_feed->psz_image );
959 if( p_feed->p_pic != NULL )
960 picture_Release( p_feed->p_pic );
962 free( p_sys->p_feeds );