--- /dev/null
+/*****************************************************************************
+ * vlc_strings.h: String functions
+ *****************************************************************************
+ * Copyright (C) 2006 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan dot 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.
+ *****************************************************************************/
+
+#ifndef _VLC_STRINGS_H
+#define _VLC_STRINGS_H 1
+
+#include <vlc/vlc.h>
+
+/**
+ * \defgroup strings Strings
+ * @{
+ */
+
+VLC_EXPORT( char *, decode_encoded_URI_duplicate, ( const char *psz ) );
+VLC_EXPORT( void, decode_encoded_URI, ( char *psz ) );
+
+VLC_EXPORT( void, resolve_xml_special_chars, ( char *psz_value ) );
+VLC_EXPORT( char *, convert_xml_special_chars, ( const char *psz_content ) );
+
+/**
+ * @}
+ */
+
+#endif
double (*us_atof_inner) (const char *);
double (*us_strtod_inner) (const char *, char **);
lldiv_t (*vlc_lldiv_inner) (long long numer, long long denom);
+ void (*decode_encoded_URI_inner) (char *psz);
+ char * (*convert_xml_special_chars_inner) (const char *psz_content);
+ char * (*decode_encoded_URI_duplicate_inner) (const char *psz);
+ void (*resolve_xml_special_chars_inner) (char *psz_value);
};
# if defined (__PLUGIN__)
# define aout_FiltersCreatePipeline (p_symbols)->aout_FiltersCreatePipeline_inner
# define us_atof (p_symbols)->us_atof_inner
# define us_strtod (p_symbols)->us_strtod_inner
# define vlc_lldiv (p_symbols)->vlc_lldiv_inner
+# define decode_encoded_URI (p_symbols)->decode_encoded_URI_inner
+# define convert_xml_special_chars (p_symbols)->convert_xml_special_chars_inner
+# define decode_encoded_URI_duplicate (p_symbols)->decode_encoded_URI_duplicate_inner
+# define resolve_xml_special_chars (p_symbols)->resolve_xml_special_chars_inner
# elif defined (HAVE_DYNAMIC_PLUGINS) && !defined (__BUILTIN__)
/******************************************************************
* STORE_SYMBOLS: store VLC APIs into p_symbols for plugin access.
((p_symbols)->us_atof_inner) = us_atof; \
((p_symbols)->us_strtod_inner) = us_strtod; \
((p_symbols)->vlc_lldiv_inner) = vlc_lldiv; \
+ ((p_symbols)->decode_encoded_URI_inner) = decode_encoded_URI; \
+ ((p_symbols)->convert_xml_special_chars_inner) = convert_xml_special_chars; \
+ ((p_symbols)->decode_encoded_URI_duplicate_inner) = decode_encoded_URI_duplicate; \
+ ((p_symbols)->resolve_xml_special_chars_inner) = resolve_xml_special_chars; \
(p_symbols)->net_ConvertIPv4_deprecated = NULL; \
(p_symbols)->__stats_CounterGet_deprecated = NULL; \
(p_symbols)->__stats_TimerDumpAll_deprecated = NULL; \
/* Grrrr! detect ultravox server and force NSV demuxer */
p_access->psz_demux = strdup( "nsv" );
}
+ else if( p_sys->psz_mime &&
+ !strcasecmp( p_sys->psz_mime, "application/xspf+xml" ) )
+ p_access->psz_demux = strdup( "xspf-open" );
if( p_sys->b_reconnect ) msg_Dbg( p_access, "auto re-connect enabled" );
{
char *psz_src = E_(SSPop)( st );
char *psz_dest;
- char *str = psz_src;
-
- p = psz_dest = malloc( strlen( str ) * 6 + 1 );
- while( *str != '\0' )
- {
- if( *str == '&' )
- {
- strcpy( p, "&" );
- p += 5;
- }
- else if( *str == '\"' )
- {
- strcpy( p, """ );
- p += 6;
- }
- else if( *str == '\'' )
- {
- strcpy( p, "'" );
- p += 6;
- }
- else if( *str == '<' )
- {
- strcpy( p, "<" );
- p += 4;
- }
- else if( *str == '>' )
- {
- strcpy( p, ">" );
- p += 4;
- }
- else
- {
- *p++ = *str;
- }
- str++;
- }
- *p = '\0';
+ psz_dest = convert_xml_special_chars( psz_src );
E_(SSPush)( st, psz_dest );
free( psz_src );
*****************************************************************************/
#include "http.h"
+#include "vlc_strings.h"
/****************************************************************************
* File and directory functions
void E_(DecodeEncodedURI)( char *psz )
{
- char *dup = strdup( psz );
- char *p = dup;
-
- while( *p )
- {
- if( *p == '%' )
- {
- char val[3];
- p++;
- if( !*p )
- {
- break;
- }
-
- val[0] = *p++;
- val[1] = *p++;
- val[2] = '\0';
-
- *psz++ = strtol( val, NULL, 16 );
- }
- else if( *p == '+' )
- {
- *psz++ = ' ';
- p++;
- }
- else
- {
- *psz++ = *p++;
- }
- }
- *psz++ = '\0';
- free( dup );
+ decode_encoded_URI( psz );
}
/* Since the resulting string is smaller we can work in place, so it is
-SOURCES_playlist = playlist.c \
- playlist.h \
- old.c \
- m3u.c \
- b4s.c \
- pls.c \
- dvb.c \
- podcast.c
+SOURCES_playlist = \
+ playlist.c \
+ playlist.h \
+ old.c \
+ m3u.c \
+ b4s.c \
+ pls.c \
+ dvb.c \
+ podcast.c \
+ xspf.c \
+ xspf.h \
+ $(NULL)
+
+
add_shortcut( "podcast" );
set_capability( "demux2", 10 );
set_callbacks( E_(Import_podcast), E_(Close_podcast) );
+ add_submodule();
+ set_description( _("XSPF playlist import") );
+ add_shortcut( "xspf-open" );
+ set_capability( "demux2", 10 );
+ set_callbacks( E_(xspf_import_Activate), NULL );
vlc_module_end();
int E_(Import_podcast) ( vlc_object_t * );
void E_(Close_podcast) ( vlc_object_t * );
+
+int E_(xspf_import_Activate) ( vlc_object_t * );
--- /dev/null
+/******************************************************************************
+ * Copyright (C) 2006 Daniel Stränger <vlc at schmaller dot de>
+ *
+ * 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.
+ *******************************************************************************/
+/**
+ * \file modules/demux/playlist/xspf.c
+ * \brief XSPF playlist import functions
+ */
+
+#include <vlc/vlc.h>
+#include <vlc/input.h>
+#include <vlc/intf.h>
+
+#include "playlist.h"
+#include "vlc_xml.h"
+#include "vlc_strings.h"
+#include "xspf.h"
+
+/**
+ * \brief XSPF submodule initialization function
+ */
+int E_(xspf_import_Activate)( vlc_object_t *p_this )
+{
+ demux_t *p_demux = (demux_t *)p_this;
+ char *psz_ext;
+
+ psz_ext = strrchr ( p_demux->psz_path, '.' );
+
+ if( ( psz_ext && !strcasecmp( psz_ext, ".xspf") ) ||
+ ( p_demux->psz_demux && !strcmp(p_demux->psz_demux, "xspf-open") ) )
+ {
+ ;
+ }
+ else
+ {
+ return VLC_EGENERIC;
+ }
+ msg_Dbg( p_demux, "using xspf playlist import");
+
+ p_demux->pf_control = xspf_import_Control;
+ p_demux->pf_demux = xspf_import_Demux;
+
+ return VLC_SUCCESS;
+}
+
+/**
+ * \brief demuxer function for XSPF parsing
+ */
+int xspf_import_Demux( demux_t *p_demux )
+{
+ playlist_t *p_playlist = NULL;
+ playlist_item_t *p_current = NULL;
+
+ vlc_bool_t b_play;
+ int i_ret = VLC_SUCCESS;
+
+ xml_t *p_xml = NULL;
+ xml_reader_t *p_xml_reader = NULL;
+ char *psz_name = NULL;
+
+ /* create new xml parser from stream */
+ p_xml = xml_Create( p_demux );
+ if( !p_xml )
+ i_ret = VLC_ENOMOD;
+ else
+ {
+ p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
+ if( !p_xml_reader )
+ i_ret = VLC_EGENERIC;
+ }
+
+ /* start with parsing the root node */
+ if ( i_ret == VLC_SUCCESS )
+ if ( xml_ReaderRead( p_xml_reader ) != 1 )
+ {
+ msg_Err( p_demux, "can't read xml stream" );
+ i_ret = VLC_EGENERIC;
+ }
+ /* checking root nody type */
+ if ( i_ret == VLC_SUCCESS )
+ if( xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM )
+ {
+ msg_Err( p_demux, "invalid root node type: %i", xml_ReaderNodeType( p_xml_reader ) );
+ i_ret = VLC_EGENERIC;
+ }
+ /* checking root node name */
+ if ( i_ret == VLC_SUCCESS )
+ psz_name = xml_ReaderName( p_xml_reader );
+ if ( !psz_name || strcmp( psz_name, "playlist" ) )
+ {
+ msg_Err( p_demux, "invalid root node name: %s", psz_name );
+ i_ret = VLC_EGENERIC;
+ }
+ FREE_NAME();
+
+ /* get the playlist ... */
+ if ( i_ret == VLC_SUCCESS )
+ {
+ p_playlist = (playlist_t *) vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST, FIND_PARENT );
+ if( !p_playlist )
+ {
+ msg_Err( p_demux, "can't find playlist" );
+ i_ret = VLC_ENOOBJ;
+ }
+ }
+ /* ... and its current item (to convert it to a node) */
+ if ( i_ret == VLC_SUCCESS )
+ {
+ b_play = E_(FindItem)( p_demux, p_playlist, &p_current );
+ playlist_ItemToNode( p_playlist, p_current );
+ p_current->input.i_type = ITEM_TYPE_PLAYLIST;
+ /* parse the playlist node */
+ i_ret = parse_playlist_node( p_demux, p_playlist, p_current,
+ p_xml_reader, "playlist" );
+ /* true/false - success/egeneric mapping */
+ i_ret = ( i_ret==VLC_TRUE ? VLC_SUCCESS : VLC_EGENERIC );
+
+ if( b_play )
+ {
+ playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
+ p_playlist->status.i_view,
+ p_playlist->status.p_item, NULL );
+ }
+ }
+
+ if ( p_playlist )
+ vlc_object_release( p_playlist );
+ if ( p_xml_reader )
+ xml_ReaderDelete( p_xml, p_xml_reader );
+ if ( p_xml )
+ xml_Delete( p_xml );
+
+ return i_ret;
+}
+
+/** \brief dummy function for demux callback interface */
+int xspf_import_Control( demux_t *p_demux, int i_query, va_list args )
+{
+ return VLC_EGENERIC;
+}
+
+/**
+ * \brief parse the root node of a XSPF playlist
+ * \param p_demux demuxer instance
+ * \param p_playlist playlist instance
+ * \param p_item current playlist node
+ * \param p_xml_reader xml reader instance
+ * \param psz_element name of element to parse
+ */
+static vlc_bool_t parse_playlist_node COMPLEX_INTERFACE
+{
+ char *psz_name=NULL;
+ char *psz_value=NULL;
+ vlc_bool_t b_version_found = VLC_FALSE;
+ int i_node;
+ xml_elem_hnd_t *p_handler=NULL;
+
+ xml_elem_hnd_t pl_elements[] =
+ { {"title", SIMPLE_CONTENT, {.smpl = set_item_info} },
+ {"creator", SIMPLE_CONTENT, {.smpl = set_item_info} },
+ {"annotation", SIMPLE_CONTENT, {NULL} },
+ {"info", SIMPLE_CONTENT, {NULL} },
+ {"location", SIMPLE_CONTENT, {NULL} },
+ {"identifier", SIMPLE_CONTENT, {NULL} },
+ {"image", SIMPLE_CONTENT, {NULL} },
+ {"date", SIMPLE_CONTENT, {NULL} },
+ {"license", SIMPLE_CONTENT, {NULL} },
+ {"attribution", COMPLEX_CONTENT, {.cmplx = skip_element} },
+ {"link", SIMPLE_CONTENT, {NULL} },
+ {"meta", SIMPLE_CONTENT, {NULL} },
+ {"extension", COMPLEX_CONTENT, {.cmplx = skip_element} },
+ {"trackList", COMPLEX_CONTENT, {.cmplx = parse_tracklist_node} },
+ {NULL, UNKNOWN_CONTENT, {NULL} }
+ };
+
+ /* read all playlist attributes */
+ while ( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
+ {
+ psz_name = xml_ReaderName ( p_xml_reader );
+ psz_value = xml_ReaderValue ( p_xml_reader );
+ if ( !psz_name || !psz_value )
+ {
+ msg_Err( p_demux, "invalid xml stream @ <playlist>" );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ /* attribute: version */
+ if ( !strcmp( psz_name, "version" ) )
+ {
+ b_version_found = VLC_TRUE;
+ if ( strcmp( psz_value, "0" ) && strcmp( psz_value, "1" ) )
+ msg_Warn( p_demux, "unsupported XSPF version" );
+ }
+ /* attribute: xmlns */
+ else if ( !strcmp ( psz_name, "xmlns" ) )
+ ;
+ /* unknown attribute */
+ else
+ msg_Warn( p_demux, "invalid <playlist> attribute:\"%s\"", psz_name);
+
+ FREE_ATT();
+ }
+ /* attribute version is mandatory !!! */
+ if ( !b_version_found )
+ msg_Warn( p_demux, "<playlist> requires \"version\" attribute" );
+
+ /* parse the child elements - we only take care of <trackList> */
+ while ( xml_ReaderRead( p_xml_reader ) == 1 )
+ {
+ i_node = xml_ReaderNodeType( p_xml_reader );
+ switch ( i_node )
+ {
+ case XML_READER_NONE:
+ break;
+ case XML_READER_STARTELEM:
+ /* element start tag */
+ psz_name = xml_ReaderName( p_xml_reader );
+ if ( !psz_name || !*psz_name )
+ {
+ msg_Err( p_demux, "invalid xml stream" );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ /* choose handler */
+ for( p_handler = pl_elements;
+ p_handler->name && strcmp( psz_name, p_handler->name );
+ p_handler++ );
+ if ( !p_handler->name )
+ {
+ msg_Err( p_demux, "unexpected element <%s>", psz_name );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ FREE_NAME();
+ /* complex content is parsed in a separate function */
+ if ( p_handler->type == COMPLEX_CONTENT )
+ {
+ if ( p_handler->pf_handler.cmplx( p_demux,
+ p_playlist,
+ p_item,
+ p_xml_reader,
+ p_handler->name ) )
+ {
+ p_handler = NULL;
+ FREE_ATT();
+ }
+ else
+ {
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ }
+ break;
+
+ case XML_READER_TEXT:
+ /* simple element content */
+ FREE_ATT();
+ psz_value = xml_ReaderValue( p_xml_reader );
+ if ( !psz_value )
+ {
+ msg_Err( p_demux, "invalid xml stream" );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ break;
+
+ case XML_READER_ENDELEM:
+ /* element end tag */
+ psz_name = xml_ReaderName( p_xml_reader );
+ if ( !psz_name )
+ {
+ msg_Err( p_demux, "invalid xml stream" );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ /* leave if the current parent node <playlist> is terminated */
+ if ( !strcmp( psz_name, psz_element ) )
+ {
+ FREE_ATT();
+ return VLC_TRUE;
+ }
+ /* there MUST have been a start tag for that element name */
+ if ( !p_handler || !p_handler->name
+ || strcmp( p_handler->name, psz_name ))
+ {
+ msg_Err( p_demux, "there's no open element left for <%s>",
+ psz_name );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+
+ if ( p_handler->pf_handler.smpl )
+ {
+ p_handler->pf_handler.smpl( p_item, p_handler->name,
+ psz_value );
+ }
+ FREE_ATT();
+ p_handler = NULL;
+ break;
+
+ default:
+ /* unknown/unexpected xml node */
+ msg_Err( p_demux, "unexpected xml node %i", i_node );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ FREE_NAME();
+ }
+ return VLC_FALSE;
+}
+
+/**
+ * \brief parses the tracklist node which only may contain <track>s
+ */
+static vlc_bool_t parse_tracklist_node COMPLEX_INTERFACE
+{
+ char *psz_name=NULL;
+ int i_node;
+ int i_ntracks = 0;
+
+ /* now parse the <track>s */
+ while ( xml_ReaderRead( p_xml_reader ) == 1 )
+ {
+ i_node = xml_ReaderNodeType( p_xml_reader );
+ if ( i_node == XML_READER_STARTELEM )
+ {
+ psz_name = xml_ReaderName( p_xml_reader );
+ if ( !psz_name )
+ {
+ msg_Err( p_demux, "unexpected end of xml data" );
+ FREE_NAME();
+ return VLC_FALSE;
+ }
+ if ( strcmp( psz_name, "track") )
+ {
+ msg_Err( p_demux, "unexpected child of <trackList>: <%s>",
+ psz_name );
+ FREE_NAME();
+ return VLC_FALSE;
+ }
+ FREE_NAME();
+
+ /* parse the track data in a separate function */
+ if ( parse_track_node( p_demux, p_playlist, p_item, p_xml_reader,
+ "track" ) == VLC_TRUE )
+ i_ntracks++;
+ }
+ else if ( i_node == XML_READER_ENDELEM )
+ break;
+ }
+
+ /* the <trackList> has to be terminated */
+ if ( xml_ReaderNodeType( p_xml_reader ) != XML_READER_ENDELEM )
+ {
+ msg_Err( p_demux, "there's a missing </trackList>" );
+ FREE_NAME();
+ return VLC_FALSE;
+ }
+ psz_name = xml_ReaderName( p_xml_reader );
+ if ( !psz_name || strcmp( psz_name, "trackList" ) )
+ {
+ msg_Err( p_demux, "expected: </trackList>, found: </%s>", psz_name );
+ FREE_NAME();
+ return VLC_FALSE;
+ }
+ FREE_NAME();
+
+ msg_Dbg( p_demux, "parsed %i tracks successfully", i_ntracks );
+
+ return VLC_TRUE;
+}
+
+/**
+ * \brief parse one track element
+ * \param COMPLEX_INTERFACE
+ */
+static vlc_bool_t parse_track_node COMPLEX_INTERFACE
+{
+ playlist_item_t *p_new=NULL;
+ int i_node;
+ char *psz_name=NULL;
+ char *psz_value=NULL;
+ xml_elem_hnd_t *p_handler=NULL;
+
+ xml_elem_hnd_t track_elements[] =
+ { {"location", SIMPLE_CONTENT, {NULL} },
+ {"identifier", SIMPLE_CONTENT, {NULL} },
+ {"title", SIMPLE_CONTENT, {.smpl = set_item_info} },
+ {"creator", SIMPLE_CONTENT, {.smpl = set_item_info} },
+ {"annotation", SIMPLE_CONTENT, {NULL} },
+ {"info", SIMPLE_CONTENT, {NULL} },
+ {"image", SIMPLE_CONTENT, {NULL} },
+ {"album", SIMPLE_CONTENT, {.smpl = set_item_info} },
+ {"trackNum", SIMPLE_CONTENT, {.smpl = set_item_info} },
+ {"duration", SIMPLE_CONTENT, {.smpl = set_item_info} },
+ {"link", SIMPLE_CONTENT, {NULL} },
+ {"meta", SIMPLE_CONTENT, {NULL} },
+ {"extension", COMPLEX_CONTENT, {.cmplx = skip_element} },
+ {NULL, UNKNOWN_CONTENT, {NULL} }
+ };
+
+ while ( xml_ReaderRead( p_xml_reader ) == 1 )
+ {
+ i_node = xml_ReaderNodeType( p_xml_reader );
+ switch ( i_node )
+ {
+ case XML_READER_NONE:
+ break;
+
+ case XML_READER_STARTELEM:
+ /* element start tag */
+ psz_name = xml_ReaderName( p_xml_reader );
+ if ( !psz_name || !*psz_name )
+ {
+ msg_Err( p_demux, "invalid xml stream" );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ /* choose handler */
+ for( p_handler = track_elements;
+ p_handler->name && strcmp( psz_name, p_handler->name );
+ p_handler++ );
+ if ( !p_handler->name )
+ {
+ msg_Err( p_demux, "unexpected element <%s>", psz_name );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ FREE_NAME();
+ /* complex content is parsed in a separate function */
+ if ( p_handler->type == COMPLEX_CONTENT )
+ {
+ if ( !p_new )
+ {
+ msg_Err( p_demux,
+ "at <%s> level no new item has been allocated",
+ p_handler->name );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ if ( p_handler->pf_handler.cmplx( p_demux,
+ p_playlist,
+ p_new,
+ p_xml_reader,
+ p_handler->name ) )
+ {
+ p_handler = NULL;
+ FREE_ATT();
+ }
+ else
+ {
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ }
+ break;
+
+ case XML_READER_TEXT:
+ /* simple element content */
+ FREE_ATT();
+ psz_value = xml_ReaderValue( p_xml_reader );
+ if ( !psz_value )
+ {
+ msg_Err( p_demux, "invalid xml stream" );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ break;
+
+ case XML_READER_ENDELEM:
+ /* element end tag */
+ psz_name = xml_ReaderName( p_xml_reader );
+ if ( !psz_name )
+ {
+ msg_Err( p_demux, "invalid xml stream" );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ /* leave if the current parent node <track> is terminated */
+ if ( !strcmp( psz_name, psz_element ) )
+ {
+ FREE_ATT();
+ return VLC_TRUE;
+ }
+ /* there MUST have been a start tag for that element name */
+ if ( !p_handler || !p_handler->name
+ || strcmp( p_handler->name, psz_name ))
+ {
+ msg_Err( p_demux, "there's no open element left for <%s>",
+ psz_name );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+
+ /* special case: location */
+ if ( !strcmp( p_handler->name, "location" ) )
+ {
+ /* there MUST NOT be an item */
+ if ( p_new )
+ {
+ msg_Err( p_demux,
+ "a new item has just been created <%s>",
+ psz_name );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ /* create it now */
+ if ( insert_new_item( p_playlist, p_item,
+ &p_new, psz_value ) )
+ {
+ FREE_ATT();
+ p_handler = NULL;
+ }
+ else
+ {
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ }
+ else
+ {
+ /* there MUST be an item */
+ if ( !p_new )
+ {
+ msg_Err( p_demux,
+ "an item hasn't been created yet <%s>",
+ psz_name );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ if ( p_handler->pf_handler.smpl )
+ {
+ p_handler->pf_handler.smpl( p_new, p_handler->name,
+ psz_value );
+ FREE_ATT();
+ }
+ }
+ FREE_ATT();
+ p_handler = NULL;
+ break;
+
+ default:
+ /* unknown/unexpected xml node */
+ msg_Err( p_demux, "unexpected xml node %i", i_node );
+ FREE_ATT();
+ return VLC_FALSE;
+ }
+ FREE_NAME();
+ }
+ msg_Err( p_demux, "unexpected end of xml data" );
+ FREE_ATT();
+ return VLC_FALSE;
+}
+
+/**
+ * \brief handles the supported <track> sub-elements
+ */
+static vlc_bool_t set_item_info SIMPLE_INTERFACE
+{
+ /* exit if setting is impossible */
+ if ( !psz_name || !psz_value || !p_item )
+ return VLC_FALSE;
+
+ /* re-convert xml special characters inside psz_value */
+ resolve_xml_special_chars ( psz_value );
+
+ /* handle each info element in a separate "if" clause */
+ if ( !strcmp( psz_name, "title" ) )
+ {
+ if ( playlist_ItemSetName ( p_item, (char *)psz_value ) == VLC_SUCCESS )
+ return VLC_TRUE;
+ return VLC_FALSE;
+ }
+ else if ( !strcmp( psz_name, "creator" ) )
+ {
+ if ( vlc_input_item_AddInfo( &(p_item->input),
+ _(VLC_META_INFO_CAT), _(VLC_META_ARTIST),
+ "%s", psz_value ) == VLC_SUCCESS )
+ return VLC_TRUE;
+ return VLC_FALSE;
+
+ }
+ else if ( !strcmp( psz_name, "album" ) )
+ {
+ if ( vlc_input_item_AddInfo( &(p_item->input),
+ _(VLC_META_INFO_CAT),
+ _(VLC_META_COLLECTION),
+ "%s", psz_value ) == VLC_SUCCESS )
+ return VLC_TRUE;
+ return VLC_FALSE;
+
+ } else if ( !strcmp( psz_name, "trackNum" ) )
+ {
+ long i_num = atol( psz_value );
+ if ( i_num > 0
+ && vlc_input_item_AddInfo( &(p_item->input),
+ _(VLC_META_INFO_CAT),
+ _(VLC_META_SEQ_NUM),
+ "%s", psz_value ) == VLC_SUCCESS )
+ return VLC_TRUE;
+ return VLC_FALSE;
+
+ } else if ( !strcmp( psz_name, "duration" ) )
+ {
+ long i_num = atol( psz_value );
+ if ( i_num > 0
+ && playlist_ItemSetDuration( p_item, i_num*1000 ) == VLC_SUCCESS )
+ return VLC_TRUE;
+ return VLC_FALSE;
+
+ }
+
+ return VLC_TRUE;
+}
+
+/**
+ * \brief skips complex element content that we can't manage
+ */
+static vlc_bool_t skip_element COMPLEX_INTERFACE
+{
+ char *psz_endname;
+
+ while ( xml_ReaderRead( p_xml_reader ) == 1 )
+ {
+ if ( xml_ReaderNodeType( p_xml_reader ) == XML_READER_ENDELEM )
+ {
+ psz_endname = xml_ReaderName( p_xml_reader );
+ if ( !psz_endname )
+ return VLC_FALSE;
+ if ( !strcmp( psz_element, psz_endname ) )
+ {
+ free( psz_endname );
+ return VLC_TRUE;
+ }
+ else
+ free( psz_endname );
+ }
+ }
+ return VLC_FALSE;
+}
+
+/**
+ * \brief creates a new playlist item from the given mrl
+ */
+static vlc_bool_t insert_new_item( playlist_t *p_pl, playlist_item_t *p_cur,
+ playlist_item_t **pp_new, char *psz_location )
+{
+ char *psz_uri=NULL;
+ psz_uri = decode_encoded_URI_duplicate( psz_location );
+
+ if ( psz_uri )
+ {
+ *pp_new = playlist_ItemNew( p_pl, psz_uri, NULL );
+ free( psz_uri );
+ psz_uri = NULL;
+ }
+
+ if ( !*pp_new )
+ return VLC_FALSE;
+
+ playlist_NodeAddItem( p_pl, *pp_new, p_cur->pp_parents[0]->i_view,
+ p_cur, PLAYLIST_APPEND, PLAYLIST_END );
+
+ playlist_CopyParents( p_cur, *pp_new );
+
+ vlc_input_item_CopyOptions( &p_cur->input, &((*pp_new)->input) );
+
+ return VLC_TRUE;
+}
--- /dev/null
+/*****************************************************************************
+ * Copyright (C) 2006 Daniel Stränger <vlc at schmaller dot de>
+ *
+ * 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.
+ *******************************************************************************/
+/**
+ * \file modules/demux/playlist/xspf.h
+ * \brief XSPF playlist import: prototypes, datatypes, defines
+ */
+
+/* defines */
+#define FREE_NAME() if (psz_name) {free(psz_name);psz_name=NULL;}
+#define FREE_VALUE() if (psz_value) {free(psz_value);psz_value=NULL;}
+#define FREE_ATT() FREE_NAME();FREE_VALUE()
+
+#define UNKNOWN_CONTENT 0
+#define SIMPLE_CONTENT 1
+#define COMPLEX_CONTENT 2
+
+#define SIMPLE_INTERFACE (playlist_item_t *p_item,\
+ const char *psz_name,\
+ char *psz_value)
+#define COMPLEX_INTERFACE (demux_t *p_demux,\
+ playlist_t *p_playlist,\
+ playlist_item_t *p_item,\
+ xml_reader_t *p_xml_reader,\
+ const char *psz_element)
+
+/* prototypes */
+int xspf_import_Demux( demux_t *);
+int xspf_import_Control( demux_t *, int, va_list );
+
+static vlc_bool_t parse_playlist_node COMPLEX_INTERFACE;
+static vlc_bool_t parse_tracklist_node COMPLEX_INTERFACE;
+static vlc_bool_t parse_track_node COMPLEX_INTERFACE;
+static vlc_bool_t set_item_info SIMPLE_INTERFACE;
+static vlc_bool_t skip_element COMPLEX_INTERFACE;
+static vlc_bool_t insert_new_item( playlist_t *, playlist_item_t *, playlist_item_t **, char *);
+
+/* datatypes */
+typedef struct
+{
+ const char *name;
+ int type;
+ union
+ {
+ vlc_bool_t (*smpl) SIMPLE_INTERFACE;
+ vlc_bool_t (*cmplx) COMPLEX_INTERFACE;
+ } pf_handler;
+} xml_elem_hnd_t;
void Dialogs::showPlaylistLoad()
{
showFileGeneric( _("Open playlist"),
- _("All playlists|*.pls;*.m3u;*.asx;*.b4s|M3U files|*.m3u"),
+ _("All playlists|*.pls;*.m3u;*.asx;*.b4s;*.xspf|"
+ "M3U files|*.m3u|"
+ "XSPF playlist|*.xspf"),
showPlaylistLoadCB, kOPEN );
}
void Dialogs::showPlaylistSave()
{
- showFileGeneric( _("Save playlist"), _("M3U file|*.m3u"),
+ showFileGeneric( _("Save playlist"), _("M3U file|*.m3u|XSPF playlist|*.xspf"),
showPlaylistSaveCB, kSAVE );
}
char *psz_desc;
char *psz_filter;
char *psz_module;
- } formats[] = {{ _("M3U file"), "*.m3u", "export-m3u" }};
+ } formats[] = {{ _("M3U file"), "*.m3u", "export-m3u" },
+ { _("XSPF playlist"), "*.xspf", "export-xspf"}
+ };
wxString filter = wxT("");
void Playlist::OnOpen( wxCommandEvent& WXUNUSED(event) )
{
wxFileDialog dialog( this, wxU(_("Open playlist")), wxT(""), wxT(""),
- wxT("All playlists|*.pls;*.m3u;*.asx;*.b4s|M3U files|*.m3u"), wxOPEN );
+ wxT("All playlists|*.pls;*.m3u;*.asx;*.b4s;*.xspf|XSPF playlist|*.xspf|M3U files|*.m3u"), wxOPEN );
if( dialog.ShowModal() == wxID_OK )
{
-SOURCES_export = export.c \
- m3u.c \
- old.c
+SOURCES_export = \
+ export.c \
+ m3u.c \
+ xspf.c \
+ xspf.h \
+ old.c \
+ $(NULL)
***************************************************************************/
int Export_M3U ( vlc_object_t *p_intf );
int Export_Old ( vlc_object_t *p_intf );
-
+int E_(xspf_export_playlist)( vlc_object_t *p_intf );
/*****************************************************************************
* Module descriptor
set_capability( "playlist export" , 0);
set_callbacks( Export_Old , NULL );
+ add_submodule();
+ set_description( _("XSPF playlist export") );
+ add_shortcut( "export-xspf" );
+ set_capability( "playlist export" , 0);
+ set_callbacks( E_(xspf_export_playlist) , NULL );
+
vlc_module_end();
--- /dev/null
+/******************************************************************************
+ * Copyright (C) 2006 Daniel Stränger <vlc at schmaller dot de>
+ *
+ * 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.
+ *******************************************************************************/
+
+/**
+ * \file modules/misc/playlist/xspf.c
+ * \brief XSPF playlist export functions
+ */
+#include <stdio.h>
+#include <vlc/vlc.h>
+#include <vlc/intf.h>
+#include "vlc_meta.h"
+#include "vlc_strings.h"
+#include "xspf.h"
+
+/**
+ * \brief Prints the XSPF header to file, writes each item by xspf_export_item()
+ * and closes the open xml elements
+ * \param p_this the VLC playlist object
+ * \return VLC_SUCCESS if some memory is available, otherwise VLC_ENONMEM
+ */
+int E_(xspf_export_playlist)( vlc_object_t *p_this )
+{
+ const playlist_t *p_playlist = (playlist_t *)p_this;
+ const playlist_export_t *p_export =
+ (playlist_export_t *)p_playlist->p_private;
+ int i;
+ char *psz;
+ char *psz_temp;
+ playlist_item_t **pp_items = NULL;
+ int i_size;
+ playlist_item_t *p_node;
+
+ /* write XSPF XML header - since we don't use <extension>,
+ * we get by with version 0 */
+ fprintf( p_export->p_file, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
+ fprintf( p_export->p_file,
+ "<playlist version=\"0\" xmlns=\"http://xspf.org/ns/0/\">\n" );
+
+ /* save tho whole playlist or only the current node */
+#define p_item p_playlist->status.p_item
+ if ( p_item )
+ {
+ for (i = 0; i < p_item->i_parents; i++ )
+ {
+ if ( p_item->pp_parents[i]->p_parent->input.i_type
+ == ITEM_TYPE_PLAYLIST )
+ {
+ /* set the current node and its children */
+ p_node = p_item->pp_parents[i]->p_parent;
+ pp_items = p_node->pp_children;
+ i_size = p_node->i_children;
+#undef p_item
+
+ /* save name of the playlist node */
+ psz_temp = convert_xml_special_chars( p_node->input.psz_name );
+ if ( *psz_temp )
+ fprintf( p_export->p_file, "\t<title>%s</title>\n",
+ psz_temp );
+ free( psz_temp );
+
+ /* save the creator of the playlist node */
+ psz = vlc_input_item_GetInfo( &p_node->input,
+ _(VLC_META_INFO_CAT),
+ _(VLC_META_ARTIST) );
+ if ( psz && !*psz )
+ {
+ free ( psz );
+ psz = NULL;
+ }
+
+ if ( !psz )
+ psz = vlc_input_item_GetInfo( &p_node->input,
+ _(VLC_META_INFO_CAT),
+ _(VLC_META_AUTHOR) );
+
+ psz_temp = convert_xml_special_chars( psz );
+
+ if ( psz ) free( psz );
+ if ( *psz_temp )
+ fprintf( p_export->p_file, "\t<creator>%s</creator>\n",
+ psz_temp );
+ free( psz_temp );
+
+ /* save location of the playlist node */
+ psz = assertUTF8URI( p_export->psz_filename );
+ if ( psz && *psz )
+ {
+ fprintf( p_export->p_file, "\t<location>%s</location>\n",
+ psz );
+ free( psz );
+ }
+ break;
+ }
+ }
+ }
+
+ /* prepare all the playlist children for export */
+ if ( !pp_items )
+ {
+ pp_items = p_playlist->pp_items;
+ i_size = p_playlist->i_size;
+ }
+
+ /* export all items */
+ fprintf( p_export->p_file, "\t<trackList>\n" );
+ for ( i = 0; i < i_size; i++ )
+ {
+ xspf_export_item( pp_items[i], p_export->p_file );
+ }
+
+ /* close the header elements */
+ fprintf( p_export->p_file, "\t</trackList>\n" );
+ fprintf( p_export->p_file, "</playlist>\n" );
+
+ return VLC_SUCCESS;
+}
+
+/**
+ * \brief exports one item to file or traverse if item is a node
+ * \param p_item playlist item to export
+ * \param p_file file to write xml-converted item to
+ */
+static void xspf_export_item( playlist_item_t *p_item, FILE *p_file )
+{
+ int i; /**< iterator for all children if the current item is a node */
+ char *psz;
+ char *psz_temp;
+
+ if ( !p_item )
+ return;
+
+ /** \todo only "flat" playlists supported at this time.
+ * extend to save the tree structure.
+ */
+ /* if we get a node here, we must traverse it */
+ if ( p_item->i_children > 0 )
+ {
+ for ( i = 0; i < p_item->i_children; i++ )
+ {
+ xspf_export_item( p_item->pp_children[i], p_file );
+ }
+ return;
+ }
+
+ /* leaves can be written directly */
+ fprintf( p_file, "\t\t<track>\n" );
+
+ /* -> the location */
+ if ( p_item->input.psz_uri && *p_item->input.psz_uri )
+ {
+ psz = assertUTF8URI( p_item->input.psz_uri );
+ fprintf( p_file, "\t\t\t<location>%s</location>\n", psz );
+ free( psz );
+ }
+
+ /* -> the name/title (only if different from uri)*/
+ if ( p_item->input.psz_name &&
+ p_item->input.psz_uri &&
+ strcmp( p_item->input.psz_uri, p_item->input.psz_name ) )
+ {
+ psz_temp = convert_xml_special_chars( p_item->input.psz_name );
+ if ( *psz_temp )
+ fprintf( p_file, "\t\t\t<title>%s</title>\n", psz_temp );
+ free( psz_temp );
+ }
+
+ /* -> the artist/creator */
+ psz = vlc_input_item_GetInfo( &p_item->input,
+ _(VLC_META_INFO_CAT),
+ _(VLC_META_ARTIST) );
+ if ( psz && !*psz )
+ {
+ free ( psz );
+ psz = NULL;
+ }
+ if ( !psz )
+ psz = vlc_input_item_GetInfo( &p_item->input,
+ _(VLC_META_INFO_CAT),
+ _(VLC_META_AUTHOR) );
+ psz_temp = convert_xml_special_chars( psz );
+ if ( psz ) free( psz );
+ if ( *psz_temp )
+ fprintf( p_file, "\t\t\t<creator>%s</creator>\n", psz_temp );
+ free( psz_temp );
+
+ /* -> the album */
+ psz = vlc_input_item_GetInfo( &p_item->input,
+ _(VLC_META_INFO_CAT),
+ _(VLC_META_COLLECTION) );
+ psz_temp = convert_xml_special_chars( psz );
+ if ( psz ) free( psz );
+ if ( *psz_temp )
+ fprintf( p_file, "\t\t\t<album>%s</album>\n", psz_temp );
+ free( psz_temp );
+
+ /* -> the track number */
+ psz = vlc_input_item_GetInfo( &p_item->input,
+ _(VLC_META_INFO_CAT),
+ _(VLC_META_SEQ_NUM) );
+ if ( psz )
+ {
+ if ( *psz )
+ fprintf( p_file, "\t\t\t<trackNum>%i</trackNum>\n", atoi( psz ) );
+ free( psz );
+ }
+
+ /* -> the duration */
+ if ( p_item->input.i_duration > 0 )
+ {
+ fprintf( p_file, "\t\t\t<duration>%ld</duration>\n",
+ (long)(p_item->input.i_duration / 1000) );
+ }
+
+ fprintf( p_file, "\t\t</track>\n" );
+
+ return;
+}
+
+/**
+ * \param psz_name the location of the media ressource (e.g. local file,
+ * device, network stream, etc.)
+ * \return a new char buffer which asserts that the location is valid UTF-8
+ * and a valid URI
+ * \note the returned buffer must be freed, when it isn't used anymore
+ */
+static char *assertUTF8URI( char *psz_name )
+{
+ char *psz_ret = NULL; /**< the new result buffer to return */
+ char *psz_s = NULL, *psz_d = NULL; /**< src & dest pointers for URI conversion */
+ vlc_bool_t b_name_is_uri = VLC_FALSE;
+
+ if ( !psz_name || !*psz_name )
+ return NULL;
+
+ /* check that string is valid UTF-8 */
+ /* XXX: Why do we even need to do that ? (all strings in core are UTF-8 encoded */
+ if( !( psz_s = EnsureUTF8( psz_name ) ) )
+ return NULL;
+
+ /* max. 3x for URI conversion (percent escaping) and
+ 8 bytes for "file://" and NULL-termination */
+ psz_ret = (char *)malloc( sizeof(char)*strlen(psz_name)*6*3+8 );
+ if ( !psz_ret )
+ return NULL;
+
+ /** \todo check for a valid scheme part preceding the colon */
+ if ( strchr( psz_s, ':' ) )
+ {
+ psz_d = psz_ret;
+ b_name_is_uri = VLC_TRUE;
+ }
+ /* assume "file" scheme if no scheme-part is included */
+ else
+ {
+ strcpy( psz_ret, "file://" );
+ psz_d = psz_ret + 7;
+ }
+
+ while ( *psz_s )
+ {
+ /* percent-encode all non-ASCII and the XML special characters and the percent sign itself */
+ if ( *psz_s & B10000000 ||
+ *psz_s == '<' ||
+ *psz_s == '>' ||
+ *psz_s == '&' ||
+ *psz_s == ' ' ||
+ ( *psz_s == '%' && !b_name_is_uri ) )
+ {
+ *psz_d++ = '%';
+ *psz_d++ = hexchars[(*psz_s >> 4) & B00001111];
+ *psz_d++ = hexchars[*psz_s & B00001111];
+ } else
+ *psz_d++ = *psz_s;
+
+ psz_s++;
+ }
+ *psz_d = '\0';
+
+ return (char *)realloc( psz_ret, sizeof(char)*strlen( psz_ret ) + 1 );
+}
--- /dev/null
+/*****************************************************************************
+ * Copyright (C) 2006 Daniel Stränger <vlc at schmaller dot de>
+ *
+ * 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.
+ *******************************************************************************/
+/**
+ * \file modules/misc/playlist/xspf.h
+ * \brief XSPF playlist export module header file
+ */
+
+/* defs */
+#define B10000000 0x80
+#define B01000000 0x40
+#define B11000000 0xc0
+#define B00001111 0x0f
+
+#define XSPF_MAX_CONTENT 2000
+
+/* constants */
+const char hexchars[16] = "0123456789ABCDEF";
+
+/* prototypes */
+int E_(xspf_export_playlist)( vlc_object_t * );
+static void xspf_export_item( playlist_item_t *, FILE * );
+static char *assertUTF8URI( char * );
* $Id$
*
* Authors: RĂ©mi Denis-Courmont <rem # videolan.org> (original plugin)
- * Christian Henz <henz # c-lab.de>
- *
+ * Christian Henz <henz # c-lab.de>
+ *
* UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink
*
* This program is free software; you can redistribute it and/or modify
#include <vlc/vlc.h>
#include <vlc/intf.h>
+#include "vlc_strings.h"
+
// VLC handle
const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
-// Classes
+// Classes
class MediaServer;
class MediaServerList;
// Cookie that is passed to the callback
-typedef struct
+typedef struct
{
services_discovery_t* serviceDiscovery;
UpnpClient_Handle clientHandle;
- MediaServerList* serverList;
+ MediaServerList* serverList;
} Cookie;
// Class definitions...
-class Lockable
-{
+class Lockable
+{
public:
- Lockable( Cookie* c )
+ Lockable( Cookie* c )
{
- vlc_mutex_init( c->serviceDiscovery, &_mutex );
+ vlc_mutex_init( c->serviceDiscovery, &_mutex );
}
- ~Lockable()
+ ~Lockable()
{
- vlc_mutex_destroy( &_mutex );
+ vlc_mutex_destroy( &_mutex );
}
void lock() { vlc_mutex_lock( &_mutex ); }
void unlock() { vlc_mutex_unlock( &_mutex ); }
private:
-
+
vlc_mutex_t _mutex;
};
-class Locker
+class Locker
{
public:
- Locker( Lockable* l )
+ Locker( Lockable* l )
{
- _lockable = l;
- _lockable->lock();
+ _lockable = l;
+ _lockable->lock();
}
- ~Locker()
+ ~Locker()
{
- _lockable->unlock();
+ _lockable->unlock();
}
private:
public:
static void parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie );
-
+
MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie );
~MediaServer();
-
+
const char* getUDN() const;
const char* getFriendlyName() const;
IXML_Document* _browseAction( const char*, const char*, const char*, const char*, const char*, const char* );
Cookie* _cookie;
-
+
Container* _contents;
playlist_item_t* _playlistNode;
std::string _UDN;
std::string _friendlyName;
-
+
std::string _contentDirectoryEventURL;
std::string _contentDirectoryControlURL;
};
-class Item
+class Item
{
public:
private:
playlist_item_t* _playlistNode;
-
+
Container* _parent;
std::string _objectID;
std::string _title;
};
-class Container
+class Container
{
public:
static Lockable* CallbackLock;
static int Callback( Upnp_EventType eventType, void* event, void* pCookie );
-char* xml_makeSpecialChars( const char* in );
const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName );
IXML_Document* parseBrowseResult( IXML_Document* doc );
{
services_discovery_t *p_sd = ( services_discovery_t* )p_this;
services_discovery_sys_t *p_sys = ( services_discovery_sys_t * )
- malloc( sizeof( services_discovery_sys_t ) );
-
+ malloc( sizeof( services_discovery_sys_t ) );
+
playlist_view_t *p_view;
vlc_value_t val;
/* Create our playlist node */
p_sys->p_playlist = ( playlist_t * )vlc_object_find( p_sd,
- VLC_OBJECT_PLAYLIST,
- FIND_ANYWHERE );
+ VLC_OBJECT_PLAYLIST,
+ FIND_ANYWHERE );
if( !p_sys->p_playlist )
{
- msg_Warn( p_sd, "unable to find playlist, cancelling UPnP listening" );
- return VLC_EGENERIC;
+ msg_Warn( p_sd, "unable to find playlist, cancelling UPnP listening" );
+ return VLC_EGENERIC;
}
p_view = playlist_ViewFind( p_sys->p_playlist, VIEW_CATEGORY );
if( p_sys->p_playlist )
{
- playlist_NodeDelete( p_sys->p_playlist, p_sys->p_node, VLC_TRUE,
- VLC_TRUE );
- vlc_object_release( p_sys->p_playlist );
+ playlist_NodeDelete( p_sys->p_playlist, p_sys->p_node, VLC_TRUE,
+ VLC_TRUE );
+ vlc_object_release( p_sys->p_playlist );
}
free( p_sys );
static void Run( services_discovery_t* p_sd )
{
int res;
-
+
res = UpnpInit( 0, 0 );
- if( res != UPNP_E_SUCCESS )
+ if( res != UPNP_E_SUCCESS )
{
- msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
- return;
+ msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
+ return;
}
Cookie cookie;
CallbackLock = new Lockable( &cookie );
res = UpnpRegisterClient( Callback, &cookie, &cookie.clientHandle );
- if( res != UPNP_E_SUCCESS )
+ if( res != UPNP_E_SUCCESS )
{
- msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
- goto shutDown;
+ msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
+ goto shutDown;
}
res = UpnpSearchAsync( cookie.clientHandle, 5, MEDIA_SERVER_DEVICE_TYPE, &cookie );
- if( res != UPNP_E_SUCCESS )
+ if( res != UPNP_E_SUCCESS )
{
- msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
- goto shutDown;
+ msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
+ goto shutDown;
}
-
+
msg_Dbg( p_sd, "UPnP discovery started" );
- while( !p_sd->b_die )
+ while( !p_sd->b_die )
{
- msleep( 500 );
+ msleep( 500 );
}
msg_Dbg( p_sd, "UPnP discovery stopped" );
// XML utility functions:
// Returns the value of a child element, or 0 on error
-const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName )
+const char* xml_getChildElementValue( IXML_Element* parent, const char* tagName )
{
if ( !parent ) return 0;
if ( !tagName ) return 0;
return ixmlNode_getNodeValue( textNode );
}
-
-// Replaces "<" with "<" etc.
-// Returns a newly created string that has to be freed by the caller.
-// Returns 0 on error ( out of mem )
-// \TODO: Probably not very robust!!!
-char* xml_makeSpecialChars( const char* in )
-{
- if ( !in ) return 0;
-
- char* result = ( char* )malloc( strlen( in ) + 1 );
- if ( !result ) return 0;
-
- char* out = result;
-
- while( *in )
- {
- if ( strncmp( "&", in, 5 ) == 0 )
- {
- *out = '&';
-
- in += 5;
- out++;
- }
- else if ( strncmp( """, in, 6 ) == 0 )
- {
- *out = '"';
-
- in += 6;
- out++;
- }
- else if ( strncmp( ">", in, 4 ) == 0 )
- {
- *out = '>';
-
- in += 4;
- out++;
- }
- else if ( strncmp( "<", in, 4 ) == 0 )
- {
- *out = '<';
-
- in += 4;
- out++;
- }
- else
- {
- *out = *in;
-
- in++;
- out++;
- }
- }
-
- *out = '\0';
- return result;
-}
-
-
// Extracts the result document from a SOAP response
-IXML_Document* parseBrowseResult( IXML_Document* doc )
+IXML_Document* parseBrowseResult( IXML_Document* doc )
{
if ( !doc ) return 0;
-
+
IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc, "Result" );
if ( !resultList ) return 0;
-
+
IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 );
ixmlNodeList_free( resultList );
if ( !textNode ) return 0;
const char* resultString = ixmlNode_getNodeValue( textNode );
- char* resultXML = xml_makeSpecialChars( resultString );
+ char* resultXML = convert_xml_special_chars( resultString );
IXML_Document* browseDoc = ixmlParseBuffer( resultXML );
// Handles all UPnP events
-static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
+static int Callback( Upnp_EventType eventType, void* event, void* pCookie )
{
Locker locker( CallbackLock );
Cookie* cookie = ( Cookie* )pCookie;
switch( eventType ) {
-
+
case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
case UPNP_DISCOVERY_SEARCH_RESULT:
- {
- struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
-
- IXML_Document *descriptionDoc = 0;
-
- int res;
- res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc );
- if ( res != UPNP_E_SUCCESS )
- {
- msg_Dbg( cookie->serviceDiscovery, "%s:%d: Could not download device description!", __FILE__, __LINE__ );
- return res;
- }
-
- MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie );
-
- ixmlDocument_free( descriptionDoc );
- }
- break;
-
+ {
+ struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
+
+ IXML_Document *descriptionDoc = 0;
+
+ int res;
+ res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc );
+ if ( res != UPNP_E_SUCCESS )
+ {
+ msg_Dbg( cookie->serviceDiscovery, "%s:%d: Could not download device description!", __FILE__, __LINE__ );
+ return res;
+ }
+
+ MediaServer::parseDeviceDescription( descriptionDoc, discovery->Location, cookie );
+
+ ixmlDocument_free( descriptionDoc );
+ }
+ break;
+
case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
- {
- struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
+ {
+ struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
- cookie->serverList->removeServer( discovery->DeviceId );
- }
- break;
+ cookie->serverList->removeServer( discovery->DeviceId );
+ }
+ break;
case UPNP_EVENT_RECEIVED:
- {
- Upnp_Event* e = ( Upnp_Event* )event;
+ {
+ Upnp_Event* e = ( Upnp_Event* )event;
- MediaServer* server = cookie->serverList->getServerBySID( e->Sid );
- if ( server ) server->fetchContents();
- }
- break;
+ MediaServer* server = cookie->serverList->getServerBySID( e->Sid );
+ if ( server ) server->fetchContents();
+ }
+ break;
case UPNP_EVENT_AUTORENEWAL_FAILED:
case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
- {
- // Re-subscribe...
+ {
+ // Re-subscribe...
- Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
+ Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
+
+ MediaServer* server = cookie->serverList->getServerBySID( s->Sid );
+ if ( server ) server->subscribeToContentDirectory();
+ }
+ break;
- MediaServer* server = cookie->serverList->getServerBySID( s->Sid );
- if ( server ) server->subscribeToContentDirectory();
- }
- break;
-
default:
- msg_Dbg( cookie->serviceDiscovery, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__, __LINE__, eventType );
- break;
+ msg_Dbg( cookie->serviceDiscovery, "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )", __FILE__, __LINE__, eventType );
+ break;
}
return UPNP_E_SUCCESS;
// MediaServer...
-void MediaServer::parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie )
+void MediaServer::parseDeviceDescription( IXML_Document* doc, const char* location, Cookie* cookie )
{
if ( !doc ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
if ( !location ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: NULL", __FILE__, __LINE__ ); return; }
-
+
const char* baseURL = location;
// Try to extract baseURL
IXML_NodeList* urlList = ixmlDocument_getElementsByTagName( doc, "baseURL" );
- if ( urlList )
- {
- if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
- {
- IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
- if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
- }
-
- ixmlNodeList_free( urlList );
+ if ( urlList )
+ {
+ if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
+ {
+ IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
+ if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
+ }
+
+ ixmlNodeList_free( urlList );
}
-
+
// Get devices
IXML_NodeList* deviceList = ixmlDocument_getElementsByTagName( doc, "device" );
if ( deviceList )
{
-
- for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
- {
- IXML_Element* deviceElement = ( IXML_Element* )ixmlNodeList_item( deviceList, i );
-
- const char* deviceType = xml_getChildElementValue( deviceElement, "deviceType" );
- if ( !deviceType ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no deviceType!", __FILE__, __LINE__ ); continue; }
- if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 ) continue;
-
- const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
- if ( !UDN ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no UDN!", __FILE__, __LINE__ ); continue; }
- if ( cookie->serverList->getServer( UDN ) != 0 ) continue;
-
- const char* friendlyName = xml_getChildElementValue( deviceElement, "friendlyName" );
- if ( !friendlyName ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no friendlyName!", __FILE__, __LINE__ ); continue; }
-
- MediaServer* server = new MediaServer( UDN, friendlyName, cookie );
- if ( !cookie->serverList->addServer( server ) ) {
-
- delete server;
- server = 0;
- continue;
- }
-
- // Check for ContentDirectory service...
-
- IXML_NodeList* serviceList = ixmlElement_getElementsByTagName( deviceElement, "service" );
- if ( serviceList )
- {
- for ( unsigned int j = 0; j < ixmlNodeList_length( serviceList ); j++ )
- {
- IXML_Element* serviceElement = ( IXML_Element* )ixmlNodeList_item( serviceList, j );
-
- const char* serviceType = xml_getChildElementValue( serviceElement, "serviceType" );
- if ( !serviceType ) continue;
- if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE, serviceType ) != 0 ) continue;
-
- const char* eventSubURL = xml_getChildElementValue( serviceElement, "eventSubURL" );
- if ( !eventSubURL ) continue;
-
- const char* controlURL = xml_getChildElementValue( serviceElement, "controlURL" );
- if ( !controlURL ) continue;
-
- // Try to subscribe to ContentDirectory service
-
- char* url = ( char* )malloc( strlen( baseURL ) + strlen( eventSubURL ) + 1 );
- if ( url )
- {
- char* s1 = strdup( baseURL );
- char* s2 = strdup( eventSubURL );
-
- if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
- {
- // msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url );
-
- server->setContentDirectoryEventURL( url );
- server->subscribeToContentDirectory();
- }
-
- free( s1 );
- free( s2 );
- free( url );
- }
-
- // Try to browse content directory...
-
- url = ( char* )malloc( strlen( baseURL ) + strlen( controlURL ) + 1 );
- if ( url )
- {
- char* s1 = strdup( baseURL );
- char* s2 = strdup( controlURL );
-
- if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
- {
- // msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url );
-
- server->setContentDirectoryControlURL( url );
- server->fetchContents();
- }
-
- free( s1 );
- free( s2 );
- free( url );
- }
- }
-
- ixmlNodeList_free( serviceList );
- }
- }
-
- ixmlNodeList_free( deviceList );
+
+ for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
+ {
+ IXML_Element* deviceElement = ( IXML_Element* )ixmlNodeList_item( deviceList, i );
+
+ const char* deviceType = xml_getChildElementValue( deviceElement, "deviceType" );
+ if ( !deviceType ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no deviceType!", __FILE__, __LINE__ ); continue; }
+ if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 ) continue;
+
+ const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
+ if ( !UDN ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no UDN!", __FILE__, __LINE__ ); continue; }
+ if ( cookie->serverList->getServer( UDN ) != 0 ) continue;
+
+ const char* friendlyName = xml_getChildElementValue( deviceElement, "friendlyName" );
+ if ( !friendlyName ) { msg_Dbg( cookie->serviceDiscovery, "%s:%d: no friendlyName!", __FILE__, __LINE__ ); continue; }
+
+ MediaServer* server = new MediaServer( UDN, friendlyName, cookie );
+ if ( !cookie->serverList->addServer( server ) ) {
+
+ delete server;
+ server = 0;
+ continue;
+ }
+
+ // Check for ContentDirectory service...
+
+ IXML_NodeList* serviceList = ixmlElement_getElementsByTagName( deviceElement, "service" );
+ if ( serviceList )
+ {
+ for ( unsigned int j = 0; j < ixmlNodeList_length( serviceList ); j++ )
+ {
+ IXML_Element* serviceElement = ( IXML_Element* )ixmlNodeList_item( serviceList, j );
+
+ const char* serviceType = xml_getChildElementValue( serviceElement, "serviceType" );
+ if ( !serviceType ) continue;
+ if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE, serviceType ) != 0 ) continue;
+
+ const char* eventSubURL = xml_getChildElementValue( serviceElement, "eventSubURL" );
+ if ( !eventSubURL ) continue;
+
+ const char* controlURL = xml_getChildElementValue( serviceElement, "controlURL" );
+ if ( !controlURL ) continue;
+
+ // Try to subscribe to ContentDirectory service
+
+ char* url = ( char* )malloc( strlen( baseURL ) + strlen( eventSubURL ) + 1 );
+ if ( url )
+ {
+ char* s1 = strdup( baseURL );
+ char* s2 = strdup( eventSubURL );
+
+ if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
+ {
+ // msg_Dbg( cookie->serviceDiscovery, "CDS EVENT URL: %s", url );
+
+ server->setContentDirectoryEventURL( url );
+ server->subscribeToContentDirectory();
+ }
+
+ free( s1 );
+ free( s2 );
+ free( url );
+ }
+
+ // Try to browse content directory...
+
+ url = ( char* )malloc( strlen( baseURL ) + strlen( controlURL ) + 1 );
+ if ( url )
+ {
+ char* s1 = strdup( baseURL );
+ char* s2 = strdup( controlURL );
+
+ if ( UpnpResolveURL( s1, s2, url ) == UPNP_E_SUCCESS )
+ {
+ // msg_Dbg( cookie->serviceDiscovery, "CDS CTRL URL: %s", url );
+
+ server->setContentDirectoryControlURL( url );
+ server->fetchContents();
+ }
+
+ free( s1 );
+ free( s2 );
+ free( url );
+ }
+ }
+
+ ixmlNodeList_free( serviceList );
+ }
+ }
+
+ ixmlNodeList_free( deviceList );
}
}
-MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie )
+MediaServer::MediaServer( const char* UDN, const char* friendlyName, Cookie* cookie )
{
_cookie = cookie;
_playlistNode = 0;
}
-MediaServer::~MediaServer()
-{
- if ( _contents )
+MediaServer::~MediaServer()
+{
+ if ( _contents )
{
- playlist_NodeDelete( _cookie->serviceDiscovery->p_sys->p_playlist,
- _playlistNode,
- true,
- true );
+ playlist_NodeDelete( _cookie->serviceDiscovery->p_sys->p_playlist,
+ _playlistNode,
+ true,
+ true );
}
delete _contents;
return s;
}
-void MediaServer::setContentDirectoryEventURL( const char* url )
+void MediaServer::setContentDirectoryEventURL( const char* url )
{
_contentDirectoryEventURL = url;
}
return s;
}
-void MediaServer::setContentDirectoryControlURL( const char* url )
+void MediaServer::setContentDirectoryControlURL( const char* url )
{
_contentDirectoryControlURL = url;
}
return _contentDirectoryControlURL.c_str();
}
-void MediaServer::subscribeToContentDirectory()
+void MediaServer::subscribeToContentDirectory()
{
const char* url = getContentDirectoryEventURL();
- if ( !url || strcmp( url, "" ) == 0 )
- {
- msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" );
- return;
+ if ( !url || strcmp( url, "" ) == 0 )
+ {
+ msg_Dbg( _cookie->serviceDiscovery, "No subscription url set!" );
+ return;
}
int timeOut = 1810;
Upnp_SID sid;
-
+
int res = UpnpSubscribe( _cookie->clientHandle, url, &timeOut, sid );
- if ( res == UPNP_E_SUCCESS )
+ if ( res == UPNP_E_SUCCESS )
{
- _subscriptionTimeOut = timeOut;
- memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
- }
- else
+ _subscriptionTimeOut = timeOut;
+ memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
+ }
+ else
{
- msg_Dbg( _cookie->serviceDiscovery, "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__, getFriendlyName(), UpnpGetErrorMessage( res ) );
+ msg_Dbg( _cookie->serviceDiscovery, "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__, getFriendlyName(), UpnpGetErrorMessage( res ) );
}
}
-IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pBrowseFlag, const char* pFilter,
- const char* pStartingIndex, const char* pRequestedCount, const char* pSortCriteria )
+IXML_Document* MediaServer::_browseAction( const char* pObjectID, const char* pBrowseFlag, const char* pFilter,
+ const char* pStartingIndex, const char* pRequestedCount, const char* pSortCriteria )
{
IXML_Document* action = 0;
IXML_Document* response = 0;
res = UpnpAddToAction( &action, "Browse", serviceType, "SortCriteria", SortCriteria );
if ( res != UPNP_E_SUCCESS ) { /* msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) ); */ goto browseActionCleanup; }
-
- res = UpnpSendAction( _cookie->clientHandle,
- url,
- CONTENT_DIRECTORY_SERVICE_TYPE,
- 0,
- action,
- &response );
- if ( res != UPNP_E_SUCCESS )
- {
- msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) );
- ixmlDocument_free( response );
- response = 0;
+
+ res = UpnpSendAction( _cookie->clientHandle,
+ url,
+ CONTENT_DIRECTORY_SERVICE_TYPE,
+ 0,
+ action,
+ &response );
+ if ( res != UPNP_E_SUCCESS )
+ {
+ msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR: %s", __FILE__, __LINE__, UpnpGetErrorMessage( res ) );
+ ixmlDocument_free( response );
+ response = 0;
}
browseActionCleanup:
return response;
}
-void MediaServer::fetchContents()
+void MediaServer::fetchContents()
{
Container* root = new Container( 0, "0", getFriendlyName() );
_fetchContents( root );
- if ( _contents )
+ if ( _contents )
{
- vlc_mutex_lock( &_cookie->serviceDiscovery->p_sys->p_playlist->object_lock );
+ vlc_mutex_lock( &_cookie->serviceDiscovery->p_sys->p_playlist->object_lock );
- playlist_NodeEmpty( _cookie->serviceDiscovery->p_sys->p_playlist,
- _playlistNode,
- true );
+ playlist_NodeEmpty( _cookie->serviceDiscovery->p_sys->p_playlist,
+ _playlistNode,
+ true );
- vlc_mutex_unlock( &_cookie->serviceDiscovery->p_sys->p_playlist->object_lock );
-
- delete _contents;
+ vlc_mutex_unlock( &_cookie->serviceDiscovery->p_sys->p_playlist->object_lock );
+
+ delete _contents;
}
_contents = root;
_buildPlaylist( _contents );
}
-bool MediaServer::_fetchContents( Container* parent )
+bool MediaServer::_fetchContents( Container* parent )
{
if (!parent) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: parent==NULL", __FILE__, __LINE__ ); return false; }
if ( !result ) { msg_Dbg( _cookie->serviceDiscovery, "%s:%d: ERROR!", __FILE__, __LINE__ ); return false; }
IXML_NodeList* containerNodeList = ixmlDocument_getElementsByTagName( result, "container" );
- if ( containerNodeList )
+ if ( containerNodeList )
{
- for ( unsigned int i = 0; i < ixmlNodeList_length( containerNodeList ); i++ )
- {
- IXML_Element* containerElement = ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
-
- const char* objectID = ixmlElement_getAttribute( containerElement, "id" );
- if ( !objectID ) continue;
-
- const char* childCountStr = ixmlElement_getAttribute( containerElement, "childCount" );
- if ( !childCountStr ) continue;
- int childCount = atoi( childCountStr );
-
- const char* title = xml_getChildElementValue( containerElement, "dc:title" );
- if ( !title ) continue;
-
- const char* resource = xml_getChildElementValue( containerElement, "res" );
-
- if ( resource && childCount < 1 )
- {
- Item* item = new Item( parent, objectID, title, resource );
- parent->addItem( item );
- }
- else
- {
- Container* container = new Container( parent, objectID, title );
- parent->addContainer( container );
-
- if ( childCount > 0 ) _fetchContents( container );
- }
- }
-
- ixmlNodeList_free( containerNodeList );
+ for ( unsigned int i = 0; i < ixmlNodeList_length( containerNodeList ); i++ )
+ {
+ IXML_Element* containerElement = ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
+
+ const char* objectID = ixmlElement_getAttribute( containerElement, "id" );
+ if ( !objectID ) continue;
+
+ const char* childCountStr = ixmlElement_getAttribute( containerElement, "childCount" );
+ if ( !childCountStr ) continue;
+ int childCount = atoi( childCountStr );
+
+ const char* title = xml_getChildElementValue( containerElement, "dc:title" );
+ if ( !title ) continue;
+
+ const char* resource = xml_getChildElementValue( containerElement, "res" );
+
+ if ( resource && childCount < 1 )
+ {
+ Item* item = new Item( parent, objectID, title, resource );
+ parent->addItem( item );
+ }
+ else
+ {
+ Container* container = new Container( parent, objectID, title );
+ parent->addContainer( container );
+
+ if ( childCount > 0 ) _fetchContents( container );
+ }
+ }
+
+ ixmlNodeList_free( containerNodeList );
}
IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( result, "item" );
- if ( itemNodeList )
+ if ( itemNodeList )
+ {
+ for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
{
- for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
- {
- IXML_Element* itemElement = ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
-
- const char* objectID = ixmlElement_getAttribute( itemElement, "id" );
- if ( !objectID ) continue;
-
- const char* title = xml_getChildElementValue( itemElement, "dc:title" );
- if ( !title ) continue;
-
- const char* resource = xml_getChildElementValue( itemElement, "res" );
- if ( !resource ) continue;
-
- Item* item = new Item( parent, objectID, title, resource );
- parent->addItem( item );
- }
-
- ixmlNodeList_free( itemNodeList );
+ IXML_Element* itemElement = ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
+
+ const char* objectID = ixmlElement_getAttribute( itemElement, "id" );
+ if ( !objectID ) continue;
+
+ const char* title = xml_getChildElementValue( itemElement, "dc:title" );
+ if ( !title ) continue;
+
+ const char* resource = xml_getChildElementValue( itemElement, "res" );
+ if ( !resource ) continue;
+
+ Item* item = new Item( parent, objectID, title, resource );
+ parent->addItem( item );
+ }
+
+ ixmlNodeList_free( itemNodeList );
}
-
+
ixmlDocument_free( result );
-
+
return true;
}
-void MediaServer::_buildPlaylist( Container* parent )
-{
- for ( unsigned int i = 0; i < parent->getNumContainers(); i++ )
+void MediaServer::_buildPlaylist( Container* parent )
+{
+ for ( unsigned int i = 0; i < parent->getNumContainers(); i++ )
{
- Container* container = parent->getContainer( i );
- playlist_item_t* parentNode = parent->getPlaylistNode();
-
- char* title = strdup( container->getTitle() );
- playlist_item_t* node = playlist_NodeCreate( _cookie->serviceDiscovery->p_sys->p_playlist,
- VIEW_CATEGORY,
- title,
- parentNode );
- free( title );
-
- container->setPlaylistNode( node );
- _buildPlaylist( container );
+ Container* container = parent->getContainer( i );
+ playlist_item_t* parentNode = parent->getPlaylistNode();
+
+ char* title = strdup( container->getTitle() );
+ playlist_item_t* node = playlist_NodeCreate( _cookie->serviceDiscovery->p_sys->p_playlist,
+ VIEW_CATEGORY,
+ title,
+ parentNode );
+ free( title );
+
+ container->setPlaylistNode( node );
+ _buildPlaylist( container );
}
- for ( unsigned int i = 0; i < parent->getNumItems(); i++ )
+ for ( unsigned int i = 0; i < parent->getNumItems(); i++ )
{
- Item* item = parent->getItem( i );
- playlist_item_t* parentNode = parent->getPlaylistNode();
-
- playlist_item_t* node = playlist_ItemNew( _cookie->serviceDiscovery,
- item->getResource(),
- item->getTitle() );
-
- playlist_NodeAddItem( _cookie->serviceDiscovery->p_sys->p_playlist,
- node,
- VIEW_CATEGORY,
- parentNode, PLAYLIST_APPEND, PLAYLIST_END );
-
- item->setPlaylistNode( node );
+ Item* item = parent->getItem( i );
+ playlist_item_t* parentNode = parent->getPlaylistNode();
+
+ playlist_item_t* node = playlist_ItemNew( _cookie->serviceDiscovery,
+ item->getResource(),
+ item->getTitle() );
+
+ playlist_NodeAddItem( _cookie->serviceDiscovery->p_sys->p_playlist,
+ node,
+ VIEW_CATEGORY,
+ parentNode, PLAYLIST_APPEND, PLAYLIST_END );
+
+ item->setPlaylistNode( node );
}
}
-void MediaServer::setPlaylistNode( playlist_item_t* playlistNode )
+void MediaServer::setPlaylistNode( playlist_item_t* playlistNode )
{
_playlistNode = playlistNode;
}
-bool MediaServer::compareSID( const char* sid )
+bool MediaServer::compareSID( const char* sid )
{
return ( strncmp( _subscriptionID, sid, sizeof( Upnp_SID ) ) == 0 );
}
// MediaServerList...
-MediaServerList::MediaServerList( Cookie* cookie )
+MediaServerList::MediaServerList( Cookie* cookie )
{
_cookie = cookie;
}
-MediaServerList::~MediaServerList()
+MediaServerList::~MediaServerList()
{
for ( unsigned int i = 0; i < _list.size(); i++ )
{
- delete _list[i];
+ delete _list[i];
}
}
-bool MediaServerList::addServer( MediaServer* s )
+bool MediaServerList::addServer( MediaServer* s )
{
if ( getServer( s->getUDN() ) != 0 ) return false;
char* name = strdup( s->getFriendlyName() );
playlist_item_t* node = playlist_NodeCreate( _cookie->serviceDiscovery->p_sys->p_playlist,
- VIEW_CATEGORY,
- name,
- _cookie->serviceDiscovery->p_sys->p_node );
+ VIEW_CATEGORY,
+ name,
+ _cookie->serviceDiscovery->p_sys->p_node );
free( name );
s->setPlaylistNode( node );
return true;
}
-MediaServer* MediaServerList::getServer( const char* UDN )
+MediaServer* MediaServerList::getServer( const char* UDN )
{
MediaServer* result = 0;
-
- for ( unsigned int i = 0; i < _list.size(); i++ )
+
+ for ( unsigned int i = 0; i < _list.size(); i++ )
+ {
+ if( strcmp( UDN, _list[i]->getUDN() ) == 0 )
{
- if( strcmp( UDN, _list[i]->getUDN() ) == 0 )
- {
- result = _list[i];
- break;
- }
+ result = _list[i];
+ break;
+ }
}
return result;
}
-MediaServer* MediaServerList::getServerBySID( const char* sid )
-{
+MediaServer* MediaServerList::getServerBySID( const char* sid )
+{
MediaServer* server = 0;
- for ( unsigned int i = 0; i < _list.size(); i++ )
+ for ( unsigned int i = 0; i < _list.size(); i++ )
+ {
+ if ( _list[i]->compareSID( sid ) )
{
- if ( _list[i]->compareSID( sid ) )
- {
- server = _list[i];
- break;
- }
+ server = _list[i];
+ break;
}
-
+ }
+
return server;
}
-void MediaServerList::removeServer( const char* UDN )
+void MediaServerList::removeServer( const char* UDN )
{
MediaServer* server = getServer( UDN );
if ( !server ) return;
- msg_Dbg( _cookie->serviceDiscovery, "Removing server '%s'", server->getFriendlyName() );
+ msg_Dbg( _cookie->serviceDiscovery, "Removing server '%s'", server->getFriendlyName() );
std::vector<MediaServer*>::iterator it;
- for ( it = _list.begin(); it != _list.end(); it++ )
+ for ( it = _list.begin(); it != _list.end(); it++ )
{
- if ( *it == server )
- {
- _list.erase( it );
- delete server;
- break;
- }
+ if ( *it == server )
+ {
+ _list.erase( it );
+ delete server;
+ break;
+ }
}
}
// Item...
-Item::Item( Container* parent, const char* objectID, const char* title, const char* resource )
+Item::Item( Container* parent, const char* objectID, const char* title, const char* resource )
{
_parent = parent;
-
+
_objectID = objectID;
_title = title;
_resource = resource;
_playlistNode = 0;
}
-const char* Item::getObjectID() const
+const char* Item::getObjectID() const
{
return _objectID.c_str();
}
-const char* Item::getTitle() const
+const char* Item::getTitle() const
{
return _title.c_str();
}
-const char* Item::getResource() const
+const char* Item::getResource() const
{
return _resource.c_str();
}
-void Item::setPlaylistNode( playlist_item_t* node )
-{
- _playlistNode = node;
+void Item::setPlaylistNode( playlist_item_t* node )
+{
+ _playlistNode = node;
}
-playlist_item_t* Item::getPlaylistNode() const
-{
- return _playlistNode;
+playlist_item_t* Item::getPlaylistNode() const
+{
+ return _playlistNode;
}
// Container...
-Container::Container( Container* parent, const char* objectID, const char* title )
+Container::Container( Container* parent, const char* objectID, const char* title )
{
_parent = parent;
-
+
_objectID = objectID;
_title = title;
_playlistNode = 0;
}
-Container::~Container()
+Container::~Container()
{
- for ( unsigned int i = 0; i < _containers.size(); i++ )
+ for ( unsigned int i = 0; i < _containers.size(); i++ )
{
- delete _containers[i];
+ delete _containers[i];
}
- for ( unsigned int i = 0; i < _items.size(); i++ )
+ for ( unsigned int i = 0; i < _items.size(); i++ )
{
- delete _items[i];
+ delete _items[i];
}
}
-void Container::addItem( Item* item )
+void Container::addItem( Item* item )
{
_items.push_back( item );
}
-void Container::addContainer( Container* container )
+void Container::addContainer( Container* container )
{
_containers.push_back( container );
}
-const char* Container::getObjectID() const
-{
- return _objectID.c_str();
+const char* Container::getObjectID() const
+{
+ return _objectID.c_str();
}
-const char* Container::getTitle() const
-{
- return _title.c_str();
+const char* Container::getTitle() const
+{
+ return _title.c_str();
}
-unsigned int Container::getNumItems() const
-{
- return _items.size();
+unsigned int Container::getNumItems() const
+{
+ return _items.size();
}
-unsigned int Container::getNumContainers() const
-{
- return _containers.size();
+unsigned int Container::getNumContainers() const
+{
+ return _containers.size();
}
-Item* Container::getItem( unsigned int i ) const
+Item* Container::getItem( unsigned int i ) const
{
if ( i < _items.size() ) return _items[i];
return 0;
}
-Container* Container::getContainer( unsigned int i ) const
+Container* Container::getContainer( unsigned int i ) const
{
if ( i < _containers.size() ) return _containers[i];
return 0;
}
-void Container::setPlaylistNode( playlist_item_t* node )
-{
- _playlistNode = node;
+void Container::setPlaylistNode( playlist_item_t* node )
+{
+ _playlistNode = node;
}
-playlist_item_t* Container::getPlaylistNode() const
-{
- return _playlistNode;
+playlist_item_t* Container::getPlaylistNode() const
+{
+ return _playlistNode;
}
../include/vlc_playlist.h \
../include/vlc_spu.h \
../include/vlc_stream.h \
+ ../include/vlc_strings.h \
../include/vlc_symbols.h \
../include/vlc_threads_funcs.h \
../include/vlc_threads.h \
misc/modules.c \
misc/threads.c \
misc/stats.c \
+ misc/strings.c \
misc/unicode.c \
misc/cpu.c \
misc/configuration.c \
#include "vlc_osd.h"
#include "vlc_update.h"
+#include "vlc_strings.h"
#if defined( _MSC_VER ) && defined( UNDER_CE )
# include "modules_builtin_evc.h"
--- /dev/null
+/*****************************************************************************
+ * strings.c: String related functions
+ *****************************************************************************
+ * Copyright (C) 2006 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan dot org>
+ * Daniel Stranger <vlc at schmaller dot de>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "vlc_strings.h"
+
+/**
+ * Decode URI encoded string
+ * \return decoded duplicated string
+ */
+char *decode_encoded_URI_duplicate( const char *psz )
+{
+ char *psz_dup = strdup( psz );
+ decode_encoded_URI( psz_dup );
+ return psz_dup;
+}
+
+/**
+ * Decode URI encoded string
+ * \return nothing
+ */
+void decode_encoded_URI( char *psz )
+{
+ char *dup = strdup( psz );
+ char *p = dup;
+
+ while( *p )
+ {
+ if( *p == '%' )
+ {
+ char val[3];
+ p++;
+ if( !*p )
+ {
+ break;
+ }
+
+ val[0] = *p++;
+ val[1] = *p++;
+ val[2] = '\0';
+
+ *psz++ = strtol( val, NULL, 16 );
+ }
+ else if( *p == '+' )
+ {
+ *psz++ = ' ';
+ p++;
+ }
+ else
+ {
+ *psz++ = *p++;
+ }
+ }
+ *psz++ = '\0';
+ free( dup );
+}
+
+/**
+ * Converts "<", ">" and "&" to "<", ">" and "&"
+ * \param string to convert
+ */
+void resolve_xml_special_chars( char *psz_value )
+{
+ char *p_pos = psz_value;
+
+ while ( *psz_value )
+ {
+ if( !strncmp( psz_value, "<", 4 ) )
+ {
+ *p_pos = '<';
+ psz_value += 4;
+ }
+ else if( !strncmp( psz_value, ">", 4 ) )
+ {
+ *p_pos = '>';
+ psz_value += 4;
+ }
+ else if( !strncmp( psz_value, "&", 5 ) )
+ {
+ *p_pos = '&';
+ psz_value += 5;
+ }
+ else if( !strncmp( psz_value, """, 6 ) )
+ {
+ *p_pos = '\"';
+ psz_value += 6;
+ }
+ else if( !strncmp( psz_value, "'", 6 ) )
+ {
+ *p_pos = '\'';
+ psz_value += 6;
+ }
+ else
+ {
+ *p_pos = *psz_value;
+ psz_value++;
+ }
+
+ p_pos++;
+ }
+
+ *p_pos = '\0';
+}
+
+/**
+ * Converts '<', '>', '\"', '\'' and '&' to their html entities
+ * \param psz_content simple element content that is to be converted
+ */
+char *convert_xml_special_chars( const char *psz_content )
+{
+ char *psz_temp = malloc( 6 * strlen( psz_content ) + 1 );
+ const char *p_from = psz_content;
+ char *p_to = psz_temp;
+
+ while ( *p_from )
+ {
+ if ( *p_from == '<' )
+ {
+ strcpy( p_to, "<" );
+ p_to += 4;
+ }
+ else if ( *p_from == '>' )
+ {
+ strcpy( p_to, ">" );
+ p_to += 4;
+ }
+ else if ( *p_from == '&' )
+ {
+ strcpy( p_to, "&" );
+ p_to += 5;
+ }
+ else if( *p_from == '\"' )
+ {
+ strcpy( p_to, """ );
+ p_to += 6;
+ }
+ else if( *p_from == '\'' )
+ {
+ strcpy( p_to, "'" );
+ p_to += 6;
+ }
+ else
+ {
+ *p_to = *p_from;
+ p_to++;
+ }
+ p_from++;
+ }
+ *p_to = '\0';
+
+ return psz_temp;
+}
msg_Err( p_playlist, "out of memory");
return VLC_ENOMEM;
}
+ p_export->psz_filename = NULL;
+ if ( psz_filename )
+ p_export->psz_filename = strdup( psz_filename );
p_export->p_file = utf8_fopen( psz_filename, "wt" );
if( !p_export->p_file )
{
}
module_Unneed( p_playlist , p_module );
+ /* Clean up */
fclose( p_export->p_file );
-
+ if ( p_export->psz_filename )
+ free( p_export->psz_filename );
+ free ( p_export );
+ p_playlist->p_private = NULL;
vlc_mutex_unlock( &p_playlist->object_lock );
return VLC_SUCCESS;