]> git.sesse.net Git - vlc/blob - modules/video_filter/rss.c
use config chain stuff in logo, rss and mosaic sub filters too.
[vlc] / modules / video_filter / rss.c
1 /*****************************************************************************
2  * rss.c : rss/atom feed display video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Atom : http://www.ietf.org/rfc/rfc4287.txt
26  * RSS : http://www.rssboard.org/rss-specification
27  *****************************************************************************/
28
29 /*****************************************************************************
30  * Preamble
31  *****************************************************************************/
32 #include <stdlib.h>                                      /* malloc(), free() */
33 #include <string.h>
34
35 #include <vlc/vlc.h>
36 #include <vlc/vout.h>
37
38 #include "vlc_filter.h"
39 #include "vlc_block.h"
40 #include "vlc_osd.h"
41
42 #include "vlc_block.h"
43 #include "vlc_stream.h"
44 #include "vlc_xml.h"
45 #include "charset.h"
46
47 #include "vlc_image.h"
48
49 /*****************************************************************************
50  * Local prototypes
51  *****************************************************************************/
52 static int  CreateFilter ( vlc_object_t * );
53 static void DestroyFilter( vlc_object_t * );
54 static subpicture_t *Filter( filter_t *, mtime_t );
55
56 static int FetchRSS( filter_t * );
57 static void FreeRSS( filter_t * );
58
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 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"),
67                N_("Aqua") };
68
69 /*****************************************************************************
70  * filter_sys_t: rss filter descriptor
71  *****************************************************************************/
72
73 struct rss_item_t
74 {
75     char *psz_title;
76     char *psz_description;
77     char *psz_link;
78 };
79
80 struct rss_feed_t
81 {
82     char *psz_title;
83     char *psz_description;
84     char *psz_link;
85     char *psz_image;
86     picture_t *p_pic;
87
88     int i_items;
89     struct rss_item_t *p_items;
90 };
91
92 struct filter_sys_t
93 {
94     vlc_mutex_t lock;
95     vlc_mutex_t *p_lock;
96
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) */
99     int i_speed;
100     int i_length;
101
102     char *psz_marquee;    /* marquee string */
103
104     text_style_t *p_style; /* font control */
105
106     mtime_t last_date;
107
108     char *psz_urls;
109     int i_feeds;
110     struct rss_feed_t *p_feeds;
111
112     int i_ttl;
113     time_t t_last_update;
114     vlc_bool_t b_images;
115
116     int i_cur_feed;
117     int i_cur_item;
118     int i_cur_char;
119 };
120
121 #define MSG_TEXT N_("Feed URLs")
122 #define MSG_LONGTEXT N_("RSS/Atom feed '|' (pipe) seperated URLs.")
123 #define SPEED_TEXT N_("Speed of feeds")
124 #define SPEED_LONGTEXT N_("Speed of the RSS/Atom feeds (bigger is slower).")
125 #define LENGTH_TEXT N_("Max length")
126 #define LENGTH_LONGTEXT N_("Maximum number of characters displayed on the " \
127                 "screen." )
128 #define TTL_TEXT N_("Refresh time")
129 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh " \
130         "of the feeds. 0 means that the feeds are never updated." )
131 #define IMAGE_TEXT N_("Feed images")
132 #define IMAGE_LONGTEXT N_("Display feed images if available.")
133
134 #define POSX_TEXT N_("X offset")
135 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
136 #define POSY_TEXT N_("Y offset")
137 #define POSY_LONGTEXT N_("Y offset, down from the top." )
138 #define OPACITY_TEXT N_("Opacity")
139 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
140     "overlay text. 0 = transparent, 255 = totally opaque." )
141
142 #define SIZE_TEXT N_("Font size, pixels")
143 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
144     "font size)." )
145
146 #define COLOR_TEXT N_("Color")
147 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
148     "the video. This must be an hexadecimal (like HTML colors). The first two "\
149     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
150     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
151
152 #define POS_TEXT N_("Text position")
153 #define POS_LONGTEXT N_( \
154   "You can enforce the text position on the video " \
155   "(0=center, 1=left, 2=right, 4=top, 8=bottom; you can " \
156   "also use combinations of these values, eg 6 = top-right).")
157
158 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
159 static char *ppsz_pos_descriptions[] =
160      { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
161      N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
162
163 #define CFG_PREFIX "rrs-"
164
165 /*****************************************************************************
166  * Module descriptor
167  *****************************************************************************/
168 vlc_module_begin();
169     set_capability( "sub filter", 0 );
170     set_shortname( "RSS / Atom" );
171     set_callbacks( CreateFilter, DestroyFilter );
172     set_category( CAT_VIDEO );
173     set_subcategory( SUBCAT_VIDEO_SUBPIC );
174     add_string( CFG_PREFIX "urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, VLC_FALSE );
175
176     set_section( N_("Position"), NULL );
177     add_integer( CFG_PREFIX "x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
178     add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
179     add_integer( CFG_PREFIX "position", 5, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
180         change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
181
182     set_section( N_("Font"), NULL );
183     /* 5 sets the default to top [1] left [4] */
184     add_integer_with_range( CFG_PREFIX "opacity", 255, 0, 255, NULL,
185         OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
186     add_integer( CFG_PREFIX "color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
187                   VLC_FALSE );
188         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
189     add_integer( CFG_PREFIX "size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
190
191     set_section( N_("Misc"), NULL );
192     add_integer( CFG_PREFIX "speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
193                  VLC_FALSE );
194     add_integer( CFG_PREFIX "length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
195                  VLC_FALSE );
196     add_integer( CFG_PREFIX "ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_FALSE );
197     add_bool( CFG_PREFIX "images", 1, NULL, IMAGE_TEXT, IMAGE_LONGTEXT, VLC_FALSE );
198
199     set_description( _("RSS and Atom feed display") );
200     add_shortcut( "rss" );
201     add_shortcut( "atom" );
202 vlc_module_end();
203
204 static const char *ppsz_filter_options[] = {
205     "urls", "x", "y", "position", "color", "size", "speed", "length",
206     "ttl", "images", NULL
207 };
208
209 /*****************************************************************************
210  * CreateFilter: allocates RSS video filter
211  *****************************************************************************/
212 static int CreateFilter( vlc_object_t *p_this )
213 {
214     filter_t *p_filter = (filter_t *)p_this;
215     filter_sys_t *p_sys;
216     int i_feed;
217
218     /* Allocate structure */
219     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
220     if( p_sys == NULL )
221     {
222         msg_Err( p_filter, "out of memory" );
223         return VLC_ENOMEM;
224     }
225
226     vlc_mutex_init( p_filter, &p_sys->lock );
227     vlc_mutex_lock( &p_sys->lock );
228
229     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
230                        p_filter->p_cfg );
231
232     p_sys->psz_urls = var_CreateGetString( p_filter, "rss-urls" );
233     p_sys->i_cur_feed = 0;
234     p_sys->i_cur_item = 0;
235     p_sys->i_cur_char = 0;
236     p_sys->i_feeds = 0;
237     p_sys->p_feeds = NULL;
238     p_sys->i_speed = var_CreateGetInteger( p_filter, "rss-speed" );
239     p_sys->i_length = var_CreateGetInteger( p_filter, "rss-length" );
240     p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, "rss-ttl" ) );
241     p_sys->b_images = var_CreateGetBool( p_filter, "rss-images" );
242     p_sys->psz_marquee = (char *)malloc( p_sys->i_length );
243
244     p_sys->p_style = malloc( sizeof( text_style_t ));
245     memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ));
246
247     p_sys->i_xoff = var_CreateGetInteger( p_filter, "rss-x" );
248     p_sys->i_yoff = var_CreateGetInteger( p_filter, "rss-y" );
249     p_sys->i_pos = var_CreateGetInteger( p_filter, "rss-position" );
250     p_sys->p_style->i_font_alpha = 255 - var_CreateGetInteger( p_filter, "rss-opacity" );
251     p_sys->p_style->i_font_color = var_CreateGetInteger( p_filter, "rss-color" );
252     p_sys->p_style->i_font_size = var_CreateGetInteger( p_filter, "rss-size" );
253
254     if( p_sys->b_images == VLC_TRUE && p_sys->p_style->i_font_size == -1 )
255     {
256         msg_Warn( p_filter, "rrs-size wasn't specified. Feed images will thus be displayed without being resized" );
257     }
258
259     if( FetchRSS( p_filter ) )
260     {
261         msg_Err( p_filter, "failed while fetching RSS ... too bad" );
262         vlc_mutex_unlock( &p_sys->lock );
263         return VLC_EGENERIC;
264     }
265     p_sys->t_last_update = time( NULL );
266
267     if( p_sys->i_feeds == 0 )
268     {
269         vlc_mutex_unlock( &p_sys->lock );
270         return VLC_EGENERIC;
271     }
272     for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
273         if( p_sys->p_feeds[i_feed].i_items == 0 )
274         {
275             vlc_mutex_unlock( &p_sys->lock );
276             return VLC_EGENERIC;
277         }
278
279     /* Misc init */
280     p_filter->pf_sub_filter = Filter;
281     p_sys->last_date = (mtime_t)0;
282
283     vlc_mutex_unlock( &p_sys->lock );
284
285     return VLC_SUCCESS;
286 }
287 /*****************************************************************************
288  * DestroyFilter: destroy RSS video filter
289  *****************************************************************************/
290 static void DestroyFilter( vlc_object_t *p_this )
291 {
292     filter_t *p_filter = (filter_t *)p_this;
293     filter_sys_t *p_sys = p_filter->p_sys;
294
295     vlc_mutex_lock( &p_sys->lock );
296
297     if( p_sys->p_style ) free( p_sys->p_style );
298     if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
299     free( p_sys->psz_urls );
300     FreeRSS( p_filter );
301     vlc_mutex_unlock( &p_sys->lock );
302     vlc_mutex_destroy( &p_sys->lock );
303     free( p_sys );
304
305     /* Delete the RSS variables */
306     var_Destroy( p_filter, "rss-urls" );
307     var_Destroy( p_filter, "rss-speed" );
308     var_Destroy( p_filter, "rss-length" );
309     var_Destroy( p_filter, "rss-ttl" );
310     var_Destroy( p_filter, "rss-images" );
311     var_Destroy( p_filter, "rss-x" );
312     var_Destroy( p_filter, "rss-y" );
313     var_Destroy( p_filter, "rss-position" );
314     var_Destroy( p_filter, "rss-color");
315     var_Destroy( p_filter, "rss-opacity");
316     var_Destroy( p_filter, "rss-size");
317 }
318
319 /****************************************************************************
320  * Filter: the whole thing
321  ****************************************************************************
322  * This function outputs subpictures at regular time intervals.
323  ****************************************************************************/
324 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
325 {
326     filter_sys_t *p_sys = p_filter->p_sys;
327     subpicture_t *p_spu;
328     video_format_t fmt = {0};
329
330     subpicture_region_t *p_region;
331
332     int i_feed, i_item;
333
334     struct rss_feed_t *p_feed;
335
336     vlc_mutex_lock( &p_sys->lock );
337
338     if( p_sys->last_date
339        + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == 0 ? 5 : 1 )
340            /* ( ... ? 5 : 1 ) means "wait more for the 1st char" */
341        * p_sys->i_speed > date )
342     {
343         vlc_mutex_unlock( &p_sys->lock );
344         return NULL;
345     }
346
347     /* Do we need to update the feeds ? */
348     if( p_sys->i_ttl
349         && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
350     {
351         msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
352         if( FetchRSS( p_filter ) )
353         {
354             msg_Err( p_filter, "failed while fetching RSS ... too bad" );
355             vlc_mutex_unlock( &p_sys->lock );
356             return NULL; /* FIXME : we most likely messed up all the data,
357                           * so we might need to do something about it */
358         }
359         p_sys->t_last_update = time( NULL );
360     }
361
362     p_sys->last_date = date;
363     p_sys->i_cur_char++;
364     if( p_sys->p_feeds[p_sys->i_cur_feed].p_items[p_sys->i_cur_item].psz_title[p_sys->i_cur_char] == 0 )
365     {
366         p_sys->i_cur_char = 0;
367         p_sys->i_cur_item++;
368         if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
369         {
370             p_sys->i_cur_item = 0;
371             p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
372         }
373     }
374
375     p_spu = p_filter->pf_sub_buffer_new( p_filter );
376     if( !p_spu )
377     {
378         vlc_mutex_unlock( &p_sys->lock );
379         return NULL;
380     }
381
382     fmt.i_chroma = VLC_FOURCC('T','E','X','T');
383
384     p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
385     if( !p_spu->p_region )
386     {
387         p_filter->pf_sub_buffer_del( p_filter, p_spu );
388         vlc_mutex_unlock( &p_sys->lock );
389         return NULL;
390     }
391
392     /* Generate the string that will be displayed. This string is supposed to
393        be p_sys->i_length characters long. */
394     i_item = p_sys->i_cur_item;
395     i_feed = p_sys->i_cur_feed;
396     p_feed = &p_sys->p_feeds[p_sys->i_cur_feed];
397
398     if( p_feed->p_pic )
399     {
400         /* Don't display the feed's title if we have an image */
401         snprintf( p_sys->psz_marquee, p_sys->i_length, "%s",
402                   p_sys->p_feeds[i_feed].p_items[i_item].psz_title
403                   +p_sys->i_cur_char );
404     }
405     else
406     {
407         snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s",
408                   p_sys->p_feeds[i_feed].psz_title,
409                   p_sys->p_feeds[i_feed].p_items[i_item].psz_title
410                   +p_sys->i_cur_char );
411     }
412
413     while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
414     {
415         i_item++;
416         if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
417         snprintf( strchr( p_sys->psz_marquee, 0 ),
418                   p_sys->i_length - strlen( p_sys->psz_marquee ),
419                   " - %s",
420                   p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
421     }
422
423     /* Calls to snprintf might split multibyte UTF8 chars ...
424      * which freetype doesn't like. */
425     {
426         char *a = strdup( p_sys->psz_marquee );
427         char *a2 = a;
428         char *b = p_sys->psz_marquee;
429         EnsureUTF8( p_sys->psz_marquee );
430         /* we want to use ' ' instead of '?' for erroneous chars */
431         while( *b != '\0' )
432         {
433             if( *b != *a ) *b = ' ';
434             b++;a++;
435         }
436         free( a2 );
437     }
438
439     p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
440     p_spu->i_start = date;
441     p_spu->i_stop  = 0;
442     p_spu->b_ephemer = VLC_TRUE;
443
444     /*  where to locate the string: */
445     if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
446     {   /* set to one of the 9 relative locations */
447         p_spu->i_flags = p_sys->i_pos;
448         p_spu->i_x = 0;
449         p_spu->i_y = 0;
450         p_spu->b_absolute = VLC_FALSE;
451     }
452     else
453     {   /*  set to an absolute xy, referenced to upper left corner */
454         p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
455         p_spu->i_x = p_sys->i_xoff;
456         p_spu->i_y = p_sys->i_yoff;
457         p_spu->b_absolute = VLC_TRUE;
458     }
459
460     p_spu->i_height = 1;
461     p_spu->p_region->p_style = p_sys->p_style;
462
463     if( p_feed->p_pic )
464     {
465         /* Display the feed's image */
466         picture_t *p_pic = p_feed->p_pic;
467         video_format_t fmt_out = {0};
468
469         fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
470         fmt_out.i_aspect = VOUT_ASPECT_FACTOR;
471         fmt_out.i_sar_num = fmt_out.i_sar_den = 1;
472         fmt_out.i_width =
473             fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
474         fmt_out.i_height =
475             fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
476
477         p_region = p_spu->pf_create_region( VLC_OBJECT( p_filter ), &fmt_out );
478         if( !p_region )
479         {
480             msg_Err( p_filter, "cannot allocate SPU region" );
481         }
482         else
483         {
484             vout_CopyPicture( p_filter, &p_region->picture, p_pic );
485             p_spu->p_region->p_next = p_region;
486         }
487
488         /* Offset text to display right next to the image */
489         p_spu->p_region->i_x = p_pic->p[Y_PLANE].i_visible_pitch;
490     }
491
492     vlc_mutex_unlock( &p_sys->lock );
493     return p_spu;
494 }
495
496 /****************************************************************************
497  * RSS related functions
498  ****************************************************************************
499  * You should always lock the p_filter mutex before using any of these
500  * functions
501  ***************************************************************************/
502
503 #undef LoadImage /* do not conflict with Win32 API */
504
505 /****************************************************************************
506  * download and resize image located at psz_url
507  ***************************************************************************/
508 picture_t *LoadImage( filter_t *p_filter, const char *psz_url )
509 {
510     filter_sys_t *p_sys = p_filter->p_sys;
511
512     video_format_t fmt_in={0}, fmt_out={0};
513     picture_t *p_orig, *p_pic=NULL;
514     image_handler_t *p_handler = image_HandlerCreate( p_filter );
515
516     char *psz_local;
517
518     psz_local = ToLocale( psz_url );
519     fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
520     p_orig = image_ReadUrl( p_handler, psz_local, &fmt_in, &fmt_out );
521     LocaleFree( psz_local );
522
523     if( !p_orig )
524     {
525         msg_Warn( p_filter, "Unable to read image %s", psz_url );
526     }
527     else if( p_sys->p_style->i_font_size > 0 )
528     {
529
530         fmt_in.i_chroma = VLC_FOURCC('Y','U','V','A');
531         fmt_in.i_height = p_orig->p[Y_PLANE].i_visible_lines;
532         fmt_in.i_width = p_orig->p[Y_PLANE].i_visible_pitch;
533         fmt_out.i_width = p_orig->p[Y_PLANE].i_visible_pitch
534             *p_sys->p_style->i_font_size/p_orig->p[Y_PLANE].i_visible_lines;
535         fmt_out.i_height = p_sys->p_style->i_font_size;
536
537         p_pic = image_Convert( p_handler, p_orig, &fmt_in, &fmt_out );
538         p_orig->pf_release( p_orig );
539         if( !p_pic )
540         {
541             msg_Warn( p_filter, "Error while converting %s", psz_url );
542         }
543     }
544     else
545     {
546         p_pic = p_orig;
547     }
548
549     image_HandlerDelete( p_handler );
550
551     return p_pic;
552 }
553
554 /****************************************************************************
555  * remove all ' ' '\t' '\n' '\r' characters from the begining and end of the
556  * string.
557  ***************************************************************************/
558 char *removeWhiteChars( char *psz_src )
559 {
560     char *psz_src2 = strdup( psz_src );
561     char *psz_clean = strdup( psz_src2 );
562     char *psz_clean2;
563     int i;
564     while( ( *psz_clean == ' ' || *psz_clean == '\t'
565            || *psz_clean == '\n' || *psz_clean == '\r' )
566            && *psz_clean != '\0' )
567     {
568         psz_clean++;
569     }
570     i = strlen( psz_clean );
571     while( --i > 0 &&
572          ( psz_clean[i] == ' ' || psz_clean[i] == '\t'
573         || psz_clean[i] == '\n' || psz_clean[i] == '\r' ) );
574     psz_clean[i+1] = '\0';
575     psz_clean2 = strdup( psz_clean );
576     free( psz_src2 );
577     return psz_clean2;
578 }
579
580 /****************************************************************************
581  * FetchRSS (or Atom) feeds
582  ***************************************************************************/
583 static int FetchRSS( filter_t *p_filter)
584 {
585     filter_sys_t *p_sys = p_filter->p_sys;
586
587     stream_t *p_stream = NULL;
588     xml_t *p_xml = NULL;
589     xml_reader_t *p_xml_reader = NULL;
590
591     char *psz_eltname = NULL;
592     char *psz_eltvalue = NULL;
593     char *psz_feed = NULL;
594     char *psz_buffer = NULL;
595     char *psz_buffer_2 = NULL;
596
597     int i_feed;
598     int i_item;
599     vlc_bool_t b_is_item;
600     vlc_bool_t b_is_image;
601     int i_int;
602
603     FreeRSS( p_filter );
604     p_sys->i_feeds = 1;
605     i_int = 0;
606     while( p_sys->psz_urls[i_int] != 0 )
607         if( p_sys->psz_urls[i_int++] == '|' )
608             p_sys->i_feeds++;
609     p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
610                                 * sizeof( struct rss_feed_t ) );
611
612     p_xml = xml_Create( p_filter );
613     if( !p_xml )
614     {
615         msg_Err( p_filter, "Failed to open XML parser" );
616         return 1;
617     }
618
619     psz_buffer = strdup( p_sys->psz_urls );
620     psz_buffer_2 = psz_buffer; /* keep track so we can free it */
621     for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
622     {
623         struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
624
625         if( psz_buffer == NULL ) break;
626         if( psz_buffer[0] == 0 ) psz_buffer++;
627         psz_feed = psz_buffer;
628         psz_buffer = strchr( psz_buffer, '|' );
629         if( psz_buffer != NULL ) psz_buffer[0] = 0;
630
631         p_feed->psz_title = NULL;
632         p_feed->psz_description = NULL;
633         p_feed->psz_link = NULL;
634         p_feed->psz_image = NULL;
635         p_feed->p_pic = NULL;
636         p_feed->i_items = 0;
637         p_feed->p_items = NULL;
638
639         msg_Dbg( p_filter, "opening %s RSS/Atom feed ...", psz_feed );
640
641         p_stream = stream_UrlNew( p_filter, psz_feed );
642         if( !p_stream )
643         {
644             msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
645             return 1;
646         }
647
648         p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
649         if( !p_xml_reader )
650         {
651             msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
652             return 1;
653         }
654
655         i_item = 0;
656         b_is_item = VLC_FALSE;
657         b_is_image = VLC_FALSE;
658
659         while( xml_ReaderRead( p_xml_reader ) == 1 )
660         {
661             switch( xml_ReaderNodeType( p_xml_reader ) )
662             {
663                 // Error
664                 case -1:
665                     return 1;
666
667                 case XML_READER_STARTELEM:
668                     if( psz_eltname )
669                     {
670                         free( psz_eltname );
671                         psz_eltname = NULL;
672                     }
673                     psz_eltname = xml_ReaderName( p_xml_reader );
674                     if( !psz_eltname )
675                     {
676                         return 1;
677                     }
678 #                   ifdef RSS_DEBUG
679                     msg_Dbg( p_filter, "element name: %s", psz_eltname );
680 #                   endif
681                     if( !strcmp( psz_eltname, "item" ) /* rss */
682                      || !strcmp( psz_eltname, "entry" ) ) /* atom */
683                     {
684                         b_is_item = VLC_TRUE;
685                         p_feed->i_items++;
686                         p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
687                         p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
688                         p_feed->p_items[p_feed->i_items-1].psz_description
689                                                                      = NULL;
690                         p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
691                     }
692                     else if( !strcmp( psz_eltname, "image" ) ) /* rss */
693                     {
694                         b_is_image = VLC_TRUE;
695                     }
696                     else if( !strcmp( psz_eltname, "link" ) ) /* atom */
697                     {
698                         char *psz_href = NULL;
699                         char *psz_rel = NULL;
700                         while( xml_ReaderNextAttr( p_xml_reader )
701                                == VLC_SUCCESS )
702                         {
703                             char *psz_name = xml_ReaderName( p_xml_reader );
704                             char *psz_value = xml_ReaderValue( p_xml_reader );
705                             if( !strcmp( psz_name, "rel" ) )
706                             {
707                                 psz_rel = psz_value;
708                             }
709                             else if( !strcmp( psz_name, "href" ) )
710                             {
711                                 psz_href = psz_value;
712                             }
713                             else
714                             {
715                                 free( psz_value );
716                             }
717                             free( psz_name );
718                         }
719                         if( psz_rel && psz_href )
720                         {
721                             if( !strcmp( psz_rel, "alternate" )
722                                 && b_is_item == VLC_FALSE
723                                 && b_is_image == VLC_FALSE
724                                 && !p_feed->psz_link )
725                             {
726                                 p_feed->psz_link = psz_href;
727                             }
728                             /* this isn't in the rfc but i found some ... */
729                             else if( ( !strcmp( psz_rel, "logo" )
730                                     || !strcmp( psz_rel, "icon" ) )
731                                     && b_is_item == VLC_FALSE
732                                     && b_is_image == VLC_FALSE
733                                     && !p_feed->psz_image )
734                             {
735                                 p_feed->psz_image = psz_href;
736                             }
737                             else
738                             {
739                                 free( psz_href );
740                             }
741                         }
742                         else
743                         {
744                             if( psz_href ) free( psz_href );
745                         }
746                         if( psz_rel ) free( psz_rel );
747                     }
748                     break;
749
750                 case XML_READER_ENDELEM:
751                     if( psz_eltname )
752                     {
753                         free( psz_eltname );
754                         psz_eltname = NULL;
755                     }
756                     psz_eltname = xml_ReaderName( p_xml_reader );
757                     if( !psz_eltname )
758                     {
759                         return 1;
760                     }
761 #                   ifdef RSS_DEBUG
762                     msg_Dbg( p_filter, "element end : %s", psz_eltname );
763 #                   endif
764                     if( !strcmp( psz_eltname, "item" ) /* rss */
765                      || !strcmp( psz_eltname, "entry" ) ) /* atom */
766                     {
767                         b_is_item = VLC_FALSE;
768                         i_item++;
769                     }
770                     else if( !strcmp( psz_eltname, "image" ) ) /* rss */
771                     {
772                         b_is_image = VLC_FALSE;
773                     }
774                     free( psz_eltname );
775                     psz_eltname = NULL;
776                     break;
777
778                 case XML_READER_TEXT:
779                     if( !psz_eltname ) break;
780                     psz_eltvalue = xml_ReaderValue( p_xml_reader );
781                     if( !psz_eltvalue )
782                     {
783                         return 1;
784                     }
785                     else
786                     {
787                         char *psz_clean;
788                         psz_clean = removeWhiteChars( psz_eltvalue );
789                         free( psz_eltvalue ); psz_eltvalue = psz_clean;
790                     }
791 #                   ifdef RSS_DEBUG
792                     msg_Dbg( p_filter, "  text : <%s>", psz_eltvalue );
793 #                   endif
794                     if( b_is_item == VLC_TRUE )
795                     {
796                         struct rss_item_t *p_item;
797                         p_item = p_feed->p_items+i_item;
798                         if( !strcmp( psz_eltname, "title" ) /* rss/atom */
799                             && !p_item->psz_title )
800                         {
801                             p_item->psz_title = psz_eltvalue;
802                         }
803                         else if( !strcmp( psz_eltname, "link" ) /* rss */
804                                  && !p_item->psz_link )
805                         {
806                             p_item->psz_link = psz_eltvalue;
807                         }
808                         else if((!strcmp( psz_eltname, "description" ) /* rss */
809                               || !strcmp( psz_eltname, "summary" ) ) /* atom */
810                               && !p_item->psz_description )
811                         {
812                             p_item->psz_description = psz_eltvalue;
813                         }
814                         else
815                         {
816                             free( psz_eltvalue );
817                             psz_eltvalue = NULL;
818                         }
819                     }
820                     else if( b_is_image == VLC_TRUE )
821                     {
822                         if( !strcmp( psz_eltname, "url" ) /* rss */
823                             && !p_feed->psz_image )
824                         {
825                             p_feed->psz_image = psz_eltvalue;
826                         }
827                         else
828                         {
829                             free( psz_eltvalue );
830                             psz_eltvalue = NULL;
831                         }
832                     }
833                     else
834                     {
835                         if( !strcmp( psz_eltname, "title" ) /* rss/atom */
836                             && !p_feed->psz_title )
837                         {
838                             p_feed->psz_title = psz_eltvalue;
839                         }
840                         else if( !strcmp( psz_eltname, "link" ) /* rss */
841                                  && !p_feed->psz_link )
842                         {
843                             p_feed->psz_link = psz_eltvalue;
844                         }
845                         else if((!strcmp( psz_eltname, "description" ) /* rss */
846                               || !strcmp( psz_eltname, "subtitle" ) ) /* atom */
847                               && !p_feed->psz_description )
848                         {
849                             p_feed->psz_description = psz_eltvalue;
850                         }
851                         else if( ( !strcmp( psz_eltname, "logo" ) /* atom */
852                               || !strcmp( psz_eltname, "icon" ) ) /* atom */
853                               && !p_feed->psz_image )
854                         {
855                             p_feed->psz_image = psz_eltvalue;
856                         }
857                         else
858                         {
859                             free( psz_eltvalue );
860                             psz_eltvalue = NULL;
861                         }
862                     }
863                     break;
864             }
865         }
866
867         if( p_sys->b_images == VLC_TRUE
868             && p_feed->psz_image && !p_feed->p_pic )
869         {
870             p_feed->p_pic = LoadImage( p_filter, p_feed->psz_image );
871         }
872
873         if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
874         if( p_stream ) stream_Delete( p_stream );
875         msg_Dbg( p_filter, "done with %s RSS/Atom feed", psz_feed );
876     }
877     free( psz_buffer_2 );
878     if( p_xml ) xml_Delete( p_xml );
879
880     return 0;
881 }
882
883 /****************************************************************************
884  * FreeRSS
885  ***************************************************************************/
886 static void FreeRSS( filter_t *p_filter)
887 {
888     filter_sys_t *p_sys = p_filter->p_sys;
889
890     struct rss_item_t *p_item;
891     struct rss_feed_t *p_feed;
892
893     int i_feed;
894     int i_item;
895
896     for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
897     {
898         p_feed = p_sys->p_feeds+i_feed;
899         for( i_item = 0; i_item < p_feed->i_items; i_item++ )
900         {
901             p_item = p_feed->p_items+i_item;
902             free( p_item->psz_title );
903             free( p_item->psz_link );
904             free( p_item->psz_description );
905         }
906         free( p_feed->p_items );
907         free( p_feed->psz_title);
908         free( p_feed->psz_link );
909         free( p_feed->psz_description );
910         free( p_feed->psz_image );
911         if( p_feed->p_pic != NULL )
912             p_feed->p_pic->pf_release( p_feed->p_pic );
913     }
914     free( p_sys->p_feeds );
915     p_sys->i_feeds = 0;
916 }