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