Commit acb5da73 authored by Antoine Cellerier's avatar Antoine Cellerier

Change shoutcast service discovery module and write a new demux to be...

Change shoutcast service discovery module and write a new demux to be compatible with the new winamp 5.2 format. See trac ticket for detailed info. I'd appreciate if people could test this and proof read the code since we'll have to include this in 0.8.5 which is due to be released soon.

Ref #640.
parent fea437ab
......@@ -9,6 +9,7 @@ SOURCES_playlist = \
podcast.c \
xspf.c \
xspf.h \
shoutcast.c \
$(NULL)
......@@ -90,6 +90,11 @@ vlc_module_begin();
add_shortcut( "xspf-open" );
set_capability( "demux2", 10 );
set_callbacks( E_(xspf_import_Activate), NULL );
add_submodule();
set_description( _("New winamp 5.2 shoutcast import") );
add_shortcut( "shout-winamp" );
set_capability( "demux2", 10 );
set_callbacks( E_(Import_Shoutcast), E_(Close_Shoutcast) );
vlc_module_end();
......
......@@ -47,3 +47,6 @@ int E_(Import_podcast) ( vlc_object_t * );
void E_(Close_podcast) ( vlc_object_t * );
int E_(xspf_import_Activate) ( vlc_object_t * );
int E_(Import_Shoutcast) ( vlc_object_t * );
void E_(Close_Shoutcast) ( vlc_object_t * );
/*****************************************************************************
* shoutcast.c: Winamp >=5.2 shoutcast demuxer
*****************************************************************************
* Copyright (C) 2006 the VideoLAN team
* $Id$
*
* Authors: Antoine Cellerier <dionoea -@t- videolan -Dot- org>
* based on b4s.c by Sigmund Augdal Helberg <dnumgis@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h> /* malloc(), free() */
#include <ctype.h> /* isspace() */
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/intf.h>
#include <errno.h> /* ENOMEM */
#include "playlist.h"
#include "vlc_xml.h"
struct demux_sys_t
{
playlist_t *p_playlist;
playlist_item_t *p_current;
xml_t *p_xml;
xml_reader_t *p_xml_reader;
};
/* duplicate from modules/services_discovery/shout.c */
#define SHOUTCAST_BASE_URL "http/shout-winamp://www.shoutcast.com/sbin/newxml.phtml"
#define SHOUTCAST_TUNEIN_BASE_URL "http://www.shoutcast.com"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Demux( demux_t *p_demux);
static int Control( demux_t *p_demux, int i_query, va_list args );
#if 0
static void ShoutcastAdd( playlist_t *p_playlist, playlist_item_t* p_genre,
playlist_item_t *p_bitrate, playlist_item_t *p_item,
char *psz_genre, char *psz_bitrate );
#endif
static int DemuxGenre( demux_t *p_demux );
static int DemuxStation( demux_t *p_demux );
/*****************************************************************************
* Import_Shoutcast: main import function
*****************************************************************************/
int E_(Import_Shoutcast)( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t *)p_this;
demux_sys_t *p_sys;
char *psz_ext;
psz_ext = strrchr ( p_demux->psz_path, '.' );
if( !p_demux->psz_demux || strcmp(p_demux->psz_demux, "shout-winamp") )
{
return VLC_EGENERIC;
}
msg_Dbg( p_demux, "using shoutcast playlist import");
p_demux->pf_control = Control;
p_demux->pf_demux = Demux;
p_demux->p_sys = p_sys = malloc( sizeof(demux_sys_t) );
if( p_sys == NULL )
{
msg_Err( p_demux, "out of memory" );
return VLC_ENOMEM;
}
p_sys->p_playlist = NULL;
p_sys->p_xml = NULL;
p_sys->p_xml_reader = NULL;
return VLC_SUCCESS;
}
/*****************************************************************************
* Deactivate: frees unused data
*****************************************************************************/
void E_(Close_Shoutcast)( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t *)p_this;
demux_sys_t *p_sys = p_demux->p_sys;
if( p_sys->p_playlist )
vlc_object_release( p_sys->p_playlist );
if( p_sys->p_xml_reader )
xml_ReaderDelete( p_sys->p_xml, p_sys->p_xml_reader );
if( p_sys->p_xml )
xml_Delete( p_sys->p_xml );
free( p_sys );
}
static int Demux( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
playlist_t *p_playlist;
vlc_bool_t b_play;
xml_t *p_xml;
xml_reader_t *p_xml_reader;
char *psz_eltname = NULL;
p_playlist = (playlist_t *) vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE );
if( !p_playlist )
{
msg_Err( p_demux, "can't find playlist" );
return -1;
}
p_sys->p_playlist = p_playlist;
b_play = E_(FindItem)( p_demux, p_playlist, &p_sys->p_current );
msg_Warn( p_demux, "item: %s", p_sys->p_current->input.psz_name );
playlist_ItemToNode( p_playlist, p_sys->p_current );
p_sys->p_current->input.i_type = ITEM_TYPE_PLAYLIST;
p_xml = p_sys->p_xml = xml_Create( p_demux );
if( !p_xml ) return -1;
/* psz_eltname = stream_ReadLine( p_demux->s );
if( psz_eltname ) free( psz_eltname );
psz_eltname = 0;*/
p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
if( !p_xml_reader ) return -1;
p_sys->p_xml_reader = p_xml_reader;
/* xml */
/* check root node */
if( xml_ReaderRead( p_xml_reader ) != 1 )
{
msg_Err( p_demux, "invalid file (no root node)" );
return -1;
}
if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM ||
( psz_eltname = xml_ReaderName( p_xml_reader ) ) == NULL ||
( strcmp( psz_eltname, "genrelist" )
&& strcmp( psz_eltname, "stationlist" ) ) )
{
msg_Err( p_demux, "invalid root node %i, %s",
xml_ReaderNodeType( p_xml_reader ), psz_eltname );
if( psz_eltname ) free( psz_eltname );
return -1;
}
if( !strcmp( psz_eltname, "genrelist" ) )
{
/* we're reading a genre list */
if( DemuxGenre( p_demux ) ) return -1;
}
else
{
/* we're reading a station list */
if( DemuxStation( p_demux ) ) return -1;
}
free( psz_eltname );
/* Go back and play the playlist */
if( b_play && p_playlist->status.p_item &&
p_playlist->status.p_item->i_children > 0 )
{
playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
p_playlist->status.i_view,
p_playlist->status.p_item,
p_playlist->status.p_item->pp_children[0] );
}
vlc_object_release( p_playlist );
p_sys->p_playlist = NULL;
return VLC_SUCCESS;
}
#define GET_VALUE( a ) \
if( !strcmp( psz_attrname, #a ) ) \
{ \
psz_ ## a = strdup( psz_attrvalue ); \
}
/* <genrelist>
* <genre name="the name"></genre>
* ...
* </genrelist>
**/
static int DemuxGenre( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
char *psz_name = NULL; /* genre name */
char *psz_eltname = NULL; /* tag name */
while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
{
int i_type;
// Get the node type
i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
switch( i_type )
{
// Error
case -1:
return -1;
break;
case XML_READER_STARTELEM:
// Read the element name
if( psz_eltname ) free( psz_eltname );
psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
if( !psz_eltname ) return -1;
if( !strcmp( psz_eltname, "genre" ) )
{
// Read the attributes
while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
{
char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
char *psz_attrvalue =
xml_ReaderValue( p_sys->p_xml_reader );
if( !psz_attrname || !psz_attrvalue ) return -1;
GET_VALUE( name )
else
{
msg_Warn( p_demux,
"unexpected attribure %s in element %s",
psz_attrname,
psz_eltname );
}
free( psz_attrname );
free( psz_attrvalue );
}
}
break;
case XML_READER_TEXT:
break;
// End element
case XML_READER_ENDELEM:
// Read the element name
free( psz_eltname );
psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
if( !psz_eltname ) return -1;
if( !strcmp( psz_eltname, "genre" ) )
{
playlist_item_t *p_item;
char *psz_mrl = malloc( strlen( SHOUTCAST_BASE_URL )
+ strlen( "?genre=" ) + strlen( psz_name ) + 1 );
sprintf( psz_mrl, SHOUTCAST_BASE_URL "?genre=%s",
psz_name );
p_item = playlist_ItemNew( p_sys->p_playlist, psz_mrl,
psz_name );
free( psz_mrl );
playlist_NodeAddItem( p_sys->p_playlist, p_item,
p_sys->p_current->pp_parents[0]->i_view,
p_sys->p_current, PLAYLIST_APPEND,
PLAYLIST_END );
/* We need to declare the parents of the node as the
* * same of the parent's ones */
playlist_CopyParents( p_sys->p_current, p_item );
vlc_input_item_CopyOptions( &p_sys->p_current->input,
&p_item->input );
#define FREE(a) if( a ) free( a ); a = NULL;
FREE( psz_name );
#undef FREE
}
free( psz_eltname );
psz_eltname = strdup("");
break;
}
}
return 0;
}
/* <stationlist>
* <tunein base="/sbin/tunein-station.pls"></tunein>
* <station name="the name"
* mt="mime type"
* id="the id"
* br="bit rate"
* genre="A big genre string"
* ct="current track name/author/..."
* lc="listener count"></station>
* </stationlist>
**/
static int DemuxStation( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
char *psz_base = NULL; /* */
char *psz_name = NULL; /* genre name */
char *psz_mt = NULL; /* mime type */
char *psz_id = NULL; /* id */
char *psz_br = NULL; /* bit rate */
char *psz_genre = NULL; /* genre */
char *psz_ct = NULL; /* current track */
char *psz_lc = NULL; /* listener count */
char *psz_eltname = NULL; /* tag name */
while( xml_ReaderRead( p_sys->p_xml_reader ) == 1 )
{
int i_type;
printf("AAA\n");
// Get the node type
i_type = xml_ReaderNodeType( p_sys->p_xml_reader );
switch( i_type )
{
// Error
case -1:
return -1;
break;
case XML_READER_STARTELEM:
// Read the element name
if( psz_eltname ) free( psz_eltname );
psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
if( !psz_eltname ) return -1;
// Read the attributes
if( !strcmp( psz_eltname, "tunein" ) )
{
while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
{
char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
char *psz_attrvalue =
xml_ReaderValue( p_sys->p_xml_reader );
if( !psz_attrname || !psz_attrvalue ) return -1;
GET_VALUE( base )
else
{
msg_Warn( p_demux,
"unexpected attribure %s in element %s",
psz_attrname,
psz_eltname );
}
free( psz_attrname );
free( psz_attrvalue );
}
}
else if( !strcmp( psz_eltname, "station" ) )
{
while( xml_ReaderNextAttr( p_sys->p_xml_reader ) == VLC_SUCCESS )
{
char *psz_attrname = xml_ReaderName( p_sys->p_xml_reader );
char *psz_attrvalue =
xml_ReaderValue( p_sys->p_xml_reader );
if( !psz_attrname || !psz_attrvalue ) return -1;
GET_VALUE( name )
else GET_VALUE( mt )
else GET_VALUE( id )
else GET_VALUE( br )
else GET_VALUE( genre )
else GET_VALUE( ct )
else GET_VALUE( lc )
else
{
msg_Warn( p_demux,
"unexpected attribure %s in element %s",
psz_attrname,
psz_eltname );
}
free( psz_attrname );
free( psz_attrvalue );
}
}
break;
case XML_READER_TEXT:
break;
// End element
case XML_READER_ENDELEM:
// Read the element name
free( psz_eltname );
psz_eltname = xml_ReaderName( p_sys->p_xml_reader );
if( !psz_eltname ) return -1;
if( !strcmp( psz_eltname, "station" ) && psz_base )
{
playlist_item_t *p_item;
char *psz_mrl = malloc( strlen( SHOUTCAST_TUNEIN_BASE_URL )
+ strlen( psz_base ) + strlen( "?id=" )
+ strlen( psz_id ) + 1 );
sprintf( psz_mrl, SHOUTCAST_TUNEIN_BASE_URL "%s?id=%s",
psz_base, psz_id );
p_item = playlist_ItemNew( p_sys->p_playlist, psz_mrl,
psz_name );
free( psz_mrl );
if( psz_mt )
{
vlc_input_item_AddInfo( &p_item->input,
_( "Shoutcast" ),
_( "Mime type" ),
"%s",
psz_mt );
}
else if( psz_br )
{
vlc_input_item_AddInfo( &p_item->input,
_( "Shoutcast" ),
_( "Bitrate" ),
"%s",
psz_br );
}
else if( psz_genre )
{
vlc_input_item_AddInfo( &p_item->input,
_(VLC_META_INFO_CAT),
_(VLC_META_GENRE),
"%s",
psz_genre );
}
else if( psz_ct )
{
vlc_input_item_AddInfo( &p_item->input,
_(VLC_META_INFO_CAT),
_(VLC_META_NOW_PLAYING),
"%s",
psz_ct );
}
else if( psz_lc )
{
vlc_input_item_AddInfo( &p_item->input,
_( "Shoutcast" ),
_( "Listeners" ),
"%s",
psz_lc );
}
playlist_NodeAddItem( p_sys->p_playlist, p_item,
p_sys->p_current->pp_parents[0]->i_view,
p_sys->p_current, PLAYLIST_APPEND,
PLAYLIST_END );
/* We need to declare the parents of the node as the
* * same of the parent's ones */
playlist_CopyParents( p_sys->p_current, p_item );
vlc_input_item_CopyOptions( &p_sys->p_current->input,
&p_item->input );
#define FREE(a) if( a ) free( a ); a = NULL;
FREE( psz_name );
FREE( psz_mt )
FREE( psz_id )
FREE( psz_br )
FREE( psz_genre )
FREE( psz_ct )
FREE( psz_lc )
#undef FREE
}
free( psz_eltname );
psz_eltname = strdup("");
break;
}
}
return 0;
}
static int Control( demux_t *p_demux, int i_query, va_list args )
{
return VLC_EGENERIC;
}
......@@ -5,6 +5,7 @@
* $Id$
*
* Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
* Antoine Cellerier <dionoea -@T- videolan -d.t- org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -48,7 +49,7 @@
************************************************************************/
#define MAX_LINE_LENGTH 256
#define SHOUTCAST_BASE_URL "http/shout-winamp://www.shoutcast.com/sbin/newxml.phtml"
/*****************************************************************************
* Module descriptor
......@@ -58,19 +59,13 @@
static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
#define LIMIT_TEXT N_("Number of streams")
/// \bug [String] -which would be listed + to list
#define LIMIT_LONGTEXT N_("Maximum number of Shoutcast radio streams which " \
"would be listed.")
vlc_module_begin();
set_shortname( "Shoutcast");
set_description( _("Shoutcast radio listings") );
set_category( CAT_PLAYLIST );
set_subcategory( SUBCAT_PLAYLIST_SD );
add_integer( "shoutcast-limit", 1000, NULL, LIMIT_TEXT,
LIMIT_LONGTEXT, VLC_TRUE );
add_suppressed_integer( "shoutcast-limit" );
set_capability( "services_discovery", 0 );
set_callbacks( Open, Close );
......@@ -127,13 +122,11 @@ static int Open( vlc_object_t *p_this )
return VLC_EGENERIC;
}
p_sys->i_limit = config_GetInt( p_this->p_libvlc, "shoutcast-limit" );
#define SHOUTCAST_BASE_URL "http/shout-b4s://www.shoutcast.com/sbin/xmllister.phtml?service=vlc&no_compress=1&limit="
psz_shoutcast_url = (char *)malloc( strlen( SHOUTCAST_BASE_URL ) + 20 );
psz_shoutcast_title = (char *)malloc( 6 + 20 );
sprintf( psz_shoutcast_url, SHOUTCAST_BASE_URL "%d", p_sys->i_limit );
sprintf( psz_shoutcast_title, "Top %d", p_sys->i_limit );
sprintf( psz_shoutcast_url, SHOUTCAST_BASE_URL );
sprintf( psz_shoutcast_title, "Shoutcast", p_sys->i_limit );
p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
p_sys->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment