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 *****************************************************************************/
36 #include "vlc_filter.h"
37 #include "vlc_block.h"
40 #include "vlc_block.h"
41 #include "vlc_stream.h"
43 #include <vlc_charset.h>
45 #include "vlc_image.h"
47 /*****************************************************************************
49 *****************************************************************************/
50 static int CreateFilter ( vlc_object_t * );
51 static void DestroyFilter( vlc_object_t * );
52 static subpicture_t *Filter( filter_t *, mtime_t );
54 static int FetchRSS( filter_t * );
55 static void FreeRSS( filter_t * );
57 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
58 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
59 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
60 0x00000080, 0x000000FF, 0x0000FFFF};
61 static const char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
62 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
63 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
64 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
67 /*****************************************************************************
68 * filter_sys_t: rss filter descriptor
69 *****************************************************************************/
74 char *psz_description;
81 char *psz_description;
87 struct rss_item_t *p_items;
95 int i_xoff, i_yoff; /* offsets for the display string in the video window */
96 int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
100 char *psz_marquee; /* marquee string */
102 text_style_t *p_style; /* font control */
108 struct rss_feed_t *p_feeds;
111 time_t t_last_update;
120 #define MSG_TEXT N_("Feed URLs")
121 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) seperated URLs.")
122 #define SPEED_TEXT N_("Speed of feeds")
123 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds in microseconds (bigger is slower).")
124 #define LENGTH_TEXT N_("Max length")
125 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
127 #define TTL_TEXT N_("Refresh time")
128 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
129 "of the feeds. 0 means that the feeds are never updated." )
130 #define IMAGE_TEXT N_("Feed images")
131 #define IMAGE_LONGTEXT N_("Display feed images if available.")
133 #define POSX_TEXT N_("X offset")
134 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
135 #define POSY_TEXT N_("Y offset")
136 #define POSY_LONGTEXT N_("Y offset, down from the top." )
137 #define OPACITY_TEXT N_("Opacity")
138 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
139 "overlay text. 0 = transparent, 255 = totally opaque." )
141 #define SIZE_TEXT N_("Font size, pixels")
142 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
145 #define COLOR_TEXT N_("Color")
146 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
147 "the video. This must be an hexadecimal (like HTML colors). The first two "\
148 "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
149 " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
151 #define POS_TEXT N_("Text position")
152 #define POS_LONGTEXT N_( \
153 "You can enforce the text position on the video " \
154 "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
155 "also use combinations of these values, eg 6 = top-right).")
157 #define TITLE_TEXT N_("Title display mode")
158 #define TITLE_LONGTEXT N_("Title display mode. Default is 0 (hidden) if the feed has an image and feed images are enabled, 1 otherwise.")
160 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
161 static const char *ppsz_pos_descriptions[] =
162 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
163 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
171 static int pi_title_modes[] = { default_title, hide_title, prepend_title, scroll_title };
172 static const char *ppsz_title_modes[] =
173 { N_("Default"), N_("Don't show"), N_("Always visible"), N_("Scroll with feed") };
175 #define CFG_PREFIX "rss-"
177 /*****************************************************************************
179 *****************************************************************************/
181 set_capability( "sub filter", 0 );
182 set_shortname( "RSS / Atom" );
183 set_callbacks( CreateFilter, DestroyFilter );
184 set_category( CAT_VIDEO );
185 set_subcategory( SUBCAT_VIDEO_SUBPIC );
186 add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, VLC_FALSE );
188 set_section( N_("Position"), NULL );
189 add_integer( CFG_PREFIX "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
190 add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
191 add_integer( CFG_PREFIX "position", 5, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
192 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
194 set_section( N_("Font"), NULL );
195 /* 5 sets the default to top [1] left [4] */
196 add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
197 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
198 add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
200 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
201 add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
203 set_section( N_("Misc"), NULL );
204 add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
206 add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
208 add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_FALSE );
209 add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, VLC_FALSE );
210 add_integer( CFG_PREFIX "title", default_title, NULL, TITLE_TEXT, TITLE_LONGTEXT, VLC_FALSE );
211 change_integer_list( pi_title_modes, ppsz_title_modes, 0 );
213 set_description( _("RSS and Atom feed display") );
214 add_shortcut( "rss" );
215 add_shortcut( "atom" );
218 static const char *ppsz_filter_options[] = {
219 "urls", "x", "y", "position", "color", "size", "speed", "length",
220 "ttl", "images", "title", NULL
223 /*****************************************************************************
224 * CreateFilter: allocates RSS video filter
225 *****************************************************************************/
226 static int CreateFilter( vlc_object_t *p_this )
228 filter_t *p_filter = (filter_t *)p_this;
232 /* Allocate structure */
233 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
236 msg_Err( p_filter, "out of memory" );
240 vlc_mutex_init( p_filter, &p_sys->lock );
241 vlc_mutex_lock( &p_sys->lock );
243 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
246 p_sys->psz_urls = var_CreateGetString( p_filter, CFG_PREFIX "urls" );
247 p_sys->i_title = var_CreateGetInteger( p_filter, CFG_PREFIX "title" );
248 p_sys->i_cur_feed = 0;
249 p_sys->i_cur_item = p_sys->i_title == scroll_title ? -1 : 0;
250 p_sys->i_cur_char = 0;
252 p_sys->p_feeds = NULL;
253 p_sys->i_speed = var_CreateGetInteger( p_filter, CFG_PREFIX "speed" );
254 p_sys->i_length = var_CreateGetInteger( p_filter, CFG_PREFIX "length" );
255 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, CFG_PREFIX "ttl" ) );
256 p_sys->b_images = var_CreateGetBool( p_filter, CFG_PREFIX "images" );
257 p_sys->psz_marquee = (char *)malloc( p_sys->i_length + 1 );
258 p_sys->psz_marquee[p_sys->i_length] = '\0';
260 p_sys->p_style = malloc( sizeof( text_style_t ));
261 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
263 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
264 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
265 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
266 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
267 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
268 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
270 if( p_sys->b_images == VLC_TRUE && p_sys->p_style->i_font_size == -1 )
272 msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
275 if( FetchRSS( p_filter ) )
277 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
278 vlc_mutex_unlock( &p_sys->lock );
281 p_sys->t_last_update = time( NULL );
283 if( p_sys->i_feeds == 0 )
285 vlc_mutex_unlock( &p_sys->lock );
288 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
289 if( p_sys->p_feeds[i_feed].i_items == 0 )
291 vlc_mutex_unlock( &p_sys->lock );
296 p_filter->pf_sub_filter = Filter;
297 p_sys->last_date = (mtime_t)0;
299 vlc_mutex_unlock( &p_sys->lock );
303 /*****************************************************************************
304 * DestroyFilter: destroy RSS video filter
305 *****************************************************************************/
306 static void DestroyFilter( vlc_object_t *p_this )
308 filter_t *p_filter = (filter_t *)p_this;
309 filter_sys_t *p_sys = p_filter->p_sys;
311 vlc_mutex_lock( &p_sys->lock );
313 if( p_sys->p_style ) free( p_sys->p_style );
314 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
315 free( p_sys->psz_urls );
317 vlc_mutex_unlock( &p_sys->lock );
318 vlc_mutex_destroy( &p_sys->lock );
321 /* Delete the RSS variables */
322 var_Destroy( p_filter, CFG_PREFIX "urls" );
323 var_Destroy( p_filter, CFG_PREFIX "speed" );
324 var_Destroy( p_filter, CFG_PREFIX "length" );
325 var_Destroy( p_filter, CFG_PREFIX "ttl" );
326 var_Destroy( p_filter, CFG_PREFIX "images" );
327 var_Destroy( p_filter, CFG_PREFIX "x" );
328 var_Destroy( p_filter, CFG_PREFIX "y" );
329 var_Destroy( p_filter, CFG_PREFIX "position" );
330 var_Destroy( p_filter, CFG_PREFIX "color");
331 var_Destroy( p_filter, CFG_PREFIX "opacity");
332 var_Destroy( p_filter, CFG_PREFIX "size");
333 var_Destroy( p_filter, CFG_PREFIX "title" );
336 /****************************************************************************
337 * Filter: the whole thing
338 ****************************************************************************
339 * This function outputs subpictures at regular time intervals.
340 ****************************************************************************/
341 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
343 filter_sys_t *p_sys = p_filter->p_sys;
346 subpicture_region_t *p_region;
350 struct rss_feed_t *p_feed;
352 memset( &fmt, 0, sizeof(video_format_t) );
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 if( p_sys->p_style->i_font_size > 0 )
475 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
476 p_spu->i_start = date;
478 p_spu->b_ephemer = VLC_TRUE;
480 /* where to locate the string: */
481 if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
482 { /* set to one of the 9 relative locations */
483 p_spu->p_region->i_align = p_sys->i_pos;
486 p_spu->b_absolute = VLC_FALSE;
489 { /* set to an absolute xy, referenced to upper left corner */
490 p_spu->p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
491 p_spu->i_x = p_sys->i_xoff;
492 p_spu->i_y = p_sys->i_yoff;
493 p_spu->b_absolute = VLC_TRUE;
497 p_spu->p_region->p_style = p_sys->p_style;
501 /* Display the feed's image */
502 picture_t *p_pic = p_feed->p_pic;
503 video_format_t fmt_out;
505 memset( &fmt_out, 0, sizeof(video_format_t) );
507 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
508 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
509 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
511 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
513 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
515 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
518 msg_Err( p_filter, "cannot allocate SPU region" );
522 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
523 p_spu->p_region->p_next = p_region;
526 /* Offset text to display right next to the image */
527 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
530 vlc_mutex_unlock( &p_sys->lock );
534 /****************************************************************************
535 * RSS related functions
536 ****************************************************************************
537 * You should always lock the p_filter mutex before using any of these
539 ***************************************************************************/
541 #undef LoadImage /* do not conflict with Win32 API */
543 /****************************************************************************
544 * download and resize image located at psz_url
545 ***************************************************************************/
546 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
548 filter_sys_t *p_sys = p_filter->p_sys;
549 video_format_t fmt_in;
550 video_format_t fmt_out;
552 picture_t *p_pic = NULL;
553 image_handler_t *p_handler = image_HandlerCreate( p_filter );
555 memset( &fmt_in, 0, sizeof(video_format_t) );
556 memset( &fmt_out, 0, sizeof(video_format_t) );
558 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
559 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
563 msg_Warn( p_filter, "Unable to read image %s", psz_url );
565 else if( p_sys->p_style->i_font_size > 0 )
568 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
569 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
570 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
571 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
572 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
573 fmt_out.i_height = p_sys->p_style->i_font_size;
575 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
576 p_orig->pf_release( p_orig );
579 msg_Warn( p_filter, "Error while converting %s", psz_url );
587 image_HandlerDelete( p_handler );
592 /****************************************************************************
593 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
595 ***************************************************************************/
596 static char *removeWhiteChars( char *psz_src )
598 char *psz_src2 = strdup( psz_src );
599 char *psz_clean = strdup( psz_src2 );
602 while( ( *psz_clean == ' ' || *psz_clean == '\t'
603 || *psz_clean == '\n' || *psz_clean == '\r' )
604 && *psz_clean != '\0' )
608 i = strlen( psz_clean );
610 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
611 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
612 psz_clean[i+1] = '\0';
613 psz_clean2 = strdup( psz_clean );
618 /****************************************************************************
619 * FetchRSS (or Atom) feeds
620 ***************************************************************************/
621 static int FetchRSS( filter_t *p_filter)
623 filter_sys_t *p_sys = p_filter->p_sys;
625 stream_t *p_stream = NULL;
627 xml_reader_t *p_xml_reader = NULL;
629 char *psz_eltname = NULL;
630 char *psz_eltvalue = NULL;
631 char *psz_feed = NULL;
632 char *psz_buffer = NULL;
633 char *psz_buffer_2 = NULL;
637 vlc_bool_t b_is_item;
638 vlc_bool_t b_is_image;
644 while( p_sys->psz_urls[i_int] != 0 )
645 if( p_sys->psz_urls[i_int++] == '|' )
647 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
648 * sizeof( struct rss_feed_t ) );
650 p_xml = xml_Create( p_filter );
653 msg_Err( p_filter, "Failed to open XML parser" );
657 psz_buffer = strdup( p_sys->psz_urls );
658 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
659 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
661 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
663 if( psz_buffer == NULL ) break;
664 if( psz_buffer[0] == 0 ) psz_buffer++;
665 psz_feed = psz_buffer;
666 psz_buffer = strchr( psz_buffer, '|' );
667 if( psz_buffer != NULL ) psz_buffer[0] = 0;
669 p_feed->psz_title = NULL;
670 p_feed->psz_description = NULL;
671 p_feed->psz_link = NULL;
672 p_feed->psz_image = NULL;
673 p_feed->p_pic = NULL;
675 p_feed->p_items = NULL;
677 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
679 p_stream = stream_UrlNew( p_filter, psz_feed );
682 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
686 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
689 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
694 b_is_item = VLC_FALSE;
695 b_is_image = VLC_FALSE;
697 while( xml_ReaderRead( p_xml_reader ) == 1 )
699 switch( xml_ReaderNodeType( p_xml_reader ) )
705 case XML_READER_STARTELEM:
711 psz_eltname = xml_ReaderName( p_xml_reader );
717 msg_Dbg( p_filter, "element name: %s", psz_eltname );
719 if( !strcmp( psz_eltname, "item" ) /* rss */
720 || !strcmp( psz_eltname, "entry" ) ) /* atom */
722 b_is_item = VLC_TRUE;
724 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
725 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
726 p_feed->p_items[p_feed->i_items-1].psz_description
728 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
730 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
732 b_is_image = VLC_TRUE;
734 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
736 char *psz_href = NULL;
737 char *psz_rel = NULL;
738 while( xml_ReaderNextAttr( p_xml_reader )
741 char *psz_name = xml_ReaderName( p_xml_reader );
742 char *psz_value = xml_ReaderValue( p_xml_reader );
743 if( !strcmp( psz_name, "rel" ) )
747 else if( !strcmp( psz_name, "href" ) )
749 psz_href = psz_value;
757 if( psz_rel && psz_href )
759 if( !strcmp( psz_rel, "alternate" )
760 && b_is_item == VLC_FALSE
761 && b_is_image == VLC_FALSE
762 && !p_feed->psz_link )
764 p_feed->psz_link = psz_href;
766 /* this isn't in the rfc but i found some ... */
767 else if( ( !strcmp( psz_rel, "logo" )
768 || !strcmp( psz_rel, "icon" ) )
769 && b_is_item == VLC_FALSE
770 && b_is_image == VLC_FALSE
771 && !p_feed->psz_image )
773 p_feed->psz_image = psz_href;
782 if( psz_href ) free( psz_href );
784 if( psz_rel ) free( psz_rel );
788 case XML_READER_ENDELEM:
794 psz_eltname = xml_ReaderName( p_xml_reader );
800 msg_Dbg( p_filter, "element end : %s", psz_eltname );
802 if( !strcmp( psz_eltname, "item" ) /* rss */
803 || !strcmp( psz_eltname, "entry" ) ) /* atom */
805 b_is_item = VLC_FALSE;
808 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
810 b_is_image = VLC_FALSE;
816 case XML_READER_TEXT:
817 if( !psz_eltname ) break;
818 psz_eltvalue = xml_ReaderValue( p_xml_reader );
826 psz_clean = removeWhiteChars( psz_eltvalue );
827 free( psz_eltvalue ); psz_eltvalue = psz_clean;
830 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
832 if( b_is_item == VLC_TRUE )
834 struct rss_item_t *p_item;
835 p_item = p_feed->p_items+i_item;
836 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
837 && !p_item->psz_title )
839 p_item->psz_title = psz_eltvalue;
841 else if( !strcmp( psz_eltname, "link" ) /* rss */
842 && !p_item->psz_link )
844 p_item->psz_link = psz_eltvalue;
846 else if((!strcmp( psz_eltname, "description" ) /* rss */
847 || !strcmp( psz_eltname, "summary" ) ) /* atom */
848 && !p_item->psz_description )
850 p_item->psz_description = psz_eltvalue;
854 free( psz_eltvalue );
858 else if( b_is_image == VLC_TRUE )
860 if( !strcmp( psz_eltname, "url" ) /* rss */
861 && !p_feed->psz_image )
863 p_feed->psz_image = psz_eltvalue;
867 free( psz_eltvalue );
873 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
874 && !p_feed->psz_title )
876 p_feed->psz_title = psz_eltvalue;
878 else if( !strcmp( psz_eltname, "link" ) /* rss */
879 && !p_feed->psz_link )
881 p_feed->psz_link = psz_eltvalue;
883 else if((!strcmp( psz_eltname, "description" ) /* rss */
884 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
885 && !p_feed->psz_description )
887 p_feed->psz_description = psz_eltvalue;
889 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
890 || !strcmp( psz_eltname, "icon" ) ) /* atom */
891 && !p_feed->psz_image )
893 p_feed->psz_image = psz_eltvalue;
897 free( psz_eltvalue );
905 if( p_sys->b_images == VLC_TRUE
906 && p_feed->psz_image && !p_feed->p_pic )
908 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
911 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
912 if( p_stream ) stream_Delete( p_stream );
913 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
915 free( psz_buffer_2 );
916 if( p_xml ) xml_Delete( p_xml );
921 /****************************************************************************
923 ***************************************************************************/
924 static void FreeRSS( filter_t *p_filter)
926 filter_sys_t *p_sys = p_filter->p_sys;
928 struct rss_item_t *p_item;
929 struct rss_feed_t *p_feed;
934 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
936 p_feed = p_sys->p_feeds+i_feed;
937 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
939 p_item = p_feed->p_items+i_item;
940 free( p_item->psz_title );
941 free( p_item->psz_link );
942 free( p_item->psz_description );
944 free( p_feed->p_items );
945 free( p_feed->psz_title);
946 free( p_feed->psz_link );
947 free( p_feed->psz_description );
948 free( p_feed->psz_image );
949 if( p_feed->p_pic != NULL )
950 p_feed->p_pic->pf_release( p_feed->p_pic );
952 free( p_sys->p_feeds );