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_NodeDeleteExplicit( p_playlist
, p_item
, 0 );
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_Control( p_playlist
, PLAYLIST_STOP
, pl_Locked
);
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
);
193 else if( b_flat
&& p_playlist
->current
.i_size
> 0 )
195 /* If the playlist is flat, empty nodes are automatically deleted;
196 * meaning that moving from the current index (the index of a now
197 * removed node) to the next would result in a skip of one entry
198 * (as the empty node is deleted, the logical next item would be
199 * the one that now resides in its place).
201 * Imagine [ A, B, C, D ], where B (at index 1) is currently being
202 * played and deleted. C is the logically next item, but since the
203 * list now looks like [ A, C, D ], advancing to index 2 would mean
204 * D is played - and not C.
206 * By positioning the playlist-head at index 0 (A), when the
207 * playlist advance to the next item, C will correctly be played.
209 * Note: Of course, if the deleted item is at index 0, we should
210 * play whatever item is at position 0 since we cannot advance to
211 * index -1 (as there cannot possibly be any item there).
215 ResetCurrentlyPlaying( p_playlist
,
216 ARRAY_VAL( p_playlist
->current
, last_pos
- 1 ) );
218 playlist_ViewPlay( p_playlist
, NULL
,
219 ARRAY_VAL( p_playlist
->current
, 0 ) );
225 /*****************************************************************************
226 * An input item's meta or duration has changed (Event Callback)
227 *****************************************************************************/
228 static void input_item_changed( const vlc_event_t
* p_event
,
231 playlist_t
*p_playlist
= user_data
;
233 var_SetAddress( p_playlist
, "item-change", p_event
->p_obj
);
236 static int playlist_ItemCmpId( const void *a
, const void *b
)
238 const playlist_item_t
*pa
= a
, *pb
= b
;
240 /* ID are between 1 and INT_MAX, this cannot overflow. */
241 return pa
->i_id
- pb
->i_id
;
244 static int playlist_ItemCmpInput( const void *a
, const void *b
)
246 const playlist_item_t
*pa
= a
, *pb
= b
;
248 if( pa
->p_input
== pb
->p_input
)
250 return (((uintptr_t)pa
->p_input
) > ((uintptr_t)pb
->p_input
))
254 /*****************************************************************************
255 * Playlist item creation
256 *****************************************************************************/
257 playlist_item_t
*playlist_ItemNewFromInput( playlist_t
*p_playlist
,
258 input_item_t
*p_input
)
260 playlist_private_t
*p
= pl_priv(p_playlist
);
261 playlist_item_t
**pp
, *p_item
;
263 p_item
= malloc( sizeof( playlist_item_t
) );
264 if( unlikely(p_item
== NULL
) )
269 p_item
->p_input
= p_input
;
270 p_item
->i_id
= p
->i_last_playlist_id
;
271 p_item
->p_parent
= NULL
;
272 p_item
->i_children
= (p_input
->i_type
== ITEM_TYPE_NODE
) ? 0 : -1;
273 p_item
->pp_children
= NULL
;
274 p_item
->i_nb_played
= 0;
279 do /* Find an unused ID for the item */
281 if( unlikely(p_item
->i_id
== INT_MAX
) )
286 if( unlikely(p_item
->i_id
== p
->i_last_playlist_id
) )
287 goto error
; /* All IDs taken */
289 pp
= tsearch( p_item
, &p
->id_tree
, playlist_ItemCmpId
);
290 if( unlikely(pp
== NULL
) )
293 assert( (*pp
)->i_id
== p_item
->i_id
);
294 assert( (*pp
) == p_item
|| (*pp
)->p_input
!= p_input
);
296 while( p_item
!= *pp
);
298 pp
= tsearch( p_item
, &p
->input_tree
, playlist_ItemCmpInput
);
299 if( unlikely(pp
== NULL
) )
301 tdelete( p_item
, &p
->id_tree
, playlist_ItemCmpId
);
304 /* Same input item cannot be inserted twice. */
305 assert( p_item
== *pp
);
307 p
->i_last_playlist_id
= p_item
->i_id
;
308 input_item_Hold( p_item
->p_input
);
310 vlc_event_manager_t
*p_em
= &p_item
->p_input
->event_manager
;
312 vlc_event_attach( p_em
, vlc_InputItemSubItemTreeAdded
,
313 input_item_add_subitem_tree
, p_playlist
);
314 vlc_event_attach( p_em
, vlc_InputItemDurationChanged
,
315 input_item_changed
, p_playlist
);
316 vlc_event_attach( p_em
, vlc_InputItemMetaChanged
,
317 input_item_changed
, p_playlist
);
318 vlc_event_attach( p_em
, vlc_InputItemNameChanged
,
319 input_item_changed
, p_playlist
);
320 vlc_event_attach( p_em
, vlc_InputItemInfoChanged
,
321 input_item_changed
, p_playlist
);
322 vlc_event_attach( p_em
, vlc_InputItemErrorWhenReadingChanged
,
323 input_item_changed
, p_playlist
);
332 /***************************************************************************
333 * Playlist item destruction
334 ***************************************************************************/
339 * \param p_item item to delete
341 void playlist_ItemRelease( playlist_t
*p_playlist
, playlist_item_t
*p_item
)
343 playlist_private_t
*p
= pl_priv(p_playlist
);
347 vlc_event_manager_t
*p_em
= &p_item
->p_input
->event_manager
;
349 vlc_event_detach( p_em
, vlc_InputItemSubItemTreeAdded
,
350 input_item_add_subitem_tree
, p_playlist
);
351 vlc_event_detach( p_em
, vlc_InputItemMetaChanged
,
352 input_item_changed
, p_playlist
);
353 vlc_event_detach( p_em
, vlc_InputItemDurationChanged
,
354 input_item_changed
, p_playlist
);
355 vlc_event_detach( p_em
, vlc_InputItemNameChanged
,
356 input_item_changed
, p_playlist
);
357 vlc_event_detach( p_em
, vlc_InputItemInfoChanged
,
358 input_item_changed
, p_playlist
);
359 vlc_event_detach( p_em
, vlc_InputItemErrorWhenReadingChanged
,
360 input_item_changed
, p_playlist
);
362 input_item_Release( p_item
->p_input
);
364 tdelete( p_item
, &p
->input_tree
, playlist_ItemCmpInput
);
365 tdelete( p_item
, &p
->id_tree
, playlist_ItemCmpId
);
366 free( p_item
->pp_children
);
371 * Finds a playlist item by ID.
373 * Searches for a playlist item with the given ID.
375 * \note The playlist must be locked, and the result is only valid until the
376 * playlist is unlocked.
378 * \warning If an item with the given ID is deleted, it is unlikely but
379 * possible that another item will get the same ID. This can result in
381 * Where holding a reference to an input item is a viable option, then
382 * playlist_ItemGetByInput() should be used instead - to avoid this issue.
384 * @param p_playlist the playlist
385 * @param id ID to look for
386 * @return the matching item or NULL if not found
388 playlist_item_t
*playlist_ItemGetById( playlist_t
*p_playlist
, int id
)
390 playlist_private_t
*p
= pl_priv(p_playlist
);
391 playlist_item_t key
, **pp
;
395 pp
= tfind( &key
, &p
->id_tree
, playlist_ItemCmpId
);
396 return (pp
!= NULL
) ? *pp
: NULL
;
400 * Finds a playlist item by input item.
402 * Searches for a playlist item for the given input item.
404 * \note The playlist must be locked, and the result is only valid until the
405 * playlist is unlocked.
407 * \param p_playlist the playlist
408 * \param item input item to look for
409 * \return the playlist item or NULL on failure
411 playlist_item_t
*playlist_ItemGetByInput( playlist_t
* p_playlist
,
412 const input_item_t
*item
)
414 playlist_private_t
*p
= pl_priv(p_playlist
);
415 playlist_item_t key
, **pp
;
418 key
.p_input
= (input_item_t
*)item
;
419 pp
= tfind( &key
, &p
->input_tree
, playlist_ItemCmpInput
);
420 return (pp
!= NULL
) ? *pp
: NULL
;
426 * \param p_playlist playlist object
427 * \param b_locked TRUE if the playlist is locked
430 void playlist_Clear( playlist_t
* p_playlist
, bool b_locked
)
432 playlist_item_t
*p_root
= p_playlist
->p_playing
;
434 PL_LOCK_IF( !b_locked
);
436 for( int i
= p_root
->i_children
- 1; i
>= 0 ;i
-- )
437 playlist_NodeDelete( p_playlist
, p_root
->pp_children
[i
] );
439 PL_UNLOCK_IF( !b_locked
);
442 /***************************************************************************
443 * Playlist item addition
444 ***************************************************************************/
448 * Add an item to the playlist
449 * \param p_playlist the playlist to add into
450 * \param psz_uri the mrl to add to the playlist
451 * \param play_now whether to start playing immediately or not
452 * \return VLC_SUCCESS or a VLC error code
454 int playlist_Add( playlist_t
*p_playlist
, const char *psz_uri
, bool play_now
)
456 return playlist_AddExt( p_playlist
, psz_uri
, NULL
, play_now
,
461 * Add a MRL into the playlist or the media library, duration and options given
463 * \param p_playlist the playlist to add into
464 * \param psz_uri the mrl to add to the playlist
465 * \param psz_name a text giving a name or description of this item
466 * \param play_now whether to start playing immediately or not
467 * \param i_options the number of options
468 * \param ppsz_options an array of options
469 * \param i_option_flags options flags
470 * \param b_playlist TRUE for playlist, FALSE for media library
471 * \return VLC_SUCCESS or a VLC error code
473 int playlist_AddExt( playlist_t
*p_playlist
, const char * psz_uri
,
474 const char *psz_name
, bool play_now
,
475 int i_options
, const char *const *ppsz_options
,
476 unsigned i_option_flags
,
479 input_item_t
*p_input
= input_item_New( psz_uri
, psz_name
);
482 input_item_AddOptions( p_input
, i_options
, ppsz_options
, i_option_flags
);
483 int i_ret
= playlist_AddInput( p_playlist
, p_input
, play_now
, b_playlist
);
484 input_item_Release( p_input
);
489 * Add an input item to the playlist node
491 * \param p_playlist the playlist to add into
492 * \param p_input the input item to add
493 * \param i_mode the mode used when adding
494 * \param b_playlist TRUE for playlist, FALSE for media library
495 * \return VLC_SUCCESS or VLC_ENOMEM or VLC_EGENERIC
497 int playlist_AddInput( playlist_t
* p_playlist
, input_item_t
*p_input
,
498 bool play_now
, bool b_playlist
)
501 playlist_item_t
*item
= b_playlist
? p_playlist
->p_playing
502 : p_playlist
->p_media_library
;
504 item
= playlist_NodeAddInput( p_playlist
, p_input
, item
, PLAYLIST_END
);
506 if( likely(item
!= NULL
) && play_now
)
507 playlist_ViewPlay( p_playlist
, NULL
, item
);
509 return (item
!= NULL
) ? VLC_SUCCESS
: VLC_ENOMEM
;
513 * Add an input item to a given node
515 * \param p_playlist the playlist to add into
516 * \param p_input the input item to add
517 * \param p_parent the parent item to add into
518 * \param i_pos the position in the playlist where to add. If this is
519 * PLAYLIST_END the item will be added at the end of the playlist
520 * regardless of its size
521 * \return the new playlist item
523 playlist_item_t
* playlist_NodeAddInput( playlist_t
*p_playlist
,
524 input_item_t
*p_input
,
525 playlist_item_t
*p_parent
, int i_pos
)
530 assert( p_parent
&& p_parent
->i_children
!= -1 );
532 playlist_item_t
*p_item
= playlist_ItemNewFromInput( p_playlist
, p_input
);
533 if( unlikely(p_item
== NULL
) )
536 ARRAY_APPEND(p_playlist
->items
, p_item
);
538 playlist_NodeInsert( p_parent
, p_item
, i_pos
);
539 playlist_SendAddNotify( p_playlist
, p_item
);
540 playlist_Preparse( p_playlist
, p_item
);
546 * Copy an item (and all its children, if any) into another node
548 * \param p_playlist the playlist to operate on
549 * \param p_item the playlist item to copy
550 * \param p_parent the parent item to copy into
551 * \param i_pos the position in the parent item for the new copy;
552 * if this is PLAYLIST_END, the copy is appended after all
554 * \return the position in parent item just behind the last new item inserted
556 int playlist_NodeAddCopy( playlist_t
*p_playlist
, playlist_item_t
*p_item
,
557 playlist_item_t
*p_parent
, int i_pos
)
560 assert( p_parent
!= NULL
&& p_item
!= NULL
);
561 assert( p_parent
->i_children
> -1 );
563 if( i_pos
== PLAYLIST_END
)
564 i_pos
= p_parent
->i_children
;
568 for( playlist_item_t
* p_up
= p_parent
; p_up
; p_up
= p_up
->p_parent
)
570 if( p_up
== p_playlist
->p_playing
&& !pl_priv(p_playlist
)->b_tree
)
574 /* TODO: We don't support copying a node into itself (yet),
575 because we insert items as we copy. Instead, we should copy
576 all items first and then insert. */
580 return RecursiveInsertCopy( p_playlist
, p_item
, p_parent
, i_pos
, b_flat
);
584 * Insert a tree of input items into a given playlist node
586 * \param p_playlist the playlist to insert into
587 * \param p_parent the receiving playlist node (can be an item)
588 * \param p_node the root of input item tree,
589 only it's contents will be inserted
590 * \param i_pos the position in the playlist where to insert. If this is
591 * PLAYLIST_END the items will be added at the end of the playlist
592 * regardless of its size
593 * \param b_flat TRUE if the new tree contents should be flattened into a list
594 * \return the first new leaf inserted (in playing order)
596 int playlist_InsertInputItemTree (
597 playlist_t
*p_playlist
, playlist_item_t
*p_parent
,
598 input_item_node_t
*p_node
, int i_pos
, bool b_flat
)
600 return RecursiveAddIntoParent( p_playlist
, p_parent
, p_node
, i_pos
, b_flat
,
601 &(playlist_item_t
*){ NULL
} );
605 /*****************************************************************************
606 * Playlist item misc operations
607 *****************************************************************************/
609 static int ItemIndex ( playlist_item_t
*p_item
)
613 TAB_FIND( p_item
->p_parent
->i_children
,
614 p_item
->p_parent
->pp_children
,
624 * This function must be entered with the playlist lock
626 * \param p_playlist the playlist
627 * \param p_item the item to move
628 * \param p_node the new parent of the item
629 * \param i_newpos the new position under this new parent
630 * \return VLC_SUCCESS or an error
632 int playlist_TreeMove( playlist_t
* p_playlist
, playlist_item_t
*p_item
,
633 playlist_item_t
*p_node
, int i_newpos
)
637 if( p_node
->i_children
== -1 ) return VLC_EGENERIC
;
639 playlist_item_t
*p_detach
= p_item
->p_parent
;
640 int i_index
= ItemIndex( p_item
);
642 TAB_ERASE(p_detach
->i_children
, p_detach
->pp_children
, i_index
);
644 if( p_detach
== p_node
&& i_index
< i_newpos
)
647 TAB_INSERT(p_node
->i_children
, p_node
->pp_children
, p_item
, i_newpos
);
648 p_item
->p_parent
= p_node
;
650 pl_priv( p_playlist
)->b_reset_currently_playing
= true;
651 vlc_cond_signal( &pl_priv( p_playlist
)->signal
);
656 * Moves an array of items
658 * This function must be entered with the playlist lock
660 * \param p_playlist the playlist
661 * \param i_items the number of indexes to move
662 * \param pp_items the array of indexes to move
663 * \param p_node the target node
664 * \param i_newpos the target position under this node
665 * \return VLC_SUCCESS or an error
667 int playlist_TreeMoveMany( playlist_t
*p_playlist
,
668 int i_items
, playlist_item_t
**pp_items
,
669 playlist_item_t
*p_node
, int i_newpos
)
673 if ( p_node
->i_children
== -1 ) return VLC_EGENERIC
;
675 for( int i
= 0; i
< i_items
; i
++ )
677 playlist_item_t
*p_item
= pp_items
[i
];
678 int i_index
= ItemIndex( p_item
);
679 playlist_item_t
*p_parent
= p_item
->p_parent
;
680 TAB_ERASE(p_parent
->i_children
, p_parent
->pp_children
, i_index
);
681 if ( p_parent
== p_node
&& i_index
< i_newpos
) i_newpos
--;
683 for( int i
= i_items
- 1; i
>= 0; i
-- )
685 playlist_item_t
*p_item
= pp_items
[i
];
686 TAB_INSERT(p_node
->i_children
, p_node
->pp_children
, p_item
, i_newpos
);
687 p_item
->p_parent
= p_node
;
690 pl_priv( p_playlist
)->b_reset_currently_playing
= true;
691 vlc_cond_signal( &pl_priv( p_playlist
)->signal
);
696 * Send a notification that an item has been added to a node
698 * \param p_playlist the playlist object
699 * \param i_item_id id of the item added
700 * \param i_node_id id of the node in which the item was added
703 void playlist_SendAddNotify( playlist_t
*p_playlist
, playlist_item_t
*item
)
705 playlist_private_t
*p_sys
= pl_priv(p_playlist
);
708 p_sys
->b_reset_currently_playing
= true;
709 vlc_cond_signal( &p_sys
->signal
);
711 var_SetAddress( p_playlist
, "playlist-item-append", item
);
715 * Get the duration of all items in a node.
717 mtime_t
playlist_GetNodeDuration( playlist_item_t
* node
)
719 mtime_t duration
= input_item_GetDuration( node
->p_input
);
723 for( int i
= 0; i
< node
->i_children
; i
++ )
724 duration
+= playlist_GetNodeDuration( node
->pp_children
[i
] );
729 /***************************************************************************
730 * The following functions are local
731 ***************************************************************************/
733 /* Enqueue an item for preparsing */
734 static void playlist_Preparse( playlist_t
*p_playlist
,
735 playlist_item_t
*p_item
)
737 playlist_private_t
*sys
= pl_priv(p_playlist
);
738 input_item_t
*input
= p_item
->p_input
;
741 /* Preparse if no artist/album info, and hasn't been preparsed already
742 and if user has some preparsing option (auto-preparse variable)
744 char *psz_artist
= input_item_GetArtist( input
);
745 char *psz_album
= input_item_GetAlbum( input
);
747 if( sys
->b_preparse
&& !input_item_IsPreparsed( input
)
748 && (EMPTY_STR(psz_artist
) || EMPTY_STR(psz_album
)) )
749 libvlc_MetadataRequest( p_playlist
->obj
.libvlc
, input
, 0, -1, p_item
);
754 /* Actually convert an item to a node */
755 static void ChangeToNode( playlist_t
*p_playlist
, playlist_item_t
*p_item
)
758 if( p_item
->i_children
!= -1 ) return;
760 p_item
->i_children
= 0;
762 input_item_t
*p_input
= p_item
->p_input
;
763 vlc_mutex_lock( &p_input
->lock
);
764 p_input
->i_type
= ITEM_TYPE_NODE
;
765 vlc_mutex_unlock( &p_input
->lock
);
767 var_SetAddress( p_playlist
, "item-change", p_item
->p_input
);
769 /* Remove it from the array of available items */
770 ARRAY_BSEARCH( p_playlist
->items
,->i_id
, int, p_item
->i_id
, i
);
772 ARRAY_REMOVE( p_playlist
->items
, i
);
775 static int RecursiveAddIntoParent (
776 playlist_t
*p_playlist
, playlist_item_t
*p_parent
,
777 input_item_node_t
*p_node
, int i_pos
, bool b_flat
,
778 playlist_item_t
**pp_first_leaf
)
780 *pp_first_leaf
= NULL
;
782 if( p_parent
->i_children
== -1 ) ChangeToNode( p_playlist
, p_parent
);
784 if( i_pos
== PLAYLIST_END
) i_pos
= p_parent
->i_children
;
786 for( int i
= 0; i
< p_node
->i_children
; i
++ )
788 input_item_node_t
*p_child_node
= p_node
->pp_children
[i
];
790 playlist_item_t
*p_new_item
= NULL
;
791 bool b_children
= p_child_node
->i_children
> 0;
793 //Create the playlist item represented by input node, if allowed.
794 if( !(b_flat
&& b_children
) )
796 p_new_item
= playlist_NodeAddInput( p_playlist
,
797 p_child_node
->p_item
,
799 if( !p_new_item
) return i_pos
;
803 //Recurse if any children
806 //Substitute p_new_item for first child leaf
807 //(If flat, continue counting from current position)
808 int i_last_pos
= RecursiveAddIntoParent(
810 p_new_item
? p_new_item
: p_parent
,
812 ( b_flat
? i_pos
: 0 ),
815 //If flat, take position after recursion as current position
816 if( b_flat
) i_pos
= i_last_pos
;
819 assert( p_new_item
!= NULL
);
820 if( i
== 0 ) *pp_first_leaf
= p_new_item
;
825 static int RecursiveInsertCopy (
826 playlist_t
*p_playlist
, playlist_item_t
*p_item
,
827 playlist_item_t
*p_parent
, int i_pos
, bool b_flat
)
830 assert( p_parent
!= NULL
&& p_item
!= NULL
);
832 if( p_item
== p_parent
) return i_pos
;
834 input_item_t
*p_input
= p_item
->p_input
;
836 if( p_item
->i_children
== -1 || !b_flat
)
838 playlist_item_t
*p_new_item
= NULL
;
840 if( p_item
->i_children
== -1 )
842 input_item_t
*p_new_input
= input_item_Copy( p_input
);
844 if( likely(p_new_input
!= NULL
) )
846 p_new_item
= playlist_NodeAddInput( p_playlist
, p_new_input
,
848 input_item_Release( p_new_input
);
853 vlc_mutex_lock( &p_input
->lock
);
854 p_new_item
= playlist_NodeCreate( p_playlist
, p_input
->psz_name
,
855 p_parent
, i_pos
, 0 );
856 vlc_mutex_unlock( &p_input
->lock
);
858 if( unlikely(p_new_item
== NULL
) )
863 if( p_new_item
->i_children
!= -1 )
864 p_parent
= p_new_item
;
867 for( int i
= 0; i
< p_item
->i_children
; i
++ )
870 i_pos
= RecursiveInsertCopy( p_playlist
, p_item
->pp_children
[i
],
871 p_parent
, i_pos
, true );
873 RecursiveInsertCopy( p_playlist
, p_item
->pp_children
[i
],
874 p_parent
, p_parent
->i_children
, false );