X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fplaylist%2Fplaylist.c;h=23b67811573d433e056f6bcf59a7321afc7c0ce7;hb=97a71b4963e9eee1027f8a79929db960bd9e95ff;hp=65433e27144719108e5d1038a733ef1c69c46d36;hpb=6ffd34f1472ce84399a9ae84f0e1fafbaa0306be;p=vlc diff --git a/src/playlist/playlist.c b/src/playlist/playlist.c index 65433e2714..23b6781157 100644 --- a/src/playlist/playlist.c +++ b/src/playlist/playlist.c @@ -1,10 +1,11 @@ /***************************************************************************** * playlist.c : Playlist management functions ***************************************************************************** - * Copyright (C) 1999-2001 VideoLAN - * $Id: playlist.c,v 1.56 2003/09/24 10:21:32 zorglub Exp $ + * Copyright (C) 1999-2004 VideoLAN + * $Id$ * * Authors: Samuel Hocevar + * Clément Stenac * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,22 +28,30 @@ #include #include #include - -#include "stream_control.h" -#include "input_ext-intf.h" +#include #include "vlc_playlist.h" -#define PLAYLIST_FILE_HEADER_0_5 "# vlc playlist file version 0.5" +#define TITLE_CATEGORY N_( "By category" ) +#define TITLE_SIMPLE N_( "Manually added" ) +#define TITLE_ALL N_( "All items, unsorted" ) + +#define PLAYLIST_PROFILE 1 +#undef PLAYLIST_DEBUG /***************************************************************************** * Local prototypes *****************************************************************************/ static void RunThread ( playlist_t * ); -static void SkipItem ( playlist_t *, int ); -static void PlayItem ( playlist_t * ); +static void RunPreparse( playlist_preparse_t * ); +static playlist_item_t * NextItem ( playlist_t * ); +static int PlayItem ( playlist_t *, playlist_item_t * ); + +static int ItemChange( vlc_object_t *, const char *, + vlc_value_t, vlc_value_t, void * ); + +int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args ); -static void Poubellize ( playlist_t *, input_thread_t * ); /** * Create playlist @@ -54,6 +63,7 @@ static void Poubellize ( playlist_t *, input_thread_t * ); playlist_t * __playlist_Create ( vlc_object_t *p_parent ) { playlist_t *p_playlist; + playlist_view_t *p_view; vlc_value_t val; /* Allocate structure */ @@ -64,26 +74,77 @@ playlist_t * __playlist_Create ( vlc_object_t *p_parent ) return NULL; } + /* These variables control updates */ var_Create( p_playlist, "intf-change", VLC_VAR_BOOL ); val.b_bool = VLC_TRUE; var_Set( p_playlist, "intf-change", val ); + var_Create( p_playlist, "item-change", VLC_VAR_INTEGER ); + val.i_int = -1; + var_Set( p_playlist, "item-change", val ); + + var_Create( p_playlist, "item-deleted", VLC_VAR_INTEGER ); + val.i_int = -1; + var_Set( p_playlist, "item-deleted", val ); + + var_Create( p_playlist, "item-append", VLC_VAR_ADDRESS ); + + var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER ); + val.i_int = -1; + var_Set( p_playlist, "playlist-current", val ); + var_Create( p_playlist, "intf-popupmenu", VLC_VAR_BOOL ); var_Create( p_playlist, "intf-show", VLC_VAR_BOOL ); val.b_bool = VLC_TRUE; var_Set( p_playlist, "intf-show", val ); - var_Create( p_playlist, "random", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Create( p_playlist, "repeat", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Create( p_playlist, "loop", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + /* Variables to control playback */ + var_CreateGetBool( p_playlist, "play-and-stop" ); + var_CreateGetBool( p_playlist, "random" ); + var_CreateGetBool( p_playlist, "repeat" ); + var_CreateGetBool( p_playlist, "loop" ); + + /* Initialise data structures */ + p_playlist->b_go_next = VLC_TRUE; p_playlist->p_input = NULL; - p_playlist->i_status = PLAYLIST_STOPPED; + + p_playlist->request_date = 0; + + p_playlist->i_views = 0; + p_playlist->pp_views = NULL; + p_playlist->i_index = -1; p_playlist->i_size = 0; p_playlist->pp_items = NULL; + playlist_ViewInsert( p_playlist, VIEW_CATEGORY, TITLE_CATEGORY ); + playlist_ViewInsert( p_playlist, VIEW_SIMPLE, TITLE_SIMPLE ); + playlist_ViewInsert( p_playlist, VIEW_ALL, TITLE_ALL ); + + p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY ); + + p_playlist->p_general = playlist_NodeCreate( p_playlist, VIEW_CATEGORY, + _( "General" ), p_view->p_root ); + p_playlist->p_general->i_flags |= PLAYLIST_RO_FLAG; + + /* Set startup status + * We set to simple view on startup for interfaces that don't do + * anything */ + p_view = playlist_ViewFind( p_playlist, VIEW_SIMPLE ); + p_playlist->status.i_view = VIEW_SIMPLE; + p_playlist->status.p_item = NULL; + p_playlist->status.p_node = p_view->p_root; + p_playlist->request.b_request = VLC_FALSE; + p_playlist->status.i_status = PLAYLIST_STOPPED; + + + p_playlist->i_last_id = 0; + p_playlist->i_sort = SORT_ID; + p_playlist->i_order = ORDER_NORMAL; + + /* Finally, launch the thread ! */ if( vlc_thread_create( p_playlist, "playlist", RunThread, VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) ) { @@ -92,6 +153,29 @@ playlist_t * __playlist_Create ( vlc_object_t *p_parent ) return NULL; } + /* Preparsing stuff */ + p_playlist->p_preparse = vlc_object_create( p_playlist, + sizeof( playlist_preparse_t ) ); + if( !p_playlist->p_preparse ) + { + msg_Err( p_playlist, "unable to create preparser" ); + vlc_object_destroy( p_playlist ); + return NULL; + } + + p_playlist->p_preparse->i_waiting = 0; + p_playlist->p_preparse->pp_waiting = NULL; + + vlc_object_attach( p_playlist->p_preparse, p_playlist ); + if( vlc_thread_create( p_playlist->p_preparse, "preparser", + RunPreparse, VLC_THREAD_PRIORITY_LOW, VLC_TRUE ) ) + { + msg_Err( p_playlist, "cannot spawn preparse thread" ); + vlc_object_detach( p_playlist->p_preparse ); + vlc_object_destroy( p_playlist->p_preparse ); + return NULL; + } + /* The object has been initialized, now attach it */ vlc_object_attach( p_playlist, p_parent ); @@ -103,488 +187,259 @@ playlist_t * __playlist_Create ( vlc_object_t *p_parent ) * * Delete all items in the playlist and free the playlist structure. * \param p_playlist the playlist structure to destroy + * \return VLC_SUCCESS or an error */ -void playlist_Destroy( playlist_t * p_playlist ) +int playlist_Destroy( playlist_t * p_playlist ) { + int i; p_playlist->b_die = 1; - vlc_thread_join( p_playlist ); - - var_Destroy( p_playlist, "intf-change" ); - - vlc_object_destroy( p_playlist ); -} - -/** - * Add an MRL to the playlist. This is a simplified version of - * playlist_AddExt inculded for convenince. It equals calling playlist_AddExt - * with psz_name == psz_target and i_duration == -1 - */ - -int playlist_Add( playlist_t *p_playlist, const char *psz_target, - const char **ppsz_options, int i_options, - int i_mode, int i_pos ) -{ - return playlist_AddExt( p_playlist, psz_target, psz_target, -1, - ppsz_options, i_options, i_mode, i_pos ); -} - -/** - * Add a MRL into the playlist. - * - * \param p_playlist the playlist to add into - * \param psz_uri the mrl to add to the playlist - * \param psz_name a text giving a name or description of this item - * \param i_duration a hint about the duration of this item, in miliseconds, or - * -1 if unknown. - * \param ppsz_options array of options - * \param i_options number of items in ppsz_options - * \param i_mode the mode used when adding - * \param i_pos the position in the playlist where to add. If this is - * PLAYLIST_END the item will be added at the end of the playlist - * regardless of it's size - * \return always returns 0 -*/ -int playlist_AddExt( playlist_t *p_playlist, const char * psz_uri, - const char * psz_name, mtime_t i_duration, - const char **ppsz_options, int i_options, int i_mode, - int i_pos ) -{ - playlist_item_t * p_item; - - p_item = malloc( sizeof( playlist_item_t ) ); - if( p_item == NULL ) - { - msg_Err( p_playlist, "out of memory" ); - } - - p_item->psz_name = strdup( psz_name ); - p_item->psz_uri = strdup( psz_uri ); - p_item->i_duration = i_duration; - p_item->i_type = 0; - p_item->i_status = 0; - p_item->b_autodeletion = VLC_FALSE; - - p_item->ppsz_options = NULL; - p_item->i_options = i_options; - - if( i_options ) - { - int i; - - p_item->ppsz_options = (char **)malloc( i_options * sizeof(char *) ); - for( i = 0; i < i_options; i++ ) - p_item->ppsz_options[i] = strdup( ppsz_options[i] ); - - } - - return playlist_AddItem( p_playlist, p_item, i_mode, i_pos ); -} - -/** - * Add a playlist item into a playlist - * - * \param p_playlist the playlist to insert into - * \param p_item the playlist item to insert - * \param i_mode the mode used when adding - * \param i_pos the possition in the playlist where to add. If this is - * PLAYLIST_END the item will be added at the end of the playlist - * regardless of it's size - * \return always returns 0 -*/ -int playlist_AddItem( playlist_t *p_playlist, playlist_item_t * p_item, - int i_mode, int i_pos) -{ - vlc_value_t val; - - vlc_mutex_lock( &p_playlist->object_lock ); - - /* - * CHECK_INSERT : checks if the item is already enqued before - * enqueing it - */ - if ( i_mode & PLAYLIST_CHECK_INSERT ) + for( i = 0 ; i< p_playlist->i_sds ; i++ ) { - int j; - - if ( p_playlist->pp_items ) - { - for ( j = 0; j < p_playlist->i_size; j++ ) - { - if ( !strcmp( p_playlist->pp_items[j]->psz_uri, p_item->psz_uri ) ) - { - if( p_item->psz_name ) - { - free( p_item->psz_name ); - } - if( p_item->psz_uri ) - { - free( p_item->psz_uri ); - } - free( p_item ); - vlc_mutex_unlock( &p_playlist->object_lock ); - return 0; - } - } - } - i_mode &= ~PLAYLIST_CHECK_INSERT; - i_mode |= PLAYLIST_APPEND; + playlist_ServicesDiscoveryRemove( p_playlist, + p_playlist->pp_sds[i]->psz_module ); } + vlc_thread_join( p_playlist->p_preparse ); + vlc_thread_join( p_playlist ); - msg_Dbg( p_playlist, "adding playlist item « %s » ( %s )", p_item->psz_name, p_item->psz_uri); - - /* Create the new playlist item */ - - - /* Do a few boundary checks and allocate space for the item */ - if( i_pos == PLAYLIST_END ) - { - if( i_mode & PLAYLIST_INSERT ) - { - i_mode &= ~PLAYLIST_INSERT; - i_mode |= PLAYLIST_APPEND; - } - - i_pos = p_playlist->i_size - 1; - } - - if( !(i_mode & PLAYLIST_REPLACE) - || i_pos < 0 || i_pos >= p_playlist->i_size ) - { - /* Additional boundary checks */ - if( i_mode & PLAYLIST_APPEND ) - { - i_pos++; - } - - if( i_pos < 0 ) - { - i_pos = 0; - } - else if( i_pos > p_playlist->i_size ) - { - i_pos = p_playlist->i_size; - } - - INSERT_ELEM( p_playlist->pp_items, - p_playlist->i_size, - i_pos, - p_item ); - - if( p_playlist->i_index >= i_pos ) - { - p_playlist->i_index++; - } - } - else - { - /* i_mode == PLAYLIST_REPLACE and 0 <= i_pos < p_playlist->i_size */ - if( p_playlist->pp_items[i_pos]->psz_name ) - { - free( p_playlist->pp_items[i_pos]->psz_name ); - } - if( p_playlist->pp_items[i_pos]->psz_uri ) - { - free( p_playlist->pp_items[i_pos]->psz_uri ); - } - /* XXX: what if the item is still in use? */ - free( p_playlist->pp_items[i_pos] ); - p_playlist->pp_items[i_pos] = p_item; - } + vlc_object_detach( p_playlist->p_preparse ); - if( i_mode & PLAYLIST_GO ) + var_Destroy( p_playlist, "intf-change" ); + var_Destroy( p_playlist, "item-change" ); + var_Destroy( p_playlist, "playlist-current" ); + var_Destroy( p_playlist, "intf-popmenu" ); + var_Destroy( p_playlist, "intf-show" ); + var_Destroy( p_playlist, "play-and-stop" ); + var_Destroy( p_playlist, "random" ); + var_Destroy( p_playlist, "repeat" ); + var_Destroy( p_playlist, "loop" ); + + playlist_Clear( p_playlist ); + + for( i = p_playlist->i_views - 1; i >= 0 ; i-- ) { - p_playlist->i_index = i_pos; - if( p_playlist->p_input ) - { - input_StopThread( p_playlist->p_input ); - } - p_playlist->i_status = PLAYLIST_RUNNING; + playlist_view_t *p_view = p_playlist->pp_views[i]; + if( p_view->psz_name ) + free( p_view->psz_name ); + playlist_ItemDelete( p_view->p_root ); + REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, i ); + free( p_view ); } - vlc_mutex_unlock( &p_playlist->object_lock ); - - val.b_bool = VLC_TRUE; - var_Set( p_playlist, "intf-change", val ); + vlc_object_destroy( p_playlist->p_preparse ); + vlc_object_destroy( p_playlist ); - return 0; + return VLC_SUCCESS; } + /** - * delete an item from a playlist. + * Do a playlist action. + * + * If there is something in the playlist then you can do playlist actions. + * + * Playlist lock must not be taken when calling this function * - * \param p_playlist the playlist to remove from. - * \param i_pos the position of the item to remove - * \return returns 0 + * \param p_playlist the playlist to do the command on + * \param i_query the command to do + * \param variable number of arguments + * \return VLC_SUCCESS or an error */ -int playlist_Delete( playlist_t * p_playlist, int i_pos ) +int playlist_Control( playlist_t * p_playlist, int i_query, ... ) { - vlc_value_t val; - vlc_mutex_lock( &p_playlist->object_lock ); - - if( i_pos >= 0 && i_pos < p_playlist->i_size ) - { - msg_Dbg( p_playlist, "deleting playlist item « %s »", - p_playlist->pp_items[i_pos]->psz_name ); - - if( p_playlist->pp_items[i_pos]->psz_name ) - { - free( p_playlist->pp_items[i_pos]->psz_name ); - } - if( p_playlist->pp_items[i_pos]->psz_uri ) - { - free( p_playlist->pp_items[i_pos]->psz_uri ); - } - if( p_playlist->pp_items[i_pos]->i_options ) - { - int i; - - for( i = 0; i < p_playlist->pp_items[i_pos]->i_options; i++ ) - free( p_playlist->pp_items[i_pos]->ppsz_options[i] ); - - free( p_playlist->pp_items[i_pos]->ppsz_options ); - } + va_list args; + int i_result; + va_start( args, i_query ); + i_result = playlist_vaControl( p_playlist, i_query, args ); + va_end( args ); - /* XXX: what if the item is still in use? */ - free( p_playlist->pp_items[i_pos] ); - - if( i_pos <= p_playlist->i_index ) - { - p_playlist->i_index--; - } - - /* Renumber the playlist */ - REMOVE_ELEM( p_playlist->pp_items, - p_playlist->i_size, - i_pos ); - } - - vlc_mutex_unlock( &p_playlist->object_lock ); - - val.b_bool = VLC_TRUE; - var_Set( p_playlist, "intf-change", val ); - - return 0; + return i_result; } - -/** - * Sort the playlist - * - */ -int playlist_Sort( playlist_t * p_playlist , int i_type ) +int playlist_vaControl( playlist_t * p_playlist, int i_query, va_list args ) { - int i , i_small , i_position; - playlist_item_t *p_temp; + playlist_view_t *p_view; + vlc_value_t val; vlc_mutex_lock( &p_playlist->object_lock ); - for( i_position = 0; i_position < p_playlist->i_size -1 ; i_position ++ ) - { - i_small = i_position; - for( i = i_position + 1 ; i< p_playlist->i_size ; i++) - { - int i_test; - - i_test = strcasecmp( p_playlist->pp_items[i]->psz_name, - p_playlist->pp_items[i_small]->psz_name ); +#ifdef PLAYLIST_PROFILE + p_playlist->request_date = mdate(); +#endif - if( ( i_type == SORT_NORMAL && i_test < 0 ) || - ( i_type == SORT_REVERSE && i_test > 0 ) ) - { - i_small = i; - } - } - p_temp = p_playlist->pp_items[i_position]; - p_playlist->pp_items[i_position] = p_playlist->pp_items[i_small]; - p_playlist->pp_items[i_small] = p_temp; + if( p_playlist->i_size <= 0 ) + { + vlc_mutex_unlock( &p_playlist->object_lock ); + return VLC_EGENERIC; } - vlc_mutex_unlock( &p_playlist->object_lock ); - - return 0; -} -/** - * Move an item in a playlist - * - * Move the item in the playlist with position i_pos before the current item - * at position i_newpos. - * \param p_playlist the playlist to move items in - * \param i_pos the position of the item to move - * \param i_newpos the position of the item that will be behind the moved item - * after the move - * \return returns 0 - */ -int playlist_Move( playlist_t * p_playlist, int i_pos, int i_newpos) -{ - vlc_value_t val; - vlc_mutex_lock( &p_playlist->object_lock ); - - /* take into account that our own row disappears. */ - if ( i_pos < i_newpos ) i_newpos--; - - if( i_pos >= 0 && i_newpos >=0 && i_pos <= p_playlist->i_size - && i_newpos <= p_playlist->i_size ) + switch( i_query ) { - playlist_item_t * temp; - - msg_Dbg( p_playlist, "moving playlist item « %s »", - p_playlist->pp_items[i_pos]->psz_name ); + case PLAYLIST_STOP: + p_playlist->status.i_status = PLAYLIST_STOPPED; + p_playlist->request.b_request = VLC_TRUE; + break; - if( i_pos == p_playlist->i_index ) - { - p_playlist->i_index = i_newpos; - } - else if( i_pos > p_playlist->i_index && i_newpos <= p_playlist->i_index ) + case PLAYLIST_ITEMPLAY: + p_playlist->status.i_status = PLAYLIST_RUNNING; + p_playlist->request.i_skip = 0; + p_playlist->request.b_request = VLC_TRUE; + p_playlist->request.p_item = (playlist_item_t *)va_arg( args, + playlist_item_t *); + p_playlist->request.i_view = p_playlist->status.i_view; + p_view = playlist_ViewFind( p_playlist, p_playlist->status.i_view ); + if( p_view ) { - p_playlist->i_index++; + p_playlist->request.p_node = p_view->p_root; } - else if( i_pos < p_playlist->i_index && i_newpos >= p_playlist->i_index ) + else { - p_playlist->i_index--; + p_playlist->request.p_node = NULL; } + break; - if ( i_pos < i_newpos ) + case PLAYLIST_VIEWPLAY: + p_playlist->status.i_status = PLAYLIST_RUNNING; + p_playlist->request.i_skip = 0; + p_playlist->request.b_request = VLC_TRUE; + p_playlist->request.i_view = (int)va_arg( args,int ); + p_playlist->request.p_node = (playlist_item_t *)va_arg( args, + playlist_item_t *); + p_playlist->request.p_item = (playlist_item_t *)va_arg( args, + playlist_item_t *); + + /* If we select a node, play only it. + * If we select an item, continue */ + if( p_playlist->request.p_item == NULL || + ! p_playlist->request.p_node->i_flags & PLAYLIST_SKIP_FLAG ) { - temp = p_playlist->pp_items[i_pos]; - while ( i_pos < i_newpos ) - { - p_playlist->pp_items[i_pos] = p_playlist->pp_items[i_pos+1]; - i_pos++; - } - p_playlist->pp_items[i_newpos] = temp; - } - else if ( i_pos > i_newpos ) - { - temp = p_playlist->pp_items[i_pos]; - while ( i_pos > i_newpos ) - { - p_playlist->pp_items[i_pos] = p_playlist->pp_items[i_pos-1]; - i_pos--; - } - p_playlist->pp_items[i_newpos] = temp; + p_playlist->b_go_next = VLC_FALSE; } - } - - vlc_mutex_unlock( &p_playlist->object_lock ); - - val.b_bool = VLC_TRUE; - var_Set( p_playlist, "intf-change", val ); - - return 0; -} - -/** - * Do a playlist action - * - * \param p_playlist the playlist to do the command on - * \param i_command the command to do - * \param i_arg the argument to the command. See playlist_command_t for details - */ - void playlist_Command( playlist_t * p_playlist, playlist_command_t i_command, - int i_arg ) -{ - vlc_value_t val; - - vlc_mutex_lock( &p_playlist->object_lock ); - - switch( i_command ) - { - case PLAYLIST_STOP: - p_playlist->i_status = PLAYLIST_STOPPED; - if( p_playlist->p_input ) + else { - input_StopThread( p_playlist->p_input ); + p_playlist->b_go_next = VLC_TRUE; } break; case PLAYLIST_PLAY: - p_playlist->i_status = PLAYLIST_RUNNING; - if( !p_playlist->p_input ) - { - PlayItem( p_playlist ); - } + p_playlist->status.i_status = PLAYLIST_RUNNING; + if( p_playlist->p_input ) { val.i_int = PLAYING_S; var_Set( p_playlist->p_input, "state", val ); + break; } + + /* FIXME : needed ? */ + p_playlist->request.b_request = VLC_TRUE; + p_playlist->request.i_view = p_playlist->status.i_view; + p_playlist->request.p_node = p_playlist->status.p_node; + p_playlist->request.p_item = p_playlist->status.p_item; + p_playlist->request.i_skip = 0; + p_playlist->request.i_goto = -1; + break; + + case PLAYLIST_AUTOPLAY: + p_playlist->status.i_status = PLAYLIST_RUNNING; + + p_playlist->request.b_request = VLC_FALSE; break; case PLAYLIST_PAUSE: - p_playlist->i_status = PLAYLIST_PAUSED; + val.i_int = 0; if( p_playlist->p_input ) + var_Get( p_playlist->p_input, "state", &val ); + + if( val.i_int == PAUSE_S ) { - val.i_int = PAUSE_S; - var_Set( p_playlist->p_input, "state", val ); + p_playlist->status.i_status = PLAYLIST_RUNNING; + if( p_playlist->p_input ) + { + val.i_int = PLAYING_S; + var_Set( p_playlist->p_input, "state", val ); + } + } + else + { + p_playlist->status.i_status = PLAYLIST_PAUSED; + if( p_playlist->p_input ) + { + val.i_int = PAUSE_S; + var_Set( p_playlist->p_input, "state", val ); + } } break; case PLAYLIST_SKIP: - p_playlist->i_status = PLAYLIST_STOPPED; - SkipItem( p_playlist, i_arg ); - if( p_playlist->p_input ) + if( p_playlist->status.i_view > -1 ) { - input_StopThread( p_playlist->p_input ); + p_playlist->request.p_node = p_playlist->status.p_node; + p_playlist->request.p_item = p_playlist->status.p_item; } - p_playlist->i_status = PLAYLIST_RUNNING; + p_playlist->request.i_skip = (int) va_arg( args, int ); + p_playlist->request.b_request = VLC_TRUE; break; case PLAYLIST_GOTO: - if( i_arg >= 0 && i_arg < p_playlist->i_size ) - { - p_playlist->i_index = i_arg; - if( p_playlist->p_input ) - { - input_StopThread( p_playlist->p_input ); - } - p_playlist->i_status = PLAYLIST_RUNNING; - } + p_playlist->status.i_status = PLAYLIST_RUNNING; + p_playlist->request.p_node = NULL; + p_playlist->request.p_item = NULL; + p_playlist->request.i_view = -1; + p_playlist->request.i_goto = (int) va_arg( args, int ); + p_playlist->request.b_request = VLC_TRUE; break; default: - msg_Err( p_playlist, "unknown playlist command" ); + msg_Err( p_playlist, "unimplemented playlist query" ); + return VLC_EBADVAR; break; } vlc_mutex_unlock( &p_playlist->object_lock ); + return VLC_SUCCESS; +} - return; +int playlist_PreparseEnqueue( playlist_t *p_playlist, + input_item_t *p_item ) +{ + vlc_mutex_lock( &p_playlist->p_preparse->object_lock ); + INSERT_ELEM( p_playlist->p_preparse->pp_waiting, + p_playlist->p_preparse->i_waiting, + p_playlist->p_preparse->i_waiting, + p_item ); + vlc_mutex_unlock( &p_playlist->p_preparse->object_lock ); + return VLC_SUCCESS; } -/* Following functions are local */ -static void ObjectGarbageCollector( playlist_t *p_playlist, - int i_type, - vlc_bool_t *pb_obj_destroyed, - mtime_t *pi_obj_destroyed_date ) + +/* Destroy remaining objects */ +static mtime_t ObjectGarbageCollector( playlist_t *p_playlist, int i_type, + mtime_t destroy_date ) { vlc_object_t *p_obj; - if( *pb_obj_destroyed || *pi_obj_destroyed_date > mdate() ) - { - return; - } - if( *pi_obj_destroyed_date == 0 ) + if( destroy_date > mdate() ) return destroy_date; + + if( destroy_date == 0 ) { /* give a little time */ - *pi_obj_destroyed_date = mdate() + 300000LL; + return mdate() + I64C(1000000); } else { - while( ( p_obj = vlc_object_find( p_playlist, - i_type, - FIND_CHILD ) ) ) + while( ( p_obj = vlc_object_find( p_playlist, i_type, FIND_CHILD ) ) ) { if( p_obj->p_parent != (vlc_object_t*)p_playlist ) { - /* only first chiled (ie unused) */ + /* only first child (ie unused) */ vlc_object_release( p_obj ); break; } if( i_type == VLC_OBJECT_VOUT ) { - msg_Dbg( p_playlist, "vout garbage collector destroying 1 vout" ); + msg_Dbg( p_playlist, "garbage collector destroying 1 vout" ); vlc_object_detach( p_obj ); vlc_object_release( p_obj ); vout_Destroy( (vout_thread_t *)p_obj ); @@ -595,7 +450,7 @@ static void ObjectGarbageCollector( playlist_t *p_playlist, sout_DeleteInstance( (sout_instance_t*)p_obj ); } } - *pb_obj_destroyed = VLC_TRUE; + return 0; } } @@ -605,12 +460,13 @@ static void ObjectGarbageCollector( playlist_t *p_playlist, static void RunThread ( playlist_t *p_playlist ) { vlc_object_t *p_obj; - vlc_bool_t b_vout_destroyed = VLC_FALSE; /*we do vout garbage collector */ - mtime_t i_vout_destroyed_date = 0; + playlist_item_t *p_item; - vlc_bool_t b_sout_destroyed = VLC_FALSE; /*we do vout garbage collector */ + mtime_t i_vout_destroyed_date = 0; mtime_t i_sout_destroyed_date = 0; + playlist_item_t *p_autodelete_item = NULL; + /* Tell above that we're ready */ vlc_thread_ready( p_playlist ); @@ -618,6 +474,26 @@ static void RunThread ( playlist_t *p_playlist ) { vlc_mutex_lock( &p_playlist->object_lock ); + /* First, check if we have something to do */ + /* FIXME : this can be called several times */ + if( p_playlist->request.b_request ) + { +#ifdef PLAYLIST_PROFILE + msg_Dbg(p_playlist, "beginning processing of request, " + I64Fi" us ", mdate() - p_playlist->request_date ); +#endif + /* Stop the existing input */ + if( p_playlist->p_input ) + { + input_StopThread( p_playlist->p_input ); + } + /* The code below will start the next input for us */ + if( p_playlist->status.i_status == PLAYLIST_STOPPED ) + { + p_playlist->request.b_request = VLC_FALSE; + } + } + /* If there is an input, check that it doesn't need to die. */ if( p_playlist->p_input ) { @@ -636,16 +512,16 @@ static void RunThread ( playlist_t *p_playlist ) /* Destroy input */ input_DestroyThread( p_input ); - /* Unlink current input (_after_ input_DestroyThread for vout garbage collector)*/ + /* Unlink current input + * (_after_ input_DestroyThread for vout garbage collector) */ vlc_object_detach( p_input ); /* Destroy object */ vlc_object_destroy( p_input ); - b_vout_destroyed = VLC_FALSE; i_vout_destroyed_date = 0; - b_sout_destroyed = VLC_FALSE; i_sout_destroyed_date = 0; + continue; } /* This input is dying, let him do */ @@ -657,52 +533,86 @@ static void RunThread ( playlist_t *p_playlist ) else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof ) { + /* TODO FIXME XXX TODO FIXME XXX */ /* Check for autodeletion */ - if( p_playlist->pp_items[p_playlist->i_index]->b_autodeletion ) + + if( p_playlist->status.p_item->i_flags & PLAYLIST_DEL_FLAG ) { - vlc_mutex_unlock( &p_playlist->object_lock ); - playlist_Delete( p_playlist, p_playlist->i_index ); - vlc_mutex_lock( &p_playlist->object_lock ); + p_autodelete_item = p_playlist->status.p_item; } - - /* Select the next playlist item */ - SkipItem( p_playlist, 1 ); - input_StopThread( p_playlist->p_input ); + /* Select the next playlist item */ vlc_mutex_unlock( &p_playlist->object_lock ); continue; } - else if( p_playlist->p_input->stream.control.i_status != INIT_S ) + else if( p_playlist->p_input->i_state != INIT_S ) { vlc_mutex_unlock( &p_playlist->object_lock ); - ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, - &b_vout_destroyed, - &i_vout_destroyed_date ); - ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, - &b_sout_destroyed, - &i_sout_destroyed_date ); + i_vout_destroyed_date = + ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, + i_vout_destroyed_date ); + i_sout_destroyed_date = + ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, + i_sout_destroyed_date ); vlc_mutex_lock( &p_playlist->object_lock ); } } - else if( p_playlist->i_status != PLAYLIST_STOPPED ) + else if( p_playlist->status.i_status != PLAYLIST_STOPPED ) { - SkipItem( p_playlist, 0 ); - PlayItem( p_playlist ); + /* Start another input. + * Get the next item to play */ + p_item = NextItem( p_playlist ); + + + /* We must stop */ + if( p_item == NULL ) + { + if( p_autodelete_item ) + { + playlist_Delete( p_playlist, + p_autodelete_item->input.i_id ); + p_autodelete_item = NULL; + } + p_playlist->status.i_status = PLAYLIST_STOPPED; + vlc_mutex_unlock( &p_playlist->object_lock ); + continue; + } + + PlayItem( p_playlist, p_item ); + + if( p_autodelete_item ) + { + playlist_Delete( p_playlist, p_autodelete_item->input.i_id ); + p_autodelete_item = NULL; + } } - else if( p_playlist->i_status == PLAYLIST_STOPPED ) + else if( p_playlist->status.i_status == PLAYLIST_STOPPED ) { + /* Collect garbage */ vlc_mutex_unlock( &p_playlist->object_lock ); - ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, - &b_sout_destroyed, &i_sout_destroyed_date ); - ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, - &b_vout_destroyed, &i_vout_destroyed_date ); + i_sout_destroyed_date = + ObjectGarbageCollector( p_playlist, VLC_OBJECT_SOUT, mdate() ); + i_vout_destroyed_date = + ObjectGarbageCollector( p_playlist, VLC_OBJECT_VOUT, mdate() ); vlc_mutex_lock( &p_playlist->object_lock ); } vlc_mutex_unlock( &p_playlist->object_lock ); - msleep( INTF_IDLE_SLEEP ); + msleep( INTF_IDLE_SLEEP / 2 ); + + /* Stop sleeping earlier if we have work */ + /* TODO : statistics about this */ + if ( p_playlist->request.b_request && + p_playlist->status.i_status == PLAYLIST_RUNNING ) + { + continue; + } + + msleep( INTF_IDLE_SLEEP / 2 ); } + /* Playlist dying */ + /* If there is an input, kill it */ while( 1 ) { @@ -772,57 +682,326 @@ static void RunThread ( playlist_t *p_playlist ) } } +/* Queue for items to preparse */ +static void RunPreparse ( playlist_preparse_t *p_obj ) +{ + playlist_t *p_playlist = (playlist_t *)p_obj->p_parent; + vlc_bool_t b_sleep; + + /* Tell above that we're ready */ + vlc_thread_ready( p_obj ); + + while( !p_playlist->b_die ) + { + vlc_mutex_lock( &p_obj->object_lock ); + + if( p_obj->i_waiting > 0 ) + { + input_item_t *p_current = p_obj->pp_waiting[0]; + REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 ); + vlc_mutex_unlock( &p_obj->object_lock ); + input_Preparse( p_playlist, p_current ); + var_SetInteger( p_playlist, "item-change", p_current->i_id ); + vlc_mutex_lock( &p_obj->object_lock ); + } + b_sleep = ( p_obj->i_waiting == 0 ); + + vlc_mutex_unlock( &p_obj->object_lock ); + + if( p_obj->i_waiting == 0 ) + { + msleep( INTF_IDLE_SLEEP ); + } + } +} + /***************************************************************************** - * SkipItem: go to Xth playlist item + * NextItem ***************************************************************************** - * This function calculates the position of the next playlist item, depending - * on the playlist course mode (forward, backward, random...). + * This function calculates the next playlist item, depending + * on the playlist course mode (forward, backward, random, view,...). *****************************************************************************/ -static void SkipItem( playlist_t *p_playlist, int i_arg ) +static playlist_item_t * NextItem( playlist_t *p_playlist ) { - int i_oldindex = p_playlist->i_index; - vlc_bool_t b_random, b_repeat, b_loop; - vlc_value_t val; + playlist_item_t *p_new = NULL; + int i_skip,i_goto,i, i_new, i_count ; + playlist_view_t *p_view; - /* If the playlist is empty, there is no current item */ + vlc_bool_t b_loop = var_GetBool( p_playlist, "loop"); + vlc_bool_t b_random = var_GetBool( p_playlist, "random" ); + vlc_bool_t b_repeat = var_GetBool( p_playlist, "repeat" ); + vlc_bool_t b_playstop = var_GetBool( p_playlist, "play-and-stop" ); + +#ifdef PLAYLIST_PROFILE + /* Calculate time needed */ + int64_t start = mdate(); +#endif + + + /* Handle quickly a few special cases */ + + /* No items to play */ if( p_playlist->i_size == 0 ) { - p_playlist->i_index = -1; - return; + msg_Info( p_playlist, "playlist is empty" ); + return NULL; + } + /* Nothing requested */ + if( !p_playlist->request.b_request && p_playlist->status.p_item == NULL ) + { + msg_Dbg( p_playlist,"nothing requested, starting" ); } - var_Get( p_playlist, "random", &val ); - b_random = val.b_bool; - var_Get( p_playlist, "repeat", &val ); - b_repeat = val.b_bool; - var_Get( p_playlist, "loop", &val ); - b_loop = val.b_bool; + /* Repeat and play/stop */ + if( !p_playlist->request.b_request && b_repeat == VLC_TRUE ) + { + msg_Dbg( p_playlist,"repeating item" ); + return p_playlist->status.p_item; + } - /* Increment */ - if( b_random ) + if( !p_playlist->request.b_request && b_playstop == VLC_TRUE ) + { + msg_Dbg( p_playlist,"stopping (play and stop)"); + return NULL; + } + + if( !p_playlist->request.b_request && p_playlist->status.p_item && + !(p_playlist->status.p_item->i_flags & PLAYLIST_SKIP_FLAG) ) + { + msg_Dbg( p_playlist, "no-skip mode, stopping") ; + return NULL; + } + + /* TODO: improve this (only use current node) */ + /* TODO: use the "shuffled view" internally ? */ + /* Random case. This is an exception: if request, but request is skip +- 1 + * we don't go to next item but select a new random one. */ + if( b_random && (!p_playlist->request.b_request || + p_playlist->request.i_skip == 1 || p_playlist->request.i_skip == -1 ) ) { srand( (unsigned int)mdate() ); + i_new = 0; + for( i_count = 0; i_count < p_playlist->i_size - 1 ; i_count ++ ) + { + i_new = + (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0)); + /* Check if the item has not already been played */ + if( p_playlist->pp_items[i_new]->i_nb_played == 0 ) + break; + } + if( i_count == p_playlist->i_size ) + { + /* The whole playlist has been played: reset the counters */ + while( i_count > 0 ) + { + p_playlist->pp_items[--i_count]->i_nb_played = 0; + } + if( !b_loop ) + { + return NULL; + } + } + p_playlist->request.i_skip = 0; + p_playlist->request.b_request = VLC_FALSE; + return p_playlist->pp_items[i_new]; + } + + /* Start the real work */ + if( p_playlist->request.b_request ) + { + msg_Dbg( p_playlist,"processing request" ); + /* We are not playing from a view */ + if( p_playlist->request.i_view == -1 ) + { +#ifdef PLAYLIST_DEBUG + msg_Dbg( p_playlist, "non-view mode request"); +#endif + /* Directly select the item, just like now */ + i_skip = p_playlist->request.i_skip; + i_goto = p_playlist->request.i_goto; + + if( p_playlist->i_index == -1 ) p_playlist->i_index = 0; + p_new = p_playlist->pp_items[p_playlist->i_index]; + + if( i_goto >= 0 && i_goto < p_playlist->i_size ) + { + p_playlist->i_index = i_goto; + p_new = p_playlist->pp_items[p_playlist->i_index]; + p_playlist->request.i_goto = -1; + } + + if( i_skip != 0 ) + { + if( p_playlist->i_index + i_skip < p_playlist->i_size && + p_playlist->i_index + i_skip >= 0 ) + { + p_playlist->i_index += i_skip; + p_new = p_playlist->pp_items[p_playlist->i_index]; + } + } + } + else + { +#ifdef PLAYLIST_DEBUG + msg_Dbg( p_playlist, "view mode request" ); +#endif + p_new = p_playlist->request.p_item; + i_skip = p_playlist->request.i_skip; + + /* If we are asked for a node, take its first item */ + if( p_playlist->request.p_item == NULL && i_skip == 0 ) + { + i_skip++; + } - /* Simple random stuff - we cheat a bit to minimize the chances to - * get the same index again. */ - i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0)); - if( i_arg == 0 ) + p_view = playlist_ViewFind( p_playlist,p_playlist->request.i_view ); + p_playlist->status.p_node = p_playlist->request.p_node; + p_playlist->status.i_view = p_playlist->request.i_view; + if( !p_view ) + { + msg_Err( p_playlist, "p_view is NULL and should not! (FIXME)" ); + } + else if( i_skip > 0 ) + { + for( i = i_skip; i > 0 ; i-- ) + { + p_new = playlist_FindNextFromParent( p_playlist, + p_playlist->request.i_view, + p_view->p_root, + p_playlist->request.p_node, + p_new ); + if( p_new == NULL ) + { + if( b_loop ) + { + p_new = playlist_FindNextFromParent( p_playlist, + p_playlist->request.i_view, + p_view->p_root, + p_playlist->request.p_node, + NULL ); + if( p_new == NULL ) break; + } + else + { + break; + } + } + } + } + else if( i_skip < 0 ) + { + for( i = i_skip; i < 0 ; i++ ) + { + p_new = playlist_FindPrevFromParent( p_playlist, + p_playlist->request.i_view, + p_view->p_root, + p_playlist->request.p_node, + p_new ); + if( p_new == NULL ) break; + } + + } + } + /* Clear the request */ + p_playlist->request.b_request = VLC_FALSE; + } + /* "Automatic" item change ( next ) */ + else + { + p_playlist->request_date = 0; + + if( p_playlist->status.i_view == -1 ) + { + if( p_playlist->i_index + 1 < p_playlist->i_size ) + { + p_playlist->i_index++; + p_new = p_playlist->pp_items[p_playlist->i_index]; + if( !(p_new->i_flags & PLAYLIST_SKIP_FLAG) ) + { + return NULL; + } + } + else + { + msg_Dbg( p_playlist,"finished" ); + if( b_loop && p_playlist->i_size > 0) + { + p_playlist->i_index = 0; + p_new = p_playlist->pp_items[0]; + } + else + p_new = NULL; + } + } + /* We are playing with a view */ + else { - i_arg = (int)((float)p_playlist->i_size * rand() / (RAND_MAX+1.0)); + playlist_view_t *p_view = + playlist_ViewFind( p_playlist, + p_playlist->status.i_view ); + if( !p_view ) + { + msg_Err( p_playlist, "p_view is NULL and should not! (FIXME)" ); + } + else + { + p_new = playlist_FindNextFromParent( p_playlist, + p_playlist->status.i_view, + p_view->p_root, + p_playlist->status.p_node, + p_playlist->status.p_item ); + if( p_new == NULL && b_loop ) + { + p_new = playlist_FindNextFromParent( p_playlist, + p_playlist->status.i_view, + p_view->p_root, + p_playlist->status.p_node, + NULL ); + } + } } } - if( b_repeat ) + + /* Reset index */ + if( p_playlist->i_index >= 0 && p_new != NULL && + p_playlist->pp_items[p_playlist->i_index] != p_new ) { - i_arg = 0; + p_playlist->i_index = playlist_GetPositionById( p_playlist, + p_new->input.i_id ); + } + +#ifdef PLAYLIST_PROFILE + msg_Dbg(p_playlist,"next item found in "I64Fi " us", mdate()-start ); +#endif + + if( p_new == NULL ) + { + msg_Info( p_playlist, "Nothing to play" ); + } + return p_new; +} + + +#if 0 + + +static void SkipItem( playlist_t *p_playlist, int i_arg ) +{ + int i_oldindex = p_playlist->i_index; + vlc_bool_t b_random, b_repeat, b_loop; + vlc_value_t val; + int i_count; + + /* Increment */ + else if( !b_repeat ) + { + p_playlist->i_index += i_arg; } - p_playlist->i_index += i_arg; /* Boundary check */ if( p_playlist->i_index >= p_playlist->i_size ) { - if( p_playlist->i_status == PLAYLIST_STOPPED - || b_random - || b_loop ) + if( p_playlist->i_status == PLAYLIST_STOPPED || b_random || b_loop ) { p_playlist->i_index -= p_playlist->i_size * ( p_playlist->i_index / p_playlist->i_size ); @@ -839,160 +1018,66 @@ static void SkipItem( playlist_t *p_playlist, int i_arg ) p_playlist->i_index = p_playlist->i_size - 1; } - val.b_bool = VLC_TRUE; - var_Set( p_playlist, "intf-change", val ); -} - -/***************************************************************************** - * PlayItem: play current playlist item - ***************************************************************************** - * This function calculates the position of the next playlist item, depending - * on the playlist course mode (forward, backward, random...). - *****************************************************************************/ -static void PlayItem( playlist_t *p_playlist ) -{ - if( p_playlist->i_index == -1 ) + /* Check that the item is enabled */ + if( p_playlist->pp_items[p_playlist->i_index]->b_enabled == VLC_FALSE && + p_playlist->i_enabled != 0) { - if( p_playlist->i_size == 0 ) - { - return; - } - - SkipItem( p_playlist, 1 ); + SkipItem( p_playlist , 1 ); } - - msg_Dbg( p_playlist, "creating new input thread" ); - p_playlist->p_input = input_CreateThread( p_playlist, - p_playlist->pp_items[p_playlist->i_index] ); } -/***************************************************************************** - * Poubellize: put an input thread in the trashcan - ***************************************************************************** - * XXX: unused - *****************************************************************************/ -static void Poubellize ( playlist_t *p_playlist, input_thread_t *p_input ) -{ - msg_Dbg( p_playlist, "poubellizing input %i\n", p_input->i_object_id ); -} +#endif /***************************************************************************** - * playlist_LoadFile: load a playlist file. + * PlayItem: start the input thread for an item ****************************************************************************/ -int playlist_LoadFile( playlist_t * p_playlist, const char *psz_filename ) +static int PlayItem( playlist_t *p_playlist, playlist_item_t *p_item ) { - FILE *file; - char line[1024]; - int i_current_status; - int i; + vlc_value_t val; - msg_Dbg( p_playlist, "opening playlist file %s", psz_filename ); + msg_Dbg( p_playlist, "creating new input thread" ); - file = fopen( psz_filename, "rt" ); - if( !file ) - { - msg_Err( p_playlist, "playlist file %s does not exist", psz_filename ); - return -1; - } - fseek( file, 0L, SEEK_SET ); + p_item->i_nb_played++; + p_playlist->status.p_item = p_item; - /* check the file is not empty */ - if ( ! fgets( line, 1024, file ) ) - { - msg_Err( p_playlist, "playlist file %s is empty", psz_filename ); - fclose( file ); - return -1; - } + p_playlist->i_index = playlist_GetPositionById( p_playlist, + p_item->input.i_id ); - /* get rid of line feed */ - if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' ) - { - line[strlen(line)-1] = (char)0; - if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0; - } - /* check the file format is valid */ - if ( strcmp ( line , PLAYLIST_FILE_HEADER_0_5 ) ) +#ifdef PLAYLIST_PROFILE + if( p_playlist->request_date != 0 ) { - msg_Err( p_playlist, "playlist file %s format is unsupported" - , psz_filename ); - fclose( file ); - return -1; + msg_Dbg( p_playlist, "request processed after "I64Fi " us", + mdate() - p_playlist->request_date ); } +#endif - /* stop playing */ - i_current_status = p_playlist->i_status; - if ( p_playlist->i_status != PLAYLIST_STOPPED ) - { - playlist_Stop ( p_playlist ); - } + p_playlist->p_input = input_CreateThread( p_playlist, &p_item->input ); - /* delete current content of the playlist */ - for( i = p_playlist->i_size - 1; i >= 0; i-- ) - { - playlist_Delete ( p_playlist , i ); - } + var_AddCallback( p_playlist->p_input, "item-change", + ItemChange, p_playlist ); - /* simply add each line */ - while( fgets( line, 1024, file ) ) - { - /* ignore comments or empty lines */ - if( (line[0] == '#') || (line[0] == '\r') || (line[0] == '\n') - || (line[0] == (char)0) ) - continue; - - /* get rid of line feed */ - if( line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r' ) - { - line[strlen(line)-1] = (char)0; - if( line[strlen(line)-1] == '\r' ) line[strlen(line)-1] = (char)0; - } - - playlist_Add ( p_playlist , (char *)&line , - 0, 0, PLAYLIST_APPEND , PLAYLIST_END ); - } - - /* start playing */ - if ( i_current_status != PLAYLIST_STOPPED ) - { - playlist_Play ( p_playlist ); - } + val.i_int = p_item->input.i_id; + /* unlock the playlist to set the var...mmm */ + vlc_mutex_unlock( &p_playlist->object_lock); + var_Set( p_playlist, "playlist-current", val); + vlc_mutex_lock( &p_playlist->object_lock); - fclose( file ); + return VLC_SUCCESS; - return 0; } -/***************************************************************************** - * playlist_SaveFile: Save a playlist in a file. - *****************************************************************************/ -int playlist_SaveFile( playlist_t * p_playlist, const char * psz_filename ) +/* Forward item change from input */ +static int ItemChange( vlc_object_t *p_obj, const char *psz_var, + vlc_value_t oldval, vlc_value_t newval, void *param ) { - FILE *file; - int i; - - vlc_mutex_lock( &p_playlist->object_lock ); + playlist_t *p_playlist = (playlist_t *)param; - msg_Dbg( p_playlist, "saving playlist file %s", psz_filename ); - - file = fopen( psz_filename, "wt" ); - if( !file ) - { - msg_Err( p_playlist , "could not create playlist file %s" - , psz_filename ); - return -1; - } + //p_playlist->b_need_update = VLC_TRUE; + var_SetInteger( p_playlist, "item-change", newval.i_int ); - fprintf( file , PLAYLIST_FILE_HEADER_0_5 "\n" ); - - for ( i = 0 ; i < p_playlist->i_size ; i++ ) - { - fprintf( file , p_playlist->pp_items[i]->psz_uri ); - fprintf( file , "\n" ); - } - - fclose( file ); - - vlc_mutex_unlock( &p_playlist->object_lock ); + /* Update view */ + /* FIXME: Make that automatic */ +// playlist_ViewUpdate( p_playlist, VIEW_S_AUTHOR ); - return 0; + return VLC_SUCCESS; }