]> git.sesse.net Git - vlc/blob - modules/demux/playlist/b4s.c
* modules/demux/playlist/b4s.c: WinCE compilation fix.
[vlc] / modules / demux / playlist / b4s.c
1 /*****************************************************************************
2  * b4s.c : B4S playlist format import
3  *****************************************************************************
4  * Copyright (C) 2005 VideoLAN
5  * $Id$
6  *
7  * Authors: Sigmund Augdal <sigmunau@idi.ntnu.no>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <ctype.h>                                              /* isspace() */
29
30 #include <vlc/vlc.h>
31 #include <vlc/input.h>
32 #include <vlc/intf.h>
33
34 #include <errno.h>                                                 /* ENOMEM */
35 #include "playlist.h"
36 #include "vlc_xml.h"
37
38 struct demux_sys_t
39 {
40     char *psz_prefix;
41     playlist_t *p_playlist;
42     xml_t *p_xml;
43     xml_reader_t *p_xml_reader;
44     int b_shout;
45 };
46
47 /*****************************************************************************
48  * Local prototypes
49  *****************************************************************************/
50 static int Demux( demux_t *p_demux);
51 static int Control( demux_t *p_demux, int i_query, va_list args );
52 static char *get_next_token(char *cur_string);
53 static int IsWhitespace( char *psz_string );
54 static void ShoutcastAdd( playlist_t *p_playlist, playlist_item_t* p_genre,
55                           playlist_item_t *p_bitrate, playlist_item_t *p_item,
56                           char *psz_genre, char *psz_bitrate );
57
58 /*****************************************************************************
59  * Import_B4S: main import function
60  *****************************************************************************/
61 int E_(Import_B4S)( vlc_object_t *p_this )
62 {
63     demux_t *p_demux = (demux_t *)p_this;
64     demux_sys_t *p_sys;
65
66     char    *psz_ext;
67
68     psz_ext = strrchr ( p_demux->psz_path, '.' );
69
70     if( ( psz_ext && !strcasecmp( psz_ext, ".b4s") ) ||
71         ( p_demux->psz_demux && !strcmp(p_demux->psz_demux, "b4s-open") ) ||
72         ( p_demux->psz_demux && !strcmp(p_demux->psz_demux, "shout-b4s") ) )
73     {
74         ;
75     }
76     else
77     {
78         return VLC_EGENERIC;
79     }
80     msg_Dbg( p_demux, "using b4s playlist import");
81
82     p_demux->pf_control = Control;
83     p_demux->pf_demux = Demux;
84     p_demux->p_sys = p_sys = malloc( sizeof(demux_sys_t) );
85     if( p_sys == NULL )
86     {
87         msg_Err( p_demux, "Out of memory" );
88         return VLC_ENOMEM;
89     }
90     p_sys->b_shout = p_demux->psz_demux &&
91         !strcmp(p_demux->psz_demux, "shout-b4s");
92     p_sys->psz_prefix = E_(FindPrefix)( p_demux );
93     p_sys->p_playlist = NULL;
94     p_sys->p_xml = NULL;
95     p_sys->p_xml_reader = NULL;
96
97     return VLC_SUCCESS;
98 }
99
100 /*****************************************************************************
101  * Deactivate: frees unused data
102  *****************************************************************************/
103 void E_(Close_B4S)( vlc_object_t *p_this )
104 {
105     demux_t *p_demux = (demux_t *)p_this;
106     demux_sys_t *p_sys = p_demux->p_sys;
107
108     if( p_sys->psz_prefix ) free( p_sys->psz_prefix );
109     if( p_sys->p_playlist ) vlc_object_release( p_sys->p_playlist );
110     if( p_sys->p_xml_reader ) xml_ReaderDelete( p_sys->p_xml, p_sys->p_xml_reader );
111     if( p_sys->p_xml ) xml_Delete( p_sys->p_xml );
112     free( p_sys );
113 }
114
115 static int Demux( demux_t *p_demux )
116 {
117     demux_sys_t *p_sys = p_demux->p_sys;
118     playlist_t *p_playlist;
119     playlist_item_t *p_item, *p_current;
120     playlist_item_t *p_bitrate = NULL, *p_genre = NULL;
121
122     vlc_bool_t b_play;
123     int i_ret;
124
125     xml_t *p_xml;
126     xml_reader_t *p_xml_reader;
127     char *psz_elname = NULL;
128     int i_type, b_shoutcast;
129     char *psz_mrl = NULL, *psz_name = NULL, *psz_genre = NULL;
130     char *psz_now = NULL, *psz_listeners = NULL, *psz_bitrate = NULL;
131
132
133     b_shoutcast = p_sys->b_shout;
134
135     p_playlist = (playlist_t *) vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
136                                                  FIND_PARENT );
137     if( !p_playlist )
138     {
139         msg_Err( p_demux, "can't find playlist" );
140         return -1;
141     }
142     p_sys->p_playlist = p_playlist;
143
144     b_play = E_(FindItem)( p_demux, p_playlist, &p_current );
145
146     playlist_ItemToNode( p_playlist, p_current );
147     p_current->input.i_type = ITEM_TYPE_PLAYLIST;
148     if( b_shoutcast )
149     {
150         p_genre = playlist_NodeCreate( p_playlist, p_current->pp_parents[0]->i_view, "Genre", p_current );
151         playlist_CopyParents( p_current, p_genre );
152
153         p_bitrate = playlist_NodeCreate( p_playlist, p_current->pp_parents[0]->i_view, "Bitrate", p_current );
154         playlist_CopyParents( p_current, p_bitrate );
155     }
156     
157     p_xml = p_sys->p_xml = xml_Create( p_demux );
158     if( !p_xml ) return -1;
159
160     psz_elname = stream_ReadLine( p_demux->s );
161     if( psz_elname ) free( psz_elname );
162     psz_elname = 0;
163
164     p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
165     if( !p_xml_reader ) return -1;
166     p_sys->p_xml_reader = p_xml_reader;
167
168     /* xml */
169     /* check root node */
170     if( xml_ReaderRead( p_xml_reader ) != 1 )
171     {
172         msg_Err( p_demux, "invalid file (no root node)" );
173         return -1;
174     }
175
176     if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
177         ( psz_elname = xml_ReaderName( p_xml_reader ) ) == NULL ||
178         strcmp( psz_elname, "WinampXML" ) )
179     {
180         msg_Err( p_demux, "invalid root node %i, %s",
181                  xml_ReaderNodeType( p_xml_reader ), psz_elname );
182         if( psz_elname ) free( psz_elname );
183         return -1;
184     }
185     free( psz_elname );
186
187     /* root node should not have any attributes, and should only
188      * contain the "playlist node */
189
190     /* Skip until 1st child node */
191     while( (i_ret = xml_ReaderRead( p_xml_reader )) == 1 &&
192            xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM );
193     if( i_ret != 1 )
194     {
195         msg_Err( p_demux, "invalid file (no child node)" );
196         return -1;
197     }
198
199     if( ( psz_elname = xml_ReaderName( p_xml_reader ) ) == NULL ||
200         strcmp( psz_elname, "playlist" ) )
201     {
202         msg_Err( p_demux, "invalid child node %s", psz_elname );
203         if( psz_elname ) free( psz_elname );
204         return -1;
205     }
206     free( psz_elname ); psz_elname = 0;
207
208     // Read the attributes
209     while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
210     {
211         char *psz_name = xml_ReaderName( p_xml_reader );
212         char *psz_value = xml_ReaderValue( p_xml_reader );
213         if( !psz_name || !psz_value ) return -1;
214         if( !strcmp( psz_name, "num_entries" ) )
215         {
216             msg_Dbg( p_demux, "playlist has %d entries", atoi(psz_value) );
217         }
218         else if( !strcmp( psz_name, "label" ) )
219         {
220             playlist_ItemSetName( p_current, psz_value );
221         }
222         else
223         {
224             msg_Warn( p_demux, "stray attribute %s with value %s in element"
225                       " 'playlist'", psz_name, psz_value );
226         }
227         free( psz_name );
228         free( psz_value );
229     }
230
231     while( (i_ret = xml_ReaderRead( p_xml_reader )) == 1 )
232     {
233         // Get the node type
234         i_type = xml_ReaderNodeType( p_xml_reader );
235         switch( i_type )
236         {
237             // Error
238             case -1:
239                 return -1;
240                 break;
241
242             case XML_READER_STARTELEM:
243             {
244                 // Read the element name
245                 if( psz_elname ) free( psz_elname );
246                 psz_elname = xml_ReaderName( p_xml_reader );
247                 if( !psz_elname ) return -1;
248                 
249
250                 // Read the attributes
251                 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
252                 {
253                     char *psz_name = xml_ReaderName( p_xml_reader );
254                     char *psz_value = xml_ReaderValue( p_xml_reader );
255                     if( !psz_name || !psz_value ) return -1;
256                     if( !strcmp( psz_elname, "entry" ) &&
257                         !strcmp( psz_name, "Playstring" ) )
258                     {
259                         psz_mrl = strdup( psz_value );
260                     }
261                     else
262                     {
263                         msg_Warn( p_demux, "unexpected attribure %s in element %s",
264                                   psz_name, psz_elname );
265                     }
266                     free( psz_name );
267                     free( psz_value );
268                 }
269                 break;
270             }
271             case XML_READER_TEXT:
272             {
273                 char *psz_text = xml_ReaderValue( p_xml_reader );
274                 if( IsWhitespace( psz_text ) )
275                 {
276                     free( psz_text );
277                     break;
278                 }
279                 if( !strcmp( psz_elname, "Name" ) )
280                 {
281                     psz_name = strdup( psz_text );
282                 }
283                 else if( !strcmp( psz_elname, "Genre" ) )
284                 {
285                     psz_genre = strdup( psz_text );
286                 }
287                 else if( !strcmp( psz_elname, "Nowplaying" ) )
288                 {
289                     psz_now = strdup( psz_text );
290                 }
291                 else if( !strcmp( psz_elname, "Listeners" ) )
292                 {
293                     psz_listeners = strdup( psz_text );
294                 }
295                 else if( !strcmp( psz_elname, "Bitrate" ) )
296                 {
297                     psz_bitrate = strdup( psz_text );
298                 }
299                 else if( !strcmp( psz_elname, "" ) )
300                 {
301                     ;
302                 }
303                 else
304                 {
305                     msg_Warn( p_demux, "unexpected text in element '%s'",
306                               psz_elname );
307                 }
308                 free( psz_text );
309                 break;
310             }
311             // End element
312             case XML_READER_ENDELEM:
313             {
314                 // Read the element name
315                 free( psz_elname );
316                 psz_elname = xml_ReaderName( p_xml_reader );
317                 if( !psz_elname ) return -1;
318                 if( !strcmp( psz_elname, "entry" ) )
319                 {
320                     p_item = playlist_ItemNew( p_playlist, psz_mrl, psz_name );
321                     if( psz_now )
322                     {
323                         vlc_input_item_AddInfo( &(p_item->input),
324                                                 _("Meta-information"),
325                                                 _( VLC_META_NOW_PLAYING ),
326                                                 "%s",
327                                                 psz_now );
328                     }
329                     if( psz_genre )
330                     {
331                         vlc_input_item_AddInfo( &p_item->input,
332                                                 _("Meta-information"),
333                                                 _( VLC_META_GENRE ),
334                                                 "%s",
335                                                 psz_genre );
336                     }
337                     if( psz_listeners )
338                     {
339                         vlc_input_item_AddInfo( &p_item->input,
340                                                 _("Meta-information"),
341                                                 _( "Listeners" ),
342                                                 "%s",
343                                                 psz_listeners );
344                     }
345                     if( psz_bitrate )
346                     {
347                         vlc_input_item_AddInfo( &p_item->input,
348                                                 _("Meta-information"),
349                                                 _( "Bitrate" ),
350                                                 "%s",
351                                                 psz_bitrate );
352                     }
353                     playlist_NodeAddItem( p_playlist, p_item,
354                                           p_current->pp_parents[0]->i_view,
355                                           p_current, PLAYLIST_APPEND,
356                                           PLAYLIST_END );
357
358                     /* We need to declare the parents of the node as the
359                      *                  * same of the parent's ones */
360                     playlist_CopyParents( p_current, p_item );
361                     
362                     vlc_input_item_CopyOptions( &p_current->input,
363                                                 &p_item->input );
364                     if( b_shoutcast )
365                                         {
366                                                 char *psz_genreToken;
367                                                 char *psz_otherToken;
368
369
370                                                 psz_genreToken = psz_genre;
371
372                                                 while ( psz_genreToken && ( psz_otherToken = get_next_token(psz_genreToken )))
373                                                 {
374                                                         ShoutcastAdd( p_playlist, p_genre, p_bitrate, p_item,
375                                       psz_genreToken, psz_bitrate );
376
377                                                         psz_genreToken = psz_otherToken;
378                                                 }
379                                         }
380
381 #define FREE(a) if( a ) free( a ); a = NULL;
382                     FREE( psz_name );
383                     FREE( psz_mrl );
384                     FREE( psz_genre );
385                     FREE( psz_bitrate );
386                     FREE( psz_listeners );
387                     FREE( psz_now );
388 #undef FREE
389                 }
390                 free( psz_elname );
391                 psz_elname = strdup("");
392
393                 break;
394             }
395         }
396     }
397
398     if( i_ret != 0 )
399     {
400         msg_Warn( p_demux, "error while parsing data" );
401     }
402     if( b_shoutcast )
403     {
404         vlc_mutex_lock( &p_playlist->object_lock );
405         playlist_NodeSort( p_playlist, p_bitrate, SORT_TITLE_NUMERIC, ORDER_NORMAL );
406         vlc_mutex_unlock( &p_playlist->object_lock );
407     }
408
409     /* Go back and play the playlist */
410     if( b_play && p_playlist->status.p_item &&
411         p_playlist->status.p_item->i_children > 0 )
412     {
413         playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
414                           p_playlist->status.i_view,
415                           p_playlist->status.p_item,
416                           p_playlist->status.p_item->pp_children[0] );
417     }
418     
419     vlc_object_release( p_playlist );
420     p_sys->p_playlist = NULL;
421     return VLC_SUCCESS;
422 }
423
424 static int Control( demux_t *p_demux, int i_query, va_list args )
425 {
426     return VLC_EGENERIC;
427 }
428
429 static char *get_next_token(char *cur_string) {
430         while (*cur_string && !isspace(*cur_string)) cur_string++;
431         if (!*cur_string) return NULL;
432         *cur_string++ = '\0';
433         while (*cur_string && isspace(*cur_string)) cur_string++;
434         return cur_string;
435 }
436
437 static int IsWhitespace( char *psz_string )
438 {
439     while( *psz_string )
440     {
441         if( *psz_string != ' ' && *psz_string != '\t' && *psz_string != '\r' &&
442             *psz_string != '\n' )
443         {
444             return VLC_FALSE;
445         }
446         psz_string++;
447     }
448     return VLC_TRUE;
449 }
450
451 static void ShoutcastAdd( playlist_t *p_playlist, playlist_item_t* p_genre,
452                           playlist_item_t *p_bitrate, playlist_item_t *p_item,
453                           char *psz_genre, char *psz_bitrate )
454 {
455     playlist_item_t *p_parent;
456     if( psz_bitrate )
457     {
458         playlist_item_t *p_copy = playlist_ItemCopy(p_playlist,p_item);
459         p_parent = playlist_ChildSearchName( p_bitrate, psz_bitrate );
460         if( !p_parent )
461         {
462             p_parent = playlist_NodeCreate( p_playlist, p_genre->pp_parents[0]->i_view, psz_bitrate,
463                                             p_bitrate );
464             playlist_CopyParents( p_bitrate, p_parent );
465         }
466         playlist_NodeAddItem( p_playlist, p_copy, p_parent->pp_parents[0]->i_view, p_parent, PLAYLIST_APPEND, PLAYLIST_END  );
467         playlist_CopyParents( p_parent, p_copy );
468
469     }
470
471     if( psz_genre )
472     {
473         playlist_item_t *p_copy = playlist_ItemCopy(p_playlist,p_item);
474         p_parent = playlist_ChildSearchName( p_genre, psz_genre );
475         if( !p_parent )
476         {
477             p_parent = playlist_NodeCreate( p_playlist, p_genre->pp_parents[0]->i_view, psz_genre,
478                                             p_genre );
479             playlist_CopyParents( p_genre, p_parent );
480         }
481         playlist_NodeAddItem( p_playlist, p_copy, p_parent->pp_parents[0]->i_view, p_parent, PLAYLIST_APPEND, PLAYLIST_END );
482         playlist_CopyParents( p_parent, p_copy );
483     }
484 }