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;
45 xml_reader_t *p_xml_reader;
48 /* duplicate from modules/services_discovery/shout.c */
49 #define SHOUTCAST_BASE_URL "http/shout-winamp://www.shoutcast.com/sbin/newxml.phtml"
50 #define SHOUTCAST_TUNEIN_BASE_URL "http://www.shoutcast.com"
52 /*****************************************************************************
54 *****************************************************************************/
55 static int Demux( demux_t *p_demux);
56 static int Control( demux_t *p_demux, int i_query, va_list args );
58 static void ShoutcastAdd( playlist_t *p_playlist, playlist_item_t* p_genre,
59 playlist_item_t *p_bitrate, playlist_item_t *p_item,
60 char *psz_genre, char *psz_bitrate );
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;
100 /*****************************************************************************
101 * Deactivate: frees unused data
102 *****************************************************************************/
103 void E_(Close_Shoutcast)( vlc_object_t *p_this )
105 demux_t *p_demux = (demux_t *)p_this;
106 demux_sys_t *p_sys = p_demux->p_sys;
108 if( p_sys->p_playlist )
109 vlc_object_release( p_sys->p_playlist );
110 if( p_sys->p_xml_reader )
111 xml_ReaderDelete( p_sys->p_xml, p_sys->p_xml_reader );
113 xml_Delete( p_sys->p_xml );
117 static int Demux( demux_t *p_demux )
119 demux_sys_t *p_sys = p_demux->p_sys;
120 playlist_t *p_playlist;
125 xml_reader_t *p_xml_reader;
127 char *psz_eltname = NULL;
130 p_playlist = (playlist_t *) vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
134 msg_Err( p_demux, "can't find playlist" );
137 p_sys->p_playlist = p_playlist;
139 b_play = E_(FindItem)( p_demux, p_playlist, &p_sys->p_current );
141 msg_Warn( p_demux, "item: %s", p_sys->p_current->input.psz_name );
142 playlist_ItemToNode( p_playlist, p_sys->p_current );
143 p_sys->p_current->input.i_type = ITEM_TYPE_PLAYLIST;
145 p_xml = p_sys->p_xml = xml_Create( p_demux );
146 if( !p_xml ) return -1;
148 /* psz_eltname = stream_ReadLine( p_demux->s );
149 if( psz_eltname ) free( psz_eltname );
152 p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
153 if( !p_xml_reader ) return -1;
154 p_sys->p_xml_reader = p_xml_reader;
157 /* check root node */
158 if( xml_ReaderRead( p_xml_reader ) != 1 )
160 msg_Err( p_demux, "invalid file (no root node)" );
164 if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
165 ( psz_eltname = xml_ReaderName( p_xml_reader ) ) == NULL ||
166 ( strcmp( psz_eltname, "genrelist" )
167 && strcmp( psz_eltname, "stationlist" ) ) )
169 msg_Err( p_demux, "invalid root node %i, %s",
170 xml_ReaderNodeType( p_xml_reader ), psz_eltname );
171 if( psz_eltname ) free( psz_eltname );
175 if( !strcmp( psz_eltname, "genrelist" ) )
177 /* we're reading a genre list */
178 if( DemuxGenre( p_demux ) ) return -1;
182 /* we're reading a station list */
183 if( DemuxStation( p_demux ) ) return -1;
187 /* Go back and play the playlist */
188 if( b_play && p_playlist->status.p_item &&
189 p_playlist->status.p_item->i_children > 0 )
191 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
192 p_playlist->status.i_view,
193 p_playlist->status.p_item,
194 p_playlist->status.p_item->pp_children[0] );
197 vlc_object_release( p_playlist );
198 p_sys->p_playlist = NULL;
202 #define GET_VALUE( a ) \
203 if( !strcmp( psz_attrname, #a ) ) \
205 psz_ ## a = strdup( psz_attrvalue ); \
208 * <genre name="the name"></genre>
212 static int DemuxGenre( demux_t *p_demux )
214 demux_sys_t *p_sys = p_demux->p_sys;
215 char *psz_name = NULL; /* genre name */
216 char *psz_eltname = NULL; /* tag name */
218 while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
223 i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
231 case XML_READER_STARTELEM:
232 // Read the element name
233 if( psz_eltname ) free( psz_eltname );
234 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
235 if( !psz_eltname ) return -1;
238 if( !strcmp( psz_eltname, "genre" ) )
240 // Read the attributes
241 while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
243 char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
244 char *psz_attrvalue =
245 xml_ReaderValue( p_sys->p_xml_reader );
246 if( !psz_attrname || !psz_attrvalue ) return -1;
252 "unexpected attribure %s in element %s",
256 free( psz_attrname );
257 free( psz_attrvalue );
262 case XML_READER_TEXT:
266 case XML_READER_ENDELEM:
267 // Read the element name
269 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
270 if( !psz_eltname ) return -1;
271 if( !strcmp( psz_eltname, "genre" ) )
273 playlist_item_t *p_item;
274 char *psz_mrl = malloc( strlen( SHOUTCAST_BASE_URL )
275 + strlen( "?genre=" ) + strlen( psz_name ) + 1 );
276 sprintf( psz_mrl, SHOUTCAST_BASE_URL "?genre=%s",
278 p_item = playlist_ItemNew( p_sys->p_playlist, psz_mrl,
281 playlist_NodeAddItem( p_sys->p_playlist, p_item,
282 p_sys->p_current->pp_parents[0]->i_view,
283 p_sys->p_current, PLAYLIST_APPEND,
286 /* We need to declare the parents of the node as the
287 * * same of the parent's ones */
288 playlist_CopyParents( p_sys->p_current, p_item );
290 vlc_input_item_CopyOptions( &p_sys->p_current->input,
293 #define FREE(a) if( a ) free( a ); a = NULL;
298 psz_eltname = strdup("");
307 * <tunein base="/sbin/tunein-station.pls"></tunein>
308 * <station name="the name"
312 * genre="A big genre string"
313 * ct="current track name/author/..."
314 * lc="listener count"></station>
317 static int DemuxStation( demux_t *p_demux )
319 demux_sys_t *p_sys = p_demux->p_sys;
321 char *psz_base = NULL; /* */
323 char *psz_name = NULL; /* genre name */
324 char *psz_mt = NULL; /* mime type */
325 char *psz_id = NULL; /* id */
326 char *psz_br = NULL; /* bit rate */
327 char *psz_genre = NULL; /* genre */
328 char *psz_ct = NULL; /* current track */
329 char *psz_lc = NULL; /* listener count */
331 char *psz_eltname = NULL; /* tag name */
333 while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
338 i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
346 case XML_READER_STARTELEM:
347 // Read the element name
348 if( psz_eltname ) free( psz_eltname );
349 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
350 if( !psz_eltname ) return -1;
352 // Read the attributes
353 if( !strcmp( psz_eltname, "tunein" ) )
355 while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
357 char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
358 char *psz_attrvalue =
359 xml_ReaderValue( p_sys->p_xml_reader );
360 if( !psz_attrname || !psz_attrvalue ) return -1;
366 "unexpected attribure %s in element %s",
370 free( psz_attrname );
371 free( psz_attrvalue );
374 else if( !strcmp( psz_eltname, "station" ) )
376 while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
378 char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
379 char *psz_attrvalue =
380 xml_ReaderValue( p_sys->p_xml_reader );
381 if( !psz_attrname || !psz_attrvalue ) return -1;
387 else GET_VALUE( genre )
393 "unexpected attribure %s in element %s",
397 free( psz_attrname );
398 free( psz_attrvalue );
403 case XML_READER_TEXT:
407 case XML_READER_ENDELEM:
408 // 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" ) && psz_base )
414 playlist_item_t *p_item;
415 char *psz_mrl = malloc( strlen( SHOUTCAST_TUNEIN_BASE_URL )
416 + strlen( psz_base ) + strlen( "?id=" )
417 + strlen( psz_id ) + 1 );
418 sprintf( psz_mrl, SHOUTCAST_TUNEIN_BASE_URL "%s?id=%s",
420 p_item = playlist_ItemNew( p_sys->p_playlist, psz_mrl,
426 vlc_input_item_AddInfo( &p_item->input,
434 vlc_input_item_AddInfo( &p_item->input,
442 vlc_input_item_AddInfo( &p_item->input,
443 _(VLC_META_INFO_CAT),
450 vlc_input_item_AddInfo( &p_item->input,
451 _(VLC_META_INFO_CAT),
452 _(VLC_META_NOW_PLAYING),
458 vlc_input_item_AddInfo( &p_item->input,
465 playlist_NodeAddItem( p_sys->p_playlist, p_item,
466 p_sys->p_current->pp_parents[0]->i_view,
467 p_sys->p_current, PLAYLIST_APPEND,
470 /* We need to declare the parents of the node as the
471 * * same of the parent's ones */
472 playlist_CopyParents( p_sys->p_current, p_item );
474 vlc_input_item_CopyOptions( &p_sys->p_current->input,
477 #define FREE(a) if( a ) free( a ); a = NULL;
488 psz_eltname = strdup("");
496 static int Control( demux_t *p_demux, int i_query, va_list args )