]> git.sesse.net Git - vlc/blob - modules/demux/playlist/shoutcast.c
Untested workarround for buggy asx files with an end of line char at the end of the...
[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_demux.h>
32
33 #include <errno.h>                                                 /* ENOMEM */
34 #include "playlist.h"
35 #include "vlc_xml.h"
36
37 struct demux_sys_t
38 {
39     playlist_t *p_playlist;
40     playlist_item_t *p_current;
41     playlist_item_t *p_item_in_category;
42
43     xml_t *p_xml;
44     xml_reader_t *p_xml_reader;
45
46     vlc_bool_t b_adult;
47 };
48
49 /* duplicate from modules/services_discovery/shout.c */
50 #define SHOUTCAST_BASE_URL "http/shout-winamp://www.shoutcast.com/sbin/newxml.phtml"
51 #define SHOUTCAST_TUNEIN_BASE_URL "http://www.shoutcast.com"
52 #define SHOUTCAST_TV_TUNEIN_URL "http://www.shoutcast.com/sbin/tunein-tvstation.pls?id="
53
54 /*****************************************************************************
55  * Local prototypes
56  *****************************************************************************/
57 static int Demux( demux_t *p_demux);
58 static int Control( demux_t *p_demux, int i_query, va_list args );
59
60 static int DemuxGenre( demux_t *p_demux );
61 static int DemuxStation( demux_t *p_demux );
62
63 /*****************************************************************************
64  * Import_Shoutcast: main import function
65  *****************************************************************************/
66 int E_(Import_Shoutcast)( vlc_object_t *p_this )
67 {
68     demux_t *p_demux = (demux_t *)p_this;
69
70     if( !demux2_IsForced( p_demux, "shout-winamp" ) )
71         return VLC_EGENERIC;
72
73     STANDARD_DEMUX_INIT_MSG( "using shoutcast playlist reader" );
74     p_demux->p_sys->p_playlist = NULL;
75     p_demux->p_sys->p_xml = NULL;
76     p_demux->p_sys->p_xml_reader = NULL;
77
78     /* Do we want to list adult content ? */
79     var_Create( p_demux, "shoutcast-show-adult",
80                 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
81     p_demux->p_sys->b_adult = var_GetBool( p_demux, "shoutcast-show-adult" );
82
83     return VLC_SUCCESS;
84 }
85
86 /*****************************************************************************
87  * Deactivate: frees unused data
88  *****************************************************************************/
89 void E_(Close_Shoutcast)( vlc_object_t *p_this )
90 {
91     demux_t *p_demux = (demux_t *)p_this;
92     demux_sys_t *p_sys = p_demux->p_sys;
93
94     if( p_sys->p_playlist )
95         vlc_object_release( p_sys->p_playlist );
96     if( p_sys->p_xml_reader )
97         xml_ReaderDelete( p_sys->p_xml, p_sys->p_xml_reader );
98     if( p_sys->p_xml )
99         xml_Delete( p_sys->p_xml );
100     free( p_sys );
101 }
102
103 static int Demux( demux_t *p_demux )
104 {
105     demux_sys_t *p_sys = p_demux->p_sys;
106     xml_t *p_xml;
107     xml_reader_t *p_xml_reader;
108     char *psz_eltname = NULL;
109     INIT_PLAYLIST_STUFF;
110     p_sys->p_playlist = p_playlist;
111     p_sys->p_current = p_current;
112     p_sys->p_item_in_category = p_item_in_category;
113
114     p_xml = p_sys->p_xml = xml_Create( p_demux );
115     if( !p_xml ) return -1;
116
117     p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
118     if( !p_xml_reader ) return -1;
119     p_sys->p_xml_reader = p_xml_reader;
120
121     /* check root node */
122     if( xml_ReaderRead( p_xml_reader ) != 1 )
123     {
124         msg_Err( p_demux, "invalid file (no root node)" );
125         return -1;
126     }
127
128     if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
129         ( psz_eltname = xml_ReaderName( p_xml_reader ) ) == NULL ||
130         ( strcmp( psz_eltname, "genrelist" )
131           && strcmp( psz_eltname, "stationlist" ) ) )
132     {
133         msg_Err( p_demux, "invalid root node %i, %s",
134                  xml_ReaderNodeType( p_xml_reader ), psz_eltname );
135         if( psz_eltname ) free( psz_eltname );
136         return -1;
137     }
138
139     if( !strcmp( psz_eltname, "genrelist" ) )
140     {
141         /* we're reading a genre list */
142         free( psz_eltname );
143         if( DemuxGenre( p_demux ) ) return -1;
144     }
145     else
146     {
147         /* we're reading a station list */
148         free( psz_eltname );
149         if( DemuxStation( p_demux ) ) return -1;
150     }
151
152     HANDLE_PLAY_AND_RELEASE;
153     p_sys->p_playlist = NULL;
154     return -1; /* Needed for correct operation of go back */
155 }
156
157 #define GET_VALUE( a ) \
158                         if( !strcmp( psz_attrname, #a ) ) \
159                         { \
160                             psz_ ## a = strdup( psz_attrvalue ); \
161                         }
162 /* <genrelist>
163  *   <genre name="the name"></genre>
164  *   ...
165  * </genrelist>
166  **/
167 static int DemuxGenre( demux_t *p_demux )
168 {
169     demux_sys_t *p_sys = p_demux->p_sys;
170     char *psz_name = NULL; /* genre name */
171     char *psz_eltname = NULL; /* tag name */
172     input_item_t *p_input;
173
174     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
175     {
176         int i_type;
177
178         // Get the node type
179         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
180         switch( i_type )
181         {
182             // Error
183             case -1:
184                 return -1;
185                 break;
186
187             case XML_READER_STARTELEM:
188                 // Read the element name
189                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
190                 if( !psz_eltname ) return -1;
191
192                 if( !strcmp( psz_eltname, "genre" ) )
193                 {
194                     // Read the attributes
195                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
196                     {
197                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
198                         char *psz_attrvalue =
199                             xml_ReaderValue( p_sys->p_xml_reader );
200                         if( !psz_attrname || !psz_attrvalue )
201                         {
202                             FREENULL(psz_attrname);
203                             FREENULL(psz_attrvalue);
204                             free(psz_eltname);
205                             /*FIXME: isn't return a bit too much. what about break*/
206                             return -1;
207                         }
208
209                         GET_VALUE( name )
210                         else
211                         {
212                             msg_Warn( p_demux,
213                                       "unexpected attribure %s in element %s",
214                                       psz_attrname,psz_eltname );
215                         }
216                         free( psz_attrname );
217                         free( psz_attrvalue );
218                     }
219                 }
220                 free( psz_eltname ); psz_eltname = NULL;
221                 break;
222
223             case XML_READER_TEXT:
224                 break;
225
226             // End element
227             case XML_READER_ENDELEM:
228                 // Read the element name
229                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
230                 if( !psz_eltname ) return -1;
231                 if( !strcmp( psz_eltname, "genre" ) )
232                 {
233                     char *psz_mrl = malloc( strlen( SHOUTCAST_BASE_URL )
234                             + strlen( "?genre=" ) + strlen( psz_name ) + 1 );
235                     sprintf( psz_mrl, SHOUTCAST_BASE_URL "?genre=%s",
236                              psz_name );
237                     p_input = input_ItemNewExt( p_sys->p_playlist, psz_mrl,
238                                                 psz_name, 0, NULL, -1 );
239                     input_ItemCopyOptions( p_sys->p_current->p_input,
240                                                 p_input );
241                     free( psz_mrl );
242                     playlist_BothAddInput( p_sys->p_playlist, p_input,
243                                            p_sys->p_item_in_category,
244                                            PLAYLIST_APPEND | PLAYLIST_SPREPARSE,
245                                            PLAYLIST_END, NULL, NULL, VLC_FALSE );
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_SPREPARSE,
437                                            PLAYLIST_END, NULL, NULL, VLC_FALSE );
438
439                     FREENULL( psz_name );
440                     FREENULL( psz_mt )
441                     FREENULL( psz_id )
442                     FREENULL( psz_br )
443                     FREENULL( psz_genre )
444                     FREENULL( psz_ct )
445                     FREENULL( psz_lc )
446                     FREENULL( psz_rt )
447                 }
448                 free( psz_eltname );
449                 break;
450         }
451     }
452     return 0;
453 }
454
455 static int Control( demux_t *p_demux, int i_query, va_list args )
456 {
457     return VLC_EGENERIC;
458 }