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 *****************************************************************************/
27 #include <vlc_common.h>
28 #include <vlc_access.h>
33 static int consume_tag( xml_reader_t
* p_reader
, char const* psz_tag
)
35 int i_type
, i_depth
= 0;
38 if( xml_ReaderIsEmptyElement( p_reader
) == 1 )
41 while( ( i_type
= xml_ReaderNextNode( p_reader
, &psz_name
) ) > 0 )
43 if( i_type
== XML_READER_ENDELEM
&& !strcasecmp( psz_name
, psz_tag
) )
48 else if( i_type
== XML_READER_STARTELEM
&& !strcasecmp( psz_name
, psz_tag
) )
50 if( xml_ReaderIsEmptyElement( p_reader
) != 1 )
58 static int consume_volatile_tag( stream_t
* p_demux
, char const* psz_tag
)
60 char* psz_copy
= strdup( psz_tag
);
63 if( likely( psz_copy
) )
64 ret
= consume_tag( p_demux
->p_sys
, psz_copy
);
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
)
82 if( !strcasecmp( psz_attr
, "name" ) && !psz_meta_name
)
83 psz_meta_name
= strdup( psz_value
);
85 if( !strcasecmp( psz_attr
, "content" ) && !psz_meta_content
)
86 psz_meta_content
= strdup( psz_value
);
88 if( psz_meta_name
&& psz_meta_content
)
92 if( b_empty
== false )
93 consume_tag( p_reader
, "meta" );
95 if( !psz_meta_name
|| !psz_meta_content
)
98 if( !strcasecmp( psz_meta_name
, "TotalDuration" ) )
99 input_item_AddInfo( p_input
, _("Playlist"), _("Total duration"),
100 "%lld", atoll( psz_meta_content
) );
102 if( !strcasecmp( psz_meta_name
, "Author" ) )
103 input_item_SetPublisher( p_input
, psz_meta_content
);
105 if( !strcasecmp( psz_meta_name
, "Rating" ) )
106 input_item_SetRating( p_input
, psz_meta_content
);
108 if( !strcasecmp( psz_meta_name
, "Genre" ) )
109 input_item_SetGenre( p_input
, psz_meta_content
);
111 msg_Warn( p_demux
, "ignoring unknown meta-attribute %s", psz_meta_name
);
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
) )
126 if( xml_ReaderNextNode( p_reader
, &psz_title
) != XML_READER_TEXT
)
129 input_item_SetTitle( p_input
, psz_title
);
131 consume_tag( p_reader
, "title" );
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
;
141 while( ( i_type
= xml_ReaderNextNode( p_reader
, &psz_name
) ) > 0 )
143 if( i_type
== XML_READER_ENDELEM
&& !strcasecmp( psz_name
, "head" ) )
146 if( i_type
== XML_READER_STARTELEM
)
148 if( !strcasecmp( psz_name
, "meta" ) )
150 parse_meta( p_demux
, p_input
);
154 if( !strcasecmp( psz_name
, "title" ) )
156 if( parse_title_element( p_demux
, p_input
) )
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
;
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
);
180 if( xml_ReaderIsEmptyElement( p_reader
) == 1 )
183 while ( ( i_type
= xml_ReaderNextNode( p_reader
, &psz_name
) ) > 0 )
185 if ( i_type
== XML_READER_ENDELEM
&& !strcasecmp( psz_name
, "seq" ) )
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' )
199 if (!strcasecmp( psz_attr
, "src" ) )
201 char* mrl
= ProcessMRL( psz_val
, p_demux
->psz_url
);
202 if ( unlikely( !mrl
) )
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
);
214 if( b_empty
== false )
215 consume_tag( p_reader
, "media" );
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
;
236 if( xml_ReaderNextNode( p_reader
, &psz_name
) != XML_READER_STARTELEM
||
237 strcasecmp( psz_name
, "smil" ) || xml_ReaderIsEmptyElement( p_reader
) == 1 )
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" ) )
249 if( i_type
== XML_READER_STARTELEM
)
251 if( !strcasecmp( psz_name
, "head" ) )
253 read_head( p_demux
, p_input
);
257 if( !strcasecmp( psz_name
, "body" ) )
259 read_body( p_demux
, p_node
);
263 msg_Warn( p_demux
, "skipping unknown tag <%s> in <smil>", psz_name
);
264 consume_volatile_tag( p_demux
, psz_name
);
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
;
283 if( !stream_HasExtension( p_demux
, ".wpl" ) &&
284 !stream_HasExtension( p_demux
, ".zpl" ) )
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 ) )
292 stream_t
*p_probestream
= vlc_stream_MemoryNew( p_demux
, (uint8_t *)p_peek
, i_peek
, true );
293 if( unlikely( !p_probestream
) )
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
);
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>" );
314 vlc_stream_Delete( p_probestream
);
318 xml_ReaderDelete( p_demux
->p_sys
);
320 p_demux
->p_sys
= xml_ReaderCreate( p_demux
, p_demux
->s
);
321 vlc_stream_Delete( p_probestream
);
322 if( unlikely( p_demux
->p_sys
== NULL
) )
325 msg_Dbg( p_demux
, "Found valid WPL playlist" );
326 p_demux
->pf_readdir
= Demux
;
327 p_demux
->pf_control
= access_vaDirectoryControlHelper
;