]> git.sesse.net Git - vlc/blob - modules/demux/playlist/shoutcast.c
696b4a8f51735637f287cfa526d650211d7a732a
[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
44     xml_t *p_xml;
45     xml_reader_t *p_xml_reader;
46 };
47
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"
51
52 /*****************************************************************************
53  * Local prototypes
54  *****************************************************************************/
55 static int Demux( demux_t *p_demux);
56 static int Control( demux_t *p_demux, int i_query, va_list args );
57 #if 0
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 );
61 #endif
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     return VLC_SUCCESS;
98 }
99
100 /*****************************************************************************
101  * Deactivate: frees unused data
102  *****************************************************************************/
103 void E_(Close_Shoutcast)( 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->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 );
112     if( p_sys->p_xml )
113         xml_Delete( p_sys->p_xml );
114     free( p_sys );
115 }
116
117 static int Demux( demux_t *p_demux )
118 {
119     demux_sys_t *p_sys = p_demux->p_sys;
120     playlist_t *p_playlist;
121
122     vlc_bool_t b_play;
123
124     xml_t *p_xml;
125     xml_reader_t *p_xml_reader;
126
127     char *psz_eltname = NULL;
128
129
130     p_playlist = (playlist_t *) vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
131                                                  FIND_ANYWHERE );
132     if( !p_playlist )
133     {
134         msg_Err( p_demux, "can't find playlist" );
135         return -1;
136     }
137     p_sys->p_playlist = p_playlist;
138
139     b_play = E_(FindItem)( p_demux, p_playlist, &p_sys->p_current );
140
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;
144
145     p_xml = p_sys->p_xml = xml_Create( p_demux );
146     if( !p_xml ) return -1;
147
148 /*    psz_eltname = stream_ReadLine( p_demux->s );
149     if( psz_eltname ) free( psz_eltname );
150     psz_eltname = 0;*/
151
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;
155
156     /* xml */
157     /* check root node */
158     if( xml_ReaderRead( p_xml_reader ) != 1 )
159     {
160         msg_Err( p_demux, "invalid file (no root node)" );
161         return -1;
162     }
163
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" ) ) )
168     {
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 );
172         return -1;
173     }
174
175     if( !strcmp( psz_eltname, "genrelist" ) )
176     {
177         /* we're reading a genre list */
178         if( DemuxGenre( p_demux ) ) return -1;
179     }
180     else
181     {
182         /* we're reading a station list */
183         if( DemuxStation( p_demux ) ) return -1;
184     }
185     free( psz_eltname );
186
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 )
190     {
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] );
195     }
196
197     vlc_object_release( p_playlist );
198     p_sys->p_playlist = NULL;
199     return VLC_SUCCESS;
200 }
201
202 #define GET_VALUE( a ) \
203                         if( !strcmp( psz_attrname, #a ) ) \
204                         { \
205                             psz_ ## a = strdup( psz_attrvalue ); \
206                         }
207 /* <genrelist>
208  *   <genre name="the name"></genre>
209  *   ...
210  * </genrelist>
211  **/
212 static int DemuxGenre( demux_t *p_demux )
213 {
214     demux_sys_t *p_sys = p_demux->p_sys;
215     char *psz_name = NULL; /* genre name */
216     char *psz_eltname = NULL; /* tag name */
217
218     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
219     {
220         int i_type;
221
222         // Get the node type
223         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
224         switch( i_type )
225         {
226             // Error
227             case -1:
228                 return -1;
229                 break;
230
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;
236
237
238                 if( !strcmp( psz_eltname, "genre" ) )
239                 {
240                     // Read the attributes
241                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
242                     {
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;
247
248                         GET_VALUE( name )
249                         else
250                         {
251                             msg_Warn( p_demux,
252                                       "unexpected attribure %s in element %s",
253                                       psz_attrname,
254                                       psz_eltname );
255                         }
256                         free( psz_attrname );
257                         free( psz_attrvalue );
258                     }
259                 }
260                 break;
261
262             case XML_READER_TEXT:
263                 break;
264
265             // End element
266             case XML_READER_ENDELEM:
267                 // Read the element name
268                 free( psz_eltname );
269                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
270                 if( !psz_eltname ) return -1;
271                 if( !strcmp( psz_eltname, "genre" ) )
272                 {
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",
277                              psz_name );
278                     p_item = playlist_ItemNew( p_sys->p_playlist, psz_mrl,
279                                                psz_name );
280                     free( 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,
284                                           PLAYLIST_END );
285
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 );
289
290                     vlc_input_item_CopyOptions( &p_sys->p_current->input,
291                                                 &p_item->input );
292
293 #define FREE(a) if( a ) free( a ); a = NULL;
294                     FREE( psz_name );
295 #undef FREE
296                 }
297                 free( psz_eltname );
298                 psz_eltname = strdup("");
299
300                 break;
301         }
302     }
303     return 0;
304 }
305
306 /* <stationlist>
307  *   <tunein base="/sbin/tunein-station.pls"></tunein>
308  *   <station name="the name"
309  *            mt="mime type"
310  *            id="the id"
311  *            br="bit rate"
312  *            genre="A big genre string"
313  *            ct="current track name/author/..."
314  *            lc="listener count"></station>
315  * </stationlist>
316  **/
317 static int DemuxStation( demux_t *p_demux )
318 {
319     demux_sys_t *p_sys = p_demux->p_sys;
320
321     char *psz_base = NULL; /* */
322
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 */
330
331     char *psz_eltname = NULL; /* tag name */
332
333     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
334     {
335         int i_type;
336
337         // Get the node type
338         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
339         switch( i_type )
340         {
341             // Error
342             case -1:
343                 return -1;
344                 break;
345
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;
351
352                 // Read the attributes
353                 if( !strcmp( psz_eltname, "tunein" ) )
354                 {
355                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
356                     {
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;
361
362                         GET_VALUE( base )
363                         else
364                         {
365                             msg_Warn( p_demux,
366                                       "unexpected attribure %s in element %s",
367                                       psz_attrname,
368                                       psz_eltname );
369                         }
370                         free( psz_attrname );
371                         free( psz_attrvalue );
372                     }
373                 }
374                 else if( !strcmp( psz_eltname, "station" ) )
375                 {
376                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
377                     {
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;
382
383                         GET_VALUE( name )
384                         else GET_VALUE( mt )
385                         else GET_VALUE( id )
386                         else GET_VALUE( br )
387                         else GET_VALUE( genre )
388                         else GET_VALUE( ct )
389                         else GET_VALUE( lc )
390                         else
391                         {
392                             msg_Warn( p_demux,
393                                       "unexpected attribure %s in element %s",
394                                       psz_attrname,
395                                       psz_eltname );
396                         }
397                         free( psz_attrname );
398                         free( psz_attrvalue );
399                     }
400                 }
401                 break;
402
403             case XML_READER_TEXT:
404                 break;
405
406             // End element
407             case XML_READER_ENDELEM:
408                 // Read the element name
409                 free( psz_eltname );
410                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
411                 if( !psz_eltname ) return -1;
412                 if( !strcmp( psz_eltname, "station" ) && psz_base )
413                 {
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",
419                              psz_base, psz_id );
420                     p_item = playlist_ItemNew( p_sys->p_playlist, psz_mrl,
421                                                psz_name );
422                     free( psz_mrl );
423
424                     if( psz_mt )
425                     {
426                         vlc_input_item_AddInfo( &p_item->input,
427                                                 _( "Shoutcast" ),
428                                                 _( "Mime type" ),
429                                                 "%s",
430                                                 psz_mt );
431                     }
432                     else if( psz_br )
433                     {
434                         vlc_input_item_AddInfo( &p_item->input,
435                                                 _( "Shoutcast" ),
436                                                 _( "Bitrate" ),
437                                                 "%s",
438                                                 psz_br );
439                     }
440                     else if( psz_genre )
441                     {
442                         vlc_input_item_AddInfo( &p_item->input,
443                                                 _(VLC_META_INFO_CAT),
444                                                 _(VLC_META_GENRE),
445                                                 "%s",
446                                                 psz_genre );
447                     }
448                     else if( psz_ct )
449                     {
450                         vlc_input_item_AddInfo( &p_item->input,
451                                                 _(VLC_META_INFO_CAT),
452                                                 _(VLC_META_NOW_PLAYING),
453                                                 "%s",
454                                                 psz_ct );
455                     }
456                     else if( psz_lc )
457                     {
458                         vlc_input_item_AddInfo( &p_item->input,
459                                                 _( "Shoutcast" ),
460                                                 _( "Listeners" ),
461                                                 "%s",
462                                                 psz_lc );
463                     }
464
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,
468                                           PLAYLIST_END );
469
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 );
473
474                     vlc_input_item_CopyOptions( &p_sys->p_current->input,
475                                                 &p_item->input );
476
477 #define FREE(a) if( a ) free( a ); a = NULL;
478                     FREE( psz_name );
479                     FREE( psz_mt )
480                     FREE( psz_id )
481                     FREE( psz_br )
482                     FREE( psz_genre )
483                     FREE( psz_ct )
484                     FREE( psz_lc )
485 #undef FREE
486                 }
487                 free( psz_eltname );
488                 psz_eltname = strdup("");
489
490                 break;
491         }
492     }
493     return 0;
494 }
495
496 static int Control( demux_t *p_demux, int i_query, va_list args )
497 {
498     return VLC_EGENERIC;
499 }