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