]> git.sesse.net Git - vlc/blob - modules/demux/playlist/shoutcast.c
d47e41312eaaeb9b71fce73f3ada84624d025183
[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     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
178     {
179         int i_type;
180
181         // Get the node type
182         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
183         switch( i_type )
184         {
185             // Error
186             case -1:
187                 return -1;
188                 break;
189
190             case XML_READER_STARTELEM:
191                 // Read the element name
192                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
193                 if( !psz_eltname ) return -1;
194
195                 if( !strcmp( psz_eltname, "genre" ) )
196                 {
197                     // Read the attributes
198                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
199                     {
200                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
201                         char *psz_attrvalue =
202                             xml_ReaderValue( p_sys->p_xml_reader );
203                         if( !psz_attrname || !psz_attrvalue )
204                         {
205                             FREENULL(psz_attrname);
206                             FREENULL(psz_attrvalue);
207                             free(psz_eltname);
208                             /*FIXME: isn't return a bit too much. what about break*/
209                             return -1;
210                         }
211
212                         GET_VALUE( name )
213                         else
214                         {
215                             msg_Warn( p_demux,
216                                       "unexpected attribure %s in element %s",
217                                       psz_attrname,psz_eltname );
218                         }
219                         free( psz_attrname );
220                         free( psz_attrvalue );
221                     }
222                 }
223                 free( psz_eltname ); psz_eltname = NULL;
224                 break;
225
226             case XML_READER_TEXT:
227                 break;
228
229             // End element
230             case XML_READER_ENDELEM:
231                 // Read the element name
232                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
233                 if( !psz_eltname ) return -1;
234                 if( !strcmp( psz_eltname, "genre" ) )
235                 {
236                     char *psz_mrl = malloc( strlen( SHOUTCAST_BASE_URL )
237                             + strlen( "?genre=" ) + strlen( psz_name ) + 1 );
238                     sprintf( psz_mrl, SHOUTCAST_BASE_URL "?genre=%s",
239                              psz_name );
240                     p_input = input_ItemNewExt( p_sys->p_playlist, psz_mrl,
241                                                 psz_name, 0, NULL, -1 );
242                     vlc_input_item_CopyOptions( p_sys->p_current->p_input,
243                                                 p_input );
244                     free( psz_mrl );
245                     playlist_AddWhereverNeeded( p_sys->p_playlist, p_input,
246                              p_sys->p_current, p_sys->p_item_in_category,
247                              (p_sys->i_parent_id > 0 ) ? VLC_TRUE: VLC_FALSE,
248                              PLAYLIST_APPEND );
249                     FREENULL( psz_name );
250                 }
251                 FREENULL( psz_eltname );
252                 break;
253         }
254     }
255     return 0;
256 }
257
258 /* radio stations:
259  * <stationlist>
260  *   <tunein base="/sbin/tunein-station.pls"></tunein>
261  *   <station name="the name"
262  *            mt="mime type"
263  *            id="the id"
264  *            br="bit rate"
265  *            genre="A big genre string"
266  *            ct="current track name/author/..."
267  *            lc="listener count"></station>
268  * </stationlist>
269  *
270  * TV stations:
271  * <stationlist>
272  *   <tunein base="/sbin/tunein-station.pls"></tunein>
273  *   <station name="the name"
274  *            id="the id"
275  *            br="bit rate"
276  *            rt="rating"
277  *            load="server load ?"
278  *            ct="current track name/author/..."
279  *            genre="A big genre string"
280  *            lc="listener count"></station>
281  * </stationlist>
282  **/
283 static int DemuxStation( demux_t *p_demux )
284 {
285     demux_sys_t *p_sys = p_demux->p_sys;
286     input_item_t *p_input;
287
288     char *psz_base = NULL; /* */
289
290     char *psz_name = NULL; /* genre name */
291     char *psz_mt = NULL; /* mime type */
292     char *psz_id = NULL; /* id */
293     char *psz_br = NULL; /* bit rate */
294     char *psz_genre = NULL; /* genre */
295     char *psz_ct = NULL; /* current track */
296     char *psz_lc = NULL; /* listener count */
297
298     /* If these are set then it's *not* a radio but a TV */
299     char *psz_rt = NULL; /* rating for shoutcast TV */
300     char *psz_load = NULL; /* load for shoutcast TV */
301
302     char *psz_eltname = NULL; /* tag name */
303
304     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
305     {
306         int i_type;
307
308         // Get the node type
309         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
310         switch( i_type )
311         {
312             // Error
313             case -1:
314                 return -1;
315                 break;
316
317             case XML_READER_STARTELEM:
318                 // Read the element name
319                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
320                 if( !psz_eltname ) return -1;
321
322                 // Read the attributes
323                 if( !strcmp( psz_eltname, "tunein" ) )
324                 {
325                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
326                     {
327                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
328                         char *psz_attrvalue =
329                             xml_ReaderValue( p_sys->p_xml_reader );
330                         if( !psz_attrname || !psz_attrvalue )
331                         {
332                             free(psz_eltname);
333                             FREENULL(psz_attrname);
334                             FREENULL(psz_attrvalue);
335                             return -1;
336                         }
337
338                         GET_VALUE( base )
339                         else
340                         {
341                             msg_Warn( p_demux,
342                                       "unexpected attribure %s in element %s",
343                                       psz_attrname, psz_eltname );
344                         }
345                         free( psz_attrname );
346                         free( psz_attrvalue );
347                     }
348                 }
349                 else if( !strcmp( psz_eltname, "station" ) )
350                 {
351                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
352                     {
353                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
354                         char *psz_attrvalue =
355                             xml_ReaderValue( p_sys->p_xml_reader );
356                         if( !psz_attrname || !psz_attrvalue )
357                         {
358                             free(psz_eltname);
359                             FREENULL(psz_attrname);
360                             FREENULL(psz_attrvalue);
361                             return -1;
362                         }
363
364                         GET_VALUE( name )
365                         else GET_VALUE( mt )
366                         else GET_VALUE( id )
367                         else GET_VALUE( br )
368                         else GET_VALUE( genre )
369                         else GET_VALUE( ct )
370                         else GET_VALUE( lc )
371                         else GET_VALUE( rt )
372                         else GET_VALUE( load )
373                         else
374                         {
375                             msg_Warn( p_demux,
376                                       "unexpected attribute %s in element %s",
377                                       psz_attrname, psz_eltname );
378                         }
379                         free( psz_attrname );
380                         free( psz_attrvalue );
381                     }
382                 }
383                 free(psz_eltname);
384                 break;
385
386             case XML_READER_TEXT:
387                 break;
388
389             // End element
390             case XML_READER_ENDELEM:
391                 // Read the element name
392                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
393                 if( !psz_eltname ) return -1;
394                 if( !strcmp( psz_eltname, "station" ) &&
395                     ( psz_base || ( psz_rt && psz_load &&
396                     ( p_sys->b_adult || strcmp( psz_rt, "NC17" ) ) ) ) )
397                 {
398                     char *psz_mrl = NULL;
399                     if( psz_rt || psz_load )
400                     {
401                         /* tv */
402                         psz_mrl = malloc( strlen( SHOUTCAST_TV_TUNEIN_URL )
403                                           + strlen( psz_id ) + 1 );
404                         sprintf( psz_mrl, SHOUTCAST_TV_TUNEIN_URL "%s",
405                                  psz_id );
406                     }
407                     else
408                     {
409                         /* radio */
410                         psz_mrl = malloc( strlen( SHOUTCAST_TUNEIN_BASE_URL )
411                             + strlen( psz_base ) + strlen( "?id=" )
412                             + strlen( psz_id ) + 1 );
413                         sprintf( psz_mrl, SHOUTCAST_TUNEIN_BASE_URL "%s?id=%s",
414                              psz_base, psz_id );
415                     }
416                     p_input = input_ItemNewExt( p_sys->p_playlist, psz_mrl,
417                                                 psz_name , 0, NULL, -1 );
418                     free( psz_mrl );
419
420                     vlc_input_item_CopyOptions( p_sys->p_current->p_input,
421                                                 p_input );
422
423 #define SADD_INFO( type, field ) if( field ) { vlc_input_item_AddInfo( \
424                     p_input, _("Shoutcast"), _(type), "%s", field ) ; }
425                     SADD_INFO( "Mime type", psz_mt );
426                     SADD_INFO( "Bitrate", psz_br );
427                     SADD_INFO( "Listeners", psz_lc );
428                     SADD_INFO( "Load", psz_load );
429                     p_input->p_meta = vlc_meta_New();
430                     if( psz_genre )
431                         vlc_meta_SetGenre( p_input->p_meta, psz_genre );
432                     if( psz_ct )
433                         vlc_meta_SetNowPlaying( p_input->p_meta, psz_ct );
434                     if( psz_rt )
435                         vlc_meta_SetRating( p_input->p_meta, psz_rt );
436
437                     playlist_AddWhereverNeeded( p_sys->p_playlist, p_input,
438                              p_sys->p_current, p_sys->p_item_in_category,
439                              (p_sys->i_parent_id > 0 ) ? VLC_TRUE: VLC_FALSE,
440                              PLAYLIST_APPEND );
441
442                     FREENULL( psz_name );
443                     FREENULL( psz_mt )
444                     FREENULL( psz_id )
445                     FREENULL( psz_br )
446                     FREENULL( psz_genre )
447                     FREENULL( psz_ct )
448                     FREENULL( psz_lc )
449                     FREENULL( psz_rt )
450                 }
451                 free( psz_eltname );
452                 break;
453         }
454     }
455     return 0;
456 }
457
458 static int Control( demux_t *p_demux, int i_query, va_list args )
459 {
460     return VLC_EGENERIC;
461 }