asx: fix mimetype and stream Peek
[vlc.git] / modules / demux / playlist / wpl.c
blob1effebd2fea0c5049356afeb8591a7f76e45a8da
1 /*****************************************************************************
2 * wpl.c : WPL playlist import
3 *****************************************************************************
4 * Copyright (C) 2015 VLC authors and VideoLAN
6 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #include <vlc_common.h>
28 #include <vlc_access.h>
29 #include <vlc_xml.h>
31 #include "playlist.h"
33 static int consume_tag( xml_reader_t* p_reader, char const* psz_tag )
35 int i_type, i_depth = 0;
36 char const *psz_name;
38 if( xml_ReaderIsEmptyElement( p_reader ) == 1 )
39 return VLC_SUCCESS;
41 while( ( i_type = xml_ReaderNextNode( p_reader, &psz_name ) ) > 0 )
43 if( i_type == XML_READER_ENDELEM && !strcasecmp( psz_name, psz_tag ) )
45 if( --i_depth < 0 )
46 return VLC_SUCCESS;
48 else if( i_type == XML_READER_STARTELEM && !strcasecmp( psz_name, psz_tag ) )
50 if( xml_ReaderIsEmptyElement( p_reader ) != 1 )
51 ++i_depth;
55 return VLC_EGENERIC;
58 static int consume_volatile_tag( stream_t* p_demux, char const* psz_tag )
60 char* psz_copy = strdup( psz_tag );
61 int ret = VLC_ENOMEM;
63 if( likely( psz_copy ) )
64 ret = consume_tag( p_demux->p_sys, psz_copy );
66 free( psz_copy );
67 return ret;
70 static void parse_meta( stream_t* p_demux, input_item_t* p_input )
72 xml_reader_t *p_reader = p_demux->p_sys;
73 bool const b_empty = xml_ReaderIsEmptyElement( p_reader ) == 1;
75 char *psz_meta_name = NULL, *psz_meta_content = NULL;
76 char const *psz_attr, *psz_value;
77 while( ( psz_attr = xml_ReaderNextAttr( p_reader, &psz_value ) ) )
79 if( psz_value == NULL )
80 continue;
82 if( !strcasecmp( psz_attr, "name" ) && !psz_meta_name )
83 psz_meta_name = strdup( psz_value );
84 else
85 if( !strcasecmp( psz_attr, "content" ) && !psz_meta_content )
86 psz_meta_content = strdup( psz_value );
88 if( psz_meta_name && psz_meta_content )
89 break;
92 if( b_empty == false )
93 consume_tag( p_reader, "meta" );
95 if( !psz_meta_name || !psz_meta_content )
96 goto done;
98 if( !strcasecmp( psz_meta_name, "TotalDuration" ) )
99 input_item_AddInfo( p_input, _("Playlist"), _("Total duration"),
100 "%lld", atoll( psz_meta_content ) );
101 else
102 if( !strcasecmp( psz_meta_name, "Author" ) )
103 input_item_SetPublisher( p_input, psz_meta_content );
104 else
105 if( !strcasecmp( psz_meta_name, "Rating" ) )
106 input_item_SetRating( p_input, psz_meta_content );
107 else
108 if( !strcasecmp( psz_meta_name, "Genre" ) )
109 input_item_SetGenre( p_input, psz_meta_content );
110 else
111 msg_Warn( p_demux, "ignoring unknown meta-attribute %s", psz_meta_name );
113 done:
114 free( psz_meta_name );
115 free( psz_meta_content );
118 static int parse_title_element( stream_t* p_demux, input_item_t* p_input )
120 xml_reader_t *p_reader = p_demux->p_sys;
121 char const* psz_title;
123 if( xml_ReaderIsEmptyElement( p_reader ) )
124 return VLC_SUCCESS;
126 if( xml_ReaderNextNode( p_reader, &psz_title ) != XML_READER_TEXT )
127 return VLC_EGENERIC;
129 input_item_SetTitle( p_input, psz_title );
131 consume_tag( p_reader, "title" );
132 return VLC_SUCCESS;
135 static void read_head( stream_t* p_demux, input_item_t* p_input )
137 xml_reader_t *p_reader = p_demux->p_sys;
138 char const* psz_name;
139 int i_type;
141 while( ( i_type = xml_ReaderNextNode( p_reader, &psz_name ) ) > 0 )
143 if( i_type == XML_READER_ENDELEM && !strcasecmp( psz_name, "head" ) )
144 break;
146 if( i_type == XML_READER_STARTELEM )
148 if( !strcasecmp( psz_name, "meta" ) )
150 parse_meta( p_demux, p_input );
151 continue;
154 if( !strcasecmp( psz_name, "title" ) )
156 if( parse_title_element( p_demux, p_input ) )
157 break;
158 continue;
161 msg_Warn( p_demux, "skipping unknown tag <%s> in <head>", psz_name );
162 consume_volatile_tag( p_demux, psz_name );
167 static void read_body( stream_t* p_demux, input_item_node_t* p_node )
169 xml_reader_t *p_reader = p_demux->p_sys;
170 const char* psz_name;
171 int i_type;
173 i_type = xml_ReaderNextNode( p_reader, &psz_name );
174 if ( i_type != XML_READER_STARTELEM || strcasecmp( psz_name, "seq" ) )
176 msg_Err( p_demux, "Expected opening <seq> tag. Got <%s> with type %d", psz_name, i_type );
177 return;
180 if( xml_ReaderIsEmptyElement( p_reader ) == 1 )
181 return;
183 while ( ( i_type = xml_ReaderNextNode( p_reader, &psz_name ) ) > 0 )
185 if ( i_type == XML_READER_ENDELEM && !strcasecmp( psz_name, "seq" ) )
186 break;
188 if( i_type == XML_READER_STARTELEM )
190 if( !strcasecmp( psz_name, "media" ) )
192 const bool b_empty = xml_ReaderIsEmptyElement( p_reader );
194 const char *psz_attr = NULL, *psz_val = NULL;
195 while ((psz_attr = xml_ReaderNextAttr( p_reader, &psz_val )))
197 if ( !psz_val || *psz_val == '\0' )
198 continue;
199 if (!strcasecmp( psz_attr, "src" ) )
201 char* mrl = ProcessMRL( psz_val, p_demux->psz_url );
202 if ( unlikely( !mrl ) )
203 return;
204 input_item_t* p_item = input_item_New( mrl, NULL );
205 if ( likely( p_item ) )
207 input_item_node_AppendItem( p_node, p_item );
208 input_item_Release( p_item );
210 free( mrl );
214 if( b_empty == false )
215 consume_tag( p_reader, "media" );
217 continue;
220 msg_Warn( p_demux, "skipping unknown tag <%s> in <seq>", psz_name );
221 consume_volatile_tag( p_demux, psz_name );
225 i_type = xml_ReaderNextNode( p_reader, &psz_name );
226 if ( i_type != XML_READER_ENDELEM || strcasecmp( psz_name, "body" ) )
227 msg_Err( p_demux, "Expected closing <body> tag. Got: <%s> with type %d", psz_name, i_type );
230 static int Demux( stream_t* p_demux, input_item_node_t *p_node )
232 xml_reader_t *p_reader = p_demux->p_sys;
233 const char* psz_name;
234 int i_type;
236 if( xml_ReaderNextNode( p_reader, &psz_name ) != XML_READER_STARTELEM ||
237 strcasecmp( psz_name, "smil" ) || xml_ReaderIsEmptyElement( p_reader ) == 1 )
239 return VLC_EGENERIC;
242 input_item_t* p_input = GetCurrentItem( p_demux );
244 while( ( i_type = xml_ReaderNextNode( p_reader, &psz_name ) ) > 0 )
246 if( i_type == XML_READER_ENDELEM && !strcasecmp( psz_name, "smil" ) )
247 break;
249 if( i_type == XML_READER_STARTELEM )
251 if( !strcasecmp( psz_name, "head" ) )
253 read_head( p_demux, p_input );
254 continue;
257 if( !strcasecmp( psz_name, "body" ) )
259 read_body( p_demux, p_node );
260 continue;
263 msg_Warn( p_demux, "skipping unknown tag <%s> in <smil>", psz_name );
264 consume_volatile_tag( p_demux, psz_name );
268 return VLC_SUCCESS;
271 void Close_WPL( vlc_object_t* p_this )
273 stream_t *p_demux = (stream_t*)p_this;
275 xml_ReaderDelete( p_demux->p_sys );
278 int Import_WPL( vlc_object_t* p_this )
280 stream_t* p_demux = (stream_t*)p_this;
282 CHECK_FILE(p_demux);
283 if( !stream_HasExtension( p_demux, ".wpl" ) &&
284 !stream_HasExtension( p_demux, ".zpl" ) )
285 return VLC_EGENERIC;
287 const uint8_t *p_peek;
288 ssize_t i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 2048 );
289 if( unlikely( i_peek <= 0 ) )
290 return VLC_EGENERIC;
292 stream_t *p_probestream = vlc_stream_MemoryNew( p_demux, (uint8_t *)p_peek, i_peek, true );
293 if( unlikely( !p_probestream ) )
294 return VLC_EGENERIC;
296 xml_reader_t *p_reader = xml_ReaderCreate( p_this, p_probestream );
297 if ( p_reader == NULL )
299 msg_Err( p_demux, "Failed to create an XML reader" );
300 vlc_stream_Delete( p_probestream );
301 return VLC_EGENERIC;
303 p_demux->p_sys = p_reader;
305 const int i_flags = p_reader->obj.flags;
306 p_reader->obj.flags |= OBJECT_FLAGS_QUIET;
307 const char* psz_name;
308 int type = xml_ReaderNextNode( p_reader, &psz_name );
309 p_reader->obj.flags = i_flags;
310 if ( type != XML_READER_STARTELEM || strcasecmp( psz_name, "smil" ) )
312 msg_Err( p_demux, "Invalid WPL playlist. Root element should have been <smil>" );
313 Close_WPL( p_this );
314 vlc_stream_Delete( p_probestream );
315 return VLC_EGENERIC;
318 p_demux->p_sys = xml_ReaderReset( p_reader, p_demux->s );
319 vlc_stream_Delete( p_probestream );
320 if( unlikely( p_demux->p_sys == NULL ) )
321 return VLC_EGENERIC;
323 msg_Dbg( p_demux, "Found valid WPL playlist" );
324 p_demux->pf_readdir = Demux;
325 p_demux->pf_control = access_vaDirectoryControlHelper;
327 return VLC_SUCCESS;