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 *****************************************************************************/
38 #include <vlc_plugin.h>
41 #include "vlc_filter.h"
42 #include "vlc_block.h"
45 #include "vlc_block.h"
46 #include "vlc_stream.h"
48 #include <vlc_charset.h>
50 #include "vlc_image.h"
54 /*****************************************************************************
56 *****************************************************************************/
57 static int CreateFilter ( vlc_object_t * );
58 static void DestroyFilter( vlc_object_t * );
59 static subpicture_t *Filter( filter_t *, mtime_t );
61 static int FetchRSS( filter_t * );
62 static void FreeRSS( filter_t * );
64 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
65 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
66 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
67 0x00000080, 0x000000FF, 0x0000FFFF};
68 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
69 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
70 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
71 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
74 /*****************************************************************************
75 * filter_sys_t: rss filter descriptor
76 *****************************************************************************/
81 char *psz_description;
88 char *psz_description;
94 struct rss_item_t *p_items;
102 int i_xoff, i_yoff; /* offsets for the display string in the video window */
103 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
107 char *psz_marquee; /* marquee string */
109 text_style_t *p_style; /* font control */
115 struct rss_feed_t *p_feeds;
118 time_t t_last_update;
127 #define MSG_TEXT N_("Feed URLs")
128 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) seperated URLs.")
129 #define SPEED_TEXT N_("Speed of feeds")
130 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
131 #define LENGTH_TEXT N_("Max length")
132 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
134 #define TTL_TEXT N_("Refresh time")
135 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
136 "of the feeds. 0 means that the feeds are never updated." )
137 #define IMAGE_TEXT N_("Feed images")
138 #define IMAGE_LONGTEXT N_("Display feed images if available.")
140 #define POSX_TEXT N_("X offset")
141 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
142 #define POSY_TEXT N_("Y offset")
143 #define POSY_LONGTEXT N_("Y offset, down from the top." )
144 #define OPACITY_TEXT N_("Opacity")
145 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
146 "overlay text. 0 = transparent, 255 = totally opaque." )
148 #define SIZE_TEXT N_("Font size, pixels")
149 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
152 #define COLOR_TEXT N_("Color")
153 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
154 "the video. This must be an hexadecimal (like HTML colors). The first two "\
155 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
156 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
158 #define POS_TEXT N_("Text position")
159 #define POS_LONGTEXT N_( \
160 "You can enforce the text position on the video " \
161 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
162 "also use combinations of these values, eg 6 = top-right).")
164 #define TITLE_TEXT N_("Title display mode")
165 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
167 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
168 static const char *ppsz_pos_descriptions[] =
169 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
170 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
178 static int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
179 static const char *ppsz_title_modes[] =
180 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
182 #define CFG_PREFIX "rss-"
184 /*****************************************************************************
186 *****************************************************************************/
188 set_capability( "sub filter", 1 );
189 set_shortname( "RSS / Atom" );
190 set_callbacks( CreateFilter, DestroyFilter );
191 set_category( CAT_VIDEO );
192 set_subcategory( SUBCAT_VIDEO_SUBPIC );
193 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, false );
195 set_section( N_("Position"), NULL );
196 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, true );
197 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, true );
198 add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, false );
199 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
201 set_section( N_("Font"), NULL );
202 /* 5 sets the default to top [1] left [4] */
203 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
204 OPACITY_TEXT, OPACITY_LONGTEXT, false );
205 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
207 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
208 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, false );
210 set_section( N_("Misc"), NULL );
211 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
213 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
215 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, false );
216 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, false );
217 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, false );
218 change_integer_list( pi_title_modes, ppsz_title_modes, 0 );
220 set_description( _("RSS and Atom feed display") );
221 add_shortcut( "rss" );
222 add_shortcut( "atom" );
225 static const char *ppsz_filter_options[] = {
226 "urls", "x", "y", "position", "color", "size", "speed", "length",
227 "ttl", "images", "title", NULL
230 /*****************************************************************************
231 * CreateFilter: allocates RSS video filter
232 *****************************************************************************/
233 static int CreateFilter( vlc_object_t *p_this )
235 filter_t *p_filter = (filter_t *)p_this;
239 /* Allocate structure */
240 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
243 msg_Err( p_filter, "out of memory" );
247 vlc_mutex_init( &p_sys->lock );
248 vlc_mutex_lock( &p_sys->lock );
250 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
253 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
254 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
255 p_sys->i_cur_feed = 0;
256 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
257 p_sys->i_cur_char = 0;
259 p_sys->p_feeds = NULL;
260 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
261 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
262 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
263 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
265 p_sys->psz_marquee = (char *)malloc( p_sys->i_length + 1 );
266 if( p_sys->psz_marquee == NULL )
268 msg_Err( p_filter, "out of memory" );
269 vlc_mutex_unlock( &p_sys->lock );
270 vlc_mutex_destroy( &p_sys->lock );
274 p_sys->psz_marquee[p_sys->i_length] = '\0';
276 p_sys->p_style = malloc( sizeof( text_style_t ));
277 if( p_sys->p_style == NULL )
279 msg_Err( p_filter, "out of memory" );
280 free( p_sys->psz_marquee );
281 vlc_mutex_unlock( &p_sys->lock );
282 vlc_mutex_destroy( &p_sys->lock );
286 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
288 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
289 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
290 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
291 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
292 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
293 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
295 if( p_sys->b_images == true && p_sys->p_style->i_font_size == -1 )
297 msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
300 if( FetchRSS( p_filter ) )
302 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
303 free( p_sys->p_style );
304 free( p_sys->psz_marquee );
305 vlc_mutex_unlock( &p_sys->lock );
306 vlc_mutex_destroy( &p_sys->lock );
310 p_sys->t_last_update = time( NULL );
312 if( p_sys->i_feeds == 0 )
314 free( p_sys->p_style );
315 free( p_sys->psz_marquee );
316 vlc_mutex_unlock( &p_sys->lock );
317 vlc_mutex_destroy( &p_sys->lock );
321 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
323 if( p_sys->p_feeds[i_feed].i_items == 0 )
325 free( p_sys->p_style );
326 free( p_sys->psz_marquee );
328 vlc_mutex_unlock( &p_sys->lock );
329 vlc_mutex_destroy( &p_sys->lock );
335 p_filter->pf_sub_filter = Filter;
336 p_sys->last_date = (mtime_t)0;
338 vlc_mutex_unlock( &p_sys->lock );
342 /*****************************************************************************
343 * DestroyFilter: destroy RSS video filter
344 *****************************************************************************/
345 static void DestroyFilter( vlc_object_t *p_this )
347 filter_t *p_filter = (filter_t *)p_this;
348 filter_sys_t *p_sys = p_filter->p_sys;
350 vlc_mutex_lock( &p_sys->lock );
352 free( p_sys->p_style );
353 free( p_sys->psz_marquee );
354 free( p_sys->psz_urls );
356 vlc_mutex_unlock( &p_sys->lock );
357 vlc_mutex_destroy( &p_sys->lock );
360 /* Delete the RSS variables */
361 var_Destroy( p_filter, CFG_PREFIX "urls" );
362 var_Destroy( p_filter, CFG_PREFIX "speed" );
363 var_Destroy( p_filter, CFG_PREFIX "length" );
364 var_Destroy( p_filter, CFG_PREFIX "ttl" );
365 var_Destroy( p_filter, CFG_PREFIX "images" );
366 var_Destroy( p_filter, CFG_PREFIX "x" );
367 var_Destroy( p_filter, CFG_PREFIX "y" );
368 var_Destroy( p_filter, CFG_PREFIX "position" );
369 var_Destroy( p_filter, CFG_PREFIX "color");
370 var_Destroy( p_filter, CFG_PREFIX "opacity");
371 var_Destroy( p_filter, CFG_PREFIX "size");
372 var_Destroy( p_filter, CFG_PREFIX "title" );
375 /****************************************************************************
376 * Filter: the whole thing
377 ****************************************************************************
378 * This function outputs subpictures at regular time intervals.
379 ****************************************************************************/
380 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
382 filter_sys_t *p_sys = p_filter->p_sys;
385 subpicture_region_t *p_region;
389 struct rss_feed_t *p_feed;
391 memset( &fmt, 0, sizeof(video_format_t) );
393 vlc_mutex_lock( &p_sys->lock );
396 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
397 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
398 * p_sys->i_speed > date )
400 vlc_mutex_unlock( &p_sys->lock );
404 /* Do we need to update the feeds ? */
406 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
408 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
409 if( FetchRSS( p_filter ) )
411 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
412 vlc_mutex_unlock( &p_sys->lock );
413 return NULL; /* FIXME : we most likely messed up all the data,
414 * so we might need to do something about it */
416 p_sys->t_last_update = time( NULL );
419 p_sys->last_date = date;
421 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 )
423 p_sys->i_cur_char = 0;
425 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
427 if( p_sys->i_title == scroll_title )
428 p_sys->i_cur_item = -1;
430 p_sys->i_cur_item = 0;
431 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
435 p_spu = p_filter->pf_sub_buffer_new( p_filter );
438 vlc_mutex_unlock( &p_sys->lock );
442 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
444 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
445 if( !p_spu->p_region )
447 p_filter->pf_sub_buffer_del( p_filter, p_spu );
448 vlc_mutex_unlock( &p_sys->lock );
452 /* Generate the string that will be displayed. This string is supposed to
453 be p_sys->i_length characters long. */
454 i_item = p_sys->i_cur_item;
455 i_feed = p_sys->i_cur_feed;
456 p_feed = &p_sys->p_feeds[i_feed];
458 if( ( p_feed->p_pic && p_sys->i_title == default_title )
459 || p_sys->i_title == hide_title )
461 /* Don't display the feed's title if we have an image */
462 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
463 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
464 +p_sys->i_cur_char );
466 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
467 || p_sys->i_title == prepend_title )
469 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
470 p_sys->p_feeds[i_feed].psz_title,
471 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
472 +p_sys->i_cur_char );
474 else /* scrolling title */
477 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
478 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
479 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
481 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
482 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
483 +p_sys->i_cur_char );
486 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
489 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
490 snprintf( strchr( p_sys->psz_marquee, 0 ),
491 p_sys->i_length - strlen( p_sys->psz_marquee ),
493 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
496 /* Calls to snprintf might split multibyte UTF8 chars ...
497 * which freetype doesn't like. */
499 char *a = strdup( p_sys->psz_marquee );
501 char *b = p_sys->psz_marquee;
502 EnsureUTF8( p_sys->psz_marquee );
503 /* we want to use ' ' instead of '?' for erroneous chars */
506 if( *b != *a ) *b = ' ';
512 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
513 if( p_sys->p_style->i_font_size > 0 )
514 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
515 p_spu->i_start = date;
517 p_spu->b_ephemer = true;
519 /* where to locate the string: */
520 if( p_sys->i_pos < 0 )
521 { /* set to an absolute xy */
522 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
523 p_spu->b_absolute = true;
526 { /* set to one of the 9 relative locations */
527 p_spu->p_region->i_align = p_sys->i_pos;
528 p_spu->b_absolute = false;
531 p_spu->i_x = p_sys->i_xoff;
532 p_spu->i_y = p_sys->i_yoff;
535 p_spu->p_region->p_style = p_sys->p_style;
539 /* Display the feed's image */
540 picture_t *p_pic = p_feed->p_pic;
541 video_format_t fmt_out;
543 memset( &fmt_out, 0, sizeof(video_format_t) );
545 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
546 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
547 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
549 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
551 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
553 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
556 msg_Err( p_filter, "cannot allocate SPU region" );
560 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
561 p_spu->p_region->p_next = p_region;
564 /* Offset text to display right next to the image */
565 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
568 vlc_mutex_unlock( &p_sys->lock );
572 /****************************************************************************
573 * RSS related functions
574 ****************************************************************************
575 * You should always lock the p_filter mutex before using any of these
577 ***************************************************************************/
579 #undef LoadImage /* do not conflict with Win32 API */
581 /****************************************************************************
582 * download and resize image located at psz_url
583 ***************************************************************************/
584 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
586 filter_sys_t *p_sys = p_filter->p_sys;
587 video_format_t fmt_in;
588 video_format_t fmt_out;
590 picture_t *p_pic = NULL;
591 image_handler_t *p_handler = image_HandlerCreate( p_filter );
593 memset( &fmt_in, 0, sizeof(video_format_t) );
594 memset( &fmt_out, 0, sizeof(video_format_t) );
596 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
597 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
601 msg_Warn( p_filter, "Unable to read image %s", psz_url );
603 else if( p_sys->p_style->i_font_size > 0 )
606 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
607 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
608 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
609 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
610 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
611 fmt_out.i_height = p_sys->p_style->i_font_size;
613 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
614 p_orig->pf_release( p_orig );
617 msg_Warn( p_filter, "Error while converting %s", psz_url );
625 image_HandlerDelete( p_handler );
630 /****************************************************************************
631 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
633 ***************************************************************************/
634 static char *removeWhiteChars( char *psz_src )
636 char *psz_src2 = strdup( psz_src );
637 char *psz_clean = strdup( psz_src2 );
640 while( ( *psz_clean == ' ' || *psz_clean == '\t'
641 || *psz_clean == '\n' || *psz_clean == '\r' )
642 && *psz_clean != '\0' )
646 i = strlen( psz_clean );
648 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
649 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
650 psz_clean[i+1] = '\0';
651 psz_clean2 = strdup( psz_clean );
656 /****************************************************************************
657 * FetchRSS (or Atom) feeds
658 ***************************************************************************/
659 static int FetchRSS( filter_t *p_filter)
661 filter_sys_t *p_sys = p_filter->p_sys;
663 stream_t *p_stream = NULL;
665 xml_reader_t *p_xml_reader = NULL;
667 char *psz_eltname = NULL;
668 char *psz_eltvalue = NULL;
669 char *psz_feed = NULL;
670 char *psz_buffer = NULL;
671 char *psz_buffer_2 = NULL;
682 while( p_sys->psz_urls[i_int] != 0 )
683 if( p_sys->psz_urls[i_int++] == '|' )
685 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
686 * sizeof( struct rss_feed_t ) );
688 p_xml = xml_Create( p_filter );
691 msg_Err( p_filter, "Failed to open XML parser" );
695 psz_buffer = strdup( p_sys->psz_urls );
696 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
697 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
699 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
701 if( psz_buffer == NULL ) break;
702 if( psz_buffer[0] == 0 ) psz_buffer++;
703 psz_feed = psz_buffer;
704 psz_buffer = strchr( psz_buffer, '|' );
705 if( psz_buffer != NULL ) psz_buffer[0] = 0;
707 p_feed->psz_title = NULL;
708 p_feed->psz_description = NULL;
709 p_feed->psz_link = NULL;
710 p_feed->psz_image = NULL;
711 p_feed->p_pic = NULL;
713 p_feed->p_items = NULL;
715 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
717 p_stream = stream_UrlNew( p_filter, psz_feed );
720 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
724 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
727 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
735 while( xml_ReaderRead( p_xml_reader ) == 1 )
737 switch( xml_ReaderNodeType( p_xml_reader ) )
743 case XML_READER_STARTELEM:
746 psz_eltname = xml_ReaderName( p_xml_reader );
752 msg_Dbg( p_filter, "element name: %s", psz_eltname );
754 if( !strcmp( psz_eltname, "item" ) /* rss */
755 || !strcmp( psz_eltname, "entry" ) ) /* atom */
759 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
760 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
761 p_feed->p_items[p_feed->i_items-1].psz_description
763 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
765 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
769 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
771 char *psz_href = NULL;
772 char *psz_rel = NULL;
773 while( xml_ReaderNextAttr( p_xml_reader )
776 char *psz_name = xml_ReaderName( p_xml_reader );
777 char *psz_value = xml_ReaderValue( p_xml_reader );
778 if( !strcmp( psz_name, "rel" ) )
782 else if( !strcmp( psz_name, "href" ) )
784 psz_href = psz_value;
792 if( psz_rel && psz_href )
794 if( !strcmp( psz_rel, "alternate" )
795 && b_is_item == false
796 && b_is_image == false
797 && !p_feed->psz_link )
799 p_feed->psz_link = psz_href;
801 /* this isn't in the rfc but i found some ... */
802 else if( ( !strcmp( psz_rel, "logo" )
803 || !strcmp( psz_rel, "icon" ) )
804 && b_is_item == false
805 && b_is_image == false
806 && !p_feed->psz_image )
808 p_feed->psz_image = psz_href;
823 case XML_READER_ENDELEM:
826 psz_eltname = xml_ReaderName( p_xml_reader );
832 msg_Dbg( p_filter, "element end : %s", psz_eltname );
834 if( !strcmp( psz_eltname, "item" ) /* rss */
835 || !strcmp( psz_eltname, "entry" ) ) /* atom */
840 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
848 case XML_READER_TEXT:
849 if( !psz_eltname ) break;
850 psz_eltvalue = xml_ReaderValue( p_xml_reader );
858 psz_clean = removeWhiteChars( psz_eltvalue );
859 free( psz_eltvalue ); psz_eltvalue = psz_clean;
862 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
864 if( b_is_item == true )
866 struct rss_item_t *p_item;
867 p_item = p_feed->p_items+i_item;
868 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
869 && !p_item->psz_title )
871 p_item->psz_title = psz_eltvalue;
873 else if( !strcmp( psz_eltname, "link" ) /* rss */
874 && !p_item->psz_link )
876 p_item->psz_link = psz_eltvalue;
878 else if((!strcmp( psz_eltname, "description" ) /* rss */
879 || !strcmp( psz_eltname, "summary" ) ) /* atom */
880 && !p_item->psz_description )
882 p_item->psz_description = psz_eltvalue;
886 free( psz_eltvalue );
890 else if( b_is_image == true )
892 if( !strcmp( psz_eltname, "url" ) /* rss */
893 && !p_feed->psz_image )
895 p_feed->psz_image = psz_eltvalue;
899 free( psz_eltvalue );
905 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
906 && !p_feed->psz_title )
908 p_feed->psz_title = psz_eltvalue;
910 else if( !strcmp( psz_eltname, "link" ) /* rss */
911 && !p_feed->psz_link )
913 p_feed->psz_link = psz_eltvalue;
915 else if((!strcmp( psz_eltname, "description" ) /* rss */
916 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
917 && !p_feed->psz_description )
919 p_feed->psz_description = psz_eltvalue;
921 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
922 || !strcmp( psz_eltname, "icon" ) ) /* atom */
923 && !p_feed->psz_image )
925 p_feed->psz_image = psz_eltvalue;
929 free( psz_eltvalue );
937 if( p_sys->b_images == true
938 && p_feed->psz_image && !p_feed->p_pic )
940 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
943 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
944 if( p_stream ) stream_Delete( p_stream );
945 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
947 free( psz_buffer_2 );
948 if( p_xml ) xml_Delete( p_xml );
953 /****************************************************************************
955 ***************************************************************************/
956 static void FreeRSS( filter_t *p_filter)
958 filter_sys_t *p_sys = p_filter->p_sys;
960 struct rss_item_t *p_item;
961 struct rss_feed_t *p_feed;
966 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
968 p_feed = p_sys->p_feeds+i_feed;
969 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
971 p_item = p_feed->p_items+i_item;
972 free( p_item->psz_title );
973 free( p_item->psz_link );
974 free( p_item->psz_description );
976 free( p_feed->p_items );
977 free( p_feed->psz_title);
978 free( p_feed->psz_link );
979 free( p_feed->psz_description );
980 free( p_feed->psz_image );
981 if( p_feed->p_pic != NULL )
982 p_feed->p_pic->pf_release( p_feed->p_pic );
984 free( p_sys->p_feeds );