]> git.sesse.net Git - vlc/blob - modules/demux/playlist/shoutcast.c
ac56600fcb01a573308f24541e2d4b4327ebe716
[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 #define SHOUTCAST_TV_TUNEIN_URL "http://www.shoutcast.com/sbin/tunein-tvstation.pls?id="
52
53 /*****************************************************************************
54  * Local prototypes
55  *****************************************************************************/
56 static int Demux( demux_t *p_demux);
57 static int Control( demux_t *p_demux, int i_query, va_list args );
58 #if 0
59 static void ShoutcastAdd( playlist_t *p_playlist, playlist_item_t* p_genre,
60                           playlist_item_t *p_bitrate, playlist_item_t *p_item,
61                           char *psz_genre, char *psz_bitrate );
62 #endif
63
64 static int DemuxGenre( demux_t *p_demux );
65 static int DemuxStation( demux_t *p_demux );
66
67 /*****************************************************************************
68  * Import_Shoutcast: main import function
69  *****************************************************************************/
70 int E_(Import_Shoutcast)( vlc_object_t *p_this )
71 {
72     demux_t *p_demux = (demux_t *)p_this;
73     demux_sys_t *p_sys;
74
75     char    *psz_ext;
76
77     psz_ext = strrchr ( p_demux->psz_path, '.' );
78
79     if( !p_demux->psz_demux || strcmp(p_demux->psz_demux, "shout-winamp") )
80     {
81         return VLC_EGENERIC;
82     }
83     msg_Dbg( p_demux, "using shoutcast playlist import");
84
85     p_demux->pf_control = Control;
86     p_demux->pf_demux = Demux;
87     p_demux->p_sys = p_sys = malloc( sizeof(demux_sys_t) );
88     if( p_sys == NULL )
89     {
90         msg_Err( p_demux, "out of memory" );
91         return VLC_ENOMEM;
92     }
93
94     p_sys->p_playlist = NULL;
95     p_sys->p_xml = NULL;
96     p_sys->p_xml_reader = NULL;
97
98     return VLC_SUCCESS;
99 }
100
101 /*****************************************************************************
102  * Deactivate: frees unused data
103  *****************************************************************************/
104 void E_(Close_Shoutcast)( vlc_object_t *p_this )
105 {
106     demux_t *p_demux = (demux_t *)p_this;
107     demux_sys_t *p_sys = p_demux->p_sys;
108
109     if( p_sys->p_playlist )
110         vlc_object_release( p_sys->p_playlist );
111     if( p_sys->p_xml_reader )
112         xml_ReaderDelete( p_sys->p_xml, p_sys->p_xml_reader );
113     if( p_sys->p_xml )
114         xml_Delete( p_sys->p_xml );
115     free( p_sys );
116 }
117
118 static int Demux( demux_t *p_demux )
119 {
120     demux_sys_t *p_sys = p_demux->p_sys;
121     playlist_t *p_playlist;
122
123     vlc_bool_t b_play;
124
125     xml_t *p_xml;
126     xml_reader_t *p_xml_reader;
127
128     char *psz_eltname = NULL;
129
130
131     p_playlist = (playlist_t *) vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
132                                                  FIND_ANYWHERE );
133     if( !p_playlist )
134     {
135         msg_Err( p_demux, "can't find playlist" );
136         return -1;
137     }
138     p_sys->p_playlist = p_playlist;
139
140     b_play = E_(FindItem)( p_demux, p_playlist, &p_sys->p_current );
141
142     msg_Warn( p_demux, "item: %s", p_sys->p_current->input.psz_name );
143     playlist_ItemToNode( p_playlist, p_sys->p_current );
144     p_sys->p_current->input.i_type = ITEM_TYPE_PLAYLIST;
145
146     p_xml = p_sys->p_xml = xml_Create( p_demux );
147     if( !p_xml ) return -1;
148
149 /*    psz_eltname = stream_ReadLine( p_demux->s );
150     if( psz_eltname ) free( psz_eltname );
151     psz_eltname = 0;*/
152
153     p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
154     if( !p_xml_reader ) return -1;
155     p_sys->p_xml_reader = p_xml_reader;
156
157     /* xml */
158     /* check root node */
159     if( xml_ReaderRead( p_xml_reader ) != 1 )
160     {
161         msg_Err( p_demux, "invalid file (no root node)" );
162         return -1;
163     }
164
165     if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
166         ( psz_eltname = xml_ReaderName( p_xml_reader ) ) == NULL ||
167         ( strcmp( psz_eltname, "genrelist" )
168           && strcmp( psz_eltname, "stationlist" ) ) )
169     {
170         msg_Err( p_demux, "invalid root node %i, %s",
171                  xml_ReaderNodeType( p_xml_reader ), psz_eltname );
172         if( psz_eltname ) free( psz_eltname );
173         return -1;
174     }
175
176     if( !strcmp( psz_eltname, "genrelist" ) )
177     {
178         /* we're reading a genre list */
179         if( DemuxGenre( p_demux ) ) return -1;
180     }
181     else
182     {
183         /* we're reading a station list */
184         if( DemuxStation( p_demux ) ) return -1;
185     }
186     free( psz_eltname );
187
188     /* Go back and play the playlist */
189     if( b_play && p_playlist->status.p_item &&
190         p_playlist->status.p_item->i_children > 0 )
191     {
192         playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
193                           p_playlist->status.i_view,
194                           p_playlist->status.p_item,
195                           p_playlist->status.p_item->pp_children[0] );
196     }
197
198     vlc_object_release( p_playlist );
199     p_sys->p_playlist = NULL;
200     return VLC_SUCCESS;
201 }
202
203 #define GET_VALUE( a ) \
204                         if( !strcmp( psz_attrname, #a ) ) \
205                         { \
206                             psz_ ## a = strdup( psz_attrvalue ); \
207                         }
208 /* <genrelist>
209  *   <genre name="the name"></genre>
210  *   ...
211  * </genrelist>
212  **/
213 static int DemuxGenre( demux_t *p_demux )
214 {
215     demux_sys_t *p_sys = p_demux->p_sys;
216     char *psz_name = NULL; /* genre name */
217     char *psz_eltname = NULL; /* tag name */
218
219     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
220     {
221         int i_type;
222
223         // Get the node type
224         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
225         switch( i_type )
226         {
227             // Error
228             case -1:
229                 return -1;
230                 break;
231
232             case XML_READER_STARTELEM:
233                 // Read the element name
234                 if( psz_eltname ) free( psz_eltname );
235                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
236                 if( !psz_eltname ) return -1;
237
238
239                 if( !strcmp( psz_eltname, "genre" ) )
240                 {
241                     // Read the attributes
242                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
243                     {
244                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
245                         char *psz_attrvalue =
246                             xml_ReaderValue( p_sys->p_xml_reader );
247                         if( !psz_attrname || !psz_attrvalue ) return -1;
248
249                         GET_VALUE( name )
250                         else
251                         {
252                             msg_Warn( p_demux,
253                                       "unexpected attribure %s in element %s",
254                                       psz_attrname,
255                                       psz_eltname );
256                         }
257                         free( psz_attrname );
258                         free( psz_attrvalue );
259                     }
260                 }
261                 break;
262
263             case XML_READER_TEXT:
264                 break;
265
266             // End element
267             case XML_READER_ENDELEM:
268                 // Read the element name
269                 free( psz_eltname );
270                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
271                 if( !psz_eltname ) return -1;
272                 if( !strcmp( psz_eltname, "genre" ) )
273                 {
274                     playlist_item_t *p_item;
275                     char *psz_mrl = malloc( strlen( SHOUTCAST_BASE_URL )
276                             + strlen( "?genre=" ) + strlen( psz_name ) + 1 );
277                     sprintf( psz_mrl, SHOUTCAST_BASE_URL "?genre=%s",
278                              psz_name );
279                     p_item = playlist_ItemNew( p_sys->p_playlist, psz_mrl,
280                                                psz_name );
281                     free( psz_mrl );
282                     playlist_NodeAddItem( p_sys->p_playlist, p_item,
283                                           p_sys->p_current->pp_parents[0]->i_view,
284                                           p_sys->p_current, PLAYLIST_APPEND,
285                                           PLAYLIST_END );
286
287                     /* We need to declare the parents of the node as the
288                      *                  * same of the parent's ones */
289                     playlist_CopyParents( p_sys->p_current, p_item );
290
291                     vlc_input_item_CopyOptions( &p_sys->p_current->input,
292                                                 &p_item->input );
293
294 #define FREE(a) if( a ) free( a ); a = NULL;
295                     FREE( psz_name );
296 #undef FREE
297                 }
298                 free( psz_eltname );
299                 psz_eltname = strdup("");
300
301                 break;
302         }
303     }
304     return 0;
305 }
306
307 /* radio stations:
308  * <stationlist>
309  *   <tunein base="/sbin/tunein-station.pls"></tunein>
310  *   <station name="the name"
311  *            mt="mime type"
312  *            id="the id"
313  *            br="bit rate"
314  *            genre="A big genre string"
315  *            ct="current track name/author/..."
316  *            lc="listener count"></station>
317  * </stationlist>
318  *
319  * TV stations:
320  * <stationlist>
321  *   <tunein base="/sbin/tunein-station.pls"></tunein>
322  *   <station name="the name"
323  *            id="the id"
324  *            br="bit rate"
325  *            rt="rating"
326  *            load="server load ?"
327  *            ct="current track name/author/..."
328  *            genre="A big genre string"
329  *            lc="listener count"></station>
330  * </stationlist>
331  **/
332 static int DemuxStation( demux_t *p_demux )
333 {
334     demux_sys_t *p_sys = p_demux->p_sys;
335
336     char *psz_base = NULL; /* */
337
338     char *psz_name = NULL; /* genre name */
339     char *psz_mt = NULL; /* mime type */
340     char *psz_id = NULL; /* id */
341     char *psz_br = NULL; /* bit rate */
342     char *psz_genre = NULL; /* genre */
343     char *psz_ct = NULL; /* current track */
344     char *psz_lc = NULL; /* listener count */
345
346     /* If these are set then it's *not* a radio but a TV */
347     char *psz_rt = NULL; /* rating for shoutcast TV */
348     char *psz_load = NULL; /* load for shoutcast TV */
349
350     char *psz_eltname = NULL; /* tag name */
351
352     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
353     {
354         int i_type;
355
356         // Get the node type
357         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
358         switch( i_type )
359         {
360             // Error
361             case -1:
362                 return -1;
363                 break;
364
365             case XML_READER_STARTELEM:
366                 // Read the element name
367                 if( psz_eltname ) free( psz_eltname );
368                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
369                 if( !psz_eltname ) return -1;
370
371                 // Read the attributes
372                 if( !strcmp( psz_eltname, "tunein" ) )
373                 {
374                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
375                     {
376                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
377                         char *psz_attrvalue =
378                             xml_ReaderValue( p_sys->p_xml_reader );
379                         if( !psz_attrname || !psz_attrvalue ) return -1;
380
381                         GET_VALUE( base )
382                         else
383                         {
384                             msg_Warn( p_demux,
385                                       "unexpected attribure %s in element %s",
386                                       psz_attrname,
387                                       psz_eltname );
388                         }
389                         free( psz_attrname );
390                         free( psz_attrvalue );
391                     }
392                 }
393                 else if( !strcmp( psz_eltname, "station" ) )
394                 {
395                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
396                     {
397                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
398                         char *psz_attrvalue =
399                             xml_ReaderValue( p_sys->p_xml_reader );
400                         if( !psz_attrname || !psz_attrvalue ) return -1;
401
402                         GET_VALUE( name )
403                         else GET_VALUE( mt )
404                         else GET_VALUE( id )
405                         else GET_VALUE( br )
406                         else GET_VALUE( genre )
407                         else GET_VALUE( ct )
408                         else GET_VALUE( lc )
409                         else GET_VALUE( rt )
410                         else GET_VALUE( load )
411                         else
412                         {
413                             msg_Warn( p_demux,
414                                       "unexpected attribure %s in element %s",
415                                       psz_attrname,
416                                       psz_eltname );
417                         }
418                         free( psz_attrname );
419                         free( psz_attrvalue );
420                     }
421                 }
422                 break;
423
424             case XML_READER_TEXT:
425                 break;
426
427             // End element
428             case XML_READER_ENDELEM:
429                 // Read the element name
430                 free( psz_eltname );
431                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
432                 if( !psz_eltname ) return -1;
433                 if( !strcmp( psz_eltname, "station" ) &&
434                     ( psz_base || ( psz_rt && psz_load ) ) )
435                 {
436                     playlist_item_t *p_item;
437                     char *psz_mrl = NULL;
438                     if( psz_rt || psz_load )
439                     {
440                         /* tv */
441                         psz_mrl = malloc( strlen( SHOUTCAST_TV_TUNEIN_URL )
442                                           + strlen( psz_id ) + 1 );
443                         sprintf( psz_mrl, SHOUTCAST_TV_TUNEIN_URL "%s",
444                                  psz_id );
445                     }
446                     else
447                     {
448                         /* radio */
449                         psz_mrl = malloc( strlen( SHOUTCAST_TUNEIN_BASE_URL )
450                             + strlen( psz_base ) + strlen( "?id=" )
451                             + strlen( psz_id ) + 1 );
452                         sprintf( psz_mrl, SHOUTCAST_TUNEIN_BASE_URL "%s?id=%s",
453                              psz_base, psz_id );
454                     }
455                     msg_Warn( p_demux, "%s", psz_mrl );
456                     p_item = playlist_ItemNew( p_sys->p_playlist, psz_mrl,
457                                                psz_name );
458                     free( psz_mrl );
459
460                     if( psz_mt )
461                     {
462                         vlc_input_item_AddInfo( &p_item->input,
463                                                 _( "Shoutcast" ),
464                                                 _( "Mime type" ),
465                                                 "%s",
466                                                 psz_mt );
467                     }
468                     else if( psz_br )
469                     {
470                         vlc_input_item_AddInfo( &p_item->input,
471                                                 _( "Shoutcast" ),
472                                                 _( "Bitrate" ),
473                                                 "%s",
474                                                 psz_br );
475                     }
476                     else if( psz_genre )
477                     {
478                         vlc_input_item_AddInfo( &p_item->input,
479                                                 _(VLC_META_INFO_CAT),
480                                                 _(VLC_META_GENRE),
481                                                 "%s",
482                                                 psz_genre );
483                     }
484                     else if( psz_ct )
485                     {
486                         vlc_input_item_AddInfo( &p_item->input,
487                                                 _(VLC_META_INFO_CAT),
488                                                 _(VLC_META_NOW_PLAYING),
489                                                 "%s",
490                                                 psz_ct );
491                     }
492                     else if( psz_lc )
493                     {
494                         vlc_input_item_AddInfo( &p_item->input,
495                                                 _( "Shoutcast" ),
496                                                 _( "Listeners" ),
497                                                 "%s",
498                                                 psz_lc );
499                     }
500                     else if( psz_rt )
501                     {
502                         vlc_input_item_AddInfo( &p_item->input,
503                                                 _( "Shoutcast" ),
504                                                 _( "Rating" ),
505                                                 "%s",
506                                                 psz_rt );
507                     }
508                     else if( psz_load )
509                     {
510                         vlc_input_item_AddInfo( &p_item->input,
511                                                 _( "Shoutcast" ),
512                                                 _( "Load" ),
513                                                 "%s",
514                                                 psz_load );
515                     }
516
517                     playlist_NodeAddItem( p_sys->p_playlist, p_item,
518                                           p_sys->p_current->pp_parents[0]->i_view,
519                                           p_sys->p_current, PLAYLIST_APPEND,
520                                           PLAYLIST_END );
521
522                     /* We need to declare the parents of the node as the
523                      *                  * same of the parent's ones */
524                     playlist_CopyParents( p_sys->p_current, p_item );
525
526                     vlc_input_item_CopyOptions( &p_sys->p_current->input,
527                                                 &p_item->input );
528
529 #define FREE(a) if( a ) free( a ); a = NULL;
530                     FREE( psz_name );
531                     FREE( psz_mt )
532                     FREE( psz_id )
533                     FREE( psz_br )
534                     FREE( psz_genre )
535                     FREE( psz_ct )
536                     FREE( psz_lc )
537                     FREE( psz_rt )
538 #undef FREE
539                 }
540                 free( psz_eltname );
541                 psz_eltname = strdup("");
542
543                 break;
544         }
545     }
546     return 0;
547 }
548
549 static int Control( demux_t *p_demux, int i_query, va_list args )
550 {
551     return VLC_EGENERIC;
552 }