]> git.sesse.net Git - vlc/blob - modules/demux/playlist/shoutcast.c
da6385bc231cd9710fe9b95558e1f0f744d95abc
[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     int i_parent_id;
44
45     xml_t *p_xml;
46     xml_reader_t *p_xml_reader;
47
48     vlc_bool_t b_adult;
49 };
50
51 /* duplicate from modules/services_discovery/shout.c */
52 #define SHOUTCAST_BASE_URL "http/shout-winamp://www.shoutcast.com/sbin/newxml.phtml"
53 #define SHOUTCAST_TUNEIN_BASE_URL "http://www.shoutcast.com"
54 #define SHOUTCAST_TV_TUNEIN_URL "http://www.shoutcast.com/sbin/tunein-tvstation.pls?id="
55
56 /*****************************************************************************
57  * Local prototypes
58  *****************************************************************************/
59 static int Demux( demux_t *p_demux);
60 static int Control( demux_t *p_demux, int i_query, va_list args );
61
62 static int DemuxGenre( demux_t *p_demux );
63 static int DemuxStation( demux_t *p_demux );
64
65 /*****************************************************************************
66  * Import_Shoutcast: main import function
67  *****************************************************************************/
68 int E_(Import_Shoutcast)( vlc_object_t *p_this )
69 {
70     demux_t *p_demux = (demux_t *)p_this;
71
72     if( !isDemux( p_demux, "shout-winamp" ) )
73         return VLC_EGENERIC;
74     
75     STANDARD_DEMUX_INIT_MSG( "using shoutcast playlist reader" );
76     p_demux->p_sys->p_playlist = NULL;
77     p_demux->p_sys->p_xml = NULL;
78     p_demux->p_sys->p_xml_reader = NULL;
79
80     /* Do we want to list adult content ? */
81     var_Create( p_demux, "shoutcast-show-adult",
82                 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
83     p_demux->p_sys->b_adult = var_GetBool( p_demux, "shoutcast-show-adult" );
84
85     return VLC_SUCCESS;
86 }
87
88 /*****************************************************************************
89  * Deactivate: frees unused data
90  *****************************************************************************/
91 void E_(Close_Shoutcast)( vlc_object_t *p_this )
92 {
93     demux_t *p_demux = (demux_t *)p_this;
94     demux_sys_t *p_sys = p_demux->p_sys;
95
96     if( p_sys->p_playlist )
97         vlc_object_release( p_sys->p_playlist );
98     if( p_sys->p_xml_reader )
99         xml_ReaderDelete( p_sys->p_xml, p_sys->p_xml_reader );
100     if( p_sys->p_xml )
101         xml_Delete( p_sys->p_xml );
102     free( p_sys );
103 }
104
105 static int Demux( demux_t *p_demux )
106 {
107     demux_sys_t *p_sys = p_demux->p_sys;
108     xml_t *p_xml;
109     xml_reader_t *p_xml_reader;
110     char *psz_eltname = NULL;
111     INIT_PLAYLIST_STUFF;
112     p_sys->p_playlist = p_playlist;
113     p_sys->p_current = p_current;
114     p_sys->i_parent_id = i_parent_id;
115     p_sys->p_item_in_category = p_item_in_category;
116
117     p_xml = p_sys->p_xml = xml_Create( p_demux );
118     if( !p_xml ) return -1;
119
120     p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
121     if( !p_xml_reader ) return -1;
122     p_sys->p_xml_reader = p_xml_reader;
123
124     /* check root node */
125     if( xml_ReaderRead( p_xml_reader ) != 1 )
126     {
127         msg_Err( p_demux, "invalid file (no root node)" );
128         return -1;
129     }
130
131     if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
132         ( psz_eltname = xml_ReaderName( p_xml_reader ) ) == NULL ||
133         ( strcmp( psz_eltname, "genrelist" )
134           && strcmp( psz_eltname, "stationlist" ) ) )
135     {
136         msg_Err( p_demux, "invalid root node %i, %s",
137                  xml_ReaderNodeType( p_xml_reader ), psz_eltname );
138         if( psz_eltname ) free( psz_eltname );
139         return -1;
140     }
141
142     if( !strcmp( psz_eltname, "genrelist" ) )
143     {
144         /* we're reading a genre list */
145         free( psz_eltname );
146         if( DemuxGenre( p_demux ) ) return -1;
147     }
148     else
149     {
150         /* we're reading a station list */
151         free( psz_eltname );
152         if( DemuxStation( p_demux ) ) return -1;
153     }
154
155     HANDLE_PLAY_AND_RELEASE;
156     p_sys->p_playlist = NULL;
157     return VLC_SUCCESS;
158 }
159
160 #define GET_VALUE( a ) \
161                         if( !strcmp( psz_attrname, #a ) ) \
162                         { \
163                             psz_ ## a = strdup( psz_attrvalue ); \
164                         }
165 /* <genrelist>
166  *   <genre name="the name"></genre>
167  *   ...
168  * </genrelist>
169  **/
170 static int DemuxGenre( demux_t *p_demux )
171 {
172     demux_sys_t *p_sys = p_demux->p_sys;
173     char *psz_name = NULL; /* genre name */
174     char *psz_eltname = NULL; /* tag name */
175     input_item_t *p_input;
176
177 #define FREE(a) if( a ) free( a ); a = NULL;
178     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
179     {
180         int i_type;
181
182         // Get the node type
183         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
184         switch( i_type )
185         {
186             // Error
187             case -1:
188                 return -1;
189                 break;
190
191             case XML_READER_STARTELEM:
192                 // Read the element name
193                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
194                 if( !psz_eltname ) return -1;
195
196                 if( !strcmp( psz_eltname, "genre" ) )
197                 {
198                     // Read the attributes
199                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
200                     {
201                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
202                         char *psz_attrvalue =
203                             xml_ReaderValue( p_sys->p_xml_reader );
204                         if( !psz_attrname || !psz_attrvalue )
205                         {
206                             FREE(psz_attrname);
207                             FREE(psz_attrvalue);
208                             free(psz_eltname);
209                             /*FIXME: isn't return a bit too much. what about break*/
210                             return -1;
211                         }
212
213                         GET_VALUE( name )
214                         else
215                         {
216                             msg_Warn( p_demux,
217                                       "unexpected attribure %s in element %s",
218                                       psz_attrname,psz_eltname );
219                         }
220                         free( psz_attrname );
221                         free( psz_attrvalue );
222                     }
223                 }
224                 free( psz_eltname ); psz_eltname = NULL;
225                 break;
226
227             case XML_READER_TEXT:
228                 break;
229
230             // End element
231             case XML_READER_ENDELEM:
232                 // Read the element name
233                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
234                 if( !psz_eltname ) return -1;
235                 if( !strcmp( psz_eltname, "genre" ) )
236                 {
237                     char *psz_mrl = malloc( strlen( SHOUTCAST_BASE_URL )
238                             + strlen( "?genre=" ) + strlen( psz_name ) + 1 );
239                     sprintf( psz_mrl, SHOUTCAST_BASE_URL "?genre=%s",
240                              psz_name );
241                     p_input = input_ItemNewExt( p_sys->p_playlist, psz_mrl,
242                                                 psz_name, 0, NULL, -1 );
243                     vlc_input_item_CopyOptions( p_sys->p_current->p_input,
244                                                 p_input );
245                     free( psz_mrl );
246                     playlist_AddWhereverNeeded( p_sys->p_playlist, p_input,
247                              p_sys->p_current, p_sys->p_item_in_category,
248                              (p_sys->i_parent_id > 0 ) ? VLC_TRUE: VLC_FALSE,
249                              PLAYLIST_APPEND );
250                     FREE( psz_name );
251                 }
252                 FREE( psz_eltname );
253                 break;
254         }
255     }
256     return 0;
257 }
258
259 /* radio stations:
260  * <stationlist>
261  *   <tunein base="/sbin/tunein-station.pls"></tunein>
262  *   <station name="the name"
263  *            mt="mime type"
264  *            id="the id"
265  *            br="bit rate"
266  *            genre="A big genre string"
267  *            ct="current track name/author/..."
268  *            lc="listener count"></station>
269  * </stationlist>
270  *
271  * TV stations:
272  * <stationlist>
273  *   <tunein base="/sbin/tunein-station.pls"></tunein>
274  *   <station name="the name"
275  *            id="the id"
276  *            br="bit rate"
277  *            rt="rating"
278  *            load="server load ?"
279  *            ct="current track name/author/..."
280  *            genre="A big genre string"
281  *            lc="listener count"></station>
282  * </stationlist>
283  **/
284 static int DemuxStation( demux_t *p_demux )
285 {
286     demux_sys_t *p_sys = p_demux->p_sys;
287     input_item_t *p_input;
288
289     char *psz_base = NULL; /* */
290
291     char *psz_name = NULL; /* genre name */
292     char *psz_mt = NULL; /* mime type */
293     char *psz_id = NULL; /* id */
294     char *psz_br = NULL; /* bit rate */
295     char *psz_genre = NULL; /* genre */
296     char *psz_ct = NULL; /* current track */
297     char *psz_lc = NULL; /* listener count */
298
299     /* If these are set then it's *not* a radio but a TV */
300     char *psz_rt = NULL; /* rating for shoutcast TV */
301     char *psz_load = NULL; /* load for shoutcast TV */
302
303     char *psz_eltname = NULL; /* tag name */
304
305     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
306     {
307         int i_type;
308
309         // Get the node type
310         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
311         switch( i_type )
312         {
313             // Error
314             case -1:
315                 return -1;
316                 break;
317
318             case XML_READER_STARTELEM:
319                 // Read the element name
320                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
321                 if( !psz_eltname ) return -1;
322
323                 // Read the attributes
324                 if( !strcmp( psz_eltname, "tunein" ) )
325                 {
326                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
327                     {
328                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
329                         char *psz_attrvalue =
330                             xml_ReaderValue( p_sys->p_xml_reader );
331                         if( !psz_attrname || !psz_attrvalue )
332                         {
333                             free(psz_eltname);
334                             FREE(psz_attrname);
335                             FREE(psz_attrvalue);
336                             return -1;
337                         }
338
339                         GET_VALUE( base )
340                         else
341                         {
342                             msg_Warn( p_demux,
343                                       "unexpected attribure %s in element %s",
344                                       psz_attrname, psz_eltname );
345                         }
346                         free( psz_attrname );
347                         free( psz_attrvalue );
348                     }
349                 }
350                 else if( !strcmp( psz_eltname, "station" ) )
351                 {
352                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
353                     {
354                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
355                         char *psz_attrvalue =
356                             xml_ReaderValue( p_sys->p_xml_reader );
357                         if( !psz_attrname || !psz_attrvalue )
358                         {
359                             free(psz_eltname);
360                             FREE(psz_attrname);
361                             FREE(psz_attrvalue);
362                             return -1;
363                         }
364
365                         GET_VALUE( name )
366                         else GET_VALUE( mt )
367                         else GET_VALUE( id )
368                         else GET_VALUE( br )
369                         else GET_VALUE( genre )
370                         else GET_VALUE( ct )
371                         else GET_VALUE( lc )
372                         else GET_VALUE( rt )
373                         else GET_VALUE( load )
374                         else
375                         {
376                             msg_Warn( p_demux,
377                                       "unexpected attribute %s in element %s",
378                                       psz_attrname, psz_eltname );
379                         }
380                         free( psz_attrname );
381                         free( psz_attrvalue );
382                     }
383                 }
384                 free(psz_eltname);
385                 break;
386
387             case XML_READER_TEXT:
388                 break;
389
390             // End element
391             case XML_READER_ENDELEM:
392                 // Read the element name
393                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
394                 if( !psz_eltname ) return -1;
395                 if( !strcmp( psz_eltname, "station" ) &&
396                     ( psz_base || ( psz_rt && psz_load &&
397                     ( p_sys->b_adult || strcmp( psz_rt, "NC17" ) ) ) ) )
398                 {
399                     playlist_item_t *p_item;
400                     char *psz_mrl = NULL;
401                     if( psz_rt || psz_load )
402                     {
403                         /* tv */
404                         psz_mrl = malloc( strlen( SHOUTCAST_TV_TUNEIN_URL )
405                                           + strlen( psz_id ) + 1 );
406                         sprintf( psz_mrl, SHOUTCAST_TV_TUNEIN_URL "%s",
407                                  psz_id );
408                     }
409                     else
410                     {
411                         /* radio */
412                         psz_mrl = malloc( strlen( SHOUTCAST_TUNEIN_BASE_URL )
413                             + strlen( psz_base ) + strlen( "?id=" )
414                             + strlen( psz_id ) + 1 );
415                         sprintf( psz_mrl, SHOUTCAST_TUNEIN_BASE_URL "%s?id=%s",
416                              psz_base, psz_id );
417                     }
418                     p_input = input_ItemNewExt( p_sys->p_playlist, psz_mrl,
419                                                 psz_name , 0, NULL, -1 );
420                     free( psz_mrl );
421
422                     vlc_input_item_CopyOptions( p_sys->p_current->p_input,
423                                                 p_input );
424
425 #define SADD_INFO( type, field ) if( field ) { vlc_input_item_AddInfo( \
426                     p_input, _("Shoutcast"), _(type), "%s", field ) ; }
427                     SADD_INFO( "Mime type", psz_mt );
428                     SADD_INFO( "Bitrate", psz_br );
429                     SADD_INFO( "Listeners", psz_lc );
430                     SADD_INFO( "Load", psz_load );
431                     p_input->p_meta = vlc_meta_New();
432                     if( psz_genre )
433                         vlc_meta_SetGenre( p_input->p_meta, psz_genre );
434                     if( psz_ct )
435                         vlc_meta_SetNowPlaying( p_input->p_meta, psz_ct );
436                     if( psz_rt )
437                         vlc_meta_SetRating( p_input->p_meta, psz_rt );
438
439                     playlist_AddWhereverNeeded( p_sys->p_playlist, p_input,
440                              p_sys->p_current, p_sys->p_item_in_category,
441                              (p_sys->i_parent_id > 0 ) ? VLC_TRUE: VLC_FALSE,
442                              PLAYLIST_APPEND );
443
444                     FREE( psz_name );
445                     FREE( psz_mt )
446                     FREE( psz_id )
447                     FREE( psz_br )
448                     FREE( psz_genre )
449                     FREE( psz_ct )
450                     FREE( psz_lc )
451                     FREE( psz_rt )
452                 }
453                 free( psz_eltname );
454                 break;
455         }
456     }
457     return 0;
458 }
459 #undef FREE
460
461 static int Control( demux_t *p_demux, int i_query, va_list args )
462 {
463     return VLC_EGENERIC;
464 }