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 = text_style_New();
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 );
286 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
287 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
288 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
289 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
290 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
291 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
293 if( p_sys->b_images == true && p_sys->p_style->i_font_size == -1 )
295 msg_Warn( p_filter, "rss-size wasn't specified. Feed images will thus be displayed without being resized" );
298 if( FetchRSS( p_filter ) )
300 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
301 text_style_Delete( p_sys->p_style );
302 free( p_sys->psz_marquee );
303 vlc_mutex_unlock( &p_sys->lock );
304 vlc_mutex_destroy( &p_sys->lock );
305 free( p_sys->psz_urls );
309 p_sys->t_last_update = time( NULL );
311 if( p_sys->i_feeds == 0 )
313 text_style_Delete( p_sys->p_style );
314 free( p_sys->psz_marquee );
315 vlc_mutex_unlock( &p_sys->lock );
316 vlc_mutex_destroy( &p_sys->lock );
317 free( p_sys->psz_urls );
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 text_style_Delete( p_sys->p_style );
326 free( p_sys->psz_marquee );
328 vlc_mutex_unlock( &p_sys->lock );
329 vlc_mutex_destroy( &p_sys->lock );
330 free( p_sys->psz_urls );
336 p_filter->pf_sub_filter = Filter;
337 p_sys->last_date = (mtime_t)0;
339 vlc_mutex_unlock( &p_sys->lock );
343 /*****************************************************************************
344 * DestroyFilter: destroy RSS video filter
345 *****************************************************************************/
346 static void DestroyFilter( vlc_object_t *p_this )
348 filter_t *p_filter = (filter_t *)p_this;
349 filter_sys_t *p_sys = p_filter->p_sys;
351 vlc_mutex_lock( &p_sys->lock );
353 text_style_Delete( p_sys->p_style );
354 free( p_sys->psz_marquee );
355 free( p_sys->psz_urls );
357 vlc_mutex_unlock( &p_sys->lock );
358 vlc_mutex_destroy( &p_sys->lock );
361 /* Delete the RSS variables */
362 var_Destroy( p_filter, CFG_PREFIX "urls" );
363 var_Destroy( p_filter, CFG_PREFIX "speed" );
364 var_Destroy( p_filter, CFG_PREFIX "length" );
365 var_Destroy( p_filter, CFG_PREFIX "ttl" );
366 var_Destroy( p_filter, CFG_PREFIX "images" );
367 var_Destroy( p_filter, CFG_PREFIX "x" );
368 var_Destroy( p_filter, CFG_PREFIX "y" );
369 var_Destroy( p_filter, CFG_PREFIX "position" );
370 var_Destroy( p_filter, CFG_PREFIX "color");
371 var_Destroy( p_filter, CFG_PREFIX "opacity");
372 var_Destroy( p_filter, CFG_PREFIX "size");
373 var_Destroy( p_filter, CFG_PREFIX "title" );
376 /****************************************************************************
377 * Filter: the whole thing
378 ****************************************************************************
379 * This function outputs subpictures at regular time intervals.
380 ****************************************************************************/
381 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
383 filter_sys_t *p_sys = p_filter->p_sys;
386 subpicture_region_t *p_region;
390 struct rss_feed_t *p_feed;
392 memset( &fmt, 0, sizeof(video_format_t) );
394 vlc_mutex_lock( &p_sys->lock );
397 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
398 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
399 * p_sys->i_speed > date )
401 vlc_mutex_unlock( &p_sys->lock );
405 /* Do we need to update the feeds ? */
407 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
409 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
410 if( FetchRSS( p_filter ) )
412 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
413 vlc_mutex_unlock( &p_sys->lock );
414 return NULL; /* FIXME : we most likely messed up all the data,
415 * so we might need to do something about it */
417 p_sys->t_last_update = time( NULL );
420 p_sys->last_date = date;
422 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 )
424 p_sys->i_cur_char = 0;
426 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
428 if( p_sys->i_title == scroll_title )
429 p_sys->i_cur_item = -1;
431 p_sys->i_cur_item = 0;
432 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
436 p_spu = filter_NewSubpicture( p_filter );
439 vlc_mutex_unlock( &p_sys->lock );
443 fmt.i_chroma = VLC_CODEC_TEXT;
445 p_spu->p_region = subpicture_region_New( &fmt );
446 if( !p_spu->p_region )
448 p_filter->pf_sub_buffer_del( p_filter, p_spu );
449 vlc_mutex_unlock( &p_sys->lock );
453 /* Generate the string that will be displayed. This string is supposed to
454 be p_sys->i_length characters long. */
455 i_item = p_sys->i_cur_item;
456 i_feed = p_sys->i_cur_feed;
457 p_feed = &p_sys->p_feeds[i_feed];
459 if( ( p_feed->p_pic && p_sys->i_title == default_title )
460 || p_sys->i_title == hide_title )
462 /* Don't display the feed's title if we have an image */
463 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
464 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
465 +p_sys->i_cur_char );
467 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
468 || p_sys->i_title == prepend_title )
470 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
471 p_sys->p_feeds[i_feed].psz_title,
472 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
473 +p_sys->i_cur_char );
475 else /* scrolling title */
478 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
479 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
480 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
482 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
483 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
484 +p_sys->i_cur_char );
487 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
490 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
491 snprintf( strchr( p_sys->psz_marquee, 0 ),
492 p_sys->i_length - strlen( p_sys->psz_marquee ),
494 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
497 /* Calls to snprintf might split multibyte UTF8 chars ...
498 * which freetype doesn't like. */
500 char *a = strdup( p_sys->psz_marquee );
502 char *b = p_sys->psz_marquee;
503 EnsureUTF8( p_sys->psz_marquee );
504 /* we want to use ' ' instead of '?' for erroneous chars */
507 if( *b != *a ) *b = ' ';
513 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
514 if( p_sys->p_style->i_font_size > 0 )
515 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
516 p_spu->i_start = date;
518 p_spu->b_ephemer = true;
520 /* where to locate the string: */
521 if( p_sys->i_pos < 0 )
522 { /* set to an absolute xy */
523 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
524 p_spu->b_absolute = true;
527 { /* set to one of the 9 relative locations */
528 p_spu->p_region->i_align = p_sys->i_pos;
529 p_spu->b_absolute = false;
532 p_spu->p_region->p_style = text_style_Duplicate( p_sys->p_style );
536 /* Display the feed's image */
537 picture_t *p_pic = p_feed->p_pic;
538 video_format_t fmt_out;
540 memset( &fmt_out, 0, sizeof(video_format_t) );
542 fmt_out.i_chroma = VLC_CODEC_YUVA;
543 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
544 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
546 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
548 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
550 p_region = subpicture_region_New( &fmt_out );
553 msg_Err( p_filter, "cannot allocate SPU region" );
557 p_region->i_x = p_sys->i_xoff;
558 p_region->i_y = p_sys->i_yoff;
559 /* FIXME the copy is probably not needed anymore */
560 picture_Copy( p_region->p_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_CODEC_YUVA;
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_CODEC_YUVA;
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 picture_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:
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 msg_Dbg( p_filter, "\"rel\" attribute of link atom duplicated (last value: %s)", psz_value );
786 else if( !strcmp( psz_name, "href" ) )
790 msg_Dbg( p_filter, "\"href\" attribute of link atom duplicated (last value: %s)", psz_href );
793 psz_href = psz_value;
801 if( psz_rel && psz_href )
803 if( !strcmp( psz_rel, "alternate" )
804 && b_is_item == false
805 && b_is_image == false
806 && !p_feed->psz_link )
808 p_feed->psz_link = psz_href;
810 /* this isn't in the rfc but i found some ... */
811 else if( ( !strcmp( psz_rel, "logo" )
812 || !strcmp( psz_rel, "icon" ) )
813 && b_is_item == false
814 && b_is_image == false
815 && !p_feed->psz_image )
817 p_feed->psz_image = psz_href;
832 case XML_READER_ENDELEM:
835 psz_eltname = xml_ReaderName( p_xml_reader );
841 msg_Dbg( p_filter, "element end : %s", psz_eltname );
843 if( !strcmp( psz_eltname, "item" ) /* rss */
844 || !strcmp( psz_eltname, "entry" ) ) /* atom */
849 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
857 case XML_READER_TEXT:
858 if( !psz_eltname ) break;
859 psz_eltvalue = xml_ReaderValue( p_xml_reader );
867 psz_clean = removeWhiteChars( psz_eltvalue );
868 free( psz_eltvalue ); psz_eltvalue = psz_clean;
871 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
873 if( b_is_item == true )
875 struct rss_item_t *p_item;
876 p_item = p_feed->p_items+i_item;
877 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
878 && !p_item->psz_title )
880 p_item->psz_title = psz_eltvalue;
882 else if( !strcmp( psz_eltname, "link" ) /* rss */
883 && !p_item->psz_link )
885 p_item->psz_link = psz_eltvalue;
887 else if((!strcmp( psz_eltname, "description" ) /* rss */
888 || !strcmp( psz_eltname, "summary" ) ) /* atom */
889 && !p_item->psz_description )
891 p_item->psz_description = psz_eltvalue;
895 free( psz_eltvalue );
899 else if( b_is_image == true )
901 if( !strcmp( psz_eltname, "url" ) /* rss */
902 && !p_feed->psz_image )
904 p_feed->psz_image = psz_eltvalue;
908 free( psz_eltvalue );
914 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
915 && !p_feed->psz_title )
917 p_feed->psz_title = psz_eltvalue;
919 else if( !strcmp( psz_eltname, "link" ) /* rss */
920 && !p_feed->psz_link )
922 p_feed->psz_link = psz_eltvalue;
924 else if((!strcmp( psz_eltname, "description" ) /* rss */
925 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
926 && !p_feed->psz_description )
928 p_feed->psz_description = psz_eltvalue;
930 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
931 || !strcmp( psz_eltname, "icon" ) ) /* atom */
932 && !p_feed->psz_image )
934 p_feed->psz_image = psz_eltvalue;
938 free( psz_eltvalue );
946 if( p_sys->b_images == true
947 && p_feed->psz_image && !p_feed->p_pic )
949 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
952 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
953 if( p_stream ) stream_Delete( p_stream );
954 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
956 free( psz_buffer_2 );
957 if( p_xml ) xml_Delete( p_xml );
962 /****************************************************************************
964 ***************************************************************************/
965 static void FreeRSS( filter_t *p_filter)
967 filter_sys_t *p_sys = p_filter->p_sys;
969 struct rss_item_t *p_item;
970 struct rss_feed_t *p_feed;
975 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
977 p_feed = p_sys->p_feeds+i_feed;
978 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
980 p_item = p_feed->p_items+i_item;
981 free( p_item->psz_title );
982 free( p_item->psz_link );
983 free( p_item->psz_description );
985 free( p_feed->p_items );
986 free( p_feed->psz_title);
987 free( p_feed->psz_link );
988 free( p_feed->psz_description );
989 free( p_feed->psz_image );
990 if( p_feed->p_pic != NULL )
991 picture_Release( p_feed->p_pic );
993 free( p_sys->p_feeds );