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>
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 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;
90 char *psz_description;
96 struct rss_item_t *p_items;
104 int i_xoff, i_yoff; /* offsets for the display string in the video window */
105 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
109 char *psz_marquee; /* marquee string */
111 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) seperated 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;
241 /* Allocate structure */
242 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
246 vlc_mutex_init( &p_sys->lock );
247 vlc_mutex_lock( &p_sys->lock );
249 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
252 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
253 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
254 p_sys->i_cur_feed = 0;
255 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
256 p_sys->i_cur_char = 0;
258 p_sys->p_feeds = NULL;
259 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
260 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
261 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
262 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
264 p_sys->psz_marquee = (char *)malloc( p_sys->i_length + 1 );
265 if( p_sys->psz_marquee == NULL )
267 vlc_mutex_unlock( &p_sys->lock );
268 vlc_mutex_destroy( &p_sys->lock );
269 free( p_sys->psz_urls );
273 p_sys->psz_marquee[p_sys->i_length] = '\0';
275 p_sys->p_style = malloc( sizeof( text_style_t ));
276 if( p_sys->p_style == NULL )
278 free( p_sys->psz_marquee );
279 vlc_mutex_unlock( &p_sys->lock );
280 vlc_mutex_destroy( &p_sys->lock );
281 free( p_sys->psz_urls );
285 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
287 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
288 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
289 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
290 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
291 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
292 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
294 if( p_sys->b_images == true && p_sys->p_style->i_font_size == -1 )
296 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus be displayed without being resized" );
299 if( FetchRSS( p_filter ) )
301 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
302 free( p_sys->p_style );
303 free( p_sys->psz_marquee );
304 vlc_mutex_unlock( &p_sys->lock );
305 vlc_mutex_destroy( &p_sys->lock );
306 free( p_sys->psz_urls );
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 );
318 free( p_sys->psz_urls );
322 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
324 if( p_sys->p_feeds[i_feed].i_items == 0 )
326 free( p_sys->p_style );
327 free( p_sys->psz_marquee );
329 vlc_mutex_unlock( &p_sys->lock );
330 vlc_mutex_destroy( &p_sys->lock );
331 free( p_sys->psz_urls );
337 p_filter->pf_sub_filter = Filter;
338 p_sys->last_date = (mtime_t)0;
340 vlc_mutex_unlock( &p_sys->lock );
344 /*****************************************************************************
345 * DestroyFilter: destroy RSS video filter
346 *****************************************************************************/
347 static void DestroyFilter( vlc_object_t *p_this )
349 filter_t *p_filter = (filter_t *)p_this;
350 filter_sys_t *p_sys = p_filter->p_sys;
352 vlc_mutex_lock( &p_sys->lock );
354 free( p_sys->p_style );
355 free( p_sys->psz_marquee );
356 free( p_sys->psz_urls );
358 vlc_mutex_unlock( &p_sys->lock );
359 vlc_mutex_destroy( &p_sys->lock );
362 /* Delete the RSS variables */
363 var_Destroy( p_filter, CFG_PREFIX "urls" );
364 var_Destroy( p_filter, CFG_PREFIX "speed" );
365 var_Destroy( p_filter, CFG_PREFIX "length" );
366 var_Destroy( p_filter, CFG_PREFIX "ttl" );
367 var_Destroy( p_filter, CFG_PREFIX "images" );
368 var_Destroy( p_filter, CFG_PREFIX "x" );
369 var_Destroy( p_filter, CFG_PREFIX "y" );
370 var_Destroy( p_filter, CFG_PREFIX "position" );
371 var_Destroy( p_filter, CFG_PREFIX "color");
372 var_Destroy( p_filter, CFG_PREFIX "opacity");
373 var_Destroy( p_filter, CFG_PREFIX "size");
374 var_Destroy( p_filter, CFG_PREFIX "title" );
377 /****************************************************************************
378 * Filter: the whole thing
379 ****************************************************************************
380 * This function outputs subpictures at regular time intervals.
381 ****************************************************************************/
382 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
384 filter_sys_t *p_sys = p_filter->p_sys;
387 subpicture_region_t *p_region;
391 struct rss_feed_t *p_feed;
393 memset( &fmt, 0, sizeof(video_format_t) );
395 vlc_mutex_lock( &p_sys->lock );
398 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
399 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
400 * p_sys->i_speed > date )
402 vlc_mutex_unlock( &p_sys->lock );
406 /* Do we need to update the feeds ? */
408 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
410 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
411 if( FetchRSS( p_filter ) )
413 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
414 vlc_mutex_unlock( &p_sys->lock );
415 return NULL; /* FIXME : we most likely messed up all the data,
416 * so we might need to do something about it */
418 p_sys->t_last_update = time( NULL );
421 p_sys->last_date = date;
423 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 )
425 p_sys->i_cur_char = 0;
427 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
429 if( p_sys->i_title == scroll_title )
430 p_sys->i_cur_item = -1;
432 p_sys->i_cur_item = 0;
433 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
437 p_spu = filter_NewSubpicture( p_filter );
440 vlc_mutex_unlock( &p_sys->lock );
444 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
446 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
447 if( !p_spu->p_region )
449 p_filter->pf_sub_buffer_del( p_filter, p_spu );
450 vlc_mutex_unlock( &p_sys->lock );
454 /* Generate the string that will be displayed. This string is supposed to
455 be p_sys->i_length characters long. */
456 i_item = p_sys->i_cur_item;
457 i_feed = p_sys->i_cur_feed;
458 p_feed = &p_sys->p_feeds[i_feed];
460 if( ( p_feed->p_pic && p_sys->i_title == default_title )
461 || p_sys->i_title == hide_title )
463 /* Don't display the feed's title if we have an image */
464 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
465 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
466 +p_sys->i_cur_char );
468 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
469 || p_sys->i_title == prepend_title )
471 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
472 p_sys->p_feeds[i_feed].psz_title,
473 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
474 +p_sys->i_cur_char );
476 else /* scrolling title */
479 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
480 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
481 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
483 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
484 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
485 +p_sys->i_cur_char );
488 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
491 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
492 snprintf( strchr( p_sys->psz_marquee, 0 ),
493 p_sys->i_length - strlen( p_sys->psz_marquee ),
495 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
498 /* Calls to snprintf might split multibyte UTF8 chars ...
499 * which freetype doesn't like. */
501 char *a = strdup( p_sys->psz_marquee );
503 char *b = p_sys->psz_marquee;
504 EnsureUTF8( p_sys->psz_marquee );
505 /* we want to use ' ' instead of '?' for erroneous chars */
508 if( *b != *a ) *b = ' ';
514 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
515 if( p_sys->p_style->i_font_size > 0 )
516 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
517 p_spu->i_start = date;
519 p_spu->b_ephemer = true;
521 /* where to locate the string: */
522 if( p_sys->i_pos < 0 )
523 { /* set to an absolute xy */
524 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
525 p_spu->b_absolute = true;
528 { /* set to one of the 9 relative locations */
529 p_spu->p_region->i_align = p_sys->i_pos;
530 p_spu->b_absolute = false;
533 p_spu->i_x = p_sys->i_xoff;
534 p_spu->i_y = p_sys->i_yoff;
537 p_spu->p_region->p_style = p_sys->p_style;
541 /* Display the feed's image */
542 picture_t *p_pic = p_feed->p_pic;
543 video_format_t fmt_out;
545 memset( &fmt_out, 0, sizeof(video_format_t) );
547 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
548 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
549 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
551 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
553 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
555 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
558 msg_Err( p_filter, "cannot allocate SPU region" );
562 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
563 p_spu->p_region->p_next = p_region;
566 /* Offset text to display right next to the image */
567 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
570 vlc_mutex_unlock( &p_sys->lock );
574 /****************************************************************************
575 * RSS related functions
576 ****************************************************************************
577 * You should always lock the p_filter mutex before using any of these
579 ***************************************************************************/
581 #undef LoadImage /* do not conflict with Win32 API */
583 /****************************************************************************
584 * download and resize image located at psz_url
585 ***************************************************************************/
586 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
588 filter_sys_t *p_sys = p_filter->p_sys;
589 video_format_t fmt_in;
590 video_format_t fmt_out;
592 picture_t *p_pic = NULL;
593 image_handler_t *p_handler = image_HandlerCreate( p_filter );
595 memset( &fmt_in, 0, sizeof(video_format_t) );
596 memset( &fmt_out, 0, sizeof(video_format_t) );
598 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
599 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
603 msg_Warn( p_filter, "Unable to read image %s", psz_url );
605 else if( p_sys->p_style->i_font_size > 0 )
608 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
609 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
610 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
611 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
612 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
613 fmt_out.i_height = p_sys->p_style->i_font_size;
615 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
616 picture_Release( p_orig );
619 msg_Warn( p_filter, "Error while converting %s", psz_url );
627 image_HandlerDelete( p_handler );
632 /****************************************************************************
633 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
635 ***************************************************************************/
636 static char *removeWhiteChars( char *psz_src )
638 char *psz_src2 = strdup( psz_src );
639 char *psz_clean = strdup( psz_src2 );
642 while( ( *psz_clean == ' ' || *psz_clean == '\t'
643 || *psz_clean == '\n' || *psz_clean == '\r' )
644 && *psz_clean != '\0' )
648 i = strlen( psz_clean );
650 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
651 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
652 psz_clean[i+1] = '\0';
653 psz_clean2 = strdup( psz_clean );
658 /****************************************************************************
659 * FetchRSS (or Atom) feeds
660 ***************************************************************************/
661 static int FetchRSS( filter_t *p_filter)
663 filter_sys_t *p_sys = p_filter->p_sys;
665 stream_t *p_stream = NULL;
667 xml_reader_t *p_xml_reader = NULL;
669 char *psz_eltname = NULL;
670 char *psz_eltvalue = NULL;
671 char *psz_feed = NULL;
672 char *psz_buffer = NULL;
673 char *psz_buffer_2 = NULL;
684 while( p_sys->psz_urls[i_int] != 0 )
685 if( p_sys->psz_urls[i_int++] == '|' )
687 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
688 * sizeof( struct rss_feed_t ) );
690 p_xml = xml_Create( p_filter );
693 msg_Err( p_filter, "Failed to open XML parser" );
697 psz_buffer = strdup( p_sys->psz_urls );
698 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
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 if( psz_buffer == NULL ) break;
704 if( psz_buffer[0] == 0 ) psz_buffer++;
705 psz_feed = psz_buffer;
706 psz_buffer = strchr( psz_buffer, '|' );
707 if( psz_buffer != NULL ) psz_buffer[0] = 0;
709 p_feed->psz_title = NULL;
710 p_feed->psz_description = NULL;
711 p_feed->psz_link = NULL;
712 p_feed->psz_image = NULL;
713 p_feed->p_pic = NULL;
715 p_feed->p_items = NULL;
717 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
719 p_stream = stream_UrlNew( p_filter, psz_feed );
722 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
726 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
729 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
737 while( xml_ReaderRead( p_xml_reader ) == 1 )
739 switch( xml_ReaderNodeType( p_xml_reader ) )
745 case XML_READER_STARTELEM:
747 psz_eltname = xml_ReaderName( p_xml_reader );
753 msg_Dbg( p_filter, "element name: %s", psz_eltname );
755 if( !strcmp( psz_eltname, "item" ) /* rss */
756 || !strcmp( psz_eltname, "entry" ) ) /* atom */
760 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
761 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
762 p_feed->p_items[p_feed->i_items-1].psz_description
764 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
766 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
770 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
772 char *psz_href = NULL;
773 char *psz_rel = NULL;
774 while( xml_ReaderNextAttr( p_xml_reader )
777 char *psz_name = xml_ReaderName( p_xml_reader );
778 char *psz_value = xml_ReaderValue( p_xml_reader );
779 if( !strcmp( psz_name, "rel" ) )
783 msg_Dbg( p_filter, "\"rel\" attribute of link atom duplicated (last value: %s)", psz_value );
788 else if( !strcmp( psz_name, "href" ) )
792 msg_Dbg( p_filter, "\"href\" attribute of link atom duplicated (last value: %s)", psz_href );
795 psz_href = psz_value;
803 if( psz_rel && psz_href )
805 if( !strcmp( psz_rel, "alternate" )
806 && b_is_item == false
807 && b_is_image == false
808 && !p_feed->psz_link )
810 p_feed->psz_link = psz_href;
812 /* this isn't in the rfc but i found some ... */
813 else if( ( !strcmp( psz_rel, "logo" )
814 || !strcmp( psz_rel, "icon" ) )
815 && b_is_item == false
816 && b_is_image == false
817 && !p_feed->psz_image )
819 p_feed->psz_image = psz_href;
834 case XML_READER_ENDELEM:
837 psz_eltname = xml_ReaderName( p_xml_reader );
843 msg_Dbg( p_filter, "element end : %s", psz_eltname );
845 if( !strcmp( psz_eltname, "item" ) /* rss */
846 || !strcmp( psz_eltname, "entry" ) ) /* atom */
851 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
859 case XML_READER_TEXT:
860 if( !psz_eltname ) break;
861 psz_eltvalue = xml_ReaderValue( p_xml_reader );
869 psz_clean = removeWhiteChars( psz_eltvalue );
870 free( psz_eltvalue ); psz_eltvalue = psz_clean;
873 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
875 if( b_is_item == true )
877 struct rss_item_t *p_item;
878 p_item = p_feed->p_items+i_item;
879 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
880 && !p_item->psz_title )
882 p_item->psz_title = psz_eltvalue;
884 else if( !strcmp( psz_eltname, "link" ) /* rss */
885 && !p_item->psz_link )
887 p_item->psz_link = psz_eltvalue;
889 else if((!strcmp( psz_eltname, "description" ) /* rss */
890 || !strcmp( psz_eltname, "summary" ) ) /* atom */
891 && !p_item->psz_description )
893 p_item->psz_description = psz_eltvalue;
897 free( psz_eltvalue );
901 else if( b_is_image == true )
903 if( !strcmp( psz_eltname, "url" ) /* rss */
904 && !p_feed->psz_image )
906 p_feed->psz_image = psz_eltvalue;
910 free( psz_eltvalue );
916 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
917 && !p_feed->psz_title )
919 p_feed->psz_title = psz_eltvalue;
921 else if( !strcmp( psz_eltname, "link" ) /* rss */
922 && !p_feed->psz_link )
924 p_feed->psz_link = psz_eltvalue;
926 else if((!strcmp( psz_eltname, "description" ) /* rss */
927 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
928 && !p_feed->psz_description )
930 p_feed->psz_description = psz_eltvalue;
932 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
933 || !strcmp( psz_eltname, "icon" ) ) /* atom */
934 && !p_feed->psz_image )
936 p_feed->psz_image = psz_eltvalue;
940 free( psz_eltvalue );
948 if( p_sys->b_images == true
949 && p_feed->psz_image && !p_feed->p_pic )
951 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
954 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
955 if( p_stream ) stream_Delete( p_stream );
956 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
958 free( psz_buffer_2 );
959 if( p_xml ) xml_Delete( p_xml );
964 /****************************************************************************
966 ***************************************************************************/
967 static void FreeRSS( filter_t *p_filter)
969 filter_sys_t *p_sys = p_filter->p_sys;
971 struct rss_item_t *p_item;
972 struct rss_feed_t *p_feed;
977 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
979 p_feed = p_sys->p_feeds+i_feed;
980 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
982 p_item = p_feed->p_items+i_item;
983 free( p_item->psz_title );
984 free( p_item->psz_link );
985 free( p_item->psz_description );
987 free( p_feed->p_items );
988 free( p_feed->psz_title);
989 free( p_feed->psz_link );
990 free( p_feed->psz_description );
991 free( p_feed->psz_image );
992 if( p_feed->p_pic != NULL )
993 picture_Release( p_feed->p_pic );
995 free( p_sys->p_feeds );