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