]> git.sesse.net Git - vlc/blob - modules/demux/playlist/shoutcast.c
vlc_path2uri(): rename from make_URI() and always convert path
[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 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_demux.h>
35
36 #include "playlist.h"
37 #include <vlc_xml.h>
38
39 /* duplicate from modules/services_discovery/shout.c */
40 #define SHOUTCAST_BASE_URL "http/shout-winamp://www.shoutcast.com/sbin/newxml.phtml"
41 #define SHOUTCAST_TUNEIN_BASE_URL "http://www.shoutcast.com"
42 #define SHOUTCAST_TV_TUNEIN_URL "http://www.shoutcast.com/sbin/tunein-tvstation.pls?id="
43
44 /*****************************************************************************
45  * Local prototypes
46  *****************************************************************************/
47 static int Demux( demux_t *p_demux);
48
49 static int DemuxGenre( demux_t *p_demux, xml_reader_t *p_xml_reader,
50                        input_item_node_t *p_input_node );
51 static int DemuxStation( demux_t *p_demux, xml_reader_t *p_xml_reader,
52                          input_item_node_t *p_input_node, bool b_adult );
53
54 /*****************************************************************************
55  * Import_Shoutcast: main import function
56  *****************************************************************************/
57 int Import_Shoutcast( vlc_object_t *p_this )
58 {
59     demux_t *p_demux = (demux_t *)p_this;
60
61     if( !demux_IsForced( p_demux, "shout-winamp" ) )
62         return VLC_EGENERIC;
63
64     p_demux->pf_demux = Demux;
65     p_demux->pf_control = Control;
66     msg_Dbg( p_demux, "using shoutcast playlist reader" );
67
68     return VLC_SUCCESS;
69 }
70
71 static int Demux( demux_t *p_demux )
72 {
73     xml_reader_t *p_xml_reader = NULL;
74     const char *node;
75     int i_ret = -1;
76     input_item_t *p_current_input = GetCurrentItem(p_demux);
77     input_item_node_t *p_input_node = NULL;
78
79     p_xml_reader = xml_ReaderCreate( p_demux, p_demux->s );
80     if( !p_xml_reader )
81         goto error;
82
83     /* check root node */
84     if( xml_ReaderNextNode( p_xml_reader, &node ) != XML_READER_STARTELEM )
85     {
86         msg_Err( p_demux, "invalid file (no root node)" );
87         goto error;
88     }
89
90     if( strcmp( node, "genrelist" ) && strcmp( node, "stationlist" ) )
91     {
92         msg_Err( p_demux, "invalid root node <%s>", node );
93         goto error;
94     }
95
96     p_input_node = input_item_node_Create( p_current_input );
97
98     if( !strcmp( node, "genrelist" ) )
99     {
100         /* we're reading a genre list */
101         if( DemuxGenre( p_demux, p_xml_reader, p_input_node ) )
102             goto error;
103     }
104     else
105     {
106         /* we're reading a station list */
107         if( DemuxStation( p_demux, p_xml_reader, p_input_node,
108                 var_InheritBool( p_demux, "shoutcast-show-adult" ) ) )
109             goto error;
110     }
111
112     input_item_node_PostAndDelete( p_input_node );
113     p_input_node = NULL;
114
115     i_ret = 0; /* Needed for correct operation of go back */
116
117 error:
118     if( p_xml_reader )
119         xml_ReaderDelete( p_xml_reader );
120     if( p_input_node ) input_item_node_Delete( p_input_node );
121     vlc_gc_decref(p_current_input);
122     return i_ret;
123 }
124
125 /* <genrelist>
126  *   <genre name="the name"></genre>
127  *   ...
128  * </genrelist>
129  **/
130 static int DemuxGenre( demux_t *p_demux, xml_reader_t *p_xml_reader,
131                        input_item_node_t *p_input_node )
132 {
133     const char *node;
134     char *psz_name = NULL; /* genre name */
135     int type;
136
137     while( (type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
138     {
139         switch( type )
140         {
141             case XML_READER_STARTELEM:
142             {
143                 if( !strcmp( node, "genre" ) )
144                 {
145                     // Read the attributes
146                     const char *name, *value;
147                     while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) )
148                     {
149                         if( !strcmp( name, "name" ) )
150                         {
151                             free(psz_name);
152                             psz_name = strdup( value );
153                         }
154                         else
155                             msg_Warn( p_demux,
156                                       "unexpected attribute %s in <%s>",
157                                       name, node );
158                     }
159                 }
160                 break;
161             }
162
163             case XML_READER_ENDELEM:
164                 if( !strcmp( node, "genre" ) && psz_name != NULL )
165                 {
166                     char* psz_mrl;
167
168                     if( asprintf( &psz_mrl, SHOUTCAST_BASE_URL "?genre=%s",
169                                   psz_name ) != -1 )
170                     {
171                         input_item_t *p_input;
172                         p_input = input_item_New( psz_mrl, psz_name );
173                         input_item_CopyOptions( p_input_node->p_item, p_input );
174                         free( psz_mrl );
175                         input_item_node_AppendItem( p_input_node, p_input );
176                         vlc_gc_decref( p_input );
177                     }
178                     FREENULL( psz_name );
179                 }
180                 break;
181         }
182     }
183
184     free( psz_name );
185     return 0;
186 }
187
188 /* radio stations:
189  * <stationlist>
190  *   <tunein base="/sbin/tunein-station.pls"></tunein>
191  *   <station name="the name"
192  *            mt="mime type"
193  *            id="the id"
194  *            br="bit rate"
195  *            genre="A big genre string"
196  *            ct="current track name/author/..."
197  *            lc="listener count"></station>
198  * </stationlist>
199  *
200  * TV stations:
201  * <stationlist>
202  *   <tunein base="/sbin/tunein-station.pls"></tunein>
203  *   <station name="the name"
204  *            id="the id"
205  *            br="bit rate"
206  *            rt="rating"
207  *            load="server load ?"
208  *            ct="current track name/author/..."
209  *            genre="A big genre string"
210  *            lc="listener count"></station>
211  * </stationlist>
212  **/
213 static int DemuxStation( demux_t *p_demux, xml_reader_t *p_xml_reader,
214                          input_item_node_t *p_input_node, bool b_adult )
215 {
216     char *psz_base = NULL; /* */
217
218     char *psz_name = NULL; /* genre name */
219     char *psz_mt = NULL; /* mime type */
220     char *psz_id = NULL; /* id */
221     char *psz_br = NULL; /* bit rate */
222     char *psz_genre = NULL; /* genre */
223     char *psz_ct = NULL; /* current track */
224     char *psz_lc = NULL; /* listener count */
225
226     /* If these are set then it's *not* a radio but a TV */
227     char *psz_rt = NULL; /* rating for shoutcast TV */
228     char *psz_load = NULL; /* load for shoutcast TV */
229
230     const char *node; /* tag name */
231     int i_type;
232
233     while( (i_type = xml_ReaderNextNode( p_xml_reader, &node )) > 0 )
234     {
235         switch( i_type )
236         {
237             case XML_READER_STARTELEM:
238                 // Read the attributes
239                 if( !strcmp( node, "tunein" ) )
240                 {
241                     const char *name, *value;
242                     while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) )
243                     {
244                         if( !strcmp( name, "base" ) )
245                         {
246                             free( psz_base );
247                             psz_base = strdup( value );
248                         }
249                         else
250                             msg_Warn( p_demux,
251                                       "unexpected attribute %s in <%s>",
252                                       name, node );
253                     }
254                 }
255                 else if( !strcmp( node, "station" ) )
256                 {
257                     const char *name, *value;
258                     while( (name = xml_ReaderNextAttr( p_xml_reader, &value )) )
259                     {
260                         char **p = NULL;
261                         if( !strcmp( name, "name" ) )
262                             p = &psz_name;
263                         else if ( !strcmp( name, "mt" ) )
264                             p = &psz_mt;
265                         else if ( !strcmp( name, "id" ) )
266                             p = &psz_id;
267                         else if ( !strcmp( name, "br" ) )
268                             p = &psz_br;
269                         else if ( !strcmp( name, "genre" ) )
270                             p = &psz_genre;
271                         else if ( !strcmp( name, "ct" ) )
272                             p = &psz_ct;
273                         else if ( !strcmp( name, "lc" ) )
274                             p = &psz_lc;
275                         else if ( !strcmp( name, "rt" ) )
276                             p = &psz_rt;
277                         else if ( !strcmp( name, "load" ) )
278                             p = &psz_load;
279                         if( p != NULL )
280                         {
281                             free( *p );
282                             *p = strdup( value );
283                         }
284                         else
285                             msg_Warn( p_demux,
286                                       "unexpected attribute %s in <%s>",
287                                       name, node );
288                     }
289                 }
290                 break;
291
292             // End element
293             case XML_READER_ENDELEM:
294                 if( !strcmp( node, "station" ) &&
295                     ( psz_base || ( psz_rt && psz_load &&
296                     ( b_adult || strcmp( psz_rt, "NC17" ) ) ) ) )
297                 {
298                     char *psz_mrl = NULL;
299                     if( psz_rt || psz_load )
300                     {
301                         /* tv */
302                         if( asprintf( &psz_mrl, SHOUTCAST_TV_TUNEIN_URL "%s",
303                                  psz_id ) == -1)
304                             psz_mrl = NULL;
305                     }
306                     else
307                     {
308                         /* radio */
309                         if( asprintf( &psz_mrl, SHOUTCAST_TUNEIN_BASE_URL "%s?id=%s",
310                              psz_base, psz_id ) == -1 )
311                             psz_mrl = NULL;
312                     }
313
314                     /* Create the item */
315                     input_item_t *p_input;
316                     p_input = input_item_New( psz_mrl, psz_name );
317                     input_item_CopyOptions( p_input_node->p_item, p_input );
318                     free( psz_mrl );
319
320 #define SADD_INFO( type, field ) \
321                     if( field ) \
322                         input_item_AddInfo( p_input, _("Shoutcast"), \
323                                             vlc_gettext(type), "%s", field )
324                     SADD_INFO( N_("Mime"), psz_mt );
325                     SADD_INFO( N_("Bitrate"), psz_br );
326                     SADD_INFO( N_("Listeners"), psz_lc );
327                     SADD_INFO( N_("Load"), psz_load );
328                     if( psz_genre )
329                         input_item_SetGenre( p_input, psz_genre );
330                     if( psz_ct )
331                         input_item_SetNowPlaying( p_input, psz_ct );
332                     if( psz_rt )
333                         input_item_SetRating( p_input, psz_rt );
334                     input_item_node_AppendItem( p_input_node, p_input );
335                     vlc_gc_decref( p_input );
336                     FREENULL( psz_base );
337                     FREENULL( psz_name );
338                     FREENULL( psz_mt );
339                     FREENULL( psz_id );
340                     FREENULL( psz_br );
341                     FREENULL( psz_genre );
342                     FREENULL( psz_ct );
343                     FREENULL( psz_lc );
344                     FREENULL( psz_rt );
345                     FREENULL( psz_load );
346                 }
347                 break;
348         }
349     }
350     /* FIXME: leaks on missing ENDELEMENT? */
351     return 0;
352 }