1 /*****************************************************************************
2 * shoutcast.c: Winamp >=5.2 shoutcast demuxer
3 *****************************************************************************
4 * Copyright (C) 2006 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea -@t- videolan -Dot- org>
8 * based on b4s.c by Sigmund Augdal Helberg <dnumgis@videolan.org>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
29 #include <ctype.h> /* isspace() */
32 #include <vlc/input.h>
35 #include <errno.h> /* ENOMEM */
41 playlist_t *p_playlist;
42 playlist_item_t *p_current;
43 playlist_item_t *p_item_in_category;
47 xml_reader_t *p_xml_reader;
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="
57 /*****************************************************************************
59 *****************************************************************************/
60 static int Demux( demux_t *p_demux);
61 static int Control( demux_t *p_demux, int i_query, va_list args );
63 static int DemuxGenre( demux_t *p_demux );
64 static int DemuxStation( demux_t *p_demux );
66 /*****************************************************************************
67 * Import_Shoutcast: main import function
68 *****************************************************************************/
69 int E_(Import_Shoutcast)( vlc_object_t *p_this )
71 demux_t *p_demux = (demux_t *)p_this;
76 psz_ext = strrchr ( p_demux->psz_path, '.' );
78 if( !p_demux->psz_demux || strcmp(p_demux->psz_demux, "shout-winamp") )
82 msg_Dbg( p_demux, "using shoutcast playlist import");
84 p_demux->pf_control = Control;
85 p_demux->pf_demux = Demux;
86 p_demux->p_sys = p_sys = malloc( sizeof(demux_sys_t) );
89 msg_Err( p_demux, "out of memory" );
93 p_sys->p_playlist = NULL;
95 p_sys->p_xml_reader = NULL;
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" );
105 /*****************************************************************************
106 * Deactivate: frees unused data
107 *****************************************************************************/
108 void E_(Close_Shoutcast)( vlc_object_t *p_this )
110 demux_t *p_demux = (demux_t *)p_this;
111 demux_sys_t *p_sys = p_demux->p_sys;
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 );
118 xml_Delete( p_sys->p_xml );
122 static int Demux( demux_t *p_demux )
124 demux_sys_t *p_sys = p_demux->p_sys;
126 xml_reader_t *p_xml_reader;
127 char *psz_eltname = NULL;
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;
134 p_xml = p_sys->p_xml = xml_Create( p_demux );
135 if( !p_xml ) return -1;
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;
141 /* check root node */
142 if( xml_ReaderRead( p_xml_reader ) != 1 )
144 msg_Err( p_demux, "invalid file (no root node)" );
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" ) ) )
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 );
159 if( !strcmp( psz_eltname, "genrelist" ) )
161 /* we're reading a genre list */
163 if( DemuxGenre( p_demux ) ) return -1;
167 /* we're reading a station list */
169 if( DemuxStation( p_demux ) ) return -1;
172 HANDLE_PLAY_AND_RELEASE;
173 p_sys->p_playlist = NULL;
177 #define GET_VALUE( a ) \
178 if( !strcmp( psz_attrname, #a ) ) \
180 psz_ ## a = strdup( psz_attrvalue ); \
183 * <genre name="the name"></genre>
187 static int DemuxGenre( demux_t *p_demux )
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;
194 #define FREE(a) if( a ) free( a ); a = NULL;
195 while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
200 i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
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;
213 if( !strcmp( psz_eltname, "genre" ) )
215 // Read the attributes
216 while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
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 )
226 /*FIXME: isn't return a bit too much. what about break*/
234 "unexpected attribure %s in element %s",
235 psz_attrname,psz_eltname );
237 free( psz_attrname );
238 free( psz_attrvalue );
241 free( psz_eltname ); psz_eltname = NULL;
244 case XML_READER_TEXT:
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" ) )
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",
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,
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,
278 * <tunein base="/sbin/tunein-station.pls"></tunein>
279 * <station name="the name"
283 * genre="A big genre string"
284 * ct="current track name/author/..."
285 * lc="listener count"></station>
290 * <tunein base="/sbin/tunein-station.pls"></tunein>
291 * <station name="the name"
295 * load="server load ?"
296 * ct="current track name/author/..."
297 * genre="A big genre string"
298 * lc="listener count"></station>
301 static int DemuxStation( demux_t *p_demux )
303 demux_sys_t *p_sys = p_demux->p_sys;
304 input_item_t *p_input;
306 char *psz_base = NULL; /* */
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 */
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 */
320 char *psz_eltname = NULL; /* tag name */
322 while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
327 i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
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;
340 // Read the attributes
341 if( !strcmp( psz_eltname, "tunein" ) )
343 while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
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 )
360 "unexpected attribure %s in element %s",
361 psz_attrname, psz_eltname );
363 free( psz_attrname );
364 free( psz_attrvalue );
367 else if( !strcmp( psz_eltname, "station" ) )
369 while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
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 )
386 else GET_VALUE( genre )
390 else GET_VALUE( load )
394 "unexpected attribute %s in element %s",
395 psz_attrname, psz_eltname );
397 free( psz_attrname );
398 free( psz_attrvalue );
404 case XML_READER_TEXT:
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" ) ) ) ) )
416 playlist_item_t *p_item;
417 char *psz_mrl = NULL;
418 if( psz_rt || psz_load )
421 psz_mrl = malloc( strlen( SHOUTCAST_TV_TUNEIN_URL )
422 + strlen( psz_id ) + 1 );
423 sprintf( psz_mrl, SHOUTCAST_TV_TUNEIN_URL "%s",
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",
435 p_input = input_ItemNewExt( p_sys->p_playlist, psz_mrl,
436 psz_name , 0, NULL, -1 );
439 vlc_input_item_CopyOptions( p_sys->p_current->p_input,
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();
450 vlc_meta_SetGenre( p_input->p_meta, psz_genre );
452 vlc_meta_SetNowPlaying( p_input->p_meta, psz_ct );
454 vlc_meta_SetRating( p_input->p_meta, psz_rt );
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,
478 static int Control( demux_t *p_demux, int i_query, va_list args )