* added a vlc_current_charset function. This tries to get the current charset
[vlc.git] / src / playlist / playlist.c
1 /*****************************************************************************
2  * playlist.c : Playlist management functions
3  *****************************************************************************
4  * Copyright (C) 1999-2001 VideoLAN
5  * $Id: playlist.c,v 1.46 2003/08/23 12:59:31 hartman Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23 #include <stdlib.h>                                      /* free(), strtol() */
24 #include <stdio.h>                                              /* sprintf() */
25 #include <string.h>                                            /* strerror() */
26
27 #include <vlc/vlc.h>
28
29 #include "stream_control.h"
30 #include "input_ext-intf.h"
31
32 #include "vlc_playlist.h"
33
34 #define PLAYLIST_FILE_HEADER_0_5  "# vlc playlist file version 0.5"
35
36 /*****************************************************************************
37  * Local prototypes
38  *****************************************************************************/
39 static void RunThread ( playlist_t * );
40 static void SkipItem  ( playlist_t *, int );
41 static void PlayItem  ( playlist_t * );
42
43 static void Poubellize ( playlist_t *, input_thread_t * );
44
45 /**
46  * Create playlist
47  *
48  * Create a playlist structure.
49  * \param p_parent the vlc object that is to be the parent of this playlist
50  * \return a pointer to the created playlist, or NULL on error
51  */
52 playlist_t * __playlist_Create ( vlc_object_t *p_parent )
53 {
54     playlist_t *p_playlist;
55     vlc_value_t     val;
56
57     /* Allocate structure */
58     p_playlist = vlc_object_create( p_parent, VLC_OBJECT_PLAYLIST );
59     if( !p_playlist )
60     {
61         msg_Err( p_parent, "out of memory" );
62         return NULL;
63     }
64
65     var_Create( p_playlist, "intf-change", VLC_VAR_BOOL );
66     val.b_bool = VLC_TRUE;
67     var_Set( p_playlist, "intf-change", val );
68
69     var_Create( p_playlist, "intf-popupmenu", VLC_VAR_VOID );
70
71     var_Create( p_playlist, "intf-show", VLC_VAR_BOOL );
72     val.b_bool = VLC_TRUE;
73     var_Set( p_playlist, "intf-show", val );
74
75     p_playlist->p_input = NULL;
76     p_playlist->i_status = PLAYLIST_STOPPED;
77     p_playlist->i_index = -1;
78     p_playlist->i_size = 0;
79     p_playlist->pp_items = NULL;
80
81     if( vlc_thread_create( p_playlist, "playlist", RunThread,
82                            VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) )
83     {
84         msg_Err( p_playlist, "cannot spawn playlist thread" );
85         vlc_object_destroy( p_playlist );
86         return NULL;
87     }
88
89     /* The object has been initialized, now attach it */
90     vlc_object_attach( p_playlist, p_parent );
91
92     return p_playlist;
93 }
94
95 /**
96  * Destroy the playlist.
97  *
98  * Delete all items in the playlist and free the playlist structure.
99  * \param p_playlist the playlist structure to destroy
100  */
101 void playlist_Destroy( playlist_t * p_playlist )
102 {
103     p_playlist->b_die = 1;
104
105     vlc_thread_join( p_playlist );
106
107     var_Destroy( p_playlist, "intf-change" );
108
109     vlc_object_destroy( p_playlist );
110 }
111
112 /**
113  * Add an MRL to the playlist. This is a simplified version of
114  * playlist_AddExt inculded for convenince. It equals calling playlist_AddExt
115  * with psz_name == psz_target and i_duration == -1
116  */
117
118 int playlist_Add( playlist_t *p_playlist, const char *psz_target,
119                   const char **ppsz_options, int i_options,
120                   int i_mode, int i_pos )
121 {
122     return playlist_AddExt( p_playlist, psz_target, psz_target, -1,
123                             ppsz_options, i_options, i_mode, i_pos );
124 }
125
126 /**
127  * Add a MRL into the playlist.
128  *
129  * \param p_playlist the playlist to add into
130  * \param psz_uri the mrl to add to the playlist
131  * \param psz_name a text giving a name or description of this item
132  * \param i_duration a hint about the duration of this item, in miliseconds, or
133  *        -1 if unknown.
134  * \param ppsz_options array of options
135  * \param i_options number of items in ppsz_options
136  * \param i_mode the mode used when adding
137  * \param i_pos the position in the playlist where to add. If this is
138  *        PLAYLIST_END the item will be added at the end of the playlist
139  *        regardless of it's size
140  * \return always returns 0
141 */
142 int playlist_AddExt( playlist_t *p_playlist, const char * psz_uri,
143                      const char * psz_name, mtime_t i_duration,
144                      const char **ppsz_options, int i_options, int i_mode,
145                      int i_pos )
146 {
147     playlist_item_t * p_item;
148
149     p_item = malloc( sizeof( playlist_item_t ) );
150     if( p_item == NULL )
151     {
152         msg_Err( p_playlist, "out of memory" );
153     }
154
155     p_item->psz_name = strdup( psz_name );
156     p_item->psz_uri  = strdup( psz_uri );
157     p_item->i_duration = i_duration;
158     p_item->i_type = 0;
159     p_item->i_status = 0;
160     p_item->b_autodeletion = VLC_FALSE;
161
162     p_item->ppsz_options = NULL;
163     p_item->i_options = i_options;
164
165     if( i_options )
166     {
167         int i;
168
169         p_item->ppsz_options = (char **)malloc( i_options * sizeof(char *) );
170         for( i = 0; i < i_options; i++ )
171             p_item->ppsz_options[i] = strdup( ppsz_options[i] );
172
173     }
174
175     return playlist_AddItem( p_playlist, p_item, i_mode, i_pos );
176 }
177
178 /**
179  * Add a playlist item into a playlist
180  *
181  * \param p_playlist the playlist to insert into
182  * \param p_item the playlist item to insert
183  * \param i_mode the mode used when adding
184  * \param i_pos the possition in the playlist where to add. If this is
185  *        PLAYLIST_END the item will be added at the end of the playlist
186  *        regardless of it's size
187  * \return always returns 0
188 */
189 int playlist_AddItem( playlist_t *p_playlist, playlist_item_t * p_item,
190                 int i_mode, int i_pos)
191 {
192     vlc_value_t     val;
193
194     vlc_mutex_lock( &p_playlist->object_lock );
195
196     /*
197      * CHECK_INSERT : checks if the item is already enqued before
198      * enqueing it
199      */
200     if ( i_mode & PLAYLIST_CHECK_INSERT )
201     {
202          int j;
203
204          if ( p_playlist->pp_items )
205          {
206              for ( j = 0; j < p_playlist->i_size; j++ )
207              {
208                  if ( !strcmp( p_playlist->pp_items[j]->psz_uri, p_item->psz_uri ) )
209                  {
210                       if( p_item->psz_name )
211                       {
212                           free( p_item->psz_name );
213                       }
214                       if( p_item->psz_uri )
215                       {
216                           free( p_item->psz_uri );
217                       }
218                       free( p_item );
219                       vlc_mutex_unlock( &p_playlist->object_lock );
220                       return 0;
221                  }
222              }
223          }
224          i_mode &= ~PLAYLIST_CHECK_INSERT;
225          i_mode |= PLAYLIST_APPEND;
226     }
227
228
229     msg_Dbg( p_playlist, "adding playlist item « %s » ( %s )", p_item->psz_name, p_item->psz_uri);
230
231     /* Create the new playlist item */
232
233
234     /* Do a few boundary checks and allocate space for the item */
235     if( i_pos == PLAYLIST_END )
236     {
237         if( i_mode & PLAYLIST_INSERT )
238         {
239             i_mode &= ~PLAYLIST_INSERT;
240             i_mode |= PLAYLIST_APPEND;
241         }
242
243         i_pos = p_playlist->i_size - 1;
244     }
245
246     if( !(i_mode & PLAYLIST_REPLACE)
247          || i_pos < 0 || i_pos >= p_playlist->i_size )
248     {
249         /* Additional boundary checks */
250         if( i_mode & PLAYLIST_APPEND )
251         {
252             i_pos++;
253         }
254
255         if( i_pos < 0 )
256         {
257             i_pos = 0;
258         }
259         else if( i_pos > p_playlist->i_size )
260         {
261             i_pos = p_playlist->i_size;
262         }
263
264         INSERT_ELEM( p_playlist->pp_items,
265                      p_playlist->i_size,
266                      i_pos,
267                      p_item );
268
269         if( p_playlist->i_index >= i_pos )
270         {
271             p_playlist->i_index++;
272         }
273     }
274     else
275     {
276         /* i_mode == PLAYLIST_REPLACE and 0 <= i_pos < p_playlist->i_size */
277         if( p_playlist->pp_items[i_pos]->psz_name )
278         {
279             free( p_playlist->pp_items[i_pos]->psz_name );
280         }
281         if( p_playlist->pp_items[i_pos]->psz_uri )
282         {
283             free( p_playlist->pp_items[i_pos]->psz_uri );
284         }
285         /* XXX: what if the item is still in use? */
286         free( p_playlist->pp_items[i_pos] );
287         p_playlist->pp_items[i_pos] = p_item;
288     }
289
290     if( i_mode & PLAYLIST_GO )
291     {
292         p_playlist->i_index = i_pos;
293         if( p_playlist->p_input )
294         {
295             input_StopThread( p_playlist->p_input );
296         }
297         p_playlist->i_status = PLAYLIST_RUNNING;
298     }
299
300     vlc_mutex_unlock( &p_playlist->object_lock );
301
302     val.b_bool = VLC_TRUE;
303     var_Set( p_playlist, "intf-change", val );
304
305     return 0;
306 }
307
308 /**
309  * delete an item from a playlist.
310  *
311  * \param p_playlist the playlist to remove from.
312  * \param i_pos the position of the item to remove
313  * \return returns 0
314  */
315 int playlist_Delete( playlist_t * p_playlist, int i_pos )
316 {
317     vlc_value_t     val;
318     vlc_mutex_lock( &p_playlist->object_lock );
319
320     if( i_pos >= 0 && i_pos < p_playlist->i_size )
321     {
322         msg_Dbg( p_playlist, "deleting playlist item « %s »",
323                              p_playlist->pp_items[i_pos]->psz_name );
324
325         if( p_playlist->pp_items[i_pos]->psz_name )
326         {
327             free( p_playlist->pp_items[i_pos]->psz_name );
328         }
329         if( p_playlist->pp_items[i_pos]->psz_uri )
330         {
331             free( p_playlist->pp_items[i_pos]->psz_uri );
332         }
333         if( p_playlist->pp_items[i_pos]->i_options )
334         {
335             int i;
336
337             for( i = 0; i < p_playlist->pp_items[i_pos]->i_options; i++ )
338                 free( p_playlist->pp_items[i_pos]->ppsz_options[i] );
339
340             free( p_playlist->pp_items[i_pos]->ppsz_options );
341         }
342
343         /* XXX: what if the item is still in use? */
344         free( p_playlist->pp_items[i_pos] );
345
346         if( i_pos <= p_playlist->i_index )
347         {
348             p_playlist->i_index--;
349         }
350
351         /* Renumber the playlist */
352         REMOVE_ELEM( p_playlist->pp_items,
353                      p_playlist->i_size,
354                      i_pos );
355     }
356
357     vlc_mutex_unlock( &p_playlist->object_lock );
358
359     val.b_bool = VLC_TRUE;
360     var_Set( p_playlist, "intf-change", val );
361
362     return 0;
363 }
364
365 /**
366  * Move an item in a playlist
367  *
368  * Move the item in the playlist with position i_pos before the current item
369  * at position i_newpos.
370  * \param p_playlist the playlist to move items in
371  * \param i_pos the position of the item to move
372  * \param i_newpos the position of the item that will be behind the moved item
373  *        after the move
374  * \return returns 0
375  */
376 int playlist_Move( playlist_t * p_playlist, int i_pos, int i_newpos)
377 {
378     vlc_value_t     val;
379     vlc_mutex_lock( &p_playlist->object_lock );
380
381     /* take into account that our own row disappears. */
382     if ( i_pos < i_newpos ) i_newpos--;
383
384     if( i_pos >= 0 && i_newpos >=0 && i_pos <= p_playlist->i_size 
385                      && i_newpos <= p_playlist->i_size )
386     {
387         playlist_item_t * temp;
388
389         msg_Dbg( p_playlist, "moving playlist item « %s »",
390                              p_playlist->pp_items[i_pos]->psz_name );
391
392         if( i_pos == p_playlist->i_index )
393         {
394             p_playlist->i_index = i_newpos;
395         }
396         else if( i_pos > p_playlist->i_index && i_newpos <= p_playlist->i_index )
397         {
398             p_playlist->i_index++;
399         }
400         else if( i_pos < p_playlist->i_index && i_newpos >= p_playlist->i_index )
401         {
402             p_playlist->i_index--;
403         }
404
405         if ( i_pos < i_newpos )
406         {
407             temp = p_playlist->pp_items[i_pos];
408             while ( i_pos < i_newpos )
409             {
410                 p_playlist->pp_items[i_pos] = p_playlist->pp_items[i_pos+1];
411                 i_pos++;
412             }
413             p_playlist->pp_items[i_newpos] = temp;
414         }
415         else if ( i_pos > i_newpos )
416         {
417             temp = p_playlist->pp_items[i_pos];
418             while ( i_pos > i_newpos )
419             {
420                 p_playlist->pp_items[i_pos] = p_playlist->pp_items[i_pos-1];
421                 i_pos--;
422             }
423             p_playlist->pp_items[i_newpos] = temp;
424         }
425     }
426
427     vlc_mutex_unlock( &p_playlist->object_lock );
428
429     val.b_bool = VLC_TRUE;
430     var_Set( p_playlist, "intf-change", val );
431
432     return 0;
433 }
434
435 /**
436  * Do a playlist action
437  *
438  * \param p_playlist the playlist to do the command on
439  * \param i_command the command to do
440  * \param i_arg the argument to the command. See playlist_command_t for details
441  */
442  void playlist_Command( playlist_t * p_playlist, playlist_command_t i_command,
443                        int i_arg )
444 {
445     vlc_mutex_lock( &p_playlist->object_lock );
446
447     switch( i_command )
448     {
449     case PLAYLIST_STOP:
450         p_playlist->i_status = PLAYLIST_STOPPED;
451         if( p_playlist->p_input )
452         {
453             input_StopThread( p_playlist->p_input );
454         }
455         break;
456
457     case PLAYLIST_PLAY:
458         p_playlist->i_status = PLAYLIST_RUNNING;
459         if( !p_playlist->p_input )
460         {
461             PlayItem( p_playlist );
462         }
463         if( p_playlist->p_input )
464         {
465             input_SetStatus( p_playlist->p_input, INPUT_STATUS_PLAY );
466         }
467         break;
468
469     case PLAYLIST_PAUSE:
470         p_playlist->i_status = PLAYLIST_PAUSED;
471         if( p_playlist->p_input )
472         {
473             input_SetStatus( p_playlist->p_input, INPUT_STATUS_PAUSE );
474         }
475         break;
476
477     case PLAYLIST_SKIP:
478         p_playlist->i_status = PLAYLIST_STOPPED;
479         SkipItem( p_playlist, i_arg );
480         if( p_playlist->p_input )
481         {
482             input_StopThread( p_playlist->p_input );
483         }
484         p_playlist->i_status = PLAYLIST_RUNNING;
485         break;
486
487     case PLAYLIST_GOTO:
488         if( i_arg >= 0 && i_arg < p_playlist->i_size )
489         {
490             p_playlist->i_index = i_arg;
491             if( p_playlist->p_input )
492             {
493                 input_StopThread( p_playlist->p_input );
494             }
495             p_playlist->i_status = PLAYLIST_RUNNING;
496         }
497         break;
498
499     default:
500         msg_Err( p_playlist, "unknown playlist command" );
501         break;
502     }
503
504     vlc_mutex_unlock( &p_playlist->object_lock );
505
506     return;
507 }
508 /* Following functions are local */
509
510 /*****************************************************************************
511  * RunThread: main playlist thread
512  *****************************************************************************/
513 static void RunThread ( playlist_t *p_playlist )
514 {
515     /* Tell above that we're ready */
516     vlc_thread_ready( p_playlist );
517
518     while( !p_playlist->b_die )
519     {
520         vlc_mutex_lock( &p_playlist->object_lock );
521
522         /* If there is an input, check that it doesn't need to die. */
523         if( p_playlist->p_input )
524         {
525             /* This input is dead. Remove it ! */
526             if( p_playlist->p_input->b_dead )
527             {
528                 input_thread_t *p_input;
529
530                 /* Unlink current input */
531                 p_input = p_playlist->p_input;
532                 p_playlist->p_input = NULL;
533                 vlc_object_detach( p_input );
534
535                 /* Release the playlist lock, because we may get stuck
536                  * in input_DestroyThread() for some time. */
537                 vlc_mutex_unlock( &p_playlist->object_lock );
538
539                 /* Destroy input */
540                 input_DestroyThread( p_input );
541                 vlc_object_destroy( p_input );
542                 continue;
543             }
544             /* This input is dying, let him do */
545             else if( p_playlist->p_input->b_die )
546             {
547                 ;
548             }
549             /* This input has finished, ask him to die ! */
550             else if( p_playlist->p_input->b_error
551                       || p_playlist->p_input->b_eof )
552             {
553                 /* Check for autodeletion */
554                 if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion )
555                 {
556                     vlc_mutex_unlock( &p_playlist->object_lock );
557                     playlist_Delete( p_playlist, p_playlist->i_index );
558                     vlc_mutex_lock( &p_playlist->object_lock );
559                 }
560
561                 /* Select the next playlist item */
562                 SkipItem( p_playlist, 1 );
563
564                 /* Release the playlist lock, because we may get stuck
565                  * in input_StopThread() for some time. */
566                 vlc_mutex_unlock( &p_playlist->object_lock );
567                 input_StopThread( p_playlist->p_input );
568                 continue;
569             }
570         }
571         else if( p_playlist->i_status != PLAYLIST_STOPPED )
572         {
573             SkipItem( p_playlist, 0 );
574             PlayItem( p_playlist );
575         }
576
577         vlc_mutex_unlock( &p_playlist->object_lock );
578
579         msleep( INTF_IDLE_SLEEP );
580     }
581
582     /* If there is an input, kill it */
583     while( 1 )
584     {
585         vlc_mutex_lock( &p_playlist->object_lock );
586
587         if( p_playlist->p_input == NULL )
588         {
589             vlc_mutex_unlock( &p_playlist->object_lock );
590             break;
591         }
592
593         if( p_playlist->p_input->b_dead )
594         {
595             input_thread_t *p_input;
596
597             /* Unlink current input */
598             p_input = p_playlist->p_input;
599             p_playlist->p_input = NULL;
600             vlc_object_detach( p_input );
601             vlc_mutex_unlock( &p_playlist->object_lock );
602
603             /* Destroy input */
604             input_DestroyThread( p_input );
605             vlc_object_destroy( p_input );
606             continue;
607         }
608         else if( p_playlist->p_input->b_die )
609         {
610             /* This input is dying, leave him alone */
611             ;
612         }
613         else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof )
614         {
615             vlc_mutex_unlock( &p_playlist->object_lock );
616             input_StopThread( p_playlist->p_input );
617             continue;
618         }
619         else
620         {
621             p_playlist->p_input->b_eof = 1;
622         }
623
624         vlc_mutex_unlock( &p_playlist->object_lock );
625
626         msleep( INTF_IDLE_SLEEP );
627     }
628 }
629
630 /*****************************************************************************
631  * SkipItem: go to Xth playlist item
632  *****************************************************************************
633  * This function calculates the position of the next playlist item, depending
634  * on the playlist course mode (forward, backward, random...).
635  *****************************************************************************/
636 static void SkipItem( playlist_t *p_playlist, int i_arg )
637 {
638     int i_oldindex = p_playlist->i_index;
639     vlc_bool_t b_random;
640     vlc_value_t val;
641
642     /* If the playlist is empty, there is no current item */
643     if( p_playlist->i_size == 0 )
644     {
645         p_playlist->i_index = -1;
646         return;
647     }
648
649     b_random = config_GetInt( p_playlist, "random" );
650
651     /* Increment */
652     if( b_random )
653     {
654         srand( (unsigned int)mdate() );
655
656         /* Simple random stuff - we cheat a bit to minimize the chances to
657          * get the same index again. */
658         i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
659         if( i_arg == 0 )
660         {
661             i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0));
662         }
663     }
664
665     p_playlist->i_index += i_arg;
666
667     /* Boundary check */
668     if( p_playlist->i_index >= p_playlist->i_size )
669     {
670         if( p_playlist->i_status == PLAYLIST_STOPPED
671              || b_random
672              || config_GetInt( p_playlist, "loop" ) )
673         {
674             p_playlist->i_index -= p_playlist->i_size
675                          * ( p_playlist->i_index / p_playlist->i_size );
676         }
677         else
678         {
679             /* Don't loop by default: stop at playlist end */
680             p_playlist->i_index = i_oldindex;
681             p_playlist->i_status = PLAYLIST_STOPPED;
682         }
683     }
684     else if( p_playlist->i_index < 0 )
685     {
686         p_playlist->i_index = p_playlist->i_size - 1;
687     }
688
689     val.b_bool = VLC_TRUE;
690     var_Set( p_playlist, "intf-change", val );
691 }
692
693 /*****************************************************************************
694  * PlayItem: play current playlist item
695  *****************************************************************************
696  * This function calculates the position of the next playlist item, depending
697  * on the playlist course mode (forward, backward, random...).
698  *****************************************************************************/
699 static void PlayItem( playlist_t *p_playlist )
700 {
701     if( p_playlist->i_index == -1 )
702     {
703         if( p_playlist->i_size == 0 )
704         {
705             return;
706         }
707
708         SkipItem( p_playlist, 1 );
709     }
710
711     msg_Dbg( p_playlist, "creating new input thread" );
712     p_playlist->p_input = input_CreateThread( p_playlist,
713                                   p_playlist->pp_items[p_playlist->i_index] );
714 }
715
716 /*****************************************************************************
717  * Poubellize: put an input thread in the trashcan
718  *****************************************************************************
719  * XXX: unused
720  *****************************************************************************/
721 static void Poubellize ( playlist_t *p_playlist, input_thread_t *p_input )
722 {
723     msg_Dbg( p_playlist, "poubellizing input %i\n", p_input->i_object_id );
724 }
725
726 /*****************************************************************************
727  * playlist_LoadFile: load a playlist file.
728  ****************************************************************************/
729 int playlist_LoadFile( playlist_t * p_playlist, const char *psz_filename )
730 {
731     FILE *file;
732     char line[1024];
733     int i_current_status;
734     int i;
735
736     msg_Dbg( p_playlist, "opening playlist file %s", psz_filename );
737
738     file = fopen( psz_filename, "rt" );
739     if( !file )
740     {
741         msg_Err( p_playlist, "playlist file %s does not exist", psz_filename );
742         return -1;
743     }
744     fseek( file, 0L, SEEK_SET );
745
746     /* check the file is not empty */
747     if ( ! fgets( line, 1024, file ) )
748     {
749         msg_Err( p_playlist, "playlist file %s is empty", psz_filename );
750         fclose( file );
751         return -1;
752     }
753
754     /* get rid of line feed */
755     if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' )
756     {
757        line[strlen(line)-1] = (char)0;
758        if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0;
759     }
760     /* check the file format is valid */
761     if ( strcmp ( line , PLAYLIST_FILE_HEADER_0_5 ) )
762     {
763         msg_Err( p_playlist, "playlist file %s format is unsupported"
764                 , psz_filename );
765         fclose( file );
766         return -1;
767     }
768
769     /* stop playing */
770     i_current_status = p_playlist->i_status;
771     if ( p_playlist->i_status != PLAYLIST_STOPPED )
772     {
773         playlist_Stop ( p_playlist );
774     }
775
776     /* delete current content of the playlist */
777     for( i = p_playlist->i_size - 1; i >= 0; i-- )
778     {
779         playlist_Delete ( p_playlist , i );
780     }
781
782     /* simply add each line */
783     while( fgets( line, 1024, file ) )
784     {
785        /* ignore comments or empty lines */
786        if( (line[0] == '#') || (line[0] == '\r') || (line[0] == '\n')
787                || (line[0] == (char)0) )
788            continue;
789
790        /* get rid of line feed */
791        if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' )
792        {
793            line[strlen(line)-1] = (char)0;
794            if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0;
795        }
796
797        playlist_Add ( p_playlist , (char *)&line ,
798                       0, 0, PLAYLIST_APPEND , PLAYLIST_END );
799     }
800
801     /* start playing */
802     if ( i_current_status != PLAYLIST_STOPPED )
803     {
804         playlist_Play ( p_playlist );
805     }
806
807     fclose( file );
808
809     return 0;
810 }
811
812 /*****************************************************************************
813  * playlist_SaveFile: Save a playlist in a file.
814  *****************************************************************************/
815 int playlist_SaveFile( playlist_t * p_playlist, const char * psz_filename )
816 {
817     FILE *file;
818     int i;
819
820     vlc_mutex_lock( &p_playlist->object_lock );
821
822     msg_Dbg( p_playlist, "saving playlist file %s", psz_filename );
823
824     file = fopen( psz_filename, "wt" );
825     if( !file )
826     {
827         msg_Err( p_playlist , "could not create playlist file %s"
828                 , psz_filename );
829         return -1;
830     }
831
832     fprintf( file , PLAYLIST_FILE_HEADER_0_5 "\n" );
833
834     for ( i = 0 ; i < p_playlist->i_size ; i++ )
835     {
836         fprintf( file , p_playlist->pp_items[i]->psz_uri );
837         fprintf( file , "\n" );
838     }
839
840     fclose( file );
841
842     vlc_mutex_unlock( &p_playlist->object_lock );
843
844     return 0;
845 }