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