Commit 761871b1 authored by Yoann Peronneau's avatar Yoann Peronneau

Update XSPF playlist structure to use <vlc:id> instead of <identifier>.

This fixes bugs when loading non-VLC generated XSPF playlists.
parent e1bf75ae
/*******************************************************************************
* xspf.c : XSPF playlist import functions
*******************************************************************************
......@@ -44,7 +43,7 @@ struct demux_sys_t
{
input_item_t **pp_tracklist;
int i_tracklist_entries;
int i_identifier;
int i_track_id;
char * psz_base;
};
......@@ -87,7 +86,7 @@ int Demux( demux_t *p_demux )
INIT_PLAYLIST_STUFF;
p_demux->p_sys->pp_tracklist = NULL;
p_demux->p_sys->i_tracklist_entries = 0;
p_demux->p_sys->i_identifier = 0;
p_demux->p_sys->i_track_id = -1;
p_demux->p_sys->psz_base = NULL;
/* create new xml parser from stream */
......@@ -164,11 +163,11 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
*/
static bool parse_playlist_node COMPLEX_INTERFACE
{
char *psz_name=NULL;
char *psz_value=NULL;
char *psz_name = NULL;
char *psz_value = NULL;
bool b_version_found = false;
int i_node;
xml_elem_hnd_t *p_handler=NULL;
xml_elem_hnd_t *p_handler = NULL;
xml_elem_hnd_t pl_elements[] =
{ {"title", SIMPLE_CONTENT, {.smpl = set_item_info} },
......@@ -333,7 +332,7 @@ static bool parse_playlist_node COMPLEX_INTERFACE
static bool parse_tracklist_node COMPLEX_INTERFACE
{
VLC_UNUSED(psz_element);
char *psz_name=NULL;
char *psz_name = NULL;
int i_node;
int i_ntracks = 0;
......@@ -397,9 +396,9 @@ static bool parse_track_node COMPLEX_INTERFACE
{
input_item_t *p_new_input = NULL;
int i_node;
char *psz_name=NULL;
char *psz_value=NULL;
xml_elem_hnd_t *p_handler=NULL;
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} },
......@@ -418,6 +417,9 @@ static bool parse_track_node COMPLEX_INTERFACE
{NULL, UNKNOWN_CONTENT, {NULL} }
};
/* reset i_track_id */
p_demux->p_sys->i_track_id = -1;
while( xml_ReaderRead( p_xml_reader ) == 1 )
{
i_node = xml_ReaderNodeType( p_xml_reader );
......@@ -498,22 +500,33 @@ static bool parse_track_node COMPLEX_INTERFACE
if( !strcmp( psz_name, psz_element ) )
{
FREE_ATT();
if( p_demux->p_sys->i_identifier >=
if( p_demux->p_sys->i_track_id < 0 )
{
if( p_new_input )
{
input_item_AddSubItem( p_input_item, p_new_input );
vlc_gc_decref( p_new_input );
}
return true;
}
if( p_demux->p_sys->i_track_id >=
p_demux->p_sys->i_tracklist_entries )
{
input_item_t **pp;
pp = realloc( p_demux->p_sys->pp_tracklist,
(p_demux->p_sys->i_identifier + 1) * sizeof(*pp) );
(p_demux->p_sys->i_track_id + 1) * sizeof(*pp) );
if( !pp )
return false;
p_demux->p_sys->pp_tracklist = pp;
while( p_demux->p_sys->i_identifier >=
while( p_demux->p_sys->i_track_id >=
p_demux->p_sys->i_tracklist_entries )
pp[p_demux->p_sys->i_tracklist_entries++] = NULL;
}
p_demux->p_sys->pp_tracklist[
p_demux->p_sys->i_identifier ] = p_new_input;
p_demux->p_sys->i_track_id ] = p_new_input;
return true;
}
/* there MUST have been a start tag for that element name */
......@@ -529,7 +542,7 @@ static bool parse_track_node COMPLEX_INTERFACE
/* special case: location */
if( !strcmp( p_handler->name, "location" ) )
{
char *psz_uri=NULL;
char *psz_uri = NULL;
/* there MUST NOT be an item */
if( p_new_input )
{
......@@ -569,10 +582,6 @@ static bool parse_track_node COMPLEX_INTERFACE
return false;
}
}
else if( !strcmp( p_handler->name, "identifier" ) )
{
p_demux->p_sys->i_identifier = atoi( psz_value );
}
else
{
/* there MUST be an item */
......@@ -658,7 +667,7 @@ static bool set_item_info SIMPLE_INTERFACE
}
/**
* \brief handles the <option> elements
* \brief handles the <vlc:option> elements
*/
static bool set_option SIMPLE_INTERFACE
{
......@@ -689,9 +698,10 @@ static bool parse_extension_node COMPLEX_INTERFACE
input_item_t *p_new_input = NULL;
xml_elem_hnd_t pl_elements[] =
{ {"node", COMPLEX_CONTENT, {.cmplx = parse_extension_node} },
{"item", COMPLEX_CONTENT, {.cmplx = parse_extitem_node} },
{"option", SIMPLE_CONTENT, {.smpl = set_option} },
{ {"vlc:node", COMPLEX_CONTENT, {.cmplx = parse_extension_node} },
{"vlc:item", COMPLEX_CONTENT, {.cmplx = parse_extitem_node} },
{"vlc:id", SIMPLE_CONTENT, {NULL} },
{"vlc:option", SIMPLE_CONTENT, {.smpl = set_option} },
{NULL, UNKNOWN_CONTENT, {NULL} }
};
......@@ -702,7 +712,7 @@ static bool parse_extension_node COMPLEX_INTERFACE
psz_value = xml_ReaderValue( p_xml_reader );
if( !psz_name || !psz_value )
{
msg_Err( p_demux, "invalid xml stream @ <node>" );
msg_Err( p_demux, "invalid xml stream @ <vlc:node>" );
FREE_ATT();
return false;
}
......@@ -719,21 +729,23 @@ static bool parse_extension_node COMPLEX_INTERFACE
}
/* unknown attribute */
else
msg_Warn( p_demux, "invalid <%s> attribute:\"%s\"", psz_element, psz_name );
msg_Warn( p_demux, "invalid <%s> attribute:\"%s\"", psz_element,
psz_name );
FREE_ATT();
}
/* attribute title is mandatory except for <extension> */
if( !strcmp( psz_element, "node" ) )
if( !strcmp( psz_element, "vlc:node" ) )
{
if( !psz_title )
{
msg_Warn( p_demux, "<node> requires \"title\" attribute" );
msg_Warn( p_demux, "<vlc:node> requires \"title\" attribute" );
return false;
}
p_new_input = input_item_NewWithType( VLC_OBJECT( p_demux ), "vlc://nop",
psz_title, 0, NULL, -1, ITEM_TYPE_DIRECTORY );
p_new_input = input_item_NewWithType( VLC_OBJECT( p_demux ),
"vlc://nop", psz_title, 0, NULL, -1,
ITEM_TYPE_DIRECTORY );
if( p_new_input )
{
input_item_AddSubItem( p_input_item, p_new_input );
......@@ -773,7 +785,7 @@ static bool parse_extension_node COMPLEX_INTERFACE
{
msg_Err( p_demux, "invalid xml stream" );
FREE_ATT();
if(b_release_input_item) vlc_gc_decref( p_new_input );
if( b_release_input_item ) vlc_gc_decref( p_new_input );
return false;
}
/* choose handler */
......@@ -784,7 +796,7 @@ static bool parse_extension_node COMPLEX_INTERFACE
{
msg_Err( p_demux, "unexpected element <%s>", psz_name );
FREE_ATT();
if(b_release_input_item) vlc_gc_decref( p_new_input );
if( b_release_input_item ) vlc_gc_decref( p_new_input );
return false;
}
FREE_NAME();
......@@ -802,7 +814,7 @@ static bool parse_extension_node COMPLEX_INTERFACE
else
{
FREE_ATT();
if(b_release_input_item) vlc_gc_decref( p_new_input );
if( b_release_input_item ) vlc_gc_decref( p_new_input );
return false;
}
}
......@@ -816,7 +828,7 @@ static bool parse_extension_node COMPLEX_INTERFACE
{
msg_Err( p_demux, "invalid xml stream" );
FREE_ATT();
if(b_release_input_item) vlc_gc_decref( p_new_input );
if( b_release_input_item ) vlc_gc_decref( p_new_input );
return false;
}
break;
......@@ -828,14 +840,14 @@ static bool parse_extension_node COMPLEX_INTERFACE
{
msg_Err( p_demux, "invalid xml stream" );
FREE_ATT();
if(b_release_input_item) vlc_gc_decref( p_new_input );
if( b_release_input_item ) vlc_gc_decref( p_new_input );
return false;
}
/* leave if the current parent node is terminated */
if( !strcmp( psz_name, psz_element ) )
{
FREE_ATT();
if(b_release_input_item) vlc_gc_decref( p_new_input );
if( b_release_input_item ) vlc_gc_decref( p_new_input );
return true;
}
/* there MUST have been a start tag for that element name */
......@@ -845,11 +857,16 @@ static bool parse_extension_node COMPLEX_INTERFACE
msg_Err( p_demux, "there's no open element left for <%s>",
psz_name );
FREE_ATT();
if(b_release_input_item) vlc_gc_decref( p_new_input );
if( b_release_input_item ) vlc_gc_decref( p_new_input );
return false;
}
if( p_handler->pf_handler.smpl )
/* special tag <vlc:id> */
if( !strcmp( p_handler->name, "vlc:id" ) )
{
p_demux->p_sys->i_track_id = atoi( psz_value );
}
else if( p_handler->pf_handler.smpl )
{
p_handler->pf_handler.smpl( p_input_item, p_handler->name,
psz_value );
......@@ -862,12 +879,12 @@ static bool parse_extension_node COMPLEX_INTERFACE
/* unknown/unexpected xml node */
msg_Err( p_demux, "unexpected xml node %i", i_node );
FREE_ATT();
if(b_release_input_item) vlc_gc_decref( p_new_input );
if( b_release_input_item ) vlc_gc_decref( p_new_input );
return false;
}
FREE_NAME();
}
if(b_release_input_item) vlc_gc_decref( p_new_input );
if( b_release_input_item ) vlc_gc_decref( p_new_input );
return false;
}
......@@ -880,7 +897,7 @@ static bool parse_extitem_node COMPLEX_INTERFACE
input_item_t *p_new_input = NULL;
char *psz_name = NULL;
char *psz_value = NULL;
int i_href = -1;
int i_tid = -1;
/* read all extension item attributes */
while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
......@@ -889,41 +906,41 @@ static bool parse_extitem_node COMPLEX_INTERFACE
psz_value = xml_ReaderValue( p_xml_reader );
if( !psz_name || !psz_value )
{
msg_Err( p_demux, "invalid xml stream @ <item>" );
msg_Err( p_demux, "invalid xml stream @ <vlc:item>" );
FREE_ATT();
return false;
}
/* attribute: href */
if( !strcmp( psz_name, "href" ) )
if( !strcmp( psz_name, "tid" ) )
{
i_href = atoi( psz_value );
i_tid = atoi( psz_value );
}
/* unknown attribute */
else
msg_Warn( p_demux, "invalid <item> attribute:\"%s\"", psz_name);
msg_Warn( p_demux, "invalid <vlc:item> attribute:\"%s\"", psz_name);
FREE_ATT();
}
/* attribute href is mandatory */
if( i_href < 0 )
if( i_tid < 0 )
{
msg_Warn( p_demux, "<item> requires \"href\" attribute" );
msg_Warn( p_demux, "<vlc:item> requires \"tid\" attribute" );
return false;
}
if( i_href >= p_demux->p_sys->i_tracklist_entries )
if( i_tid >= p_demux->p_sys->i_tracklist_entries )
{
msg_Warn( p_demux, "invalid \"href\" attribute" );
msg_Warn( p_demux, "invalid \"tid\" attribute" );
return false;
}
p_new_input = p_demux->p_sys->pp_tracklist[ i_href ];
p_new_input = p_demux->p_sys->pp_tracklist[ i_tid ];
if( p_new_input )
{
input_item_AddSubItem( p_input_item, p_new_input );
vlc_gc_decref( p_new_input );
p_demux->p_sys->pp_tracklist[i_href] = NULL;
p_demux->p_sys->pp_tracklist[i_tid] = NULL;
}
/* kludge for #1293 - XTAG sends ENDELEM for self closing tag */
......
......@@ -60,7 +60,8 @@ int xspf_export_playlist( vlc_object_t *p_this )
/* write XSPF XML header */
fprintf( p_export->p_file, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
fprintf( p_export->p_file,
"<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\">\n" );
"<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" " \
"xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n" );
if( !p_node ) return VLC_SUCCESS;
......@@ -91,7 +92,8 @@ int xspf_export_playlist( vlc_object_t *p_this )
fprintf( p_export->p_file, "\t</trackList>\n" );
/* export the tree structure in <extension> */
fprintf( p_export->p_file, "\t<extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" );
fprintf( p_export->p_file, "\t<extension application=\"" \
"http://www.videolan.org/vlc/playlist/0\">\n" );
i_count = 0;
for( i = 0; i < p_node->i_children; i++ )
{
......@@ -142,10 +144,6 @@ static void xspf_export_item( playlist_item_t *p_item, FILE *p_file,
/* leaves can be written directly */
fprintf( p_file, "\t\t<track>\n" );
/* print identifier and increase the counter */
fprintf( p_file, "\t\t\t<identifier>%i</identifier>\n", *p_i_count );
( *p_i_count )++;
/* -> the location */
char *psz_uri = input_item_GetURI( p_item->p_input );
......@@ -228,17 +226,6 @@ static void xspf_export_item( playlist_item_t *p_item, FILE *p_file,
}
free( psz );
/* export the input's options (bookmarks, ...) in <extension> */
fprintf( p_file, "\t\t\t<extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" );
for( i = 0; i < p_item->p_input->i_options; i++ )
{
fprintf( p_file, "\t\t\t\t<option>%s</option>\n",
p_item->p_input->ppsz_options[i][0] == ':' ?
p_item->p_input->ppsz_options[i] + 1 :
p_item->p_input->ppsz_options[i] );
}
fprintf( p_file, "\t\t\t</extension>\n" );
xspfexportitem_end:
/* -> the duration */
i_duration = input_item_GetDuration( p_item->p_input );
......@@ -248,6 +235,24 @@ xspfexportitem_end:
(long)(i_duration / 1000) );
}
/* export the intenal id and the input's options (bookmarks, ...)
* in <extension> */
fprintf( p_file, "\t\t\t<extension application=\"" \
"http://www.videolan.org/vlc/playlist/0\">\n" );
/* print the id and increase the counter */
fprintf( p_file, "\t\t\t\t<vlc:id>%i</vlc:id>\n", *p_i_count );
( *p_i_count )++;
for( i = 0; i < p_item->p_input->i_options; i++ )
{
fprintf( p_file, "\t\t\t\t<vlc:option>%s</vlc:option>\n",
p_item->p_input->ppsz_options[i][0] == ':' ?
p_item->p_input->ppsz_options[i] + 1 :
p_item->p_input->ppsz_options[i] );
}
fprintf( p_file, "\t\t\t</extension>\n" );
fprintf( p_file, "\t\t</track>\n" );
return;
......@@ -270,7 +275,7 @@ static void xspf_extension_item( playlist_item_t *p_item, FILE *p_file,
int i;
char *psz_temp;
psz_temp = convert_xml_special_chars( p_item->p_input->psz_name );
fprintf( p_file, "\t\t<node title=\"%s\">\n",
fprintf( p_file, "\t\t<vlc:node title=\"%s\">\n",
*psz_temp ? psz_temp : "" );
free( psz_temp );
......@@ -279,13 +284,13 @@ static void xspf_extension_item( playlist_item_t *p_item, FILE *p_file,
xspf_extension_item( p_item->pp_children[i], p_file, p_i_count );
}
fprintf( p_file, "\t\t</node>\n" );
fprintf( p_file, "\t\t</vlc:node>\n" );
return;
}
/* print leaf and increase the counter */
fprintf( p_file, "\t\t\t<item href=\"%i\" />\n", *p_i_count );
fprintf( p_file, "\t\t\t<vlc:item tid=\"%i\" />\n", *p_i_count );
( *p_i_count )++;
return;
......
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