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 *****************************************************************************/
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 int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
64 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
65 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
66 0x00000080, 0x000000FF, 0x0000FFFF};
67 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
68 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
69 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
70 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
73 /*****************************************************************************
74 * filter_sys_t: rss filter descriptor
75 *****************************************************************************/
80 char *psz_description;
87 char *psz_description;
93 struct rss_item_t *p_items;
101 int i_xoff, i_yoff; /* offsets for the display string in the video window */
102 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
106 char *psz_marquee; /* marquee string */
108 text_style_t *p_style; /* font control */
114 struct rss_feed_t *p_feeds;
117 time_t t_last_update;
126 #define MSG_TEXT N_("Feed URLs")
127 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) seperated URLs.")
128 #define SPEED_TEXT N_("Speed of feeds")
129 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
130 #define LENGTH_TEXT N_("Max length")
131 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
133 #define TTL_TEXT N_("Refresh time")
134 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
135 "of the feeds. 0 means that the feeds are never updated." )
136 #define IMAGE_TEXT N_("Feed images")
137 #define IMAGE_LONGTEXT N_("Display feed images if available.")
139 #define POSX_TEXT N_("X offset")
140 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
141 #define POSY_TEXT N_("Y offset")
142 #define POSY_LONGTEXT N_("Y offset, down from the top." )
143 #define OPACITY_TEXT N_("Opacity")
144 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
145 "overlay text. 0 = transparent, 255 = totally opaque." )
147 #define SIZE_TEXT N_("Font size, pixels")
148 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
151 #define COLOR_TEXT N_("Color")
152 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
153 "the video. This must be an hexadecimal (like HTML colors). The first two "\
154 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
155 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
157 #define POS_TEXT N_("Text position")
158 #define POS_LONGTEXT N_( \
159 "You can enforce the text position on the video " \
160 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
161 "also use combinations of these values, eg 6 = top-right).")
163 #define TITLE_TEXT N_("Title display mode")
164 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
166 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
167 static const char *ppsz_pos_descriptions[] =
168 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
169 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
177 static int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
178 static const char *ppsz_title_modes[] =
179 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
181 #define CFG_PREFIX "rss-"
183 /*****************************************************************************
185 *****************************************************************************/
187 set_capability( "sub filter", 1 );
188 set_shortname( "RSS / Atom" );
189 set_callbacks( CreateFilter, DestroyFilter );
190 set_category( CAT_VIDEO );
191 set_subcategory( SUBCAT_VIDEO_SUBPIC );
192 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, false );
194 set_section( N_("Position"), NULL );
195 add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, true );
196 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, true );
197 add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, false );
198 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
200 set_section( N_("Font"), NULL );
201 /* 5 sets the default to top [1] left [4] */
202 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
203 OPACITY_TEXT, OPACITY_LONGTEXT, false );
204 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
206 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
207 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, false );
209 set_section( N_("Misc"), NULL );
210 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
212 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
214 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, false );
215 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, false );
216 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, false );
217 change_integer_list( pi_title_modes, ppsz_title_modes, 0 );
219 set_description( _("RSS and Atom feed display") );
220 add_shortcut( "rss" );
221 add_shortcut( "atom" );
224 static const char *ppsz_filter_options[] = {
225 "urls", "x", "y", "position", "color", "size", "speed", "length",
226 "ttl", "images", "title", NULL
229 /*****************************************************************************
230 * CreateFilter: allocates RSS video filter
231 *****************************************************************************/
232 static int CreateFilter( vlc_object_t *p_this )
234 filter_t *p_filter = (filter_t *)p_this;
238 /* Allocate structure */
239 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
242 msg_Err( p_filter, "out of memory" );
246 vlc_mutex_init( p_filter, &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 msg_Err( p_filter, "out of memory" );
268 vlc_mutex_unlock( &p_sys->lock );
269 vlc_mutex_destroy( &p_sys->lock );
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 msg_Err( p_filter, "out of memory" );
279 free( p_sys->psz_marquee );
280 vlc_mutex_unlock( &p_sys->lock );
281 vlc_mutex_destroy( &p_sys->lock );
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, "rrs-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 );
309 p_sys->t_last_update = time( NULL );
311 if( p_sys->i_feeds == 0 )
313 free( p_sys->p_style );
314 free( p_sys->psz_marquee );
315 vlc_mutex_unlock( &p_sys->lock );
316 vlc_mutex_destroy( &p_sys->lock );
320 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
322 if( p_sys->p_feeds[i_feed].i_items == 0 )
324 free( p_sys->p_style );
325 free( p_sys->psz_marquee );
327 vlc_mutex_unlock( &p_sys->lock );
328 vlc_mutex_destroy( &p_sys->lock );
334 p_filter->pf_sub_filter = Filter;
335 p_sys->last_date = (mtime_t)0;
337 vlc_mutex_unlock( &p_sys->lock );
341 /*****************************************************************************
342 * DestroyFilter: destroy RSS video filter
343 *****************************************************************************/
344 static void DestroyFilter( vlc_object_t *p_this )
346 filter_t *p_filter = (filter_t *)p_this;
347 filter_sys_t *p_sys = p_filter->p_sys;
349 vlc_mutex_lock( &p_sys->lock );
351 free( p_sys->p_style );
352 free( p_sys->psz_marquee );
353 free( p_sys->psz_urls );
355 vlc_mutex_unlock( &p_sys->lock );
356 vlc_mutex_destroy( &p_sys->lock );
359 /* Delete the RSS variables */
360 var_Destroy( p_filter, CFG_PREFIX "urls" );
361 var_Destroy( p_filter, CFG_PREFIX "speed" );
362 var_Destroy( p_filter, CFG_PREFIX "length" );
363 var_Destroy( p_filter, CFG_PREFIX "ttl" );
364 var_Destroy( p_filter, CFG_PREFIX "images" );
365 var_Destroy( p_filter, CFG_PREFIX "x" );
366 var_Destroy( p_filter, CFG_PREFIX "y" );
367 var_Destroy( p_filter, CFG_PREFIX "position" );
368 var_Destroy( p_filter, CFG_PREFIX "color");
369 var_Destroy( p_filter, CFG_PREFIX "opacity");
370 var_Destroy( p_filter, CFG_PREFIX "size");
371 var_Destroy( p_filter, CFG_PREFIX "title" );
374 /****************************************************************************
375 * Filter: the whole thing
376 ****************************************************************************
377 * This function outputs subpictures at regular time intervals.
378 ****************************************************************************/
379 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
381 filter_sys_t *p_sys = p_filter->p_sys;
384 subpicture_region_t *p_region;
388 struct rss_feed_t *p_feed;
390 memset( &fmt, 0, sizeof(video_format_t) );
392 vlc_mutex_lock( &p_sys->lock );
395 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
396 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
397 * p_sys->i_speed > date )
399 vlc_mutex_unlock( &p_sys->lock );
403 /* Do we need to update the feeds ? */
405 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
407 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
408 if( FetchRSS( p_filter ) )
410 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
411 vlc_mutex_unlock( &p_sys->lock );
412 return NULL; /* FIXME : we most likely messed up all the data,
413 * so we might need to do something about it */
415 p_sys->t_last_update = time( NULL );
418 p_sys->last_date = date;
420 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 )
422 p_sys->i_cur_char = 0;
424 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
426 if( p_sys->i_title == scroll_title )
427 p_sys->i_cur_item = -1;
429 p_sys->i_cur_item = 0;
430 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
434 p_spu = p_filter->pf_sub_buffer_new( p_filter );
437 vlc_mutex_unlock( &p_sys->lock );
441 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
443 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
444 if( !p_spu->p_region )
446 p_filter->pf_sub_buffer_del( p_filter, p_spu );
447 vlc_mutex_unlock( &p_sys->lock );
451 /* Generate the string that will be displayed. This string is supposed to
452 be p_sys->i_length characters long. */
453 i_item = p_sys->i_cur_item;
454 i_feed = p_sys->i_cur_feed;
455 p_feed = &p_sys->p_feeds[i_feed];
457 if( ( p_feed->p_pic && p_sys->i_title == default_title )
458 || p_sys->i_title == hide_title )
460 /* Don't display the feed's title if we have an image */
461 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
462 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
463 +p_sys->i_cur_char );
465 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
466 || p_sys->i_title == prepend_title )
468 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
469 p_sys->p_feeds[i_feed].psz_title,
470 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
471 +p_sys->i_cur_char );
473 else /* scrolling title */
476 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
477 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
478 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
480 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
481 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
482 +p_sys->i_cur_char );
485 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
488 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
489 snprintf( strchr( p_sys->psz_marquee, 0 ),
490 p_sys->i_length - strlen( p_sys->psz_marquee ),
492 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
495 /* Calls to snprintf might split multibyte UTF8 chars ...
496 * which freetype doesn't like. */
498 char *a = strdup( p_sys->psz_marquee );
500 char *b = p_sys->psz_marquee;
501 EnsureUTF8( p_sys->psz_marquee );
502 /* we want to use ' ' instead of '?' for erroneous chars */
505 if( *b != *a ) *b = ' ';
511 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
512 if( p_sys->p_style->i_font_size > 0 )
513 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
514 p_spu->i_start = date;
516 p_spu->b_ephemer = true;
518 /* where to locate the string: */
519 if( p_sys->i_pos < 0 )
520 { /* set to an absolute xy */
521 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
522 p_spu->b_absolute = true;
525 { /* set to one of the 9 relative locations */
526 p_spu->p_region->i_align = p_sys->i_pos;
527 p_spu->b_absolute = false;
530 p_spu->i_x = p_sys->i_xoff;
531 p_spu->i_y = p_sys->i_yoff;
534 p_spu->p_region->p_style = p_sys->p_style;
538 /* Display the feed's image */
539 picture_t *p_pic = p_feed->p_pic;
540 video_format_t fmt_out;
542 memset( &fmt_out, 0, sizeof(video_format_t) );
544 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
545 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
546 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
548 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
550 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
552 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
555 msg_Err( p_filter, "cannot allocate SPU region" );
559 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
560 p_spu->p_region->p_next = p_region;
563 /* Offset text to display right next to the image */
564 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
567 vlc_mutex_unlock( &p_sys->lock );
571 /****************************************************************************
572 * RSS related functions
573 ****************************************************************************
574 * You should always lock the p_filter mutex before using any of these
576 ***************************************************************************/
578 #undef LoadImage /* do not conflict with Win32 API */
580 /****************************************************************************
581 * download and resize image located at psz_url
582 ***************************************************************************/
583 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
585 filter_sys_t *p_sys = p_filter->p_sys;
586 video_format_t fmt_in;
587 video_format_t fmt_out;
589 picture_t *p_pic = NULL;
590 image_handler_t *p_handler = image_HandlerCreate( p_filter );
592 memset( &fmt_in, 0, sizeof(video_format_t) );
593 memset( &fmt_out, 0, sizeof(video_format_t) );
595 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
596 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
600 msg_Warn( p_filter, "Unable to read image %s", psz_url );
602 else if( p_sys->p_style->i_font_size > 0 )
605 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
606 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
607 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
608 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
609 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
610 fmt_out.i_height = p_sys->p_style->i_font_size;
612 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
613 p_orig->pf_release( p_orig );
616 msg_Warn( p_filter, "Error while converting %s", psz_url );
624 image_HandlerDelete( p_handler );
629 /****************************************************************************
630 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
632 ***************************************************************************/
633 static char *removeWhiteChars( char *psz_src )
635 char *psz_src2 = strdup( psz_src );
636 char *psz_clean = strdup( psz_src2 );
639 while( ( *psz_clean == ' ' || *psz_clean == '\t'
640 || *psz_clean == '\n' || *psz_clean == '\r' )
641 && *psz_clean != '\0' )
645 i = strlen( psz_clean );
647 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
648 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
649 psz_clean[i+1] = '\0';
650 psz_clean2 = strdup( psz_clean );
655 /****************************************************************************
656 * FetchRSS (or Atom) feeds
657 ***************************************************************************/
658 static int FetchRSS( filter_t *p_filter)
660 filter_sys_t *p_sys = p_filter->p_sys;
662 stream_t *p_stream = NULL;
664 xml_reader_t *p_xml_reader = NULL;
666 char *psz_eltname = NULL;
667 char *psz_eltvalue = NULL;
668 char *psz_feed = NULL;
669 char *psz_buffer = NULL;
670 char *psz_buffer_2 = NULL;
681 while( p_sys->psz_urls[i_int] != 0 )
682 if( p_sys->psz_urls[i_int++] == '|' )
684 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
685 * sizeof( struct rss_feed_t ) );
687 p_xml = xml_Create( p_filter );
690 msg_Err( p_filter, "Failed to open XML parser" );
694 psz_buffer = strdup( p_sys->psz_urls );
695 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
696 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
698 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
700 if( psz_buffer == NULL ) break;
701 if( psz_buffer[0] == 0 ) psz_buffer++;
702 psz_feed = psz_buffer;
703 psz_buffer = strchr( psz_buffer, '|' );
704 if( psz_buffer != NULL ) psz_buffer[0] = 0;
706 p_feed->psz_title = NULL;
707 p_feed->psz_description = NULL;
708 p_feed->psz_link = NULL;
709 p_feed->psz_image = NULL;
710 p_feed->p_pic = NULL;
712 p_feed->p_items = NULL;
714 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
716 p_stream = stream_UrlNew( p_filter, psz_feed );
719 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
723 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
726 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
734 while( xml_ReaderRead( p_xml_reader ) == 1 )
736 switch( xml_ReaderNodeType( p_xml_reader ) )
742 case XML_READER_STARTELEM:
745 psz_eltname = xml_ReaderName( p_xml_reader );
751 msg_Dbg( p_filter, "element name: %s", psz_eltname );
753 if( !strcmp( psz_eltname, "item" ) /* rss */
754 || !strcmp( psz_eltname, "entry" ) ) /* atom */
758 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
759 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
760 p_feed->p_items[p_feed->i_items-1].psz_description
762 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
764 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
768 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
770 char *psz_href = NULL;
771 char *psz_rel = NULL;
772 while( xml_ReaderNextAttr( p_xml_reader )
775 char *psz_name = xml_ReaderName( p_xml_reader );
776 char *psz_value = xml_ReaderValue( p_xml_reader );
777 if( !strcmp( psz_name, "rel" ) )
781 else if( !strcmp( psz_name, "href" ) )
783 psz_href = psz_value;
791 if( psz_rel && psz_href )
793 if( !strcmp( psz_rel, "alternate" )
794 && b_is_item == false
795 && b_is_image == false
796 && !p_feed->psz_link )
798 p_feed->psz_link = psz_href;
800 /* this isn't in the rfc but i found some ... */
801 else if( ( !strcmp( psz_rel, "logo" )
802 || !strcmp( psz_rel, "icon" ) )
803 && b_is_item == false
804 && b_is_image == false
805 && !p_feed->psz_image )
807 p_feed->psz_image = psz_href;
822 case XML_READER_ENDELEM:
825 psz_eltname = xml_ReaderName( p_xml_reader );
831 msg_Dbg( p_filter, "element end : %s", psz_eltname );
833 if( !strcmp( psz_eltname, "item" ) /* rss */
834 || !strcmp( psz_eltname, "entry" ) ) /* atom */
839 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
847 case XML_READER_TEXT:
848 if( !psz_eltname ) break;
849 psz_eltvalue = xml_ReaderValue( p_xml_reader );
857 psz_clean = removeWhiteChars( psz_eltvalue );
858 free( psz_eltvalue ); psz_eltvalue = psz_clean;
861 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
863 if( b_is_item == true )
865 struct rss_item_t *p_item;
866 p_item = p_feed->p_items+i_item;
867 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
868 && !p_item->psz_title )
870 p_item->psz_title = psz_eltvalue;
872 else if( !strcmp( psz_eltname, "link" ) /* rss */
873 && !p_item->psz_link )
875 p_item->psz_link = psz_eltvalue;
877 else if((!strcmp( psz_eltname, "description" ) /* rss */
878 || !strcmp( psz_eltname, "summary" ) ) /* atom */
879 && !p_item->psz_description )
881 p_item->psz_description = psz_eltvalue;
885 free( psz_eltvalue );
889 else if( b_is_image == true )
891 if( !strcmp( psz_eltname, "url" ) /* rss */
892 && !p_feed->psz_image )
894 p_feed->psz_image = psz_eltvalue;
898 free( psz_eltvalue );
904 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
905 && !p_feed->psz_title )
907 p_feed->psz_title = psz_eltvalue;
909 else if( !strcmp( psz_eltname, "link" ) /* rss */
910 && !p_feed->psz_link )
912 p_feed->psz_link = psz_eltvalue;
914 else if((!strcmp( psz_eltname, "description" ) /* rss */
915 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
916 && !p_feed->psz_description )
918 p_feed->psz_description = psz_eltvalue;
920 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
921 || !strcmp( psz_eltname, "icon" ) ) /* atom */
922 && !p_feed->psz_image )
924 p_feed->psz_image = psz_eltvalue;
928 free( psz_eltvalue );
936 if( p_sys->b_images == true
937 && p_feed->psz_image && !p_feed->p_pic )
939 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
942 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
943 if( p_stream ) stream_Delete( p_stream );
944 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
946 free( psz_buffer_2 );
947 if( p_xml ) xml_Delete( p_xml );
952 /****************************************************************************
954 ***************************************************************************/
955 static void FreeRSS( filter_t *p_filter)
957 filter_sys_t *p_sys = p_filter->p_sys;
959 struct rss_item_t *p_item;
960 struct rss_feed_t *p_feed;
965 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
967 p_feed = p_sys->p_feeds+i_feed;
968 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
970 p_item = p_feed->p_items+i_item;
971 free( p_item->psz_title );
972 free( p_item->psz_link );
973 free( p_item->psz_description );
975 free( p_feed->p_items );
976 free( p_feed->psz_title);
977 free( p_feed->psz_link );
978 free( p_feed->psz_description );
979 free( p_feed->psz_image );
980 if( p_feed->p_pic != NULL )
981 p_feed->p_pic->pf_release( p_feed->p_pic );
983 free( p_sys->p_feeds );