]> git.sesse.net Git - vlc/blob - modules/demux/playlist/asx.c
Preparse playlist items that don't have enough meta
[vlc] / modules / demux / playlist / asx.c
1 /*****************************************************************************
2  * asx.c : ASX playlist format import
3  *****************************************************************************
4  * Copyright (C) 2005-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /* See also: http://msdn.microsoft.com/library/en-us/wmplay10/mmp_sdk/windowsmediametafilereference.asp
25  */
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #define _GNU_SOURCE
31 #include <stdlib.h>                                      /* malloc(), free() */
32 #include <ctype.h>                                              /* isspace() */
33
34 #include <vlc/vlc.h>
35 #include <vlc/input.h>
36
37 #include <errno.h>                                                 /* ENOMEM */
38 #include "charset.h"
39 #include "playlist.h"
40 #include "vlc_meta.h"
41
42 struct demux_sys_t
43 {
44     char    *psz_prefix;
45     char    *psz_data;
46     int64_t i_data_len;
47     vlc_bool_t b_utf8;
48 };
49
50 /*****************************************************************************
51  * Local prototypes
52  *****************************************************************************/
53 static int Demux( demux_t *p_demux);
54 static int Control( demux_t *p_demux, int i_query, va_list args );
55
56 static int StoreString( demux_t *p_demux, char **ppsz_string,
57                         const char *psz_source_start,
58                         const char *psz_source_end )
59 {
60     demux_sys_t *p_sys = p_demux->p_sys;
61     unsigned len = psz_source_end - psz_source_start;
62
63     if( *ppsz_string ) free( *ppsz_string );
64
65     char *buf = *ppsz_string = malloc ((len * (1 + !p_sys->b_utf8)) + 1);
66     if (buf == NULL)
67         return VLC_ENOMEM;
68
69     if( p_sys->b_utf8 )
70     {
71         memcpy (buf, psz_source_start, len);
72         (*ppsz_string)[len] = '\0';
73         EnsureUTF8 (*ppsz_string);
74     }
75     else
76     {
77         /* Latin-1 -> UTF-8 */
78         for (unsigned i = 0; i < len; i++)
79         {
80             unsigned char c = psz_source_start[i];
81             if (c & 0x80)
82             {
83                 *buf++ = 0xc0 | (c >> 6);
84                 *buf++ = 0x80 | (c & 0x3f);
85             }
86             else
87                 *buf++ = c;
88         }
89         *buf++ = '\0';
90
91         buf = *ppsz_string = realloc (*ppsz_string, buf - *ppsz_string);
92     }
93     return VLC_SUCCESS;
94 }
95
96 /*****************************************************************************
97  * Import_ASX: main import function
98  *****************************************************************************/
99 int E_(Import_ASX)( vlc_object_t *p_this )
100 {
101     demux_t *p_demux = (demux_t *)p_this;
102     uint8_t *p_peek, *p_peek_stop;
103     CHECK_PEEK( p_peek, 10 );
104
105     p_peek_stop = p_peek+6;
106
107     // skip over possible leading empty lines
108     while( (p_peek < p_peek_stop) && (*p_peek == '\n' || *p_peek == '\r')) ++p_peek;
109
110     if( POKE( p_peek, "<asx", 4 ) || isExtension( p_demux, ".asx" ) ||
111         isExtension( p_demux, ".wax" ) || isExtension( p_demux, ".wvx" ) ||
112         isDemux( p_demux, "asx-open" ) )
113     {
114         ;
115     }
116     else
117         return VLC_EGENERIC;
118
119     STANDARD_DEMUX_INIT_MSG( "found valid ASX playlist" );
120     p_demux->p_sys->psz_prefix = E_(FindPrefix)( p_demux );
121     p_demux->p_sys->psz_data = NULL;
122     p_demux->p_sys->i_data_len = -1;
123     p_demux->p_sys->b_utf8 = VLC_FALSE;
124
125     return VLC_SUCCESS;
126 }
127
128 /*****************************************************************************
129  * Deactivate: frees unused data
130  *****************************************************************************/
131 void E_(Close_ASX)( vlc_object_t *p_this )
132 {
133     demux_t *p_demux = (demux_t *)p_this;
134     demux_sys_t *p_sys = p_demux->p_sys;
135
136     if( p_sys->psz_prefix ) free( p_sys->psz_prefix );
137     if( p_sys->psz_data ) free( p_sys->psz_data );
138     free( p_sys );
139 }
140
141 static int Demux( demux_t *p_demux )
142 {
143     demux_sys_t *p_sys = p_demux->p_sys;
144     char        *psz_parse = NULL;
145     char        *psz_backup = NULL;
146     vlc_bool_t  b_entry = VLC_FALSE;
147     input_item_t *p_input;
148     INIT_PLAYLIST_STUFF;
149
150     /* init txt */
151     if( p_sys->i_data_len < 0 )
152     {
153         int64_t i_pos = 0;
154         p_sys->i_data_len = stream_Size( p_demux->s ) +1; /* This is a cheat to prevent unnecessary realloc */
155         if( p_sys->i_data_len <= 0 && p_sys->i_data_len < 16384 ) p_sys->i_data_len = 1024;
156         p_sys->psz_data = malloc( p_sys->i_data_len * sizeof(char) +1);
157         
158         /* load the complete file */
159         for( ;; )
160         {
161             int i_read = stream_Read( p_demux->s, &p_sys->psz_data[i_pos], p_sys->i_data_len - i_pos );
162             p_sys->psz_data[i_read] = '\0';
163            
164             if( i_read < p_sys->i_data_len - i_pos ) break; /* Done */
165             
166             i_pos += i_read;
167             p_sys->i_data_len += 1024;
168             p_sys->psz_data = realloc( p_sys->psz_data, p_sys->i_data_len * sizeof( char * ) +1 );
169         }
170         if( p_sys->i_data_len <= 0 ) return VLC_EGENERIC;
171     }
172
173     psz_parse = p_sys->psz_data;
174     /* Find first element */
175     if( ( psz_parse = strcasestr( psz_parse, "<ASX" ) ) )
176     {
177         /* ASX element */
178         char *psz_string = NULL;
179         int i_strlen = 0;
180
181         char *psz_base_asx = NULL;
182         char *psz_title_asx = NULL;
183         char *psz_artist_asx = NULL;
184         char *psz_copyright_asx = NULL;
185         char *psz_moreinfo_asx = NULL;
186         char *psz_abstract_asx = NULL;
187         
188         char *psz_base_entry = NULL;
189         char *psz_title_entry = NULL;
190         char *psz_artist_entry = NULL;
191         char *psz_copyright_entry = NULL;
192         char *psz_moreinfo_entry = NULL;
193         char *psz_abstract_entry = NULL;
194         int i_entry_count = 0;
195     
196         psz_parse = strcasestr( psz_parse, ">" );
197
198         while( ( psz_parse = strcasestr( psz_parse, "<" ) ) && psz_parse && *psz_parse )
199         {
200             if( !strncasecmp( psz_parse, "<!--", 4 ) )
201             {
202                 /* this is a comment */
203                 if( ( psz_parse = strcasestr( psz_parse, "-->" ) ) )
204                     psz_parse+=3;
205                 else continue;
206             }
207             else if( !strncasecmp( psz_parse, "<PARAM ", 7 ) )
208             {
209                 vlc_bool_t b_encoding_flag = VLC_FALSE;
210                 psz_parse+=7;
211                 if( !strncasecmp( psz_parse, "name", 4 ) )
212                 {
213                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
214                     {
215                         psz_backup = ++psz_parse;
216                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
217                         {
218                             i_strlen = psz_parse-psz_backup;
219                             if( i_strlen < 1 ) continue;
220                             msg_Dbg( p_demux, "param name strlen: %d", i_strlen);
221                             psz_string = malloc( i_strlen *sizeof( char ) +1);
222                             memcpy( psz_string, psz_backup, i_strlen );
223                             psz_string[i_strlen] = '\0';
224                             msg_Dbg( p_demux, "param name: %s", psz_string);
225                             b_encoding_flag = !strcasecmp( psz_string, "encoding" );
226                             free( psz_string );
227                         }
228                         else continue;
229                     }
230                     else continue;
231                 }
232                 psz_parse++;
233                 if( !strncasecmp( psz_parse, "value", 5 ) )
234                 {
235                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
236                     {
237                         psz_backup = ++psz_parse;
238                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
239                         {
240                             i_strlen = psz_parse-psz_backup;
241                             if( i_strlen < 1 ) continue;
242                             msg_Dbg( p_demux, "param value strlen: %d", i_strlen);
243                             psz_string = malloc( i_strlen *sizeof( char ) +1);
244                             memcpy( psz_string, psz_backup, i_strlen );
245                             psz_string[i_strlen] = '\0';
246                             msg_Dbg( p_demux, "param value: %s", psz_string);
247                             if( b_encoding_flag && !strcasecmp( psz_string, "utf-8" ) ) p_sys->b_utf8 = VLC_TRUE;
248                             free( psz_string );
249                         }
250                         else continue;
251                     }
252                     else continue;
253                 }
254                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
255                     psz_parse += 2;
256                 else continue;
257             }
258             else if( !strncasecmp( psz_parse, "<BANNER", 7 ) )
259             {
260                 /* We skip this element */
261                 if( ( psz_parse = strcasestr( psz_parse, "</BANNER>" ) ) )
262                     psz_parse += 9;
263                 else continue;
264             }
265             else if( !strncasecmp( psz_parse, "<PREVIEWDURATION", 16 ) ||
266                      !strncasecmp( psz_parse, "<LOGURL", 7 ) ||
267                      !strncasecmp( psz_parse, "<Skin", 5 ) )
268             {
269                 /* We skip this element */
270                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
271                     psz_parse += 2;
272                 else continue;
273             }
274             else if( !strncasecmp( psz_parse, "<BASE ", 6 ) )
275             {
276                 psz_parse+=6;
277                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
278                 {
279                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
280                     {
281                         psz_backup = ++psz_parse;
282                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
283                         {
284                             StoreString( p_demux, (b_entry ? &psz_base_entry : &psz_base_asx), psz_backup, psz_parse );
285                         }
286                         else continue;
287                     }
288                     else continue;
289                 }
290                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
291                     psz_parse += 2;
292                 else continue;
293             }
294             else if( !strncasecmp( psz_parse, "<TITLE>", 7 ) )
295             {
296                 psz_backup = psz_parse+=7;
297                 if( ( psz_parse = strcasestr( psz_parse, "</TITLE>" ) ) )
298                 {
299                     StoreString( p_demux, (b_entry ? &psz_title_entry : &psz_title_asx), psz_backup, psz_parse );
300                     psz_parse += 8;
301                 }
302                 else continue;
303             }
304             else if( !strncasecmp( psz_parse, "<Author>", 8 ) )
305             {
306                 psz_backup = psz_parse+=8;
307                 if( ( psz_parse = strcasestr( psz_parse, "</Author>" ) ) )
308                 {
309                     StoreString( p_demux, (b_entry ? &psz_artist_entry : &psz_artist_asx), psz_backup, psz_parse );
310                     psz_parse += 9;
311                 }
312                 else continue;
313             }
314             else if( !strncasecmp( psz_parse, "<Copyright", 10 ) )
315             {
316                 psz_backup = psz_parse+=11;
317                 if( ( psz_parse = strcasestr( psz_parse, "</Copyright>" ) ) )
318                 {
319                     StoreString( p_demux, (b_entry ? &psz_copyright_entry : &psz_copyright_asx), psz_backup, psz_parse );
320                     psz_parse += 12;
321                 }
322                 else continue;
323             }
324             else if( !strncasecmp( psz_parse, "<MoreInfo ", 10 ) )
325             {
326                 psz_parse+=10;
327                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
328                 {
329                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
330                     {
331                         psz_backup = ++psz_parse;
332                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
333                         {
334                             StoreString( p_demux, (b_entry ? &psz_moreinfo_entry : &psz_moreinfo_asx), psz_backup, psz_parse );
335                         }
336                         else continue;
337                     }
338                     else continue;
339                 }
340                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
341                     psz_parse += 2;
342                 else continue;
343             }
344             else if( !strncasecmp( psz_parse, "<ABSTRACT>", 10 ) )
345             {
346                 psz_backup = psz_parse+=10;
347                 if( ( psz_parse = strcasestr( psz_parse, "</ABSTRACT>" ) ) )
348                 {
349                     StoreString( p_demux, (b_entry ? &psz_abstract_entry : &psz_abstract_asx), psz_backup, psz_parse );
350                     psz_parse += 11;
351                 }
352                 else continue;
353             }
354             else if( !strncasecmp( psz_parse, "<EntryRef ", 10 ) )
355             {
356                 psz_parse+=10;
357                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
358                 {
359                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
360                     {
361                         psz_backup = ++psz_parse;
362                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
363                         {
364                             i_strlen = psz_parse-psz_backup;
365                             if( i_strlen < 1 ) continue;
366                             psz_string = malloc( i_strlen*sizeof( char ) +1);
367                             memcpy( psz_string, psz_backup, i_strlen );
368                             psz_string[i_strlen] = '\0';
369                             p_input = input_ItemNew( p_playlist, psz_string, psz_title_asx );
370                             input_ItemCopyOptions( p_current->p_input, p_input );
371                             playlist_BothAddInput( p_playlist, p_input,
372                                                    p_item_in_category,
373                                             PLAYLIST_APPEND|PLAYLIST_SPREPARSE,
374                                             PLAYLIST_END );
375                             free( psz_string );
376                         }
377                         else continue;
378                     }
379                     else continue;
380                 }
381                 if( ( psz_parse = strcasestr( psz_parse, "/>" ) ) )
382                     psz_parse += 2;
383                 else continue;
384             }
385             else if( !strncasecmp( psz_parse, "</Entry>", 8 ) )
386             {
387                 /* add a new entry */
388                 psz_parse+=8;
389                 if( !b_entry )
390                 {
391                     msg_Err( p_demux, "end of entry without start?" );
392                     continue;
393                 }
394                 /* cleanup entry */
395                 FREENULL( psz_title_entry )
396                 FREENULL( psz_base_entry )
397                 FREENULL( psz_artist_entry )
398                 FREENULL( psz_copyright_entry )
399                 FREENULL( psz_moreinfo_entry )
400                 FREENULL( psz_abstract_entry )
401                 b_entry = VLC_FALSE;
402             }
403             else if( !strncasecmp( psz_parse, "<Entry", 6 ) )
404             {
405                 psz_parse+=6;
406                 if( b_entry )
407                 {
408                     msg_Err( p_demux, "We already are in an entry section" );
409                     continue;
410                 }
411                 i_entry_count += 1;
412                 b_entry = VLC_TRUE;
413                 psz_parse = strcasestr( psz_parse, ">" );
414             }
415             else if( !strncasecmp( psz_parse, "<Ref ", 5 ) )
416             {
417                 psz_parse+=5;
418                 if( !b_entry )
419                 {
420                     msg_Err( p_demux, "A ref outside an entry section" );
421                     continue;
422                 }
423                 
424                 if( !strncasecmp( psz_parse, "HREF", 4 ) )
425                 {
426                     if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
427                     {
428                         psz_backup = ++psz_parse;
429                         if( ( psz_parse = strcasestr( psz_parse, "\"" ) ) )
430                         {
431                             input_item_t *p_entry = NULL;
432                             char *psz_name = NULL;
433                             i_strlen = psz_parse-psz_backup;
434                             if( i_strlen < 1 ) continue;
435                             psz_string = malloc( i_strlen*sizeof( char ) +1);
436                             memcpy( psz_string, psz_backup, i_strlen );
437                             psz_string[i_strlen] = '\0';
438
439                             /* create the new entry */
440                             asprintf( &psz_name, "%d %s", i_entry_count, ( psz_title_entry ? psz_title_entry : p_current->p_input->psz_name ) );
441                             p_entry = input_ItemNew( p_playlist, psz_string, psz_name );
442                             FREENULL( psz_name );
443                             
444                             input_ItemCopyOptions( p_current->p_input, p_entry );
445                             p_entry->p_meta = vlc_meta_New();
446                             if( psz_title_entry ) vlc_meta_SetTitle( p_entry->p_meta, psz_title_entry );
447                             if( psz_artist_entry ) vlc_meta_SetArtist( p_entry->p_meta, psz_artist_entry );
448                             if( psz_copyright_entry ) vlc_meta_SetCopyright( p_entry->p_meta, psz_copyright_entry );
449                             if( psz_moreinfo_entry ) vlc_meta_SetURL( p_entry->p_meta, psz_moreinfo_entry );
450                             if( psz_abstract_entry ) vlc_meta_SetDescription( p_entry->p_meta, psz_abstract_entry );
451                             playlist_BothAddInput( p_playlist, p_entry,
452                                                  p_item_in_category,
453                                                  PLAYLIST_APPEND | PLAYLIST_SPREPARSE
454                                                  , PLAYLIST_END);
455                             free( psz_string );
456                         }
457                         else continue;
458                     }
459                     else continue;
460                 }
461                 if( ( psz_parse = strcasestr( psz_parse, ">" ) ) )
462                     psz_parse++;
463                 else continue;
464             }
465             else if( !strncasecmp( psz_parse, "</ASX", 5 ) )
466             {
467                 vlc_mutex_lock( &p_current->p_input->lock );
468                 if( !p_current->p_input->p_meta ) p_current->p_input->p_meta = vlc_meta_New();
469                 if( psz_title_asx ) vlc_meta_SetTitle( p_current->p_input->p_meta, psz_title_asx );
470                 if( psz_artist_asx ) vlc_meta_SetArtist( p_current->p_input->p_meta, psz_artist_asx );
471                 if( psz_copyright_asx ) vlc_meta_SetCopyright( p_current->p_input->p_meta, psz_copyright_asx );
472                 if( psz_moreinfo_asx ) vlc_meta_SetURL( p_current->p_input->p_meta, psz_moreinfo_asx );
473                 if( psz_abstract_asx ) vlc_meta_SetDescription( p_current->p_input->p_meta, psz_abstract_asx );
474                 vlc_mutex_unlock( &p_current->p_input->lock );
475                 FREENULL( psz_base_asx );
476                 FREENULL( psz_title_asx );
477                 FREENULL( psz_artist_asx );
478                 FREENULL( psz_copyright_asx );
479                 FREENULL( psz_moreinfo_asx );
480                 FREENULL( psz_abstract_asx );
481                 psz_parse++;
482             }
483             else psz_parse++;
484         }
485 #if 0
486 /* FIXME Unsupported elements */
487             PARAM
488             EVENT
489             REPEAT
490             DURATION
491             ENDMARK
492             STARTMARK
493             STARTTIME
494 #endif
495     }
496     HANDLE_PLAY_AND_RELEASE;
497     return -1; /* Needed for correct operation of go back */
498 }
499
500 static int Control( demux_t *p_demux, int i_query, va_list args )
501 {
502     return VLC_EGENERIC;
503 }