]> git.sesse.net Git - vlc/blob - modules/demux/playlist/shoutcast.c
Preparse playlist items that don't have enough meta
[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 <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     playlist_t *p_playlist;
41     playlist_item_t *p_current;
42     playlist_item_t *p_item_in_category;
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
71     if( !isDemux( p_demux, "shout-winamp" ) )
72         return VLC_EGENERIC;
73
74     STANDARD_DEMUX_INIT_MSG( "using shoutcast playlist reader" );
75     p_demux->p_sys->p_playlist = NULL;
76     p_demux->p_sys->p_xml = NULL;
77     p_demux->p_sys->p_xml_reader = NULL;
78
79     /* Do we want to list adult content ? */
80     var_Create( p_demux, "shoutcast-show-adult",
81                 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
82     p_demux->p_sys->b_adult = var_GetBool( p_demux, "shoutcast-show-adult" );
83
84     return VLC_SUCCESS;
85 }
86
87 /*****************************************************************************
88  * Deactivate: frees unused data
89  *****************************************************************************/
90 void E_(Close_Shoutcast)( vlc_object_t *p_this )
91 {
92     demux_t *p_demux = (demux_t *)p_this;
93     demux_sys_t *p_sys = p_demux->p_sys;
94
95     if( p_sys->p_playlist )
96         vlc_object_release( p_sys->p_playlist );
97     if( p_sys->p_xml_reader )
98         xml_ReaderDelete( p_sys->p_xml, p_sys->p_xml_reader );
99     if( p_sys->p_xml )
100         xml_Delete( p_sys->p_xml );
101     free( p_sys );
102 }
103
104 static int Demux( demux_t *p_demux )
105 {
106     demux_sys_t *p_sys = p_demux->p_sys;
107     xml_t *p_xml;
108     xml_reader_t *p_xml_reader;
109     char *psz_eltname = NULL;
110     INIT_PLAYLIST_STUFF;
111     p_sys->p_playlist = p_playlist;
112     p_sys->p_current = p_current;
113     p_sys->p_item_in_category = p_item_in_category;
114
115     p_xml = p_sys->p_xml = xml_Create( p_demux );
116     if( !p_xml ) return -1;
117
118     p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
119     if( !p_xml_reader ) return -1;
120     p_sys->p_xml_reader = p_xml_reader;
121
122     /* check root node */
123     if( xml_ReaderRead( p_xml_reader ) != 1 )
124     {
125         msg_Err( p_demux, "invalid file (no root node)" );
126         return -1;
127     }
128
129     if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
130         ( psz_eltname = xml_ReaderName( p_xml_reader ) ) == NULL ||
131         ( strcmp( psz_eltname, "genrelist" )
132           && strcmp( psz_eltname, "stationlist" ) ) )
133     {
134         msg_Err( p_demux, "invalid root node %i, %s",
135                  xml_ReaderNodeType( p_xml_reader ), psz_eltname );
136         if( psz_eltname ) free( psz_eltname );
137         return -1;
138     }
139
140     if( !strcmp( psz_eltname, "genrelist" ) )
141     {
142         /* we're reading a genre list */
143         free( psz_eltname );
144         if( DemuxGenre( p_demux ) ) return -1;
145     }
146     else
147     {
148         /* we're reading a station list */
149         free( psz_eltname );
150         if( DemuxStation( p_demux ) ) return -1;
151     }
152
153     HANDLE_PLAY_AND_RELEASE;
154     p_sys->p_playlist = NULL;
155     return -1; /* Needed for correct operation of go back */
156 }
157
158 #define GET_VALUE( a ) \
159                         if( !strcmp( psz_attrname, #a ) ) \
160                         { \
161                             psz_ ## a = strdup( psz_attrvalue ); \
162                         }
163 /* <genrelist>
164  *   <genre name="the name"></genre>
165  *   ...
166  * </genrelist>
167  **/
168 static int DemuxGenre( demux_t *p_demux )
169 {
170     demux_sys_t *p_sys = p_demux->p_sys;
171     char *psz_name = NULL; /* genre name */
172     char *psz_eltname = NULL; /* tag name */
173     input_item_t *p_input;
174
175     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
176     {
177         int i_type;
178
179         // Get the node type
180         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
181         switch( i_type )
182         {
183             // Error
184             case -1:
185                 return -1;
186                 break;
187
188             case XML_READER_STARTELEM:
189                 // Read the element name
190                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
191                 if( !psz_eltname ) return -1;
192
193                 if( !strcmp( psz_eltname, "genre" ) )
194                 {
195                     // Read the attributes
196                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
197                     {
198                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
199                         char *psz_attrvalue =
200                             xml_ReaderValue( p_sys->p_xml_reader );
201                         if( !psz_attrname || !psz_attrvalue )
202                         {
203                             FREENULL(psz_attrname);
204                             FREENULL(psz_attrvalue);
205                             free(psz_eltname);
206                             /*FIXME: isn't return a bit too much. what about break*/
207                             return -1;
208                         }
209
210                         GET_VALUE( name )
211                         else
212                         {
213                             msg_Warn( p_demux,
214                                       "unexpected attribure %s in element %s",
215                                       psz_attrname,psz_eltname );
216                         }
217                         free( psz_attrname );
218                         free( psz_attrvalue );
219                     }
220                 }
221                 free( psz_eltname ); psz_eltname = NULL;
222                 break;
223
224             case XML_READER_TEXT:
225                 break;
226
227             // End element
228             case XML_READER_ENDELEM:
229                 // Read the element name
230                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
231                 if( !psz_eltname ) return -1;
232                 if( !strcmp( psz_eltname, "genre" ) )
233                 {
234                     char *psz_mrl = malloc( strlen( SHOUTCAST_BASE_URL )
235                             + strlen( "?genre=" ) + strlen( psz_name ) + 1 );
236                     sprintf( psz_mrl, SHOUTCAST_BASE_URL "?genre=%s",
237                              psz_name );
238                     p_input = input_ItemNewExt( p_sys->p_playlist, psz_mrl,
239                                                 psz_name, 0, NULL, -1 );
240                     input_ItemCopyOptions( p_sys->p_current->p_input,
241                                                 p_input );
242                     free( psz_mrl );
243                     playlist_BothAddInput( p_sys->p_playlist, p_input,
244                                            p_sys->p_item_in_category,
245                                            PLAYLIST_APPEND | PLAYLIST_SPREPARSE,
246                                            PLAYLIST_END );
247                     FREENULL( psz_name );
248                 }
249                 FREENULL( psz_eltname );
250                 break;
251         }
252     }
253     return 0;
254 }
255
256 /* radio stations:
257  * <stationlist>
258  *   <tunein base="/sbin/tunein-station.pls"></tunein>
259  *   <station name="the name"
260  *            mt="mime type"
261  *            id="the id"
262  *            br="bit rate"
263  *            genre="A big genre string"
264  *            ct="current track name/author/..."
265  *            lc="listener count"></station>
266  * </stationlist>
267  *
268  * TV stations:
269  * <stationlist>
270  *   <tunein base="/sbin/tunein-station.pls"></tunein>
271  *   <station name="the name"
272  *            id="the id"
273  *            br="bit rate"
274  *            rt="rating"
275  *            load="server load ?"
276  *            ct="current track name/author/..."
277  *            genre="A big genre string"
278  *            lc="listener count"></station>
279  * </stationlist>
280  **/
281 static int DemuxStation( demux_t *p_demux )
282 {
283     demux_sys_t *p_sys = p_demux->p_sys;
284     input_item_t *p_input;
285
286     char *psz_base = NULL; /* */
287
288     char *psz_name = NULL; /* genre name */
289     char *psz_mt = NULL; /* mime type */
290     char *psz_id = NULL; /* id */
291     char *psz_br = NULL; /* bit rate */
292     char *psz_genre = NULL; /* genre */
293     char *psz_ct = NULL; /* current track */
294     char *psz_lc = NULL; /* listener count */
295
296     /* If these are set then it's *not* a radio but a TV */
297     char *psz_rt = NULL; /* rating for shoutcast TV */
298     char *psz_load = NULL; /* load for shoutcast TV */
299
300     char *psz_eltname = NULL; /* tag name */
301
302     while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
303     {
304         int i_type;
305
306         // Get the node type
307         i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
308         switch( i_type )
309         {
310             // Error
311             case -1:
312                 return -1;
313                 break;
314
315             case XML_READER_STARTELEM:
316                 // Read the element name
317                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
318                 if( !psz_eltname ) return -1;
319
320                 // Read the attributes
321                 if( !strcmp( psz_eltname, "tunein" ) )
322                 {
323                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
324                     {
325                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
326                         char *psz_attrvalue =
327                             xml_ReaderValue( p_sys->p_xml_reader );
328                         if( !psz_attrname || !psz_attrvalue )
329                         {
330                             free(psz_eltname);
331                             FREENULL(psz_attrname);
332                             FREENULL(psz_attrvalue);
333                             return -1;
334                         }
335
336                         GET_VALUE( base )
337                         else
338                         {
339                             msg_Warn( p_demux,
340                                       "unexpected attribure %s in element %s",
341                                       psz_attrname, psz_eltname );
342                         }
343                         free( psz_attrname );
344                         free( psz_attrvalue );
345                     }
346                 }
347                 else if( !strcmp( psz_eltname, "station" ) )
348                 {
349                     while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
350                     {
351                         char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
352                         char *psz_attrvalue =
353                             xml_ReaderValue( p_sys->p_xml_reader );
354                         if( !psz_attrname || !psz_attrvalue )
355                         {
356                             free(psz_eltname);
357                             FREENULL(psz_attrname);
358                             FREENULL(psz_attrvalue);
359                             return -1;
360                         }
361
362                         GET_VALUE( name )
363                         else GET_VALUE( mt )
364                         else GET_VALUE( id )
365                         else GET_VALUE( br )
366                         else GET_VALUE( genre )
367                         else GET_VALUE( ct )
368                         else GET_VALUE( lc )
369                         else GET_VALUE( rt )
370                         else GET_VALUE( load )
371                         else
372                         {
373                             msg_Warn( p_demux,
374                                       "unexpected attribute %s in element %s",
375                                       psz_attrname, psz_eltname );
376                         }
377                         free( psz_attrname );
378                         free( psz_attrvalue );
379                     }
380                 }
381                 free(psz_eltname);
382                 break;
383
384             case XML_READER_TEXT:
385                 break;
386
387             // End element
388             case XML_READER_ENDELEM:
389                 // Read the element name
390                 psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
391                 if( !psz_eltname ) return -1;
392                 if( !strcmp( psz_eltname, "station" ) &&
393                     ( psz_base || ( psz_rt && psz_load &&
394                     ( p_sys->b_adult || strcmp( psz_rt, "NC17" ) ) ) ) )
395                 {
396                     char *psz_mrl = NULL;
397                     if( psz_rt || psz_load )
398                     {
399                         /* tv */
400                         psz_mrl = malloc( strlen( SHOUTCAST_TV_TUNEIN_URL )
401                                           + strlen( psz_id ) + 1 );
402                         sprintf( psz_mrl, SHOUTCAST_TV_TUNEIN_URL "%s",
403                                  psz_id );
404                     }
405                     else
406                     {
407                         /* radio */
408                         psz_mrl = malloc( strlen( SHOUTCAST_TUNEIN_BASE_URL )
409                             + strlen( psz_base ) + strlen( "?id=" )
410                             + strlen( psz_id ) + 1 );
411                         sprintf( psz_mrl, SHOUTCAST_TUNEIN_BASE_URL "%s?id=%s",
412                              psz_base, psz_id );
413                     }
414                     p_input = input_ItemNewExt( p_sys->p_playlist, psz_mrl,
415                                                 psz_name , 0, NULL, -1 );
416                     free( psz_mrl );
417
418                     input_ItemCopyOptions( p_sys->p_current->p_input,
419                                                 p_input );
420
421 #define SADD_INFO( type, field ) if( field ) { input_ItemAddInfo( \
422                     p_input, _("Shoutcast"), _(type), "%s", field ) ; }
423                     SADD_INFO( "Mime type", psz_mt );
424                     SADD_INFO( "Bitrate", psz_br );
425                     SADD_INFO( "Listeners", psz_lc );
426                     SADD_INFO( "Load", psz_load );
427                     p_input->p_meta = vlc_meta_New();
428                     if( psz_genre )
429                         vlc_meta_SetGenre( p_input->p_meta, psz_genre );
430                     if( psz_ct )
431                         vlc_meta_SetNowPlaying( p_input->p_meta, psz_ct );
432                     if( psz_rt )
433                         vlc_meta_SetRating( p_input->p_meta, psz_rt );
434
435                     playlist_BothAddInput( p_sys->p_playlist, p_input,
436                                            p_sys->p_item_in_category,
437                                            PLAYLIST_APPEND | PLAYLIST_SPREPARSE,
438                                            PLAYLIST_END );
439
440                     FREENULL( psz_name );
441                     FREENULL( psz_mt )
442                     FREENULL( psz_id )
443                     FREENULL( psz_br )
444                     FREENULL( psz_genre )
445                     FREENULL( psz_ct )
446                     FREENULL( psz_lc )
447                     FREENULL( psz_rt )
448                 }
449                 free( psz_eltname );
450                 break;
451         }
452     }
453     return 0;
454 }
455
456 static int Control( demux_t *p_demux, int i_query, va_list args )
457 {
458     return VLC_EGENERIC;
459 }