1 /*****************************************************************************
2 * item.c : Playlist item creation/deletion/add/removal functions
3 *****************************************************************************
4 * Copyright (C) 1999-2007 VLC authors and VideoLAN
7 * Authors: Samuel Hocevar <sam@zoy.org>
8 * Clément Stenac <zorglub@videolan.org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_playlist.h>
37 #include "playlist_internal.h"
39 static void playlist_Preparse( playlist_t
*, playlist_item_t
* );
40 static void ChangeToNode( playlist_t
*p_playlist
, playlist_item_t
*p_item
);
42 static int RecursiveAddIntoParent (
43 playlist_t
*p_playlist
, playlist_item_t
*p_parent
,
44 input_item_node_t
*p_node
, int i_pos
, bool b_flat
,
45 playlist_item_t
**pp_first_leaf
);
46 static int RecursiveInsertCopy (
47 playlist_t
*p_playlist
, playlist_item_t
*p_item
,
48 playlist_item_t
*p_parent
, int i_pos
, bool b_flat
);
50 /*****************************************************************************
51 * An input item has gained subitems (Event Callback)
52 *****************************************************************************/
54 static void input_item_add_subitem_tree ( const vlc_event_t
* p_event
,
57 input_item_t
*p_input
= p_event
->p_obj
;
58 playlist_t
*p_playlist
= user_data
;
59 playlist_private_t
*p_sys
= pl_priv( p_playlist
);
60 input_item_node_t
*p_new_root
= p_event
->u
.input_item_subitem_tree_added
.p_root
;
64 playlist_item_t
*p_item
=
65 playlist_ItemGetByInput( p_playlist
, p_input
);
67 assert( p_item
!= NULL
);
69 bool b_current
= get_current_status_item( p_playlist
) == p_item
;
70 bool b_autostart
= var_GetBool( p_playlist
, "playlist-autostart" );
71 bool b_stop
= p_item
->i_flags
& PLAYLIST_SUBITEM_STOP_FLAG
;
74 p_item
->i_flags
&= ~PLAYLIST_SUBITEM_STOP_FLAG
;
76 /* We will have to flatten the tree out if we are in "the playlist" node and
77 the user setting demands flat playlist */
79 if( !pl_priv(p_playlist
)->b_tree
) {
80 playlist_item_t
*p_up
= p_item
;
81 while( p_up
->p_parent
)
83 if( p_up
->p_parent
== p_playlist
->p_playing
)
88 p_up
= p_up
->p_parent
;
94 /* If we have to flatten out, then take the item's position in the parent as
95 insertion point and delete the item */
97 bool b_redirect_request
= false;
101 playlist_item_t
*p_parent
= p_item
->p_parent
;
102 assert( p_parent
!= NULL
);
105 for( i
= 0; i
< p_parent
->i_children
; i
++ )
107 if( p_parent
->pp_children
[i
] == p_item
)
113 assert( i
< p_parent
->i_children
);
115 playlist_NodeDelete( p_playlist
, p_item
, false );
117 /* If there is a pending request referring to the item we just deleted
118 * it needs to be updated so that we do not try to play an entity that
119 * is no longer part of the playlist. */
121 if( p_sys
->request
.b_request
&&
122 ( p_sys
->request
.p_item
== p_item
||
123 p_sys
->request
.p_node
== p_item
) )
125 b_redirect_request
= true;
132 pos
= p_item
->i_children
>= 0 ? p_item
->i_children
: 0;
136 "p_item" is the node where sub-items should be inserted,
137 "pos" is the insertion position in that node */
139 int last_pos
= playlist_InsertInputItemTree( p_playlist
,
144 if( b_redirect_request
)
146 /* a redirect of the pending request is required, as such we update the
147 * request to refer to the item that would have been the next in line
152 playlist_item_t
* p_redirect
= NULL
;
154 if( p_item
->i_children
> pos
)
155 p_redirect
= p_item
->pp_children
[pos
];
157 p_sys
->request
.p_item
= p_redirect
;
158 p_sys
->request
.p_node
= NULL
;
161 if( !b_flat
) var_SetInteger( p_playlist
, "leaf-to-parent", p_item
->i_id
);
163 //control playback only if it was the current playing item that got subitems
166 if( ( b_stop
&& !b_flat
) || !b_autostart
)
168 playlist_Stop( p_playlist
);
170 else if( last_pos
!= pos
) /* any children? */
172 /* Continue to play, either random or the first new item */
173 playlist_item_t
*p_play_item
;
175 if( var_GetBool( p_playlist
, "random" ) )
181 p_play_item
= p_item
->pp_children
[pos
];
182 /* NOTE: this is a work around the general bug:
183 if node-to-be-played contains sub-nodes, then second instead
184 of first leaf starts playing (only in case the leafs have just
185 been instered and playlist has not yet been rebuilt.)
187 while( p_play_item
->i_children
> 0 )
188 p_play_item
= p_play_item
->pp_children
[0];
191 playlist_ViewPlay( p_playlist
, NULL
, p_play_item
);
197 /*****************************************************************************
198 * An input item's meta or duration has changed (Event Callback)
199 *****************************************************************************/
200 static void input_item_changed( const vlc_event_t
* p_event
,
203 playlist_t
*p_playlist
= user_data
;
205 var_SetAddress( p_playlist
, "item-change", p_event
->p_obj
);
208 static int playlist_ItemCmpId( const void *a
, const void *b
)
210 const playlist_item_t
*pa
= a
, *pb
= b
;
212 /* ID are between 1 and INT_MAX, this cannot overflow. */
213 return pa
->i_id
- pb
->i_id
;
216 static int playlist_ItemCmpInput( const void *a
, const void *b
)
218 const playlist_item_t
*pa
= a
, *pb
= b
;
220 if( pa
->p_input
== pb
->p_input
)
222 return (((uintptr_t)pa
->p_input
) > ((uintptr_t)pb
->p_input
))
226 /*****************************************************************************
227 * Playlist item creation
228 *****************************************************************************/
229 playlist_item_t
*playlist_ItemNewFromInput( playlist_t
*p_playlist
,
230 input_item_t
*p_input
)
232 playlist_private_t
*p
= pl_priv(p_playlist
);
233 playlist_item_t
**pp
, *p_item
;
235 p_item
= malloc( sizeof( playlist_item_t
) );
236 if( unlikely(p_item
== NULL
) )
241 p_item
->p_input
= p_input
;
242 p_item
->i_id
= p
->i_last_playlist_id
;
243 p_item
->p_parent
= NULL
;
244 p_item
->i_children
= (p_input
->i_type
== ITEM_TYPE_NODE
) ? 0 : -1;
245 p_item
->pp_children
= NULL
;
246 p_item
->i_nb_played
= 0;
251 do /* Find an unused ID for the item */
253 if( unlikely(p_item
->i_id
== INT_MAX
) )
258 if( unlikely(p_item
->i_id
== p
->i_last_playlist_id
) )
259 goto error
; /* All IDs taken */
261 pp
= tsearch( p_item
, &p
->id_tree
, playlist_ItemCmpId
);
262 if( unlikely(pp
== NULL
) )
265 assert( (*pp
)->i_id
== p_item
->i_id
);
266 assert( (*pp
) == p_item
|| (*pp
)->p_input
!= p_input
);
268 while( p_item
!= *pp
);
270 pp
= tsearch( p_item
, &p
->input_tree
, playlist_ItemCmpInput
);
271 if( unlikely(pp
== NULL
) )
273 tdelete( p_item
, &p
->id_tree
, playlist_ItemCmpId
);
276 /* Same input item cannot be inserted twice. */
277 assert( p_item
== *pp
);
279 p
->i_last_playlist_id
= p_item
->i_id
;
280 input_item_Hold( p_item
->p_input
);
282 vlc_event_manager_t
*p_em
= &p_item
->p_input
->event_manager
;
284 vlc_event_attach( p_em
, vlc_InputItemSubItemTreeAdded
,
285 input_item_add_subitem_tree
, p_playlist
);
286 vlc_event_attach( p_em
, vlc_InputItemDurationChanged
,
287 input_item_changed
, p_playlist
);
288 vlc_event_attach( p_em
, vlc_InputItemMetaChanged
,
289 input_item_changed
, p_playlist
);
290 vlc_event_attach( p_em
, vlc_InputItemNameChanged
,
291 input_item_changed
, p_playlist
);
292 vlc_event_attach( p_em
, vlc_InputItemInfoChanged
,
293 input_item_changed
, p_playlist
);
294 vlc_event_attach( p_em
, vlc_InputItemErrorWhenReadingChanged
,
295 input_item_changed
, p_playlist
);
304 /***************************************************************************
305 * Playlist item destruction
306 ***************************************************************************/
311 * \param p_item item to delete
313 void playlist_ItemRelease( playlist_t
*p_playlist
, playlist_item_t
*p_item
)
315 playlist_private_t
*p
= pl_priv(p_playlist
);
319 vlc_event_manager_t
*p_em
= &p_item
->p_input
->event_manager
;
321 vlc_event_detach( p_em
, vlc_InputItemSubItemTreeAdded
,
322 input_item_add_subitem_tree
, p_playlist
);
323 vlc_event_detach( p_em
, vlc_InputItemMetaChanged
,
324 input_item_changed
, p_playlist
);
325 vlc_event_detach( p_em
, vlc_InputItemDurationChanged
,
326 input_item_changed
, p_playlist
);
327 vlc_event_detach( p_em
, vlc_InputItemNameChanged
,
328 input_item_changed
, p_playlist
);
329 vlc_event_detach( p_em
, vlc_InputItemInfoChanged
,
330 input_item_changed
, p_playlist
);
331 vlc_event_detach( p_em
, vlc_InputItemErrorWhenReadingChanged
,
332 input_item_changed
, p_playlist
);
334 input_item_Release( p_item
->p_input
);
336 tdelete( p_item
, &p
->input_tree
, playlist_ItemCmpInput
);
337 tdelete( p_item
, &p
->id_tree
, playlist_ItemCmpId
);
338 free( p_item
->pp_children
);
343 * Finds a playlist item by ID.
345 * Searches for a playlist item with the given ID.
347 * \note The playlist must be locked, and the result is only valid until the
348 * playlist is unlocked.
350 * \warning If an item with the given ID is deleted, it is unlikely but
351 * possible that another item will get the same ID. This can result in
353 * Where holding a reference to an input item is a viable option, then
354 * playlist_ItemGetByInput() should be used instead - to avoid this issue.
356 * @param p_playlist the playlist
357 * @param id ID to look for
358 * @return the matching item or NULL if not found
360 playlist_item_t
*playlist_ItemGetById( playlist_t
*p_playlist
, int id
)
362 playlist_private_t
*p
= pl_priv(p_playlist
);
363 playlist_item_t key
, **pp
;
367 pp
= tfind( &key
, &p
->id_tree
, playlist_ItemCmpId
);
368 return (pp
!= NULL
) ? *pp
: NULL
;
372 * Finds a playlist item by input item.
374 * Searches for a playlist item for the given input item.
376 * \note The playlist must be locked, and the result is only valid until the
377 * playlist is unlocked.
379 * \param p_playlist the playlist
380 * \param item input item to look for
381 * \return the playlist item or NULL on failure
383 playlist_item_t
*playlist_ItemGetByInput( playlist_t
* p_playlist
,
384 const input_item_t
*item
)
386 playlist_private_t
*p
= pl_priv(p_playlist
);
387 playlist_item_t key
, **pp
;
390 key
.p_input
= (input_item_t
*)item
;
391 pp
= tfind( &key
, &p
->input_tree
, playlist_ItemCmpInput
);
392 return (pp
!= NULL
) ? *pp
: NULL
;
398 * \param p_playlist playlist object
399 * \param b_locked TRUE if the playlist is locked
402 void playlist_Clear( playlist_t
* p_playlist
, bool b_locked
)
404 playlist_item_t
*p_root
= p_playlist
->p_playing
;
406 PL_LOCK_IF( !b_locked
);
408 for( int i
= p_root
->i_children
- 1; i
>= 0 ;i
-- )
409 playlist_NodeDelete( p_playlist
, p_root
->pp_children
[i
], false );
411 PL_UNLOCK_IF( !b_locked
);
414 /***************************************************************************
415 * Playlist item addition
416 ***************************************************************************/
420 * Add an item to the playlist
421 * \param p_playlist the playlist to add into
422 * \param psz_uri the mrl to add to the playlist
423 * \param play_now whether to start playing immediately or not
424 * \return VLC_SUCCESS or a VLC error code
426 int playlist_Add( playlist_t
*p_playlist
, const char *psz_uri
, bool play_now
)
428 return playlist_AddExt( p_playlist
, psz_uri
, NULL
, play_now
,
433 * Add a MRL into the playlist or the media library, duration and options given
435 * \param p_playlist the playlist to add into
436 * \param psz_uri the mrl to add to the playlist
437 * \param psz_name a text giving a name or description of this item
438 * \param play_now whether to start playing immediately or not
439 * \param i_options the number of options
440 * \param ppsz_options an array of options
441 * \param i_option_flags options flags
442 * \param b_playlist TRUE for playlist, FALSE for media library
443 * \return VLC_SUCCESS or a VLC error code
445 int playlist_AddExt( playlist_t
*p_playlist
, const char * psz_uri
,
446 const char *psz_name
, bool play_now
,
447 int i_options
, const char *const *ppsz_options
,
448 unsigned i_option_flags
,
451 input_item_t
*p_input
= input_item_New( psz_uri
, psz_name
);
454 input_item_AddOptions( p_input
, i_options
, ppsz_options
, i_option_flags
);
455 int i_ret
= playlist_AddInput( p_playlist
, p_input
, play_now
, b_playlist
);
456 input_item_Release( p_input
);
461 * Add an input item to the playlist node
463 * \param p_playlist the playlist to add into
464 * \param p_input the input item to add
465 * \param i_mode the mode used when adding
466 * \param b_playlist TRUE for playlist, FALSE for media library
467 * \return VLC_SUCCESS or VLC_ENOMEM or VLC_EGENERIC
469 int playlist_AddInput( playlist_t
* p_playlist
, input_item_t
*p_input
,
470 bool play_now
, bool b_playlist
)
473 playlist_item_t
*item
= b_playlist
? p_playlist
->p_playing
474 : p_playlist
->p_media_library
;
476 item
= playlist_NodeAddInput( p_playlist
, p_input
, item
, PLAYLIST_END
);
478 if( likely(item
!= NULL
) && play_now
)
479 playlist_ViewPlay( p_playlist
, NULL
, item
);
481 return (item
!= NULL
) ? VLC_SUCCESS
: VLC_ENOMEM
;
485 * Add an input item to a given node
487 * \param p_playlist the playlist to add into
488 * \param p_input the input item to add
489 * \param p_parent the parent item to add into
490 * \param i_pos the position in the playlist where to add. If this is
491 * PLAYLIST_END the item will be added at the end of the playlist
492 * regardless of its size
493 * \return the new playlist item
495 playlist_item_t
* playlist_NodeAddInput( playlist_t
*p_playlist
,
496 input_item_t
*p_input
,
497 playlist_item_t
*p_parent
, int i_pos
)
502 assert( p_parent
&& p_parent
->i_children
!= -1 );
504 playlist_item_t
*p_item
= playlist_ItemNewFromInput( p_playlist
, p_input
);
505 if( unlikely(p_item
== NULL
) )
508 ARRAY_APPEND(p_playlist
->items
, p_item
);
510 playlist_NodeInsert( p_parent
, p_item
, i_pos
);
511 playlist_SendAddNotify( p_playlist
, p_item
);
512 playlist_Preparse( p_playlist
, p_item
);
518 * Copy an item (and all its children, if any) into another node
520 * \param p_playlist the playlist to operate on
521 * \param p_item the playlist item to copy
522 * \param p_parent the parent item to copy into
523 * \param i_pos the position in the parent item for the new copy;
524 * if this is PLAYLIST_END, the copy is appended after all
526 * \return the position in parent item just behind the last new item inserted
528 int playlist_NodeAddCopy( playlist_t
*p_playlist
, playlist_item_t
*p_item
,
529 playlist_item_t
*p_parent
, int i_pos
)
532 assert( p_parent
!= NULL
&& p_item
!= NULL
);
533 assert( p_parent
->i_children
> -1 );
535 if( i_pos
== PLAYLIST_END
)
536 i_pos
= p_parent
->i_children
;
540 for( playlist_item_t
* p_up
= p_parent
; p_up
; p_up
= p_up
->p_parent
)
542 if( p_up
== p_playlist
->p_playing
&& !pl_priv(p_playlist
)->b_tree
)
546 /* TODO: We don't support copying a node into itself (yet),
547 because we insert items as we copy. Instead, we should copy
548 all items first and then insert. */
552 return RecursiveInsertCopy( p_playlist
, p_item
, p_parent
, i_pos
, b_flat
);
556 * Insert a tree of input items into a given playlist node
558 * \param p_playlist the playlist to insert into
559 * \param p_parent the receiving playlist node (can be an item)
560 * \param p_node the root of input item tree,
561 only it's contents will be inserted
562 * \param i_pos the position in the playlist where to insert. If this is
563 * PLAYLIST_END the items will be added at the end of the playlist
564 * regardless of its size
565 * \param b_flat TRUE if the new tree contents should be flattened into a list
566 * \return the first new leaf inserted (in playing order)
568 int playlist_InsertInputItemTree (
569 playlist_t
*p_playlist
, playlist_item_t
*p_parent
,
570 input_item_node_t
*p_node
, int i_pos
, bool b_flat
)
572 return RecursiveAddIntoParent( p_playlist
, p_parent
, p_node
, i_pos
, b_flat
,
573 &(playlist_item_t
*){ NULL
} );
577 /*****************************************************************************
578 * Playlist item misc operations
579 *****************************************************************************/
581 static int ItemIndex ( playlist_item_t
*p_item
)
585 TAB_FIND( p_item
->p_parent
->i_children
,
586 p_item
->p_parent
->pp_children
,
596 * This function must be entered with the playlist lock
598 * \param p_playlist the playlist
599 * \param p_item the item to move
600 * \param p_node the new parent of the item
601 * \param i_newpos the new position under this new parent
602 * \return VLC_SUCCESS or an error
604 int playlist_TreeMove( playlist_t
* p_playlist
, playlist_item_t
*p_item
,
605 playlist_item_t
*p_node
, int i_newpos
)
609 if( p_node
->i_children
== -1 ) return VLC_EGENERIC
;
611 playlist_item_t
*p_detach
= p_item
->p_parent
;
612 int i_index
= ItemIndex( p_item
);
614 REMOVE_ELEM( p_detach
->pp_children
, p_detach
->i_children
, i_index
);
616 if( p_detach
== p_node
&& i_index
< i_newpos
)
619 INSERT_ELEM( p_node
->pp_children
, p_node
->i_children
, i_newpos
, p_item
);
620 p_item
->p_parent
= p_node
;
622 pl_priv( p_playlist
)->b_reset_currently_playing
= true;
623 vlc_cond_signal( &pl_priv( p_playlist
)->signal
);
628 * Moves an array of items
630 * This function must be entered with the playlist lock
632 * \param p_playlist the playlist
633 * \param i_items the number of indexes to move
634 * \param pp_items the array of indexes to move
635 * \param p_node the target node
636 * \param i_newpos the target position under this node
637 * \return VLC_SUCCESS or an error
639 int playlist_TreeMoveMany( playlist_t
*p_playlist
,
640 int i_items
, playlist_item_t
**pp_items
,
641 playlist_item_t
*p_node
, int i_newpos
)
645 if ( p_node
->i_children
== -1 ) return VLC_EGENERIC
;
647 for( int i
= 0; i
< i_items
; i
++ )
649 playlist_item_t
*p_item
= pp_items
[i
];
650 int i_index
= ItemIndex( p_item
);
651 playlist_item_t
*p_parent
= p_item
->p_parent
;
652 REMOVE_ELEM( p_parent
->pp_children
, p_parent
->i_children
, i_index
);
653 if ( p_parent
== p_node
&& i_index
< i_newpos
) i_newpos
--;
655 for( int i
= i_items
- 1; i
>= 0; i
-- )
657 playlist_item_t
*p_item
= pp_items
[i
];
658 INSERT_ELEM( p_node
->pp_children
, p_node
->i_children
, i_newpos
, p_item
);
659 p_item
->p_parent
= p_node
;
662 pl_priv( p_playlist
)->b_reset_currently_playing
= true;
663 vlc_cond_signal( &pl_priv( p_playlist
)->signal
);
668 * Send a notification that an item has been added to a node
670 * \param p_playlist the playlist object
671 * \param i_item_id id of the item added
672 * \param i_node_id id of the node in which the item was added
675 void playlist_SendAddNotify( playlist_t
*p_playlist
, playlist_item_t
*item
)
677 playlist_private_t
*p_sys
= pl_priv(p_playlist
);
680 p_sys
->b_reset_currently_playing
= true;
681 vlc_cond_signal( &p_sys
->signal
);
683 var_SetAddress( p_playlist
, "playlist-item-append", item
);
687 * Get the duration of all items in a node.
689 mtime_t
playlist_GetNodeDuration( playlist_item_t
* node
)
691 mtime_t duration
= input_item_GetDuration( node
->p_input
);
695 for( int i
= 0; i
< node
->i_children
; i
++ )
696 duration
+= playlist_GetNodeDuration( node
->pp_children
[i
] );
701 /***************************************************************************
702 * The following functions are local
703 ***************************************************************************/
705 /* Enqueue an item for preparsing */
706 static void playlist_Preparse( playlist_t
*p_playlist
,
707 playlist_item_t
*p_item
)
709 playlist_private_t
*sys
= pl_priv(p_playlist
);
710 input_item_t
*input
= p_item
->p_input
;
713 /* Preparse if no artist/album info, and hasn't been preparsed already
714 and if user has some preparsing option (auto-preparse variable)
716 char *psz_artist
= input_item_GetArtist( input
);
717 char *psz_album
= input_item_GetAlbum( input
);
719 if( sys
->b_preparse
&& !input_item_IsPreparsed( input
)
720 && (EMPTY_STR(psz_artist
) || EMPTY_STR(psz_album
)) )
721 libvlc_MetadataRequest( p_playlist
->obj
.libvlc
, input
, 0, -1, p_item
);
726 /* Actually convert an item to a node */
727 static void ChangeToNode( playlist_t
*p_playlist
, playlist_item_t
*p_item
)
730 if( p_item
->i_children
!= -1 ) return;
732 p_item
->i_children
= 0;
734 input_item_t
*p_input
= p_item
->p_input
;
735 vlc_mutex_lock( &p_input
->lock
);
736 p_input
->i_type
= ITEM_TYPE_NODE
;
737 vlc_mutex_unlock( &p_input
->lock
);
739 var_SetAddress( p_playlist
, "item-change", p_item
->p_input
);
741 /* Remove it from the array of available items */
742 ARRAY_BSEARCH( p_playlist
->items
,->i_id
, int, p_item
->i_id
, i
);
744 ARRAY_REMOVE( p_playlist
->items
, i
);
747 static int RecursiveAddIntoParent (
748 playlist_t
*p_playlist
, playlist_item_t
*p_parent
,
749 input_item_node_t
*p_node
, int i_pos
, bool b_flat
,
750 playlist_item_t
**pp_first_leaf
)
752 *pp_first_leaf
= NULL
;
754 if( p_parent
->i_children
== -1 ) ChangeToNode( p_playlist
, p_parent
);
756 if( i_pos
== PLAYLIST_END
) i_pos
= p_parent
->i_children
;
758 for( int i
= 0; i
< p_node
->i_children
; i
++ )
760 input_item_node_t
*p_child_node
= p_node
->pp_children
[i
];
762 playlist_item_t
*p_new_item
= NULL
;
763 bool b_children
= p_child_node
->i_children
> 0;
765 //Create the playlist item represented by input node, if allowed.
766 if( !(b_flat
&& b_children
) )
768 p_new_item
= playlist_NodeAddInput( p_playlist
,
769 p_child_node
->p_item
,
771 if( !p_new_item
) return i_pos
;
775 //Recurse if any children
778 //Substitute p_new_item for first child leaf
779 //(If flat, continue counting from current position)
780 int i_last_pos
= RecursiveAddIntoParent(
782 p_new_item
? p_new_item
: p_parent
,
784 ( b_flat
? i_pos
: 0 ),
787 //If flat, take position after recursion as current position
788 if( b_flat
) i_pos
= i_last_pos
;
791 assert( p_new_item
!= NULL
);
792 if( i
== 0 ) *pp_first_leaf
= p_new_item
;
797 static int RecursiveInsertCopy (
798 playlist_t
*p_playlist
, playlist_item_t
*p_item
,
799 playlist_item_t
*p_parent
, int i_pos
, bool b_flat
)
802 assert( p_parent
!= NULL
&& p_item
!= NULL
);
804 if( p_item
== p_parent
) return i_pos
;
806 input_item_t
*p_input
= p_item
->p_input
;
808 if( p_item
->i_children
== -1 || !b_flat
)
810 playlist_item_t
*p_new_item
= NULL
;
812 if( p_item
->i_children
== -1 )
814 input_item_t
*p_new_input
= input_item_Copy( p_input
);
816 if( likely(p_new_input
!= NULL
) )
818 p_new_item
= playlist_NodeAddInput( p_playlist
, p_new_input
,
820 input_item_Release( p_new_input
);
825 vlc_mutex_lock( &p_input
->lock
);
826 p_new_item
= playlist_NodeCreate( p_playlist
, p_input
->psz_name
,
827 p_parent
, i_pos
, 0 );
828 vlc_mutex_unlock( &p_input
->lock
);
830 if( unlikely(p_new_item
== NULL
) )
835 if( p_new_item
->i_children
!= -1 )
836 p_parent
= p_new_item
;
839 for( int i
= 0; i
< p_item
->i_children
; i
++ )
842 i_pos
= RecursiveInsertCopy( p_playlist
, p_item
->pp_children
[i
],
843 p_parent
, i_pos
, true );
845 RecursiveInsertCopy( p_playlist
, p_item
->pp_children
[i
],
846 p_parent
, p_parent
->i_children
, false );