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 *****************************************************************************/
32 #include <stdlib.h> /* malloc(), free() */
38 #include "vlc_filter.h"
39 #include "vlc_block.h"
42 #include "vlc_block.h"
43 #include "vlc_stream.h"
45 #include <vlc_charset.h>
47 #include "vlc_image.h"
49 /*****************************************************************************
51 *****************************************************************************/
52 static int CreateFilter ( vlc_object_t * );
53 static void DestroyFilter( vlc_object_t * );
54 static subpicture_t *Filter( filter_t *, mtime_t );
56 static int FetchRSS( filter_t * );
57 static void FreeRSS( filter_t * );
59 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
60 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
61 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
62 0x00000080, 0x000000FF, 0x0000FFFF};
63 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
64 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
65 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
66 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
69 /*****************************************************************************
70 * filter_sys_t: rss filter descriptor
71 *****************************************************************************/
76 char *psz_description;
83 char *psz_description;
89 struct rss_item_t *p_items;
97 int i_xoff, i_yoff; /* offsets for the display string in the video window */
98 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
102 char *psz_marquee; /* marquee string */
104 text_style_t *p_style; /* font control */
110 struct rss_feed_t *p_feeds;
113 time_t t_last_update;
122 #define MSG_TEXT N_("Feed URLs")
123 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) seperated URLs.")
124 #define SPEED_TEXT N_("Speed of feeds")
125 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in milliseconds (bigger is slower).")
126 #define LENGTH_TEXT N_("Max length")
127 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
129 #define TTL_TEXT N_("Refresh time")
130 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
131 "of the feeds. 0 means that the feeds are never updated." )
132 #define IMAGE_TEXT N_("Feed images")
133 #define IMAGE_LONGTEXT N_("Display feed images if available.")
135 #define POSX_TEXT N_("X offset")
136 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
137 #define POSY_TEXT N_("Y offset")
138 #define POSY_LONGTEXT N_("Y offset, down from the top." )
139 #define OPACITY_TEXT N_("Opacity")
140 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
141 "overlay text. 0 = transparent, 255 = totally opaque." )
143 #define SIZE_TEXT N_("Font size, pixels")
144 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
147 #define COLOR_TEXT N_("Color")
148 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
149 "the video. This must be an hexadecimal (like HTML colors). The first two "\
150 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
151 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
153 #define POS_TEXT N_("Text position")
154 #define POS_LONGTEXT N_( \
155 "You can enforce the text position on the video " \
156 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
157 "also use combinations of these values, eg 6 = top-right).")
159 #define TITLE_TEXT N_("Title display mode")
160 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
162 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
163 static const char *ppsz_pos_descriptions[] =
164 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
165 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
173 static int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
174 static const char *ppsz_title_modes[] =
175 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
177 #define CFG_PREFIX "rss-"
179 /*****************************************************************************
181 *****************************************************************************/
183 set_capability( "sub filter", 0 );
184 set_shortname( "RSS / Atom" );
185 set_callbacks( CreateFilter, DestroyFilter );
186 set_category( CAT_VIDEO );
187 set_subcategory( SUBCAT_VIDEO_SUBPIC );
188 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, VLC_FALSE );
190 set_section( N_("Position"), NULL );
191 add_integer( CFG_PREFIX "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
192 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
193 add_integer( CFG_PREFIX "position", 5, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
194 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
196 set_section( N_("Font"), NULL );
197 /* 5 sets the default to top [1] left [4] */
198 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
199 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
200 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
202 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
203 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
205 set_section( N_("Misc"), NULL );
206 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
208 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
210 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_FALSE );
211 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, VLC_FALSE );
212 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, VLC_FALSE );
213 change_integer_list( pi_title_modes, ppsz_title_modes, 0 );
215 set_description( _("RSS and Atom feed display") );
216 add_shortcut( "rss" );
217 add_shortcut( "atom" );
220 static const char *ppsz_filter_options[] = {
221 "urls", "x", "y", "position", "color", "size", "speed", "length",
222 "ttl", "images", "title", NULL
225 /*****************************************************************************
226 * CreateFilter: allocates RSS video filter
227 *****************************************************************************/
228 static int CreateFilter( vlc_object_t *p_this )
230 filter_t *p_filter = (filter_t *)p_this;
234 /* Allocate structure */
235 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
238 msg_Err( p_filter, "out of memory" );
242 vlc_mutex_init( p_filter, &p_sys->lock );
243 vlc_mutex_lock( &p_sys->lock );
245 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
248 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
249 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
250 p_sys->i_cur_feed = 0;
251 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
252 p_sys->i_cur_char = 0;
254 p_sys->p_feeds = NULL;
255 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
256 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
257 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
258 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
259 p_sys->psz_marquee = (char *)malloc( p_sys->i_length );
261 p_sys->p_style = malloc( sizeof( text_style_t ));
262 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
264 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
265 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
266 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
267 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
268 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
269 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
271 if( p_sys->b_images == VLC_TRUE && p_sys->p_style->i_font_size == -1 )
273 msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
276 if( FetchRSS( p_filter ) )
278 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
279 vlc_mutex_unlock( &p_sys->lock );
282 p_sys->t_last_update = time( NULL );
284 if( p_sys->i_feeds == 0 )
286 vlc_mutex_unlock( &p_sys->lock );
289 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
290 if( p_sys->p_feeds[i_feed].i_items == 0 )
292 vlc_mutex_unlock( &p_sys->lock );
297 p_filter->pf_sub_filter = Filter;
298 p_sys->last_date = (mtime_t)0;
300 vlc_mutex_unlock( &p_sys->lock );
304 /*****************************************************************************
305 * DestroyFilter: destroy RSS video filter
306 *****************************************************************************/
307 static void DestroyFilter( vlc_object_t *p_this )
309 filter_t *p_filter = (filter_t *)p_this;
310 filter_sys_t *p_sys = p_filter->p_sys;
312 vlc_mutex_lock( &p_sys->lock );
314 if( p_sys->p_style ) free( p_sys->p_style );
315 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
316 free( p_sys->psz_urls );
318 vlc_mutex_unlock( &p_sys->lock );
319 vlc_mutex_destroy( &p_sys->lock );
322 /* Delete the RSS variables */
323 var_Destroy( p_filter, CFG_PREFIX "urls" );
324 var_Destroy( p_filter, CFG_PREFIX "speed" );
325 var_Destroy( p_filter, CFG_PREFIX "length" );
326 var_Destroy( p_filter, CFG_PREFIX "ttl" );
327 var_Destroy( p_filter, CFG_PREFIX "images" );
328 var_Destroy( p_filter, CFG_PREFIX "x" );
329 var_Destroy( p_filter, CFG_PREFIX "y" );
330 var_Destroy( p_filter, CFG_PREFIX "position" );
331 var_Destroy( p_filter, CFG_PREFIX "color");
332 var_Destroy( p_filter, CFG_PREFIX "opacity");
333 var_Destroy( p_filter, CFG_PREFIX "size");
334 var_Destroy( p_filter, CFG_PREFIX "title" );
337 /****************************************************************************
338 * Filter: the whole thing
339 ****************************************************************************
340 * This function outputs subpictures at regular time intervals.
341 ****************************************************************************/
342 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
344 filter_sys_t *p_sys = p_filter->p_sys;
346 video_format_t fmt = {0};
348 subpicture_region_t *p_region;
352 struct rss_feed_t *p_feed;
354 vlc_mutex_lock( &p_sys->lock );
357 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
358 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
359 * p_sys->i_speed > date )
361 vlc_mutex_unlock( &p_sys->lock );
365 /* Do we need to update the feeds ? */
367 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
369 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
370 if( FetchRSS( p_filter ) )
372 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
373 vlc_mutex_unlock( &p_sys->lock );
374 return NULL; /* FIXME : we most likely messed up all the data,
375 * so we might need to do something about it */
377 p_sys->t_last_update = time( NULL );
380 p_sys->last_date = date;
382 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 )
384 p_sys->i_cur_char = 0;
386 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
388 if( p_sys->i_title == scroll_title )
389 p_sys->i_cur_item = -1;
391 p_sys->i_cur_item = 0;
392 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
396 p_spu = p_filter->pf_sub_buffer_new( p_filter );
399 vlc_mutex_unlock( &p_sys->lock );
403 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
405 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
406 if( !p_spu->p_region )
408 p_filter->pf_sub_buffer_del( p_filter, p_spu );
409 vlc_mutex_unlock( &p_sys->lock );
413 /* Generate the string that will be displayed. This string is supposed to
414 be p_sys->i_length characters long. */
415 i_item = p_sys->i_cur_item;
416 i_feed = p_sys->i_cur_feed;
417 p_feed = &p_sys->p_feeds[i_feed];
419 if( ( p_feed->p_pic && p_sys->i_title == default_title )
420 || p_sys->i_title == hide_title )
422 /* Don't display the feed's title if we have an image */
423 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
424 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
425 +p_sys->i_cur_char );
427 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
428 || p_sys->i_title == prepend_title )
430 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
431 p_sys->p_feeds[i_feed].psz_title,
432 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
433 +p_sys->i_cur_char );
435 else /* scrolling title */
438 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
439 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
440 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
442 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
443 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
444 +p_sys->i_cur_char );
447 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
450 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
451 snprintf( strchr( p_sys->psz_marquee, 0 ),
452 p_sys->i_length - strlen( p_sys->psz_marquee ),
454 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
457 /* Calls to snprintf might split multibyte UTF8 chars ...
458 * which freetype doesn't like. */
460 char *a = strdup( p_sys->psz_marquee );
462 char *b = p_sys->psz_marquee;
463 EnsureUTF8( p_sys->psz_marquee );
464 /* we want to use ' ' instead of '?' for erroneous chars */
467 if( *b != *a ) *b = ' ';
473 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
474 p_spu->i_start = date;
476 p_spu->b_ephemer = VLC_TRUE;
478 /* where to locate the string: */
479 if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
480 { /* set to one of the 9 relative locations */
481 p_spu->i_flags = p_sys->i_pos;
484 p_spu->b_absolute = VLC_FALSE;
487 { /* set to an absolute xy, referenced to upper left corner */
488 p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
489 p_spu->i_x = p_sys->i_xoff;
490 p_spu->i_y = p_sys->i_yoff;
491 p_spu->b_absolute = VLC_TRUE;
495 p_spu->p_region->p_style = p_sys->p_style;
499 /* Display the feed's image */
500 picture_t *p_pic = p_feed->p_pic;
501 video_format_t fmt_out = {0};
503 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
504 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
505 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
507 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
509 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
511 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
514 msg_Err( p_filter, "cannot allocate SPU region" );
518 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
519 p_spu->p_region->p_next = p_region;
522 /* Offset text to display right next to the image */
523 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
526 vlc_mutex_unlock( &p_sys->lock );
530 /****************************************************************************
531 * RSS related functions
532 ****************************************************************************
533 * You should always lock the p_filter mutex before using any of these
535 ***************************************************************************/
537 #undef LoadImage /* do not conflict with Win32 API */
539 /****************************************************************************
540 * download and resize image located at psz_url
541 ***************************************************************************/
542 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
544 filter_sys_t *p_sys = p_filter->p_sys;
546 video_format_t fmt_in={0}, fmt_out={0};
547 picture_t *p_orig, *p_pic=NULL;
548 image_handler_t *p_handler = image_HandlerCreate( p_filter );
550 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
551 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
555 msg_Warn( p_filter, "Unable to read image %s", psz_url );
557 else if( p_sys->p_style->i_font_size > 0 )
560 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
561 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
562 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
563 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
564 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
565 fmt_out.i_height = p_sys->p_style->i_font_size;
567 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
568 p_orig->pf_release( p_orig );
571 msg_Warn( p_filter, "Error while converting %s", psz_url );
579 image_HandlerDelete( p_handler );
584 /****************************************************************************
585 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
587 ***************************************************************************/
588 static char *removeWhiteChars( char *psz_src )
590 char *psz_src2 = strdup( psz_src );
591 char *psz_clean = strdup( psz_src2 );
594 while( ( *psz_clean == ' ' || *psz_clean == '\t'
595 || *psz_clean == '\n' || *psz_clean == '\r' )
596 && *psz_clean != '\0' )
600 i = strlen( psz_clean );
602 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
603 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
604 psz_clean[i+1] = '\0';
605 psz_clean2 = strdup( psz_clean );
610 /****************************************************************************
611 * FetchRSS (or Atom) feeds
612 ***************************************************************************/
613 static int FetchRSS( filter_t *p_filter)
615 filter_sys_t *p_sys = p_filter->p_sys;
617 stream_t *p_stream = NULL;
619 xml_reader_t *p_xml_reader = NULL;
621 char *psz_eltname = NULL;
622 char *psz_eltvalue = NULL;
623 char *psz_feed = NULL;
624 char *psz_buffer = NULL;
625 char *psz_buffer_2 = NULL;
629 vlc_bool_t b_is_item;
630 vlc_bool_t b_is_image;
636 while( p_sys->psz_urls[i_int] != 0 )
637 if( p_sys->psz_urls[i_int++] == '|' )
639 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
640 * sizeof( struct rss_feed_t ) );
642 p_xml = xml_Create( p_filter );
645 msg_Err( p_filter, "Failed to open XML parser" );
649 psz_buffer = strdup( p_sys->psz_urls );
650 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
651 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
653 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
655 if( psz_buffer == NULL ) break;
656 if( psz_buffer[0] == 0 ) psz_buffer++;
657 psz_feed = psz_buffer;
658 psz_buffer = strchr( psz_buffer, '|' );
659 if( psz_buffer != NULL ) psz_buffer[0] = 0;
661 p_feed->psz_title = NULL;
662 p_feed->psz_description = NULL;
663 p_feed->psz_link = NULL;
664 p_feed->psz_image = NULL;
665 p_feed->p_pic = NULL;
667 p_feed->p_items = NULL;
669 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
671 p_stream = stream_UrlNew( p_filter, psz_feed );
674 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
678 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
681 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
686 b_is_item = VLC_FALSE;
687 b_is_image = VLC_FALSE;
689 while( xml_ReaderRead( p_xml_reader ) == 1 )
691 switch( xml_ReaderNodeType( p_xml_reader ) )
697 case XML_READER_STARTELEM:
703 psz_eltname = xml_ReaderName( p_xml_reader );
709 msg_Dbg( p_filter, "element name: %s", psz_eltname );
711 if( !strcmp( psz_eltname, "item" ) /* rss */
712 || !strcmp( psz_eltname, "entry" ) ) /* atom */
714 b_is_item = VLC_TRUE;
716 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
717 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
718 p_feed->p_items[p_feed->i_items-1].psz_description
720 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
722 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
724 b_is_image = VLC_TRUE;
726 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
728 char *psz_href = NULL;
729 char *psz_rel = NULL;
730 while( xml_ReaderNextAttr( p_xml_reader )
733 char *psz_name = xml_ReaderName( p_xml_reader );
734 char *psz_value = xml_ReaderValue( p_xml_reader );
735 if( !strcmp( psz_name, "rel" ) )
739 else if( !strcmp( psz_name, "href" ) )
741 psz_href = psz_value;
749 if( psz_rel && psz_href )
751 if( !strcmp( psz_rel, "alternate" )
752 && b_is_item == VLC_FALSE
753 && b_is_image == VLC_FALSE
754 && !p_feed->psz_link )
756 p_feed->psz_link = psz_href;
758 /* this isn't in the rfc but i found some ... */
759 else if( ( !strcmp( psz_rel, "logo" )
760 || !strcmp( psz_rel, "icon" ) )
761 && b_is_item == VLC_FALSE
762 && b_is_image == VLC_FALSE
763 && !p_feed->psz_image )
765 p_feed->psz_image = psz_href;
774 if( psz_href ) free( psz_href );
776 if( psz_rel ) free( psz_rel );
780 case XML_READER_ENDELEM:
786 psz_eltname = xml_ReaderName( p_xml_reader );
792 msg_Dbg( p_filter, "element end : %s", psz_eltname );
794 if( !strcmp( psz_eltname, "item" ) /* rss */
795 || !strcmp( psz_eltname, "entry" ) ) /* atom */
797 b_is_item = VLC_FALSE;
800 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
802 b_is_image = VLC_FALSE;
808 case XML_READER_TEXT:
809 if( !psz_eltname ) break;
810 psz_eltvalue = xml_ReaderValue( p_xml_reader );
818 psz_clean = removeWhiteChars( psz_eltvalue );
819 free( psz_eltvalue ); psz_eltvalue = psz_clean;
822 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
824 if( b_is_item == VLC_TRUE )
826 struct rss_item_t *p_item;
827 p_item = p_feed->p_items+i_item;
828 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
829 && !p_item->psz_title )
831 p_item->psz_title = psz_eltvalue;
833 else if( !strcmp( psz_eltname, "link" ) /* rss */
834 && !p_item->psz_link )
836 p_item->psz_link = psz_eltvalue;
838 else if((!strcmp( psz_eltname, "description" ) /* rss */
839 || !strcmp( psz_eltname, "summary" ) ) /* atom */
840 && !p_item->psz_description )
842 p_item->psz_description = psz_eltvalue;
846 free( psz_eltvalue );
850 else if( b_is_image == VLC_TRUE )
852 if( !strcmp( psz_eltname, "url" ) /* rss */
853 && !p_feed->psz_image )
855 p_feed->psz_image = psz_eltvalue;
859 free( psz_eltvalue );
865 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
866 && !p_feed->psz_title )
868 p_feed->psz_title = psz_eltvalue;
870 else if( !strcmp( psz_eltname, "link" ) /* rss */
871 && !p_feed->psz_link )
873 p_feed->psz_link = psz_eltvalue;
875 else if((!strcmp( psz_eltname, "description" ) /* rss */
876 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
877 && !p_feed->psz_description )
879 p_feed->psz_description = psz_eltvalue;
881 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
882 || !strcmp( psz_eltname, "icon" ) ) /* atom */
883 && !p_feed->psz_image )
885 p_feed->psz_image = psz_eltvalue;
889 free( psz_eltvalue );
897 if( p_sys->b_images == VLC_TRUE
898 && p_feed->psz_image && !p_feed->p_pic )
900 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
903 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
904 if( p_stream ) stream_Delete( p_stream );
905 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
907 free( psz_buffer_2 );
908 if( p_xml ) xml_Delete( p_xml );
913 /****************************************************************************
915 ***************************************************************************/
916 static void FreeRSS( filter_t *p_filter)
918 filter_sys_t *p_sys = p_filter->p_sys;
920 struct rss_item_t *p_item;
921 struct rss_feed_t *p_feed;
926 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
928 p_feed = p_sys->p_feeds+i_feed;
929 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
931 p_item = p_feed->p_items+i_item;
932 free( p_item->psz_title );
933 free( p_item->psz_link );
934 free( p_item->psz_description );
936 free( p_feed->p_items );
937 free( p_feed->psz_title);
938 free( p_feed->psz_link );
939 free( p_feed->psz_description );
940 free( p_feed->psz_image );
941 if( p_feed->p_pic != NULL )
942 p_feed->p_pic->pf_release( p_feed->p_pic );
944 free( p_sys->p_feeds );