From defbc4a490e65cfa1d587dceef1b7cb5c230e8e0 Mon Sep 17 00:00:00 2001 From: Jakob Leben Date: Tue, 2 Feb 2010 03:56:10 +0100 Subject: [PATCH] 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) --- include/vlc_common.h | 1 + include/vlc_events.h | 5 + include/vlc_input_item.h | 21 ++ include/vlc_playlist.h | 7 +- src/input/item.c | 75 +++++ src/libvlccore.sym | 7 +- src/playlist/engine.c | 54 ++-- src/playlist/item.c | 438 +++++++++++++----------------- src/playlist/loadsave.c | 38 ++- src/playlist/playlist_internal.h | 6 +- src/playlist/services_discovery.c | 39 +-- src/playlist/tree.c | 65 +---- 12 files changed, 357 insertions(+), 399 deletions(-) diff --git a/include/vlc_common.h b/include/vlc_common.h index b29b35c9f9..a45bd973d3 100644 --- a/include/vlc_common.h +++ b/include/vlc_common.h @@ -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; diff --git a/include/vlc_events.h b/include/vlc_events.h index cba7fb9e2b..de36a27abf 100644 --- a/include/vlc_events.h +++ b/include/vlc_events.h @@ -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; diff --git a/include/vlc_input_item.h b/include/vlc_input_item.h index 6cea7d9bce..4dcd5fc0c1 100644 --- a/include/vlc_input_item.h +++ b/include/vlc_input_item.h @@ -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 ******************/ diff --git a/include/vlc_playlist.h b/include/vlc_playlist.h index 26ed795e80..20d1030f58 100644 --- a/include/vlc_playlist.h +++ b/include/vlc_playlist.h @@ -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 ) ); diff --git a/src/input/item.c b/src/input/item.c index 588e36b504..a16cf7993e 100644 --- a/src/input/item.c +++ b/src/input/item.c @@ -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; +} diff --git a/src/libvlccore.sym b/src/libvlccore.sym index b3219c03ed..848fdd993d 100644 --- a/src/libvlccore.sym +++ b/src/libvlccore.sym @@ -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 diff --git a/src/playlist/engine.c b/src/playlist/engine.c index b492c909f8..379412ece5 100644 --- a/src/playlist/engine.c +++ b/src/playlist/engine.c @@ -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; diff --git a/src/playlist/item.c b/src/playlist/item.c index f6a7528a52..ed01246a2f 100644 --- a/src/playlist/item.c +++ b/src/playlist/item.c @@ -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 ); } diff --git a/src/playlist/loadsave.c b/src/playlist/loadsave.c index 357592b6e7..847fdafadb 100644 --- a/src/playlist/loadsave.c +++ b/src/playlist/loadsave.c @@ -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 ); diff --git a/src/playlist/playlist_internal.h b/src/playlist/playlist_internal.h index efe3c5b5c7..3d4eb4c2b0 100644 --- a/src/playlist/playlist_internal.h +++ b/src/playlist/playlist_internal.h @@ -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, diff --git a/src/playlist/services_discovery.c b/src/playlist/services_discovery.c index 6b21140a78..a0e5a7498d 100644 --- a/src/playlist/services_discovery.c +++ b/src/playlist/services_discovery.c @@ -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 ); diff --git a/src/playlist/tree.c b/src/playlist/tree.c index 54fb47e1af..ce9397176d 100644 --- a/src/playlist/tree.c +++ b/src/playlist/tree.c @@ -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 **********************************************************************/ -- 2.39.5