]> git.sesse.net Git - vlc/blob - modules/demux/playlist/shoutcast.c
Fix playlist unable to terminate (Closes:#829)
[vlc] / modules / demux / playlist / shoutcast.c
1 /*****************************************************************************
2  * shoutcast.c: Winamp >=5.2 shoutcast demuxer
3  *****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea -@t- videolan -Dot- org>
8  *          based on b4s.c by Sigmund Augdal Helberg <dnumgis@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <ctype.h>                                              /* isspace() */
29
30 #include <vlc/vlc.h>
31 #include <vlc/input.h>
32 #include <vlc/intf.h>
33
34 #include <errno.h>                                                 /* ENOMEM */
35 #include "playlist.h"
36 #include "vlc_xml.h"
37
38 struct demux_sys_t
39 {
40     playlist_t *p_playlist;
41     playlist_item_t *p_current;
42     playlist_item_t *p_item_in_category;
43
44     xml_t *p_xml;
45     xml_reader_t *p_xml_reader;
46
47     vlc_bool_t b_adult;
48 };
49
50 /* duplicate from modules/services_discovery/shout.c */
51 #define SHOUTCAST_BASE_URL "http/shout-winamp://www.shoutcast.com/sbin/newxml.phtml"
52 #define SHOUTCAST_TUNEIN_BASE_URL "http://www.shoutcast.com"
53 #define SHOUTCAST_TV_TUNEIN_URL "http://www.shoutcast.com/sbin/tunein-tvstation.pls?id="
54
55 /*****************************************************************************
56  * Local prototypes
57  *****************************************************************************/
58 static int Demux( demux_t *p_demux);
59 static int Control( demux_t *p_demux, int i_query, va_list args );
60
61 static int DemuxGenre( demux_t *p_demux );
62 static int DemuxStation( demux_t *p_demux );
63
64 /*****************************************************************************
65  * Import_Shoutcast: main import function
66  *****************************************************************************/
67 int E_(Import_Shoutcast)( vlc_object_t *p_this )
68 {
69     demux_t *p_demux = (demux_t *)p_this;
70
71     if( !isDemux( p_demux, "shout-winamp" ) )
72         return VLC_EGENERIC;
73
74     STANDARD_DEMUX_INIT_MSG( "using shoutcast playlist reader" );
75     p_demux->p_sys->p_playlist = NULL;
76     p_demux->p_sys->p_xml = NULL;
77     p_demux->p_sys->p_xml_reader = NULL;
78
79     /* Do we want to list adult content ? */
80     var_Create( p_demux, "shoutcast-show-adult",
81                 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
82     p_demux->p_sys->b_adult = var_GetBool( p_demux, "shoutcast-show-adult" );
83
84     return VLC_SUCCESS;
85 }
86
87 /*****************************************************************************
88  * Deactivate: frees unused data
89  *****************************************************************************/
90 void E_(Close_Shoutcast)( vlc_object_t *p_this )
91 {
92     demux_t *p_demux = (demux_t *)p_this;
93     demux_sys_t *p_sys = p_demux->p_sys;
94
95     if( p_sys->p_playlist )
96         vlc_object_release( p_sys->p_playlist );
97     if( p_sys->p_xml_reader )
98         xml_ReaderDelete( p_sys->p_xml, p_sys->p_xml_reader );
99     if( p_sys->p_xml )
100         xml_Delete( p_sys->p_xml );
101     free( p_sys );
102 }
103
104 static int Demux( demux_t *p_demux )
105 {
106     demux_sys_t *p_sys = p_demux->p_sys;
107     xml_t *p_xml;
108     xml_reader_t *p_xml_reader;
109     char *psz_eltname = NULL;
110     INIT_PLAYLIST_STUFF;
111     p_sys->p_playlist = p_playlist;
112     p_sys->p_current = p_current;
113     p_sys->p_item_in_category = p_item_in_category;
114
115     p_xml = p_sys->p_xml = xml_Create( p_demux );
116     if( !p_xml ) return -1;
117
118     p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
119     if( !p_xml_reader ) return -1;
120     p_sys->p_xml_reader = p_xml_reader;
121
122     /* check root node */
123     if( xml_ReaderRead( p_xml_reader ) != 1 )
124     {
125         msg_Err( p_demux, "invalid file (no root node)" );
126         return -1;
127     }
128
129     if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
130         ( psz_eltname = xml_ReaderName( p_xml_reader ) ) == NULL ||
131         ( strcmp( psz_eltname, "genrelist" )
132           && strcmp( psz_eltname, "stationlist" ) ) )
133     {
134         msg_Err( p_demux, "invalid root node %i, %s",
135                  xml_ReaderNodeType( p_xml_reader ), psz_eltname );
136         if( psz_eltname ) free( psz_eltname );
137         return -1;
138     }
139
140     if( !strcmp( psz_eltname, "genrelist" ) )
141     {
142         /* we're reading a genre list */
143         free( psz_eltname );
144         if( DemuxGenre( p_demux ) ) return -1;
145     }
146     else
147     {
148         /* we're reading a station list */
149         free( psz_eltname );
150         if( DemuxStation( p_demux ) ) return -1;
151     }
152
153     HANDLE_PLAY_AND_RELEASE;
154     p_sys->p_playlist = NULL;
155     return -1; /* Needed for correct operation of go back */
156 }
157
158 #define GET_VALUE( a ) \
159                         if( !strcmp( psz_attrname, #a ) ) \
160                         { \
161                             psz_ ## a = strdup( psz_attrvalue ); \
162                         }
163 /* <genrelist>
164  *   <genre name="the name"></genre>
165  *   ...
166  * </genrelist>
167  **/
168 static int DemuxGenre( demux_t *p_demux )
169 {
170     demux_sys_t *p_sys = p_demux->p_sys;
171     char *psz_name = NULL; /* genre name */
172     char *psz_eltname = NULL; /* tag name */
173     input_item_t *p_input;
174
175     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
176     {
177         int i_type;
178
179         // Get the node type
180         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
181         switch( i_type )
182         {
183             // Error
184             case -1:
185                 return -1;
186                 break;
187
188             case XML_READER_STARTELEM:
189                 // Read the element name
190                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
191                 if( !psz_eltname ) return -1;
192
193                 if( !strcmp( psz_eltname, "genre" ) )
194                 {
195                     // Read the attributes
196                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
197                     {
198                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
199                         char *psz_attrvalue =
200                             xml_ReaderValue( p_sys->p_xml_reader );
201                         if( !psz_attrname || !psz_attrvalue )
202                         {
203                             FREENULL(psz_attrname);
204                             FREENULL(psz_attrvalue);
205                             free(psz_eltname);
206                             /*FIXME: isn't return a bit too much. what about break*/
207                             return -1;
208                         }
209
210                         GET_VALUE( name )
211                         else
212                         {
213                             msg_Warn( p_demux,
214                                       "unexpected attribure %s in element %s",
215                                       psz_attrname,psz_eltname );
216                         }
217                         free( psz_attrname );
218                         free( psz_attrvalue );
219                     }
220                 }
221                 free( psz_eltname ); psz_eltname = NULL;
222                 break;
223
224             case XML_READER_TEXT:
225                 break;
226
227             // End element
228             case XML_READER_ENDELEM:
229                 // Read the element name
230                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
231                 if( !psz_eltname ) return -1;
232                 if( !strcmp( psz_eltname, "genre" ) )
233                 {
234                     char *psz_mrl = malloc( strlen( SHOUTCAST_BASE_URL )
235                             + strlen( "?genre=" ) + strlen( psz_name ) + 1 );
236                     sprintf( psz_mrl, SHOUTCAST_BASE_URL "?genre=%s",
237                              psz_name );
238                     p_input = input_ItemNewExt( p_sys->p_playlist, psz_mrl,
239                                                 psz_name, 0, NULL, -1 );
240                     input_ItemCopyOptions( p_sys->p_current->p_input,
241                                                 p_input );
242                     free( psz_mrl );
243                     playlist_BothAddInput( p_sys->p_playlist, p_input,
244                                            p_sys->p_item_in_category,
245                                            PLAYLIST_APPEND, PLAYLIST_END );
246                     FREENULL( psz_name );
247                 }
248                 FREENULL( psz_eltname );
249                 break;
250         }
251     }
252     return 0;
253 }
254
255 /* radio stations:
256  * <stationlist>
257  *   <tunein base="/sbin/tunein-station.pls"></tunein>
258  *   <station name="the name"
259  *            mt="mime type"
260  *            id="the id"
261  *            br="bit rate"
262  *            genre="A big genre string"
263  *            ct="current track name/author/..."
264  *            lc="listener count"></station>
265  * </stationlist>
266  *
267  * TV stations:
268  * <stationlist>
269  *   <tunein base="/sbin/tunein-station.pls"></tunein>
270  *   <station name="the name"
271  *            id="the id"
272  *            br="bit rate"
273  *            rt="rating"
274  *            load="server load ?"
275  *            ct="current track name/author/..."
276  *            genre="A big genre string"
277  *            lc="listener count"></station>
278  * </stationlist>
279  **/
280 static int DemuxStation( demux_t *p_demux )
281 {
282     demux_sys_t *p_sys = p_demux->p_sys;
283     input_item_t *p_input;
284
285     char *psz_base = NULL; /* */
286
287     char *psz_name = NULL; /* genre name */
288     char *psz_mt = NULL; /* mime type */
289     char *psz_id = NULL; /* id */
290     char *psz_br = NULL; /* bit rate */
291     char *psz_genre = NULL; /* genre */
292     char *psz_ct = NULL; /* current track */
293     char *psz_lc = NULL; /* listener count */
294
295     /* If these are set then it's *not* a radio but a TV */
296     char *psz_rt = NULL; /* rating for shoutcast TV */
297     char *psz_load = NULL; /* load for shoutcast TV */
298
299     char *psz_eltname = NULL; /* tag name */
300
301     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
302     {
303         int i_type;
304
305         // Get the node type
306         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
307         switch( i_type )
308         {
309             // Error
310             case -1:
311                 return -1;
312                 break;
313
314             case XML_READER_STARTELEM:
315                 // Read the element name
316                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
317                 if( !psz_eltname ) return -1;
318
319                 // Read the attributes
320                 if( !strcmp( psz_eltname, "tunein" ) )
321                 {
322                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
323                     {
324                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
325                         char *psz_attrvalue =
326                             xml_ReaderValue( p_sys->p_xml_reader );
327                         if( !psz_attrname || !psz_attrvalue )
328                         {
329                             free(psz_eltname);
330                             FREENULL(psz_attrname);
331                             FREENULL(psz_attrvalue);
332                             return -1;
333                         }
334
335                         GET_VALUE( base )
336                         else
337                         {
338                             msg_Warn( p_demux,
339                                       "unexpected attribure %s in element %s",
340                                       psz_attrname, psz_eltname );
341                         }
342                         free( psz_attrname );
343                         free( psz_attrvalue );
344                     }
345                 }
346                 else if( !strcmp( psz_eltname, "station" ) )
347                 {
348                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
349                     {
350                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
351                         char *psz_attrvalue =
352                             xml_ReaderValue( p_sys->p_xml_reader );
353                         if( !psz_attrname || !psz_attrvalue )
354                         {
355                             free(psz_eltname);
356                             FREENULL(psz_attrname);
357                             FREENULL(psz_attrvalue);
358                             return -1;
359                         }
360
361                         GET_VALUE( name )
362                         else GET_VALUE( mt )
363                         else GET_VALUE( id )
364                         else GET_VALUE( br )
365                         else GET_VALUE( genre )
366                         else GET_VALUE( ct )
367                         else GET_VALUE( lc )
368                         else GET_VALUE( rt )
369                         else GET_VALUE( load )
370                         else
371                         {
372                             msg_Warn( p_demux,
373                                       "unexpected attribute %s in element %s",
374                                       psz_attrname, psz_eltname );
375                         }
376                         free( psz_attrname );
377                         free( psz_attrvalue );
378                     }
379                 }
380                 free(psz_eltname);
381                 break;
382
383             case XML_READER_TEXT:
384                 break;
385
386             // End element
387             case XML_READER_ENDELEM:
388                 // Read the element name
389                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
390                 if( !psz_eltname ) return -1;
391                 if( !strcmp( psz_eltname, "station" ) &&
392                     ( psz_base || ( psz_rt && psz_load &&
393                     ( p_sys->b_adult || strcmp( psz_rt, "NC17" ) ) ) ) )
394                 {
395                     char *psz_mrl = NULL;
396                     if( psz_rt || psz_load )
397                     {
398                         /* tv */
399                         psz_mrl = malloc( strlen( SHOUTCAST_TV_TUNEIN_URL )
400                                           + strlen( psz_id ) + 1 );
401                         sprintf( psz_mrl, SHOUTCAST_TV_TUNEIN_URL "%s",
402                                  psz_id );
403                     }
404                     else
405                     {
406                         /* radio */
407                         psz_mrl = malloc( strlen( SHOUTCAST_TUNEIN_BASE_URL )
408                             + strlen( psz_base ) + strlen( "?id=" )
409                             + strlen( psz_id ) + 1 );
410                         sprintf( psz_mrl, SHOUTCAST_TUNEIN_BASE_URL "%s?id=%s",
411                              psz_base, psz_id );
412                     }
413                     p_input = input_ItemNewExt( p_sys->p_playlist, psz_mrl,
414                                                 psz_name , 0, NULL, -1 );
415                     free( psz_mrl );
416
417                     input_ItemCopyOptions( p_sys->p_current->p_input,
418                                                 p_input );
419
420 #define SADD_INFO( type, field ) if( field ) { input_ItemAddInfo( \
421                     p_input, _("Shoutcast"), _(type), "%s", field ) ; }
422                     SADD_INFO( "Mime type", psz_mt );
423                     SADD_INFO( "Bitrate", psz_br );
424                     SADD_INFO( "Listeners", psz_lc );
425                     SADD_INFO( "Load", psz_load );
426                     p_input->p_meta = vlc_meta_New();
427                     if( psz_genre )
428                         vlc_meta_SetGenre( p_input->p_meta, psz_genre );
429                     if( psz_ct )
430                         vlc_meta_SetNowPlaying( p_input->p_meta, psz_ct );
431                     if( psz_rt )
432                         vlc_meta_SetRating( p_input->p_meta, psz_rt );
433
434                     playlist_BothAddInput( p_sys->p_playlist, p_input,
435                                            p_sys->p_item_in_category,
436                                            PLAYLIST_APPEND, PLAYLIST_END );
437
438                     FREENULL( psz_name );
439                     FREENULL( psz_mt )
440                     FREENULL( psz_id )
441                     FREENULL( psz_br )
442                     FREENULL( psz_genre )
443                     FREENULL( psz_ct )
444                     FREENULL( psz_lc )
445                     FREENULL( psz_rt )
446                 }
447                 free( psz_eltname );
448                 break;
449         }
450     }
451     return 0;
452 }
453
454 static int Control( demux_t *p_demux, int i_query, va_list args )
455 {
456     return VLC_EGENERIC;
457 }