Commit defbc4a4 authored by Jakob Leben's avatar Jakob Leben

vlc core: single storage playlist

- Playlist items are stored only once. No category / onelevel separation
- Services Discovery nodes always stay a tree
- The playlist and Media Library are tree or flat depending on variable "playlist-tree".
  It means that if the setting variable says flat, the items that come from playlist
  demuxers are flattened into a single level list.
- The design demanded a different playlist demuxing system. Now playlist demuxers should
  construct a tree of items under an input_item_node_t root and send that using
  input_item_AddSubItemTree. Currently, the old scheme will be retained in modules, because
  there is still some listening to the old event in code (the libvlc media system for example)
parent 53d2132c
......@@ -212,6 +212,7 @@ typedef struct config_category_t config_category_t;
typedef struct input_thread_t input_thread_t;
typedef struct input_thread_sys_t input_thread_sys_t;
typedef struct input_item_t input_item_t;
typedef struct input_item_node_t input_item_node_t;
typedef struct access_t access_t;
typedef struct access_sys_t access_sys_t;
typedef struct stream_t stream_t;
......
......@@ -119,6 +119,7 @@ typedef enum vlc_event_type_t {
/* Input item events */
vlc_InputItemMetaChanged,
vlc_InputItemSubItemAdded,
vlc_InputItemSubItemTreeAdded,
vlc_InputItemDurationChanged,
vlc_InputItemPreparsedChanged,
vlc_InputItemNameChanged,
......@@ -158,6 +159,10 @@ typedef struct vlc_event_t
{
input_item_t * p_new_child;
} input_item_subitem_added;
struct vlc_input_item_subitem_tree_added
{
input_item_node_t * p_root;
} input_item_subitem_tree_added;
struct vlc_input_item_duration_changed
{
mtime_t new_duration;
......
......@@ -107,6 +107,14 @@ enum input_item_type_e
ITEM_TYPE_NUMBER
};
struct input_item_node_t
{
input_item_t * p_item;
int i_children;
input_item_node_t **pp_children;
input_item_node_t *p_parent;
};
VLC_EXPORT( void, input_item_CopyOptions, ( input_item_t *p_parent, input_item_t *p_child ) );
VLC_EXPORT( void, input_item_SetName, ( input_item_t *p_item, const char *psz_name ) );
......@@ -116,6 +124,11 @@ VLC_EXPORT( void, input_item_SetName, ( input_item_t *p_item, const char *psz_na
* the input item children. */
VLC_EXPORT( void, input_item_AddSubItem, ( input_item_t *p_parent, input_item_t *p_child ) );
VLC_EXPORT( void, input_item_AddSubItemTree, ( input_item_node_t *p_root ) );
/* Will send vlc_InputItemSubItemTreeAdded event, just as input_item_AddSubItemTree */
VLC_EXPORT( void, input_item_AddSubItem2, ( input_item_t *p_parent, input_item_t *p_child ) );
/**
* Option flags
......@@ -215,6 +228,14 @@ VLC_EXPORT( input_item_t *, __input_item_NewExt, (vlc_object_t *, const char *ps
*/
#define input_item_New( a,b,c ) input_item_NewExt( a, b, c, 0, NULL, 0, -1 )
VLC_EXPORT( input_item_node_t *, input_item_node_Create, ( input_item_t *p_input ) );
VLC_EXPORT( void, input_item_node_Delete, ( input_item_node_t *p_node ) );
VLC_EXPORT( input_item_node_t *, input_item_node_AppendItem, ( input_item_node_t *p_node, input_item_t *p_item ) );
VLC_EXPORT( void, input_item_node_AppendNode, ( input_item_node_t *p_node, input_item_node_t *p_item ) );
/******************
* Input stats
******************/
......
......@@ -153,7 +153,6 @@ struct playlist_item_t
int i_id; /**< Playlist item specific id */
uint8_t i_flags; /**< Flags */
playlist_t *p_playlist; /**< Parent playlist */
bool b_input_item_observer;
};
#define PLAYLIST_SAVE_FLAG 0x0001 /**< Must it be saved */
......@@ -180,6 +179,11 @@ struct playlist_t
int i_current_index; /**< Index in current array */
/* Predefined items */
playlist_item_t * p_root;
playlist_item_t * p_playing;
playlist_item_t * p_media_library;
//Phony ones, point to those above;
playlist_item_t * p_root_category; /**< Root of category tree */
playlist_item_t * p_root_onelevel; /**< Root of onelevel tree */
playlist_item_t * p_local_category; /** < "Playlist" in CATEGORY view */
......@@ -346,7 +350,6 @@ VLC_EXPORT( int, playlist_DeleteFromInput, ( playlist_t *, input_item_t *, bool
VLC_EXPORT( int, playlist_Add, ( playlist_t *, const char *, const char *, int, int, bool, bool ) );
VLC_EXPORT( int, playlist_AddExt, ( playlist_t *, const char *, const char *, int, int, mtime_t, int, const char *const *, unsigned, bool, bool ) );
VLC_EXPORT( int, playlist_AddInput, ( playlist_t *, input_item_t *, int, int, bool, bool ) );
VLC_EXPORT( int, playlist_BothAddInput, ( playlist_t *, input_item_t *,playlist_item_t *,int , int, int*, int*, bool ) );
/********************************** Item search *************************/
VLC_EXPORT( playlist_item_t *, playlist_ItemGetById, (playlist_t *, int ) );
......
......@@ -60,6 +60,7 @@ static inline void input_item_Init( vlc_object_t *p_o, input_item_t *p_i )
vlc_event_manager_init( p_em, p_i, p_o );
vlc_event_manager_register_event_type( p_em, vlc_InputItemMetaChanged );
vlc_event_manager_register_event_type( p_em, vlc_InputItemSubItemAdded );
vlc_event_manager_register_event_type( p_em, vlc_InputItemSubItemTreeAdded );
vlc_event_manager_register_event_type( p_em, vlc_InputItemDurationChanged );
vlc_event_manager_register_event_type( p_em, vlc_InputItemPreparsedChanged );
vlc_event_manager_register_event_type( p_em, vlc_InputItemNameChanged );
......@@ -268,6 +269,22 @@ void input_item_AddSubItem( input_item_t *p_parent, input_item_t *p_child )
vlc_event_send( &p_parent->event_manager, &event );
}
void input_item_AddSubItemTree ( input_item_node_t *p_root )
{
vlc_event_t event;
event.type = vlc_InputItemSubItemTreeAdded;
event.u.input_item_subitem_tree_added.p_root = p_root;
vlc_event_send( &p_root->p_item->event_manager, &event );
}
void input_item_AddSubItem2 ( input_item_t *p_parent, input_item_t *p_child )
{
input_item_node_t *p_node = input_item_node_Create( p_parent );
input_item_node_AppendItem( p_node, p_child );
input_item_AddSubItemTree( p_node );
input_item_node_Delete( p_node );
}
bool input_item_HasErrorWhenReading( input_item_t *p_item )
{
vlc_mutex_lock( &p_item->lock );
......@@ -962,3 +979,61 @@ static int GuessType( const input_item_t *p_item )
sizeof( tab[0] ), typecmp );
return e ? e->i_type : ITEM_TYPE_FILE;
}
input_item_node_t *input_item_node_Create( input_item_t *p_input )
{
input_item_node_t* p_node = malloc( sizeof( input_item_node_t ) );
if( !p_node )
return NULL;
assert( p_input );
p_node->p_item = p_input;
vlc_gc_incref( p_input );
p_node->p_parent = NULL;
p_node->i_children = 0;
p_node->pp_children = NULL;
return p_node;
}
void input_item_node_Delete( input_item_node_t *p_node )
{
int i;
for( i = 0; i < p_node->i_children; i++ )
input_item_node_Delete( p_node->pp_children[i] );
if( p_node->p_parent )
{
for( i = 0; i < p_node->p_parent->i_children; i++ )
if( p_node->p_parent->pp_children[i] == p_node )
{
REMOVE_ELEM( p_node->p_parent->pp_children,
p_node->p_parent->i_children,
i );
break;
}
}
vlc_gc_decref( p_node->p_item );
free( p_node );
}
input_item_node_t *input_item_node_AppendItem( input_item_node_t *p_node, input_item_t *p_item )
{
input_item_node_t *p_new_child = input_item_node_Create( p_item );
if( !p_new_child ) return NULL;
input_item_node_AppendNode( p_node, p_new_child );
return p_new_child;
}
void input_item_node_AppendNode( input_item_node_t *p_parent, input_item_node_t *p_child )
{
assert( p_parent && p_child && p_child->p_parent == NULL );
INSERT_ELEM( p_parent->pp_children,
p_parent->i_children,
p_parent->i_children,
p_child );
p_child->p_parent = p_parent;
}
......@@ -189,6 +189,8 @@ input_GetItem
input_item_AddInfo
input_item_AddOption
input_item_AddSubItem
input_item_AddSubItem2
input_item_AddSubItemTree
input_item_CopyOptions
input_item_DelInfo
input_item_GetDuration
......@@ -203,6 +205,10 @@ input_item_IsPreparsed
input_item_MetaMatch
__input_item_NewExt
input_item_NewWithType
input_item_node_AppendItem
input_item_node_AppendNode
input_item_node_Create
input_item_node_Delete
input_item_SetDuration
input_item_SetMeta
input_item_SetName
......@@ -313,7 +319,6 @@ playlist_AddExt
playlist_AddInput
playlist_AskForArtEnqueue
playlist_AssertLocked
playlist_BothAddInput
playlist_ChildSearchName
playlist_Clear
playlist_Control
......
......@@ -105,54 +105,50 @@ playlist_t * playlist_Create( vlc_object_t *p_parent )
pl_priv(p_playlist)->b_auto_preparse =
var_InheritBool( p_parent, "auto-preparse" );
PL_LOCK; /* playlist_NodeCreate will check for it */
p_playlist->p_root_category = playlist_NodeCreate( p_playlist, NULL, NULL,
/* Create the root node */
PL_LOCK;
p_playlist->p_root = playlist_NodeCreate( p_playlist, NULL, NULL,
0, NULL );
p_playlist->p_root_onelevel = playlist_NodeCreate( p_playlist, NULL, NULL,
0, p_playlist->p_root_category->p_input );
PL_UNLOCK;
if( !p_playlist->p_root ) return NULL;
if( !p_playlist->p_root_category || !p_playlist->p_root_onelevel )
return NULL;
/* Create currently playing items node */
PL_LOCK;
p_playlist->p_playing = playlist_NodeCreate(
p_playlist, _( "Playlist" ), p_playlist->p_root,
PLAYLIST_RO_FLAG, NULL );
/* Create playlist and media library */
PL_LOCK; /* playlist_NodesPairCreate will check for it */
playlist_NodesPairCreate( p_playlist, _( "Playlist" ),
&p_playlist->p_local_category,
&p_playlist->p_local_onelevel, false );
PL_UNLOCK;
p_playlist->p_local_category->i_flags |= PLAYLIST_RO_FLAG;
p_playlist->p_local_onelevel->i_flags |= PLAYLIST_RO_FLAG;
if( !p_playlist->p_local_category || !p_playlist->p_local_onelevel ||
!p_playlist->p_local_category->p_input ||
!p_playlist->p_local_onelevel->p_input )
return NULL;
if( !p_playlist->p_playing ) return NULL;
/* Create media library node */
const bool b_ml = var_InheritBool( p_parent, "media-library");
if( b_ml )
{
PL_LOCK; /* playlist_NodesPairCreate will check for it */
playlist_NodesPairCreate( p_playlist, _( "Media Library" ),
&p_playlist->p_ml_category,
&p_playlist->p_ml_onelevel, false );
PL_LOCK;
p_playlist->p_media_library = playlist_NodeCreate(
p_playlist, _( "Media Library" ), p_playlist->p_root,
PLAYLIST_RO_FLAG, NULL );
PL_UNLOCK;
if(!p_playlist->p_ml_category || !p_playlist->p_ml_onelevel)
return NULL;
p_playlist->p_ml_category->i_flags |= PLAYLIST_RO_FLAG;
p_playlist->p_ml_onelevel->i_flags |= PLAYLIST_RO_FLAG;
if(!p_playlist->p_media_library ) return NULL;
}
else
{
p_playlist->p_ml_category = p_playlist->p_ml_onelevel = NULL;
p_playlist->p_media_library = NULL;
}
p_playlist->p_root_category = p_playlist->p_root;
p_playlist->p_root_onelevel = p_playlist->p_root;
p_playlist->p_local_category = p_playlist->p_playing;
p_playlist->p_local_onelevel = p_playlist->p_playing;
p_playlist->p_ml_category = p_playlist->p_media_library;
p_playlist->p_ml_onelevel = p_playlist->p_media_library;;
/* Initial status */
pl_priv(p_playlist)->status.p_item = NULL;
pl_priv(p_playlist)->status.p_node = p_playlist->p_local_onelevel;
pl_priv(p_playlist)->status.p_node = p_playlist->p_playing;
pl_priv(p_playlist)->request.b_request = false;
pl_priv(p_playlist)->status.i_status = PLAYLIST_STOPPED;
......
......@@ -33,90 +33,82 @@
static void AddItem( playlist_t *p_playlist, playlist_item_t *p_item,
playlist_item_t *p_node, int i_mode, int i_pos );
static void GoAndPreparse( playlist_t *p_playlist, int i_mode,
playlist_item_t *, playlist_item_t * );
playlist_item_t * );
static void ChangeToNode( playlist_t *p_playlist, playlist_item_t *p_item );
static playlist_item_t *ItemToNode( playlist_t *, playlist_item_t *, bool );
/*****************************************************************************
* An input item has gained a subitem (Event Callback)
* An input item has gained subitems (Event Callback)
*****************************************************************************/
static void input_item_subitem_added( const vlc_event_t * p_event,
void * user_data )
{
playlist_item_t *p_parent_playlist_item = user_data;
playlist_t * p_playlist = p_parent_playlist_item->p_playlist;
input_item_t * p_parent, * p_child;
playlist_item_t * p_child_in_category;
playlist_item_t * p_item_in_category;
bool b_play;
p_parent = p_event->p_obj;
p_child = p_event->u.input_item_subitem_added.p_new_child;
static void input_item_add_subitem_tree ( const vlc_event_t * p_event,
void * user_data )
{
input_item_t *p_input = p_event->p_obj;
playlist_t *p_playlist = (( playlist_item_t* ) user_data)->p_playlist;
input_item_node_t *p_new_root = p_event->u.input_item_subitem_tree_added.p_root;
PL_LOCK;
b_play = var_CreateGetBool( p_playlist, "playlist-autostart" );
/* This part is really hakish, but this playlist system isn't simple */
/* First check if we haven't already added the item as we are
* listening using the onelevel and the category representent
* (Because of the playlist design) */
p_child_in_category = playlist_ItemFindFromInputAndRoot(
p_playlist, p_child,
p_playlist->p_root_category,
false /* Only non-node */ );
if( !p_child_in_category )
{
/* Then, transform to a node if needed */
p_item_in_category = playlist_ItemFindFromInputAndRoot(
p_playlist, p_parent,
p_playlist->p_root_category,
false /* Only non-node */ );
if( !p_item_in_category )
{
/* Item may have been removed */
PL_UNLOCK;
return;
}
bool b_stop = p_item_in_category->i_flags & PLAYLIST_SUBITEM_STOP_FLAG;
playlist_item_t *p_item =
playlist_ItemGetByInput( p_playlist, p_input );
assert( p_item != NULL );
playlist_item_t *p_parent = p_item->p_parent;
assert( p_parent != NULL );
b_play = b_play &&
p_item_in_category == get_current_status_item( p_playlist ) &&
p_item_in_category->i_children == -1;
bool b_play = var_CreateGetBool( p_playlist, "playlist-autostart" ) &&
get_current_status_item( p_playlist ) == p_item;
bool b_stop = b_play && p_item->i_flags & PLAYLIST_SUBITEM_STOP_FLAG;
p_item->i_flags &= ~PLAYLIST_SUBITEM_STOP_FLAG;
/* If this item is already a node don't transform it */
if( p_item_in_category->i_children == -1 )
int pos = 0;
for( int i = 0; i < p_parent->i_children; i++ )
{
if( p_parent->pp_children[i] == p_item )
{
p_item_in_category = ItemToNode( p_playlist,
p_item_in_category, pl_Locked );
p_item_in_category->p_input->i_type = ITEM_TYPE_PLAYLIST;
pos = i;
break;
}
}
int i_ret = playlist_BothAddInput( p_playlist, p_child,
p_item_in_category,
PLAYLIST_APPEND | PLAYLIST_SPREPARSE , PLAYLIST_END,
NULL, NULL, pl_Locked );
if( i_ret == VLC_SUCCESS && b_play )
bool b_flat = false;
playlist_item_t *p_up = p_item;
while( p_up->p_parent )
{
if( p_up->p_parent == p_playlist->p_playing ||
p_up->p_parent == p_playlist->p_media_library )
{
if( b_stop )
{
p_item_in_category->i_flags &= ~PLAYLIST_SUBITEM_STOP_FLAG;
PL_UNLOCK;
playlist_Stop( p_playlist );
return;
}
playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
pl_Locked, p_item_in_category, NULL );
if( !pl_priv(p_playlist)->b_tree ) b_flat = true;
break;
}
p_up = p_up->p_parent;
}
if( b_flat )
{
playlist_DeleteItem( p_playlist, p_item, true );
p_item = playlist_InsertInputItemTree( p_playlist, p_parent,
p_new_root, pos, true );
}
else
p_item = playlist_InsertInputItemTree( p_playlist, p_item,
p_new_root, 0, false );
PL_UNLOCK;
if( b_stop )
{
PL_UNLOCK;
playlist_Stop( p_playlist );
return;
}
else if( b_play )
{
playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
pl_Locked, get_current_status_node( p_playlist ), p_item );
}
PL_UNLOCK;
}
/*****************************************************************************
* An input item's meta or duration has changed (Event Callback)
*****************************************************************************/
......@@ -134,8 +126,8 @@ static void input_item_changed( const vlc_event_t * p_event,
static void install_input_item_observer( playlist_item_t * p_item )
{
vlc_event_manager_t * p_em = &p_item->p_input->event_manager;
vlc_event_attach( p_em, vlc_InputItemSubItemAdded,
input_item_subitem_added, p_item );
vlc_event_attach( p_em, vlc_InputItemSubItemTreeAdded,
input_item_add_subitem_tree, p_item );
vlc_event_attach( p_em, vlc_InputItemDurationChanged,
input_item_changed, p_item );
vlc_event_attach( p_em, vlc_InputItemMetaChanged,
......@@ -151,8 +143,8 @@ static void install_input_item_observer( playlist_item_t * p_item )
static void uninstall_input_item_observer( playlist_item_t * p_item )
{
vlc_event_manager_t * p_em = &p_item->p_input->event_manager;
vlc_event_detach( p_em, vlc_InputItemSubItemAdded,
input_item_subitem_added, p_item );
vlc_event_detach( p_em, vlc_InputItemSubItemTreeAdded,
input_item_add_subitem_tree, p_item );
vlc_event_detach( p_em, vlc_InputItemMetaChanged,
input_item_changed, p_item );
vlc_event_detach( p_em, vlc_InputItemDurationChanged,
......@@ -169,7 +161,7 @@ static void uninstall_input_item_observer( playlist_item_t * p_item )
* Playlist item creation
*****************************************************************************/
playlist_item_t *playlist_ItemNewFromInput( playlist_t *p_playlist,
input_item_t *p_input, bool install_observer )
input_item_t *p_input )
{
playlist_item_t* p_item = malloc( sizeof( playlist_item_t ) );
if( !p_item )
......@@ -187,10 +179,8 @@ playlist_item_t *playlist_ItemNewFromInput( playlist_t *p_playlist,
p_item->pp_children = NULL;
p_item->i_flags = 0;
p_item->p_playlist = p_playlist;
p_item->b_input_item_observer = install_observer;
if( install_observer )
install_input_item_observer( p_item );
install_input_item_observer( p_item );
return p_item;
}
......@@ -218,8 +208,7 @@ int playlist_ItemRelease( playlist_item_t *p_item )
* Most of the modules does that.
*
* Who wants to add proper memory management? */
if( p_item->b_input_item_observer )
uninstall_input_item_observer( p_item );
uninstall_input_item_observer( p_item );
ARRAY_APPEND( pl_priv(p_playlist)->items_to_delete, p_item);
return VLC_SUCCESS;
}
......@@ -268,7 +257,7 @@ int playlist_DeleteFromInputInParent( playlist_t *p_playlist,
/**
* Delete from input
*
* Remove an input item from ONELEVEL and CATEGORY
* Search anywhere in playlist for an an input item and delete it
* \param p_playlist playlist object
* \param p_input the input to delete
* \param b_locked TRUE if the playlist is locked
......@@ -277,15 +266,12 @@ int playlist_DeleteFromInputInParent( playlist_t *p_playlist,
int playlist_DeleteFromInput( playlist_t *p_playlist, input_item_t *p_input,
bool b_locked )
{
int i_ret1, i_ret2;
int i_ret;
PL_LOCK_IF( !b_locked );
i_ret1 = DeleteFromInput( p_playlist, p_input,
p_playlist->p_root_category, true );
i_ret2 = DeleteFromInput( p_playlist, p_input,
p_playlist->p_root_onelevel, true );
i_ret = DeleteFromInput( p_playlist, p_input,
p_playlist->p_root, true );
PL_UNLOCK_IF( !b_locked );
return ( i_ret1 == VLC_SUCCESS || i_ret2 == VLC_SUCCESS ) ?
VLC_SUCCESS : VLC_ENOITEM;
return ( i_ret == VLC_SUCCESS ? VLC_SUCCESS : VLC_ENOITEM );
}
/**
......@@ -298,8 +284,7 @@ int playlist_DeleteFromInput( playlist_t *p_playlist, input_item_t *p_input,
void playlist_Clear( playlist_t * p_playlist, bool b_locked )
{
PL_LOCK_IF( !b_locked );
playlist_NodeEmpty( p_playlist, p_playlist->p_local_category, true );
playlist_NodeEmpty( p_playlist, p_playlist->p_local_onelevel, true );
playlist_NodeEmpty( p_playlist, p_playlist->p_playing, true );
PL_UNLOCK_IF( !b_locked );
}
......@@ -402,7 +387,7 @@ int playlist_AddInput( playlist_t* p_playlist, input_item_t *p_input,
int i_mode, int i_pos, bool b_playlist,
bool b_locked )
{
playlist_item_t *p_item_cat, *p_item_one;
playlist_item_t *p_item;
if( p_playlist->b_die ) return VLC_EGENERIC;
if( !pl_priv(p_playlist)->b_doing_ml )
PL_DEBUG( "adding item `%s' ( %s )", p_input->psz_name,
......@@ -410,88 +395,13 @@ int playlist_AddInput( playlist_t* p_playlist, input_item_t *p_input,
PL_LOCK_IF( !b_locked );
/* Add to ONELEVEL */
p_item_one = playlist_ItemNewFromInput( p_playlist, p_input, false );
if( p_item_one == NULL ) return VLC_ENOMEM;
AddItem( p_playlist, p_item_one,
b_playlist ? p_playlist->p_local_onelevel :
p_playlist->p_ml_onelevel , i_mode, i_pos );
/* Add to CATEGORY */
p_item_cat = playlist_ItemNewFromInput( p_playlist, p_input, true );
if( p_item_cat == NULL ) return VLC_ENOMEM;
AddItem( p_playlist, p_item_cat,
b_playlist ? p_playlist->p_local_category :
p_playlist->p_ml_category , i_mode, i_pos );
p_item = playlist_ItemNewFromInput( p_playlist, p_input );
if( p_item == NULL ) return VLC_ENOMEM;
AddItem( p_playlist, p_item,
b_playlist ? p_playlist->p_playing :
p_playlist->p_media_library , i_mode, i_pos );
GoAndPreparse( p_playlist, i_mode, p_item_cat, p_item_one );
PL_UNLOCK_IF( !b_locked );
return VLC_SUCCESS;
}
/**
* Add input
*
* Add an input item to p_direct_parent in the category tree, and to the
* matching top category in onelevel
* \param p_playlist the playlist to add into
* \param p_input the input item to add
* \param p_direct_parent the parent item to add into
* \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 its size
* \param i_cat id of the items category
* \param i_one id of the item onelevel category
* \param b_locked TRUE if the playlist is locked
* \return VLC_SUCCESS if success, VLC_EGENERIC if fail, VLC_ENOMEM if OOM
*/
int playlist_BothAddInput( playlist_t *p_playlist,
input_item_t *p_input,
playlist_item_t *p_direct_parent,
int i_mode, int i_pos,
int *i_cat, int *i_one, bool b_locked )
{
playlist_item_t *p_item_cat, *p_item_one, *p_up;
int i_top;
assert( p_input );
if( !vlc_object_alive( p_playlist ) )
return VLC_EGENERIC;
PL_LOCK_IF( !b_locked );
/* Add to category */
p_item_cat = playlist_ItemNewFromInput( p_playlist, p_input, true );
if( p_item_cat == NULL ) return VLC_ENOMEM;
AddItem( p_playlist, p_item_cat, p_direct_parent, i_mode, i_pos );
/* Add to onelevel */
/** \todo make a faster case for ml import */
p_item_one = playlist_ItemNewFromInput( p_playlist, p_input, false );
if( p_item_one == NULL ) return VLC_ENOMEM;
p_up = p_direct_parent;
while( p_up->p_parent != p_playlist->p_root_category )
{
p_up = p_up->p_parent;
}
for( i_top = 0 ; i_top < p_playlist->p_root_onelevel->i_children; i_top++ )
{
if( p_playlist->p_root_onelevel->pp_children[i_top]->p_input ==
p_up->p_input )
{
AddItem( p_playlist, p_item_one,
p_playlist->p_root_onelevel->pp_children[i_top],
i_mode, i_pos );
break;
}
}
GoAndPreparse( p_playlist, i_mode, p_item_cat, p_item_one );
if( i_cat ) *i_cat = p_item_cat->i_id;
if( i_one ) *i_one = p_item_one->i_id;
GoAndPreparse( p_playlist, i_mode, p_item );
PL_UNLOCK_IF( !b_locked );
return VLC_SUCCESS;
......@@ -524,7 +434,7 @@ playlist_item_t * playlist_NodeAddInput( playlist_t *p_playlist,
return NULL;
PL_LOCK_IF( !b_locked );
p_item = playlist_ItemNewFromInput( p_playlist, p_input, true );
p_item = playlist_ItemNewFromInput( p_playlist, p_input );
if( p_item == NULL ) return NULL;
AddItem( p_playlist, p_item, p_parent, i_mode, i_pos );
......@@ -533,6 +443,65 @@ playlist_item_t * playlist_NodeAddInput( playlist_t *p_playlist,
return p_item;
}
/**
* Insert a tree of input items into a given playlist node
*
* \param p_playlist the playlist to insert into
* \param p_parent the receiving playlist node (can be an item)
* \param p_node the root of input item tree,
only it's contents will be inserted
* \param i_pos the position in the playlist where to insert. If this is
* PLAYLIST_END the items will be added at the end of the playlist
* regardless of its size
* \param b_flat TRUE if the new tree contents should be flattened into a list
* \return the first new leaf inserted (in playing order)
*/
playlist_item_t *playlist_InsertInputItemTree (
playlist_t *p_playlist, playlist_item_t *p_parent,
input_item_node_t *p_node, int i_pos, bool b_flat )
{
playlist_item_t *p_first_leaf = NULL;
if( p_parent->i_children == -1 ) ChangeToNode( p_playlist, p_parent );
if( i_pos == PLAYLIST_END ) i_pos = p_parent->i_children;
for( int i = 0; i < p_node->i_children; i++, i_pos++ )
{
playlist_item_t *p_child = NULL;
if( b_flat ? p_node->pp_children[i]->i_children == 0 : 1 )
{
printf("creating a leaf: %i\n", i_pos);
p_child = playlist_NodeAddInput( p_playlist,
p_node->pp_children[i]->p_item,
p_parent,
PLAYLIST_INSERT, i_pos,
pl_Locked );
printf("leaf done\n");
}
if( p_node->pp_children[i]->i_children > 0 )
{
if( b_flat )
{
printf("flat -> subnode into parent\n");
p_child = playlist_InsertInputItemTree( p_playlist, p_parent,
p_node->pp_children[i], i_pos, true );
i_pos += p_node->i_children - 1; /* i_pos += 1 on loop */
}
else
{
printf("tree -> subnode on its own\n");
p_child = playlist_InsertInputItemTree( p_playlist, p_child,
p_node->pp_children[i], 0, false );
}
}
if( i == 0 ) p_first_leaf = p_child;
}
printf("leaving a node\n");
return p_first_leaf;
}
/*****************************************************************************
* Playlist item misc operations
*****************************************************************************/
......@@ -552,87 +521,62 @@ static playlist_item_t *ItemToNode( playlist_t *p_playlist,
playlist_item_t *p_item,
bool b_locked )
{
PL_LOCK_IF( !b_locked );
playlist_item_t *p_item_in_category;
/* What we do
* Find the input in CATEGORY.
* - If we find it
* - change it to node
* - we'll return it at the end
* - If we are a direct child of onelevel root, change to node, else
* delete the input from ONELEVEL
* - If we don't find it, just change to node (we are probably in VLM)
* and return NULL
*
* If we were in ONELEVEL, we thus retrieve the node in CATEGORY (will be
* useful for later BothAddInput )
*/
assert( p_item->p_parent );
PL_LOCK_IF( !b_locked );
bool b_flat = false;
playlist_item_t *p_up = p_item;
while( p_up->p_parent )
{
if( p_up->p_parent == p_playlist->p_playing ||
p_up->p_parent == p_playlist->p_media_library )
{
if( !pl_priv(p_playlist)->b_tree ) b_flat = true;
break;
}
p_up = p_up->p_parent;
}
/* Fast track the media library, no time to loose */
if( p_item == p_playlist->p_ml_category ) {
if( !b_flat )
{
ChangeToNode( p_playlist, p_item );
if( p_up == p_playlist->p_root )
var_SetAddress( p_playlist, "item-change", p_item->p_input );
PL_UNLOCK_IF( !b_locked );
return p_item;
}
/** \todo First look if we don't already have it */
p_item_in_category = playlist_ItemFindFromInputAndRoot(
p_playlist, p_item->p_input,
p_playlist->p_root_category,
true );
if( p_item_in_category )
else
{
playlist_item_t *p_item_in_one = playlist_ItemFindFromInputAndRoot(
p_playlist, p_item->p_input,
p_playlist->p_root_onelevel,
true );
assert( p_item_in_one );
/* We already have it, and there is nothing more to do */
ChangeToNode( p_playlist, p_item_in_category );
/* Item in one is a root, change it to node */
if( p_item_in_one->p_parent == p_playlist->p_root_onelevel )
ChangeToNode( p_playlist, p_item_in_one );
else
playlist_item_t *p_status_item = get_current_status_item( p_playlist );
playlist_item_t *p_status_node = get_current_status_node( p_playlist );
if( p_item == p_status_item )
{
playlist_item_t *p_status_item = get_current_status_item( p_playlist );
playlist_item_t *p_status_node = get_current_status_node( p_playlist );
if( p_item_in_one == p_status_item )
/* We're deleting the current playlist item. Update
* the playlist object to point at the previous item
* so the playlist won't be restarted */
playlist_item_t *p_prev_status_item = NULL;
int i = 0;
while( i < p_status_node->i_children &&
p_status_node->pp_children[i] != p_status_item )
{
/* We're deleting the current playlist item. Update
* the playlist object to point at the previous item
* so the playlist won't be restarted */
playlist_item_t *p_prev_status_item = NULL;
int i = 0;
while( i < p_status_node->i_children &&
p_status_node->pp_children[i] != p_status_item )
{
p_prev_status_item = p_status_node->pp_children[i];
i++;
}
if( i == p_status_node->i_children )
p_prev_status_item = NULL;
if( p_prev_status_item )
set_current_status_item( p_playlist, p_prev_status_item );
p_prev_status_item = p_status_node->pp_children[i];
i++;
}
DeleteFromInput( p_playlist, p_item_in_one->p_input,
p_playlist->p_root_onelevel, false );
if( i == p_status_node->i_children )
p_prev_status_item = NULL;
if( p_prev_status_item )
set_current_status_item( p_playlist, p_prev_status_item );
}
DeleteFromInput( p_playlist, p_item->p_input,
p_playlist->p_root, false );
pl_priv(p_playlist)->b_reset_currently_playing = true;
vlc_cond_signal( &pl_priv(p_playlist)->signal );
var_SetAddress( p_playlist, "item-change", p_item_in_category->p_input );
var_SetAddress( p_playlist, "leaf-to-parent", p_item_in_category->p_input );
PL_UNLOCK_IF( !b_locked );
return p_item_in_category;
}
else
{
ChangeToNode( p_playlist, p_item );
PL_UNLOCK_IF( !b_locked );
return p_item;
return p_item->p_parent;
}
}
......@@ -788,47 +732,31 @@ void playlist_SendAddNotify( playlist_t *p_playlist, int i_item_id,
/* Enqueue an item for preparsing, and play it, if needed */
static void GoAndPreparse( playlist_t *p_playlist, int i_mode,
playlist_item_t *p_item_cat,
playlist_item_t *p_item_one )
playlist_item_t *p_item )
{
PL_ASSERT_LOCKED;
if( (i_mode & PLAYLIST_GO ) )
{
playlist_item_t *p_parent = p_item_one;
playlist_item_t *p_toplay = NULL;
while( p_parent )
{
if( p_parent == p_playlist->p_root_category )
{
p_toplay = p_item_cat; break;
}
else if( p_parent == p_playlist->p_root_onelevel )
{
p_toplay = p_item_one; break;
}
p_parent = p_parent->p_parent;
}
assert( p_toplay );
pl_priv(p_playlist)->request.b_request = true;
pl_priv(p_playlist)->request.i_skip = 0;
pl_priv(p_playlist)->request.p_item = p_toplay;
pl_priv(p_playlist)->request.p_item = p_item;
if( pl_priv(p_playlist)->p_input )
input_Stop( pl_priv(p_playlist)->p_input, true );
pl_priv(p_playlist)->request.i_status = PLAYLIST_RUNNING;
vlc_cond_signal( &pl_priv(p_playlist)->signal );
}
/* Preparse if PREPARSE or SPREPARSE & not enough meta */
char *psz_artist = input_item_GetArtist( p_item_cat->p_input );
char *psz_album = input_item_GetAlbum( p_item_cat->p_input );
char *psz_artist = input_item_GetArtist( p_item->p_input );
char *psz_album = input_item_GetAlbum( p_item->p_input );
if( pl_priv(p_playlist)->b_auto_preparse &&
(i_mode & PLAYLIST_PREPARSE ||
( i_mode & PLAYLIST_SPREPARSE &&
( EMPTY_STR( psz_artist ) || ( EMPTY_STR( psz_album ) ) )
) ) )
playlist_PreparseEnqueue( p_playlist, p_item_cat->p_input, pl_Locked );
playlist_PreparseEnqueue( p_playlist, p_item->p_input, pl_Locked );
/* If we already have it, signal it */
else if( !EMPTY_STR( psz_artist ) && !EMPTY_STR( psz_album ) )
input_item_SetPreparsed( p_item_cat->p_input, true );
input_item_SetPreparsed( p_item->p_input, true );
free( psz_artist );
free( psz_album );
}
......
......@@ -103,16 +103,17 @@ int playlist_Import( playlist_t *p_playlist, const char *psz_file )
/*****************************************************************************
* A subitem has been added to the Media Library (Event Callback)
*****************************************************************************/
static void input_item_subitem_added( const vlc_event_t * p_event,
static void input_item_subitem_tree_added( const vlc_event_t * p_event,
void * user_data )
{
playlist_t *p_playlist = user_data;
input_item_t *p_item = p_event->u.input_item_subitem_added.p_new_child;
input_item_node_t *p_root =
p_event->u.input_item_subitem_tree_added.p_root;
/* playlist_AddInput() can fail, but we have no way to report that ..
* Any way when it has failed, either the playlist is dying, either OOM */
playlist_AddInput( p_playlist, p_item, PLAYLIST_APPEND, PLAYLIST_END,
false, pl_Unlocked );
PL_LOCK;
playlist_InsertInputItemTree ( p_playlist, p_playlist->p_media_library,
p_root, 0, !pl_priv(p_playlist)->b_tree );
PL_UNLOCK;
}
int playlist_MLLoad( playlist_t *p_playlist )
......@@ -156,7 +157,7 @@ int playlist_MLLoad( playlist_t *p_playlist )
return VLC_ENOMEM;
const char *const options[1] = { "meta-file", };
/* that option has to be cleaned in input_item_subitem_added() */
/* that option has to be cleaned in input_item_subitem_tree_added() */
/* vlc_gc_decref() in the same function */
p_input = input_item_NewExt( p_playlist, psz_uri, _("Media Library"),
1, options, VLC_INPUT_OPTION_TRUSTED, -1 );
......@@ -165,19 +166,13 @@ int playlist_MLLoad( playlist_t *p_playlist )
return VLC_EGENERIC;
PL_LOCK;
if( p_playlist->p_ml_onelevel->p_input )
vlc_gc_decref( p_playlist->p_ml_onelevel->p_input );
if( p_playlist->p_ml_category->p_input )
vlc_gc_decref( p_playlist->p_ml_category->p_input );
if( p_playlist->p_media_library->p_input )
vlc_gc_decref( p_playlist->p_media_library->p_input );
p_playlist->p_ml_onelevel->p_input =
p_playlist->p_ml_category->p_input = p_input;
/* We save the input at two different place, incref */
vlc_gc_incref( p_input );
vlc_gc_incref( p_input );
p_playlist->p_media_library->p_input = p_input;
vlc_event_attach( &p_input->event_manager, vlc_InputItemSubItemAdded,
input_item_subitem_added, p_playlist );
vlc_event_attach( &p_input->event_manager, vlc_InputItemSubItemTreeAdded,
input_item_subitem_tree_added, p_playlist );
pl_priv(p_playlist)->b_doing_ml = true;
PL_UNLOCK;
......@@ -190,10 +185,9 @@ int playlist_MLLoad( playlist_t *p_playlist )
pl_priv(p_playlist)->b_doing_ml = false;
PL_UNLOCK;
vlc_event_detach( &p_input->event_manager, vlc_InputItemSubItemAdded,
input_item_subitem_added, p_playlist );
vlc_event_detach( &p_input->event_manager, vlc_InputItemSubItemTreeAdded,
input_item_subitem_tree_added, p_playlist );
vlc_gc_decref( p_input );
return VLC_SUCCESS;
}
......@@ -220,7 +214,7 @@ int playlist_MLDump( playlist_t *p_playlist )
strcat( psz_dirname, DIR_SEP "ml.xspf" );
stats_TimerStart( p_playlist, "ML Dump", STATS_TIMER_ML_DUMP );
playlist_Export( p_playlist, psz_dirname, p_playlist->p_ml_category,
playlist_Export( p_playlist, psz_dirname, p_playlist->p_media_library,
"export-xspf" );
stats_TimerStop( p_playlist, STATS_TIMER_ML_DUMP );
......
......@@ -109,8 +109,7 @@ void pl_Deactivate (libvlc_int_t *);
/* */
playlist_item_t *playlist_ItemNewFromInput( playlist_t *p_playlist,
input_item_t *p_input,
bool install_observer );
input_item_t *p_input );
/* Engine */
playlist_item_t * get_current_status_item( playlist_t * p_playlist);
......@@ -132,6 +131,9 @@ void playlist_SendAddNotify( playlist_t *p_playlist, int i_item_id,
playlist_item_t * playlist_NodeAddInput( playlist_t *, input_item_t *,
playlist_item_t *,int , int, bool );
playlist_item_t *playlist_InsertInputItemTree ( playlist_t *,
playlist_item_t *, input_item_node_t *, int, bool );
/* Tree walking */
playlist_item_t *playlist_ItemFindFromInputAndRoot( playlist_t *p_playlist,
input_item_t *p_input, playlist_item_t *p_root,
......
......@@ -90,8 +90,7 @@ char **vlc_sd_GetNames (vlc_object_t *obj, char ***pppsz_longnames)
struct vlc_sd_internal_t
{
/* the playlist items for category and onelevel */
playlist_item_t *p_cat;
playlist_item_t *p_one;
playlist_item_t *p_node;
services_discovery_t *p_sd; /**< Loaded service discovery modules */
char *psz_name;
};
......@@ -244,8 +243,7 @@ static void playlist_sd_item_added( const vlc_event_t * p_event, void * user_dat
PL_LOCK;
/* If p_parent is in root category (this is clearly a hack) and we have a cat */
if( !EMPTY_STR(psz_cat) &&
p_parent->p_parent == p_playlist->p_root_category )
if( !EMPTY_STR(psz_cat) )
{
/* */
playlist_item_t * p_cat;
......@@ -259,9 +257,9 @@ static void playlist_sd_item_added( const vlc_event_t * p_event, void * user_dat
p_parent = p_cat;
}
playlist_BothAddInput( p_playlist, p_input, p_parent,
PLAYLIST_APPEND, PLAYLIST_END,
NULL, NULL, pl_Locked );
playlist_NodeAddInput( p_playlist, p_input, p_parent,
PLAYLIST_APPEND, PLAYLIST_END,
pl_Locked );
PL_UNLOCK;
}
......@@ -299,22 +297,21 @@ int playlist_ServicesDiscoveryAdd( playlist_t *p_playlist, const char *psz_modul
return VLC_ENOMEM;
}
playlist_item_t * p_cat;
playlist_item_t * p_one;
playlist_item_t *p_node;
PL_LOCK;
playlist_NodesPairCreate( p_playlist, module_get_name( m, true ),
&p_cat, &p_one, false );
p_node = playlist_NodeCreate( p_playlist, module_get_name( m, true ),
p_playlist->p_root, 0, NULL );
PL_UNLOCK;
module_release( m );
vlc_event_attach( services_discovery_EventManager( p_sd ),
vlc_ServicesDiscoveryItemAdded,
playlist_sd_item_added, p_cat );
playlist_sd_item_added, p_node );
vlc_event_attach( services_discovery_EventManager( p_sd ),
vlc_ServicesDiscoveryItemRemoved,
playlist_sd_item_removed, p_cat );
playlist_sd_item_removed, p_node );
if( !vlc_sd_Start( p_sd, psz_module ) )
{
......@@ -324,10 +321,9 @@ int playlist_ServicesDiscoveryAdd( playlist_t *p_playlist, const char *psz_modul
}
/* We want tree-view for service directory */
p_one->p_input->b_prefers_tree = true;
p_node->p_input->b_prefers_tree = true;
p_sds->p_sd = p_sd;
p_sds->p_one = p_one;
p_sds->p_cat = p_cat;
p_sds->p_node = p_node;
p_sds->psz_name = strdup( psz_module );
PL_LOCK;
......@@ -369,21 +365,16 @@ int playlist_ServicesDiscoveryRemove( playlist_t * p_playlist,
vlc_event_detach( services_discovery_EventManager( p_sd ),
vlc_ServicesDiscoveryItemAdded,
playlist_sd_item_added,
p_sds->p_cat );
p_sds->p_node );
vlc_event_detach( services_discovery_EventManager( p_sd ),
vlc_ServicesDiscoveryItemRemoved,
playlist_sd_item_removed,
p_sds->p_cat );
p_sds->p_node );
/* Remove the sd playlist node if it exists */
PL_LOCK;
if( p_sds->p_cat != p_playlist->p_root_category &&
p_sds->p_one != p_playlist->p_root_onelevel )
{
playlist_NodeDelete( p_playlist, p_sds->p_cat, true, false );
playlist_NodeDelete( p_playlist, p_sds->p_one, true, false );
}
playlist_NodeDelete( p_playlist, p_sds->p_node, true, false );
PL_UNLOCK;
vlc_sd_Destroy( p_sd );
......
......@@ -69,7 +69,7 @@ playlist_item_t * playlist_NodeCreate( playlist_t *p_playlist,
p_new_input = input_item_NewWithType( VLC_OBJECT(p_playlist), NULL,
psz_name, 0, NULL, 0, -1, ITEM_TYPE_NODE );
p_item = playlist_ItemNewFromInput( p_playlist,
p_input ? p_input : p_new_input, p_input == NULL );
p_input ? p_input : p_new_input );
if( p_new_input )
vlc_gc_decref( p_new_input );
......@@ -266,69 +266,6 @@ playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node,
return NULL;
}
/**
* Create a pair of nodes in the category and onelevel trees.
* They share the same input item.
* \param p_playlist the playlist
* \param psz_name the name of the nodes
* \param pp_node_cat pointer to return the node in category tree
* \param pp_node_one pointer to return the node in onelevel tree
* \param b_for_sd For Services Discovery ? (make node read-only and unskipping)
*/
void playlist_NodesPairCreate( playlist_t *p_playlist, const char *psz_name,
playlist_item_t **pp_node_cat,
playlist_item_t **pp_node_one,
bool b_for_sd )
{
PL_ASSERT_LOCKED;
*pp_node_cat = playlist_NodeCreate( p_playlist, psz_name,
p_playlist->p_root_category, 0, NULL );
*pp_node_one = playlist_NodeCreate( p_playlist, psz_name,
p_playlist->p_root_onelevel, 0,
(*pp_node_cat)->p_input );
if( b_for_sd )
{
(*pp_node_cat)->i_flags |= PLAYLIST_RO_FLAG;
(*pp_node_cat)->i_flags |= PLAYLIST_SKIP_FLAG;
(*pp_node_one)->i_flags |= PLAYLIST_RO_FLAG;
(*pp_node_one)->i_flags |= PLAYLIST_SKIP_FLAG;
}
}
/**
* Get the node in the preferred tree from a node in one of category
* or onelevel tree.
*/
playlist_item_t * playlist_GetPreferredNode( playlist_t *p_playlist,
playlist_item_t *p_node )
{
PL_ASSERT_LOCKED;
int i;
if( p_node->p_parent == p_playlist->p_root_category )
{
if( pl_priv(p_playlist)->b_tree || p_node->p_input->b_prefers_tree )
return p_node;
for( i = 0 ; i< p_playlist->p_root_onelevel->i_children; i++ )
{
if( p_playlist->p_root_onelevel->pp_children[i]->p_input ==
p_node->p_input )
return p_playlist->p_root_onelevel->pp_children[i];
}
}
else if( p_node->p_parent == p_playlist->p_root_onelevel )
{
if( !pl_priv(p_playlist)->b_tree || !p_node->p_input->b_prefers_tree )
return p_node;
for( i = 0 ; i< p_playlist->p_root_category->i_children; i++ )
{
if( p_playlist->p_root_category->pp_children[i]->p_input ==
p_node->p_input )
return p_playlist->p_root_category->pp_children[i];
}
}
return NULL;
}
/**********************************************************************
* Tree walking functions
**********************************************************************/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment