1 /*****************************************************************************
2 * m3u.c: a meta demux to parse pls, m3u and asx playlists
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: m3u.c,v 1.15 2003/03/06 15:30:42 sigmunau Exp $
7 * Authors: Sigmund Augdal <sigmunau@idi.ntnu.no>
8 * Gildas Bazin <gbazin@netcourrier.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
29 #include <string.h> /* strdup() */
33 #include <vlc/input.h>
34 #include <vlc_playlist.h>
36 #include <sys/types.h>
38 /*****************************************************************************
39 * Constants and structures
40 *****************************************************************************/
43 #define TYPE_UNKNOWN 0
51 int i_type; /* playlist type (m3u/asx) */
54 /*****************************************************************************
56 *****************************************************************************/
57 static int Activate ( vlc_object_t * );
58 static void Deactivate( vlc_object_t * );
59 static int Demux ( input_thread_t * );
61 /*****************************************************************************
63 *****************************************************************************/
65 set_description( _("playlist metademux") );
66 set_capability( "demux", 10 );
67 set_callbacks( Activate, Deactivate );
68 add_shortcut( "m3u" );
69 add_shortcut( "asx" );
70 add_shortcut( "html" );
71 add_shortcut( "pls" );
74 /*****************************************************************************
75 * Activate: initializes m3u demux structures
76 *****************************************************************************/
77 static int Activate( vlc_object_t * p_this )
79 input_thread_t *p_input = (input_thread_t *)p_this;
84 /* Initialize access plug-in structures. */
85 if( p_input->i_mtu == 0 )
88 p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
91 p_input->pf_demux = Demux;
92 p_input->pf_rewind = NULL;
94 /* Check for m3u/asx file extension */
95 psz_ext = strrchr ( p_input->psz_name, '.' );
98 if( !strcasecmp( psz_ext, ".m3u") ||
99 ( p_input->psz_demux && !strncmp(p_input->psz_demux, "m3u", 3) ) )
103 else if( !strcasecmp( psz_ext, ".asx") ||
104 ( p_input->psz_demux && !strncmp(p_input->psz_demux, "asx", 3) ) )
108 else if( !strcasecmp( psz_ext, ".html") ||
109 ( p_input->psz_demux && !strncmp(p_input->psz_demux, "html", 4) ) )
113 else if( !strcasecmp( psz_ext, ".pls") ||
114 ( p_input->psz_demux && !strncmp(p_input->psz_demux, "pls", 3) ) )
120 /* we had no luck looking at the file extention, so we have a look
121 * at the content. This is useful for .asp, .php and similar files
122 * that are actually html. Also useful for som asx files that have
123 * another extention */
127 int i_size = input_Peek( p_input, &p_peek, MAX_LINE );
128 i_size -= sizeof("[playlist]") - 1;
131 && strncasecmp( p_peek, "[playlist]", sizeof("[playlist]") - 1 )
132 && strncasecmp( p_peek, "<html>", sizeof("<html>") - 1 )
133 && strncasecmp( p_peek, "<asx", sizeof("<asx") - 1 ) )
142 else if ( !strncasecmp( p_peek, "[playlist]", sizeof("[playlist]") -1 ) )
146 else if ( !strncasecmp( p_peek, "<html>", sizeof("<html>") -1 ) )
150 else if ( !strncasecmp( p_peek, "<asx", sizeof("<asx") -1 ) )
157 if( !( p_m3u = malloc( sizeof( demux_sys_t ) ) ) )
159 msg_Err( p_input, "out of memory" );
162 p_input->p_demux_data = p_m3u;
164 p_m3u->i_type = i_type;
169 /*****************************************************************************
170 * Deactivate: frees unused data
171 *****************************************************************************/
172 static void Deactivate( vlc_object_t *p_this )
174 input_thread_t *p_input = (input_thread_t *)p_this;
175 demux_sys_t *p_m3u = (demux_sys_t *)p_input->p_demux_data ;
180 /*****************************************************************************
181 * ProcessLine: read a "line" from the file and add any entries found
182 * to the playlist. Return number of items added ( 0 or 1 )
183 *****************************************************************************/
184 static int ProcessLine ( input_thread_t *p_input , demux_sys_t *p_m3u
185 , playlist_t *p_playlist , char psz_line[MAX_LINE], int i_position )
187 char *psz_bol, *psz_name;
190 /* Remove unnecessary tabs or spaces at the beginning of line */
191 while( *psz_bol == ' ' || *psz_bol == '\t' ||
192 *psz_bol == '\n' || *psz_bol == '\r' )
195 if( p_m3u->i_type == TYPE_M3U )
197 /* Check for comment line */
198 if( *psz_bol == '#' )
199 /*line is comment or extended info, ignored for now */
202 else if ( p_m3u->i_type == TYPE_PLS )
204 /* We are dealing with .pls files from shoutcast
205 * We are looking for lines like "File1=http://..." */
206 if( !strncasecmp( psz_bol, "File", sizeof("File") - 1 ) )
208 psz_bol += sizeof("File") - 1;
209 psz_bol = strchr( psz_bol, '=' );
210 if ( !psz_bol ) return 0;
218 else if ( p_m3u->i_type == TYPE_ASX )
220 /* We are dealing with ASX files.
221 * We are looking for "<ref href=" xml markups that
222 * begins with "mms://", "http://" or "file://" */
226 strncasecmp( psz_bol, "ref", sizeof("ref") - 1 ) )
229 if( !*psz_bol ) return 0;
232 strncasecmp( psz_bol, "href", sizeof("href") - 1 ) )
235 if( !*psz_bol ) return 0;
238 strncasecmp( psz_bol, "mms://",
239 sizeof("mms://") - 1 ) &&
240 strncasecmp( psz_bol, "http://",
241 sizeof("http://") - 1 ) &&
242 strncasecmp( psz_bol, "file://",
243 sizeof("file://") - 1 ) )
246 if( !*psz_bol ) return 0;
248 psz_eol = strchr( psz_bol, '"');
254 else if ( p_m3u->i_type == TYPE_HTML )
256 /* We are dealing with a html file with embedded
257 * video. We are looking for "<param name="filename"
258 * value=" html markups that begin with "http://" */
262 strncasecmp( psz_bol, "param", sizeof("param") - 1 ) )
265 if( !*psz_bol ) return 0;
268 strncasecmp( psz_bol, "filename", sizeof("filename") - 1 ) )
271 if( !*psz_bol ) return 0;
274 strncasecmp( psz_bol, "http://",
275 sizeof("http://") - 1 ) )
278 if( !*psz_bol ) return 0;
280 psz_eol = strchr( psz_bol, '"');
289 msg_Warn( p_input, "unknown file type" );
294 if ( !*psz_bol ) return 0;
297 * From now on, we know we've got a meaningful line
300 /* check for a protocol name */
301 /* for URL, we should look for "://"
302 * for MRL (Media Resource Locator) ([[<access>][/<demux>]:][<source>]),
303 * we should look for ":"
304 * so we end up looking simply for ":"*/
305 /* PB: on some file systems, ':' are valid characters though*/
307 while( *psz_name && *psz_name!=':' )
312 if ( *psz_name && ( psz_name == psz_bol + 1 ) )
314 /* if it is not an URL,
315 * as it is unlikely to be an MRL (PB: if it is ?)
316 * it should be an absolute file name with the drive letter */
317 if ( *(psz_name+1) == '/' )/* "*:/" */
319 if ( *(psz_name+2) != '/' )/* not "*://" */
320 while ( *psz_name ) *psz_name++;/* so now (*psz_name==0) */
322 else while ( *psz_name ) *psz_name++;/* "*:*"*/
326 /* if the line doesn't specify a protocol name,
327 * check if the line has an absolute or relative path */
329 if( !*psz_name && *psz_bol != '/' )
330 /* If this line doesn't begin with a '/' */
335 && *(psz_bol+1)!=':' )
336 /* if this line doesn't begin with
337 * "/" or "\" or "*:" or "*:\" or "*:/" or "\\" */
340 /* assume the path is relative to the path of the m3u file. */
341 char *psz_path = strdup( p_input->psz_name );
344 psz_name = strrchr( psz_path, '/' );
346 psz_name = strrchr( psz_path, '\\' );
347 if ( ! psz_name ) psz_name = strrchr( psz_path, '/' );
349 if( psz_name ) *psz_name = '\0';
350 else *psz_path = '\0';
352 psz_name = malloc( strlen(psz_path) + strlen(psz_bol) + 2 );
353 sprintf( psz_name, "%s/%s", psz_path, psz_bol );
355 if ( *psz_path != '\0' )
357 psz_name = malloc( strlen(psz_path) + strlen(psz_bol) + 2 );
358 sprintf( psz_name, "%s\\%s", psz_path, psz_bol );
360 else psz_name = strdup( psz_bol );
366 psz_name = strdup( psz_bol );
369 playlist_Add( p_playlist, psz_name,
370 PLAYLIST_INSERT, i_position );
376 /*****************************************************************************
377 * Demux: reads and demuxes data packets
378 *****************************************************************************
379 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
380 *****************************************************************************/
381 static int Demux ( input_thread_t *p_input )
383 data_packet_t *p_data;
384 char *p_buf, psz_line[MAX_LINE], eol_tok;
385 int i_size, i_bufpos, i_linepos = 0;
386 playlist_t *p_playlist;
388 vlc_bool_t b_discard = VLC_FALSE;
390 demux_sys_t *p_m3u = (demux_sys_t *)p_input->p_demux_data;
392 p_playlist = (playlist_t *) vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
396 msg_Err( p_input, "can't find playlist" );
400 p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
401 i_position = p_playlist->i_index + 1;
403 /* Depending on wether we are dealing with an m3u/asf file, the end of
404 * line token will be different */
405 if( p_m3u->i_type == TYPE_ASX || p_m3u->i_type == TYPE_HTML )
410 while( ( i_size = input_SplitBuffer( p_input, &p_data, MAX_LINE ) ) > 0 )
412 i_bufpos = 0; p_buf = p_data->p_payload_start;
416 /* Build a line < MAX_LINE */
417 while( p_buf[i_bufpos] != eol_tok && i_size )
419 if( i_linepos == MAX_LINE || b_discard == VLC_TRUE )
421 /* line is bigger than MAX_LINE, discard it */
423 b_discard = VLC_TRUE;
427 if ( eol_tok != '\n' || p_buf[i_bufpos] != '\r' )
429 psz_line[i_linepos] = p_buf[i_bufpos];
434 i_size--; i_bufpos++;
437 /* Check if we need more data */
438 if( !i_size ) continue;
440 i_size--; i_bufpos++;
441 b_discard = VLC_FALSE;
443 /* Check for empty line */
444 if( !i_linepos ) continue;
446 psz_line[i_linepos] = '\0';
448 i_position += ProcessLine ( p_input, p_m3u , p_playlist ,
449 psz_line, i_position );
452 input_DeletePacket( p_input->p_method_data, p_data );
455 if ( i_linepos && b_discard != VLC_TRUE && eol_tok == '\n' )
457 psz_line[i_linepos] = '\0';
459 i_position += ProcessLine ( p_input, p_m3u , p_playlist , psz_line,
463 vlc_object_release( p_playlist );