1 /*****************************************************************************
2 * b4s.c : B4S playlist format import
3 *****************************************************************************
4 * Copyright (C) 2005 VideoLAN
7 * Authors: Sigmund Augdal <sigmunau@idi.ntnu.no>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
30 #include <vlc/input.h>
33 #include <errno.h> /* ENOMEM */
40 playlist_t *p_playlist;
42 xml_reader_t *p_xml_reader;
46 /*****************************************************************************
48 *****************************************************************************/
49 static int Demux( demux_t *p_demux);
50 static int Control( demux_t *p_demux, int i_query, va_list args );
51 static char *get_next_token(char *cur_string);
52 static int IsWhitespace( char *psz_string );
53 static void ShoutcastAdd( playlist_t *p_playlist, playlist_item_t* p_genre,
54 playlist_item_t *p_bitrate, playlist_item_t *p_item,
55 char *psz_genre, char *psz_bitrate );
57 /*****************************************************************************
58 * Import_B4S: main import function
59 *****************************************************************************/
60 int E_(Import_B4S)( vlc_object_t *p_this )
62 demux_t *p_demux = (demux_t *)p_this;
67 psz_ext = strrchr ( p_demux->psz_path, '.' );
69 if( ( psz_ext && !strcasecmp( psz_ext, ".b4s") ) ||
70 ( p_demux->psz_demux && !strcmp(p_demux->psz_demux, "b4s-open") ) ||
71 ( p_demux->psz_demux && !strcmp(p_demux->psz_demux, "shout-b4s") ) )
79 msg_Dbg( p_demux, "using b4s playlist import");
81 p_demux->pf_control = Control;
82 p_demux->pf_demux = Demux;
83 p_demux->p_sys = p_sys = malloc( sizeof(demux_sys_t) );
86 msg_Err( p_demux, "Out of memory" );
89 p_sys->b_shout = p_demux->psz_demux &&
90 !strcmp(p_demux->psz_demux, "shout-b4s");
91 p_sys->psz_prefix = E_(FindPrefix)( p_demux );
92 p_sys->p_playlist = NULL;
94 p_sys->p_xml_reader = NULL;
99 /*****************************************************************************
100 * Deactivate: frees unused data
101 *****************************************************************************/
102 void E_(Close_B4S)( vlc_object_t *p_this )
104 demux_t *p_demux = (demux_t *)p_this;
105 demux_sys_t *p_sys = p_demux->p_sys;
107 if( p_sys->psz_prefix ) free( p_sys->psz_prefix );
108 if( p_sys->p_playlist ) vlc_object_release( p_sys->p_playlist );
109 if( p_sys->p_xml_reader ) xml_ReaderDelete( p_sys->p_xml, p_sys->p_xml_reader );
110 if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
114 static int Demux( demux_t *p_demux )
116 demux_sys_t *p_sys = p_demux->p_sys;
117 playlist_t *p_playlist;
118 playlist_item_t *p_item, *p_current;
119 playlist_item_t *p_bitrate = NULL, *p_genre = NULL;
125 xml_reader_t *p_xml_reader;
126 char *psz_elname = NULL;
127 int i_type, b_shoutcast;
128 char *psz_mrl = NULL, *psz_name = NULL, *psz_genre = NULL;
129 char *psz_now = NULL, *psz_listeners = NULL, *psz_bitrate = NULL;
132 b_shoutcast = p_sys->b_shout;
134 p_playlist = (playlist_t *) vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
138 msg_Err( p_demux, "can't find playlist" );
141 p_sys->p_playlist = p_playlist;
143 b_play = E_(FindItem)( p_demux, p_playlist, &p_current );
145 playlist_ItemToNode( p_playlist, p_current );
146 p_current->input.i_type = ITEM_TYPE_PLAYLIST;
149 p_genre = playlist_NodeCreate( p_playlist, p_current->pp_parents[0]->i_view, "Genre", p_current );
150 playlist_CopyParents( p_current, p_genre );
152 p_bitrate = playlist_NodeCreate( p_playlist, p_current->pp_parents[0]->i_view, "Bitrate", p_current );
153 playlist_CopyParents( p_current, p_bitrate );
156 p_xml = p_sys->p_xml = xml_Create( p_demux );
157 if( !p_xml ) return -1;
159 psz_elname = stream_ReadLine( p_demux->s );
160 if( psz_elname ) free( psz_elname );
163 p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
164 if( !p_xml_reader ) return -1;
165 p_sys->p_xml_reader = p_xml_reader;
168 /* check root node */
169 if( xml_ReaderRead( p_xml_reader ) != 1 )
171 msg_Err( p_demux, "invalid file (no root node)" );
175 if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
176 ( psz_elname = xml_ReaderName( p_xml_reader ) ) == NULL ||
177 strcmp( psz_elname, "WinampXML" ) )
179 msg_Err( p_demux, "invalid root node %i, %s",
180 xml_ReaderNodeType( p_xml_reader ), psz_elname );
181 if( psz_elname ) free( psz_elname );
186 /* root node should not have any attributes, and should only
187 * contain the "playlist node */
189 /* Skip until 1st child node */
190 while( (i_ret = xml_ReaderRead( p_xml_reader )) == 1 &&
191 xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM );
194 msg_Err( p_demux, "invalid file (no child node)" );
198 if( ( psz_elname = xml_ReaderName( p_xml_reader ) ) == NULL ||
199 strcmp( psz_elname, "playlist" ) )
201 msg_Err( p_demux, "invalid child node %s", psz_elname );
202 if( psz_elname ) free( psz_elname );
205 free( psz_elname ); psz_elname = 0;
207 // Read the attributes
208 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
210 char *psz_name = xml_ReaderName( p_xml_reader );
211 char *psz_value = xml_ReaderValue( p_xml_reader );
212 if( !psz_name || !psz_value ) return -1;
213 if( !strcmp( psz_name, "num_entries" ) )
215 msg_Dbg( p_demux, "playlist has %d entries", atoi(psz_value) );
217 else if( !strcmp( psz_name, "label" ) )
219 playlist_ItemSetName( p_current, psz_value );
223 msg_Warn( p_demux, "stray attribute %s with value %s in element"
224 " 'playlist'", psz_name, psz_value );
230 while( (i_ret = xml_ReaderRead( p_xml_reader )) == 1 )
233 i_type = xml_ReaderNodeType( p_xml_reader );
241 case XML_READER_STARTELEM:
243 // Read the element name
244 if( psz_elname ) free( psz_elname );
245 psz_elname = xml_ReaderName( p_xml_reader );
246 if( !psz_elname ) return -1;
249 // Read the attributes
250 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
252 char *psz_name = xml_ReaderName( p_xml_reader );
253 char *psz_value = xml_ReaderValue( p_xml_reader );
254 if( !psz_name || !psz_value ) return -1;
255 if( !strcmp( psz_elname, "entry" ) &&
256 !strcmp( psz_name, "Playstring" ) )
258 psz_mrl = strdup( psz_value );
262 msg_Warn( p_demux, "unexpected attribure %s in element %s",
263 psz_name, psz_elname );
270 case XML_READER_TEXT:
272 char *psz_text = xml_ReaderValue( p_xml_reader );
273 if( IsWhitespace( psz_text ) )
278 if( !strcmp( psz_elname, "Name" ) )
280 psz_name = strdup( psz_text );
282 else if( !strcmp( psz_elname, "Genre" ) )
284 psz_genre = strdup( psz_text );
286 else if( !strcmp( psz_elname, "Nowplaying" ) )
288 psz_now = strdup( psz_text );
290 else if( !strcmp( psz_elname, "Listeners" ) )
292 psz_listeners = strdup( psz_text );
294 else if( !strcmp( psz_elname, "Bitrate" ) )
296 psz_bitrate = strdup( psz_text );
298 else if( !strcmp( psz_elname, "" ) )
304 msg_Warn( p_demux, "unexpected text in element '%s'",
311 case XML_READER_ENDELEM:
313 // Read the element name
315 psz_elname = xml_ReaderName( p_xml_reader );
316 if( !psz_elname ) return -1;
317 if( !strcmp( psz_elname, "entry" ) )
319 p_item = playlist_ItemNew( p_playlist, psz_mrl, psz_name );
322 vlc_input_item_AddInfo( &(p_item->input),
323 _("Meta-information"),
324 _( VLC_META_NOW_PLAYING ),
330 vlc_input_item_AddInfo( &p_item->input,
331 _("Meta-information"),
338 vlc_input_item_AddInfo( &p_item->input,
339 _("Meta-information"),
346 vlc_input_item_AddInfo( &p_item->input,
347 _("Meta-information"),
352 playlist_NodeAddItem( p_playlist, p_item,
353 p_current->pp_parents[0]->i_view,
354 p_current, PLAYLIST_APPEND,
357 /* We need to declare the parents of the node as the
358 * * same of the parent's ones */
359 playlist_CopyParents( p_current, p_item );
361 vlc_input_item_CopyOptions( &p_current->input,
365 char *psz_genreToken;
366 char *psz_otherToken;
369 psz_genreToken = psz_genre;
371 while ( psz_genreToken && ( psz_otherToken = get_next_token(psz_genreToken )))
373 ShoutcastAdd( p_playlist, p_genre, p_bitrate, p_item,
374 psz_genreToken, psz_bitrate );
376 psz_genreToken = psz_otherToken;
380 #define FREE(a) if( a ) free( a ); a = NULL;
385 FREE( psz_listeners );
390 psz_elname = strdup("");
399 msg_Warn( p_demux, "error while parsing data" );
403 vlc_mutex_lock( &p_playlist->object_lock );
404 playlist_NodeSort( p_playlist, p_bitrate, SORT_TITLE_NUMERIC, ORDER_NORMAL );
405 vlc_mutex_unlock( &p_playlist->object_lock );
408 /* Go back and play the playlist */
409 if( b_play && p_playlist->status.p_item &&
410 p_playlist->status.p_item->i_children > 0 )
412 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
413 p_playlist->status.i_view,
414 p_playlist->status.p_item,
415 p_playlist->status.p_item->pp_children[0] );
418 vlc_object_release( p_playlist );
419 p_sys->p_playlist = NULL;
423 static int Control( demux_t *p_demux, int i_query, va_list args )
428 static char *get_next_token(char *cur_string) {
429 while (*cur_string && !isspace(*cur_string)) cur_string++;
430 if (!*cur_string) return NULL;
431 *cur_string++ = '\0';
432 while (*cur_string && isspace(*cur_string)) cur_string++;
436 static int IsWhitespace( char *psz_string )
440 if( *psz_string != ' ' && *psz_string != '\t' && *psz_string != '\r' &&
441 *psz_string != '\n' )
450 static void ShoutcastAdd( playlist_t *p_playlist, playlist_item_t* p_genre,
451 playlist_item_t *p_bitrate, playlist_item_t *p_item,
452 char *psz_genre, char *psz_bitrate )
454 playlist_item_t *p_parent;
457 playlist_item_t *p_copy = playlist_ItemCopy(p_playlist,p_item);
458 p_parent = playlist_ChildSearchName( p_bitrate, psz_bitrate );
461 p_parent = playlist_NodeCreate( p_playlist, p_genre->pp_parents[0]->i_view, psz_bitrate,
463 playlist_CopyParents( p_bitrate, p_parent );
465 playlist_NodeAddItem( p_playlist, p_copy, p_parent->pp_parents[0]->i_view, p_parent, PLAYLIST_APPEND, PLAYLIST_END );
466 playlist_CopyParents( p_parent, p_copy );
472 playlist_item_t *p_copy = playlist_ItemCopy(p_playlist,p_item);
473 p_parent = playlist_ChildSearchName( p_genre, psz_genre );
476 p_parent = playlist_NodeCreate( p_playlist, p_genre->pp_parents[0]->i_view, psz_genre,
478 playlist_CopyParents( p_genre, p_parent );
480 playlist_NodeAddItem( p_playlist, p_copy, p_parent->pp_parents[0]->i_view, p_parent, PLAYLIST_APPEND, PLAYLIST_END );
481 playlist_CopyParents( p_parent, p_copy );