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 microseconds (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 + 1 );
260 p_sys->psz_marquee[p_sys->i_length] = '\0';
262 p_sys->p_style = malloc( sizeof( text_style_t ));
263 memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
265 p_sys->i_xoff = var_CreateGetInteger( p_filter, CFG_PREFIX "x" );
266 p_sys->i_yoff = var_CreateGetInteger( p_filter, CFG_PREFIX "y" );
267 p_sys->i_pos = var_CreateGetInteger( p_filter, CFG_PREFIX "position" );
268 p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, CFG_PREFIX "opacity" );
269 p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, CFG_PREFIX "color" );
270 p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, CFG_PREFIX "size" );
272 if( p_sys->b_images == VLC_TRUE && p_sys->p_style->i_font_size == -1 )
274 msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
277 if( FetchRSS( p_filter ) )
279 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
280 vlc_mutex_unlock( &p_sys->lock );
283 p_sys->t_last_update = time( NULL );
285 if( p_sys->i_feeds == 0 )
287 vlc_mutex_unlock( &p_sys->lock );
290 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
291 if( p_sys->p_feeds[i_feed].i_items == 0 )
293 vlc_mutex_unlock( &p_sys->lock );
298 p_filter->pf_sub_filter = Filter;
299 p_sys->last_date = (mtime_t)0;
301 vlc_mutex_unlock( &p_sys->lock );
305 /*****************************************************************************
306 * DestroyFilter: destroy RSS video filter
307 *****************************************************************************/
308 static void DestroyFilter( vlc_object_t *p_this )
310 filter_t *p_filter = (filter_t *)p_this;
311 filter_sys_t *p_sys = p_filter->p_sys;
313 vlc_mutex_lock( &p_sys->lock );
315 if( p_sys->p_style ) free( p_sys->p_style );
316 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
317 free( p_sys->psz_urls );
319 vlc_mutex_unlock( &p_sys->lock );
320 vlc_mutex_destroy( &p_sys->lock );
323 /* Delete the RSS variables */
324 var_Destroy( p_filter, CFG_PREFIX "urls" );
325 var_Destroy( p_filter, CFG_PREFIX "speed" );
326 var_Destroy( p_filter, CFG_PREFIX "length" );
327 var_Destroy( p_filter, CFG_PREFIX "ttl" );
328 var_Destroy( p_filter, CFG_PREFIX "images" );
329 var_Destroy( p_filter, CFG_PREFIX "x" );
330 var_Destroy( p_filter, CFG_PREFIX "y" );
331 var_Destroy( p_filter, CFG_PREFIX "position" );
332 var_Destroy( p_filter, CFG_PREFIX "color");
333 var_Destroy( p_filter, CFG_PREFIX "opacity");
334 var_Destroy( p_filter, CFG_PREFIX "size");
335 var_Destroy( p_filter, CFG_PREFIX "title" );
338 /****************************************************************************
339 * Filter: the whole thing
340 ****************************************************************************
341 * This function outputs subpictures at regular time intervals.
342 ****************************************************************************/
343 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
345 filter_sys_t *p_sys = p_filter->p_sys;
347 video_format_t fmt = {0};
349 subpicture_region_t *p_region;
353 struct rss_feed_t *p_feed;
355 vlc_mutex_lock( &p_sys->lock );
358 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 )
359 /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */
360 * p_sys->i_speed > date )
362 vlc_mutex_unlock( &p_sys->lock );
366 /* Do we need to update the feeds ? */
368 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
370 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
371 if( FetchRSS( p_filter ) )
373 msg_Err( p_filter, "Failed while fetching RSS ... too bad" );
374 vlc_mutex_unlock( &p_sys->lock );
375 return NULL; /* FIXME : we most likely messed up all the data,
376 * so we might need to do something about it */
378 p_sys->t_last_update = time( NULL );
381 p_sys->last_date = date;
383 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 )
385 p_sys->i_cur_char = 0;
387 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
389 if( p_sys->i_title == scroll_title )
390 p_sys->i_cur_item = -1;
392 p_sys->i_cur_item = 0;
393 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
397 p_spu = p_filter->pf_sub_buffer_new( p_filter );
400 vlc_mutex_unlock( &p_sys->lock );
404 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
406 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
407 if( !p_spu->p_region )
409 p_filter->pf_sub_buffer_del( p_filter, p_spu );
410 vlc_mutex_unlock( &p_sys->lock );
414 /* Generate the string that will be displayed. This string is supposed to
415 be p_sys->i_length characters long. */
416 i_item = p_sys->i_cur_item;
417 i_feed = p_sys->i_cur_feed;
418 p_feed = &p_sys->p_feeds[i_feed];
420 if( ( p_feed->p_pic && p_sys->i_title == default_title )
421 || p_sys->i_title == hide_title )
423 /* Don't display the feed's title if we have an image */
424 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
425 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
426 +p_sys->i_cur_char );
428 else if( ( !p_feed->p_pic && p_sys->i_title == default_title )
429 || p_sys->i_title == prepend_title )
431 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
432 p_sys->p_feeds[i_feed].psz_title,
433 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
434 +p_sys->i_cur_char );
436 else /* scrolling title */
439 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
440 p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char,
441 p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title );
443 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
444 p_sys->p_feeds[i_feed].p_items[i_item].psz_title
445 +p_sys->i_cur_char );
448 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
451 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
452 snprintf( strchr( p_sys->psz_marquee, 0 ),
453 p_sys->i_length - strlen( p_sys->psz_marquee ),
455 p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
458 /* Calls to snprintf might split multibyte UTF8 chars ...
459 * which freetype doesn't like. */
461 char *a = strdup( p_sys->psz_marquee );
463 char *b = p_sys->psz_marquee;
464 EnsureUTF8( p_sys->psz_marquee );
465 /* we want to use ' ' instead of '?' for erroneous chars */
468 if( *b != *a ) *b = ' ';
474 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
475 if( p_sys->p_style->i_font_size > 0 )
476 p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size;
477 p_spu->i_start = date;
479 p_spu->b_ephemer = VLC_TRUE;
481 /* where to locate the string: */
482 if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
483 { /* set to one of the 9 relative locations */
484 p_spu->i_flags = p_sys->i_pos;
487 p_spu->b_absolute = VLC_FALSE;
490 { /* set to an absolute xy, referenced to upper left corner */
491 p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
492 p_spu->i_x = p_sys->i_xoff;
493 p_spu->i_y = p_sys->i_yoff;
494 p_spu->b_absolute = VLC_TRUE;
498 p_spu->p_region->p_style = p_sys->p_style;
502 /* Display the feed's image */
503 picture_t *p_pic = p_feed->p_pic;
504 video_format_t fmt_out = {0};
506 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
507 fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
508 fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
510 fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
512 fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
514 p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
517 msg_Err( p_filter, "cannot allocate SPU region" );
521 vout_CopyPicture( p_filter, &p_region->picture, p_pic );
522 p_spu->p_region->p_next = p_region;
525 /* Offset text to display right next to the image */
526 p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
529 vlc_mutex_unlock( &p_sys->lock );
533 /****************************************************************************
534 * RSS related functions
535 ****************************************************************************
536 * You should always lock the p_filter mutex before using any of these
538 ***************************************************************************/
540 #undef LoadImage /* do not conflict with Win32 API */
542 /****************************************************************************
543 * download and resize image located at psz_url
544 ***************************************************************************/
545 static picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
547 filter_sys_t *p_sys = p_filter->p_sys;
549 video_format_t fmt_in={0}, fmt_out={0};
550 picture_t *p_orig, *p_pic=NULL;
551 image_handler_t *p_handler = image_HandlerCreate( p_filter );
553 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
554 p_orig = image_ReadUrl( p_handler, psz_url, &fmt_in, &fmt_out );
558 msg_Warn( p_filter, "Unable to read image %s", psz_url );
560 else if( p_sys->p_style->i_font_size > 0 )
563 fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
564 fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
565 fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
566 fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
567 *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
568 fmt_out.i_height = p_sys->p_style->i_font_size;
570 p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
571 p_orig->pf_release( p_orig );
574 msg_Warn( p_filter, "Error while converting %s", psz_url );
582 image_HandlerDelete( p_handler );
587 /****************************************************************************
588 * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
590 ***************************************************************************/
591 static char *removeWhiteChars( char *psz_src )
593 char *psz_src2 = strdup( psz_src );
594 char *psz_clean = strdup( psz_src2 );
597 while( ( *psz_clean == ' ' || *psz_clean == '\t'
598 || *psz_clean == '\n' || *psz_clean == '\r' )
599 && *psz_clean != '\0' )
603 i = strlen( psz_clean );
605 ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
606 || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
607 psz_clean[i+1] = '\0';
608 psz_clean2 = strdup( psz_clean );
613 /****************************************************************************
614 * FetchRSS (or Atom) feeds
615 ***************************************************************************/
616 static int FetchRSS( filter_t *p_filter)
618 filter_sys_t *p_sys = p_filter->p_sys;
620 stream_t *p_stream = NULL;
622 xml_reader_t *p_xml_reader = NULL;
624 char *psz_eltname = NULL;
625 char *psz_eltvalue = NULL;
626 char *psz_feed = NULL;
627 char *psz_buffer = NULL;
628 char *psz_buffer_2 = NULL;
632 vlc_bool_t b_is_item;
633 vlc_bool_t b_is_image;
639 while( p_sys->psz_urls[i_int] != 0 )
640 if( p_sys->psz_urls[i_int++] == '|' )
642 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
643 * sizeof( struct rss_feed_t ) );
645 p_xml = xml_Create( p_filter );
648 msg_Err( p_filter, "Failed to open XML parser" );
652 psz_buffer = strdup( p_sys->psz_urls );
653 psz_buffer_2 = psz_buffer; /* keep track so we can free it */
654 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
656 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
658 if( psz_buffer == NULL ) break;
659 if( psz_buffer[0] == 0 ) psz_buffer++;
660 psz_feed = psz_buffer;
661 psz_buffer = strchr( psz_buffer, '|' );
662 if( psz_buffer != NULL ) psz_buffer[0] = 0;
664 p_feed->psz_title = NULL;
665 p_feed->psz_description = NULL;
666 p_feed->psz_link = NULL;
667 p_feed->psz_image = NULL;
668 p_feed->p_pic = NULL;
670 p_feed->p_items = NULL;
672 msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
674 p_stream = stream_UrlNew( p_filter, psz_feed );
677 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
681 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
684 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
689 b_is_item = VLC_FALSE;
690 b_is_image = VLC_FALSE;
692 while( xml_ReaderRead( p_xml_reader ) == 1 )
694 switch( xml_ReaderNodeType( p_xml_reader ) )
700 case XML_READER_STARTELEM:
706 psz_eltname = xml_ReaderName( p_xml_reader );
712 msg_Dbg( p_filter, "element name: %s", psz_eltname );
714 if( !strcmp( psz_eltname, "item" ) /* rss */
715 || !strcmp( psz_eltname, "entry" ) ) /* atom */
717 b_is_item = VLC_TRUE;
719 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
720 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
721 p_feed->p_items[p_feed->i_items-1].psz_description
723 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
725 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
727 b_is_image = VLC_TRUE;
729 else if( !strcmp( psz_eltname, "link" ) ) /* atom */
731 char *psz_href = NULL;
732 char *psz_rel = NULL;
733 while( xml_ReaderNextAttr( p_xml_reader )
736 char *psz_name = xml_ReaderName( p_xml_reader );
737 char *psz_value = xml_ReaderValue( p_xml_reader );
738 if( !strcmp( psz_name, "rel" ) )
742 else if( !strcmp( psz_name, "href" ) )
744 psz_href = psz_value;
752 if( psz_rel && psz_href )
754 if( !strcmp( psz_rel, "alternate" )
755 && b_is_item == VLC_FALSE
756 && b_is_image == VLC_FALSE
757 && !p_feed->psz_link )
759 p_feed->psz_link = psz_href;
761 /* this isn't in the rfc but i found some ... */
762 else if( ( !strcmp( psz_rel, "logo" )
763 || !strcmp( psz_rel, "icon" ) )
764 && b_is_item == VLC_FALSE
765 && b_is_image == VLC_FALSE
766 && !p_feed->psz_image )
768 p_feed->psz_image = psz_href;
777 if( psz_href ) free( psz_href );
779 if( psz_rel ) free( psz_rel );
783 case XML_READER_ENDELEM:
789 psz_eltname = xml_ReaderName( p_xml_reader );
795 msg_Dbg( p_filter, "element end : %s", psz_eltname );
797 if( !strcmp( psz_eltname, "item" ) /* rss */
798 || !strcmp( psz_eltname, "entry" ) ) /* atom */
800 b_is_item = VLC_FALSE;
803 else if( !strcmp( psz_eltname, "image" ) ) /* rss */
805 b_is_image = VLC_FALSE;
811 case XML_READER_TEXT:
812 if( !psz_eltname ) break;
813 psz_eltvalue = xml_ReaderValue( p_xml_reader );
821 psz_clean = removeWhiteChars( psz_eltvalue );
822 free( psz_eltvalue ); psz_eltvalue = psz_clean;
825 msg_Dbg( p_filter, " text : <%s>", psz_eltvalue );
827 if( b_is_item == VLC_TRUE )
829 struct rss_item_t *p_item;
830 p_item = p_feed->p_items+i_item;
831 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
832 && !p_item->psz_title )
834 p_item->psz_title = psz_eltvalue;
836 else if( !strcmp( psz_eltname, "link" ) /* rss */
837 && !p_item->psz_link )
839 p_item->psz_link = psz_eltvalue;
841 else if((!strcmp( psz_eltname, "description" ) /* rss */
842 || !strcmp( psz_eltname, "summary" ) ) /* atom */
843 && !p_item->psz_description )
845 p_item->psz_description = psz_eltvalue;
849 free( psz_eltvalue );
853 else if( b_is_image == VLC_TRUE )
855 if( !strcmp( psz_eltname, "url" ) /* rss */
856 && !p_feed->psz_image )
858 p_feed->psz_image = psz_eltvalue;
862 free( psz_eltvalue );
868 if( !strcmp( psz_eltname, "title" ) /* rss/atom */
869 && !p_feed->psz_title )
871 p_feed->psz_title = psz_eltvalue;
873 else if( !strcmp( psz_eltname, "link" ) /* rss */
874 && !p_feed->psz_link )
876 p_feed->psz_link = psz_eltvalue;
878 else if((!strcmp( psz_eltname, "description" ) /* rss */
879 || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
880 && !p_feed->psz_description )
882 p_feed->psz_description = psz_eltvalue;
884 else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
885 || !strcmp( psz_eltname, "icon" ) ) /* atom */
886 && !p_feed->psz_image )
888 p_feed->psz_image = psz_eltvalue;
892 free( psz_eltvalue );
900 if( p_sys->b_images == VLC_TRUE
901 && p_feed->psz_image && !p_feed->p_pic )
903 p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
906 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
907 if( p_stream ) stream_Delete( p_stream );
908 msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
910 free( psz_buffer_2 );
911 if( p_xml ) xml_Delete( p_xml );
916 /****************************************************************************
918 ***************************************************************************/
919 static void FreeRSS( filter_t *p_filter)
921 filter_sys_t *p_sys = p_filter->p_sys;
923 struct rss_item_t *p_item;
924 struct rss_feed_t *p_feed;
929 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
931 p_feed = p_sys->p_feeds+i_feed;
932 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
934 p_item = p_feed->p_items+i_item;
935 free( p_item->psz_title );
936 free( p_item->psz_link );
937 free( p_item->psz_description );
939 free( p_feed->p_items );
940 free( p_feed->psz_title);
941 free( p_feed->psz_link );
942 free( p_feed->psz_description );
943 free( p_feed->psz_image );
944 if( p_feed->p_pic != NULL )
945 p_feed->p_pic->pf_release( p_feed->p_pic );
947 free( p_sys->p_feeds );