1 /*****************************************************************************
2 * view.c : Playlist views functions
3 *****************************************************************************
4 * Copyright (C) 1999-2004 the VideoLAN team
5 * $Id: item.c 7997 2004-06-18 11:35:45Z sigmunau $
7 * Authors: Clément Stenac <zorglub@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
23 #include <stdlib.h> /* free(), strtol() */
24 #include <stdio.h> /* sprintf() */
25 #include <string.h> /* strerror() */
28 #include <vlc/input.h>
30 #include "vlc_playlist.h"
32 #define PLAYLIST_DEBUG 1
34 /************************************************************************
36 ************************************************************************/
39 playlist_item_t *playlist_FindDirectParent( playlist_t *p_playlist,
40 playlist_item_t *, int );
42 playlist_item_t *playlist_RecursiveFindNext( playlist_t *p_playlist,
44 playlist_item_t *p_root,
45 playlist_item_t *p_item,
46 playlist_item_t *p_parent );
48 playlist_item_t *playlist_RecursiveFindPrev( playlist_t *p_playlist,
50 playlist_item_t *p_root,
51 playlist_item_t *p_item,
52 playlist_item_t *p_parent );
54 void playlist_NodeDump( playlist_t *p_playlist, playlist_item_t *p_item,
57 /**********************************************************************
58 * Exported View management functions
59 **********************************************************************/
64 * \param p_playlist a playlist object
65 * \param i_id the view identifier
66 * \return the new view or NULL on failure
68 playlist_view_t * playlist_ViewCreate( playlist_t *p_playlist, int i_id,
71 playlist_view_t * p_view;
73 p_view = malloc( sizeof( playlist_view_t ) );
75 memset( p_view, 0, sizeof( playlist_view_t ) );
77 p_view->p_root = playlist_NodeCreate( p_playlist, i_id, NULL, NULL );
79 p_view->psz_name = psz_name ? strdup( psz_name ) : strdup(_("Undefined") );
85 * Creates a new view and add it to the list
87 * This function must be entered without the playlist lock
89 * \param p_playlist a playlist object
90 * \param i_id the view identifier
91 * \return VLC_SUCCESS or an error
93 int playlist_ViewInsert( playlist_t *p_playlist, int i_id, char *psz_name )
95 playlist_view_t *p_view =
96 playlist_ViewCreate( p_playlist, i_id , psz_name );
99 msg_Err( p_playlist, "Creation failed" );
103 vlc_mutex_lock( &p_playlist->object_lock );
105 INSERT_ELEM( p_playlist->pp_views, p_playlist->i_views,
106 p_playlist->i_views, p_view );
108 vlc_mutex_unlock( &p_playlist->object_lock );
116 * This function must be entered wit the playlist lock
118 * \param p_view the view to delete
121 int playlist_ViewDelete( playlist_t *p_playlist,playlist_view_t *p_view )
123 playlist_NodeDelete( p_playlist, p_view->p_root, VLC_TRUE, VLC_TRUE );
124 REMOVE_ELEM( p_playlist->pp_views, p_playlist->i_views, 0 );
130 * Dumps the content of a view
132 * \param p_playlist the playlist
133 * \param p_view the view to dump
136 int playlist_ViewDump( playlist_t *p_playlist, playlist_view_t *p_view )
138 #ifdef PLAYLIST_DEBUG
139 msg_Dbg( p_playlist, "dumping view %i",p_view->i_id );
140 playlist_NodeDump( p_playlist,p_view->p_root, 1 );
146 * Counts the items of a view
148 * \param p_playlist the playlist
149 * \param p_view the view to count
150 * \return the number of items
152 int playlist_ViewItemCount( playlist_t *p_playlist,
153 playlist_view_t *p_view )
155 return playlist_NodeChildrenCount( p_playlist, p_view->p_root );
160 * Updates a view. Only make sense for "sorted" and "ALL" views
162 * \param p_playlist the playlist
163 * \param i_view the view to update
166 int playlist_ViewUpdate( playlist_t *p_playlist, int i_view)
168 playlist_view_t *p_view = playlist_ViewFind( p_playlist, i_view );
175 if( i_view == VIEW_ALL )
177 p_view->p_root->i_children = p_playlist->i_size;
178 p_view->p_root->pp_children = p_playlist->pp_items;
181 /* Handle update of sorted views here */
182 if( i_view >= VIEW_FIRST_SORTED )
185 playlist_ViewEmpty( p_playlist, i_view, VLC_FALSE );
189 case VIEW_S_AUTHOR: i_sort_type = SORT_AUTHOR;break;
190 case VIEW_S_ALBUM: i_sort_type = SORT_ALBUM;break;
191 case VIEW_S_GENRE: i_sort_type = SORT_GENRE;break;
192 default: i_sort_type = SORT_AUTHOR;
194 playlist_NodeGroup( p_playlist, i_view, p_view->p_root,
195 p_playlist->pp_items,p_playlist->i_size,
196 i_sort_type, ORDER_NORMAL );
207 * \param p_playlist the playlist
208 * \param i_id the id to find
209 * \return the found view or NULL if not found
211 playlist_view_t *playlist_ViewFind( playlist_t *p_playlist, int i_id )
214 for( i=0 ; i< p_playlist->i_views ; i++ )
216 if( p_playlist->pp_views[i]->i_id == i_id )
218 return p_playlist->pp_views[i];
225 int playlist_ViewEmpty( playlist_t *p_playlist, int i_view,
226 vlc_bool_t b_delete_items )
228 playlist_view_t *p_view = playlist_ViewFind( p_playlist, i_view );
235 return playlist_NodeEmpty( p_playlist, p_view->p_root, b_delete_items );
238 /**********************************************************************
239 * Exported Nodes management functions
240 **********************************************************************/
245 * Create a playlist node
247 * \param p_playlist the playlist
248 * \paam psz_name the name of the node
249 * \param p_parent the parent node to attach to or NULL if no attach
250 * \return the new node
252 playlist_item_t * playlist_NodeCreate( playlist_t *p_playlist, int i_view,
254 playlist_item_t *p_parent )
256 /* Create the item */
257 playlist_item_t *p_item = (playlist_item_t *)malloc(
258 sizeof( playlist_item_t ) );
260 playlist_add_t *p_add;
266 p_add = (playlist_add_t*)malloc( sizeof(playlist_add_t) );
272 vlc_input_item_Init( VLC_OBJECT(p_playlist), &p_item->input );
274 if( psz_name != NULL )
276 p_item->input.psz_name = strdup( psz_name );
280 p_item->input.psz_name = strdup( _("Undefined") );
283 p_item->input.psz_uri = NULL;
285 p_item->b_enabled = VLC_TRUE;
286 p_item->i_nb_played = 0;
290 p_item->i_children = 0;
291 p_item->pp_children = NULL;
293 p_item->input.i_duration = -1;
294 p_item->input.ppsz_options = NULL;
295 p_item->input.i_options = 0;
296 p_item->input.i_categories = 0;
297 p_item->input.pp_categories = NULL;
298 p_item->input.i_id = ++p_playlist->i_last_id;
300 p_item->input.i_type = ITEM_TYPE_NODE;
302 p_item->pp_parents = NULL;
303 p_item->i_parents = 0;
304 p_item->i_serial = 0;
306 p_item->i_flags |= PLAYLIST_SKIP_FLAG; /* Default behaviour */
308 vlc_mutex_init( p_playlist, &p_item->input.lock );
310 INSERT_ELEM( p_playlist->pp_all_items,
311 p_playlist->i_all_size,
312 p_playlist->i_all_size,
315 if( p_parent != NULL )
317 playlist_NodeAppend( p_playlist, i_view, p_item, p_parent );
320 p_add->i_node = p_parent ? p_parent->input.i_id : -1;
321 p_add->i_item = p_item->input.i_id;
322 p_add->i_view = i_view;
323 val.p_address = p_add;
324 var_Set( p_playlist, "item-append", val);
332 * Remove all the children of a node
334 * This function must be entered with the playlist lock
336 * \param p_playlist the playlist
337 * \param p_root the node
338 * \param b_delete_items do we have to delete the children items ?
339 * \return VLC_SUCCESS or an error
341 int playlist_NodeEmpty( playlist_t *p_playlist, playlist_item_t *p_root,
342 vlc_bool_t b_delete_items )
345 if( p_root->i_children == -1 )
350 /* Delete the children */
351 for( i = p_root->i_children-1 ; i >= 0 ;i-- )
353 if( p_root->pp_children[i]->i_children > -1 )
355 playlist_NodeDelete( p_playlist, p_root->pp_children[i],
356 b_delete_items , VLC_FALSE );
358 else if( b_delete_items )
360 /* Delete the item here */
361 playlist_Delete( p_playlist, p_root->pp_children[i]->input.i_id );
368 * Remove all the children of a node and removes the node
370 * \param p_playlist the playlist
371 * \param p_root the node
372 * \param b_delete_items do we have to delete the children items ?
373 * \return VLC_SUCCESS or an error
375 int playlist_NodeDelete( playlist_t *p_playlist, playlist_item_t *p_root,
376 vlc_bool_t b_delete_items, vlc_bool_t b_force )
378 int i, i_top, i_bottom;
379 if( p_root->i_children == -1 )
384 /* Delete the children */
385 for( i = p_root->i_children - 1 ; i >= 0; i-- )
387 if( p_root->pp_children[i]->i_children > -1 )
389 playlist_NodeDelete( p_playlist, p_root->pp_children[i],
390 b_delete_items , b_force );
392 else if( b_delete_items )
394 /* Delete the item here */
395 playlist_Delete( p_playlist, p_root->pp_children[i]->input.i_id );
398 /* Delete the node */
399 if( p_root->i_flags & PLAYLIST_RO_FLAG && !b_force )
404 for( i = 0 ; i< p_root->i_parents; i++ )
406 playlist_NodeRemoveItem( p_playlist, p_root,
407 p_root->pp_parents[i]->p_parent );
409 var_SetInteger( p_playlist, "item-deleted", p_root->input.i_id );
411 i_bottom = 0; i_top = p_playlist->i_all_size - 1;
413 while( p_playlist->pp_all_items[i]->input.i_id != p_root->input.i_id &&
416 if( p_playlist->pp_all_items[i]->input.i_id < p_root->input.i_id )
424 i = i_bottom + ( i_top - i_bottom ) / 2;
426 if( p_playlist->pp_all_items[i]->input.i_id == p_root->input.i_id )
428 REMOVE_ELEM( p_playlist->pp_all_items, p_playlist->i_all_size, i );
430 playlist_ItemDelete( p_root );
437 * Adds an item to the childs of a node
439 * \param p_playlist the playlist
440 * \param i_view the view of the node ( needed for parent search )
441 * \param p_item the item to append
442 * \param p_parent the parent node
443 * \return VLC_SUCCESS or an error
445 int playlist_NodeAppend( playlist_t *p_playlist,
447 playlist_item_t *p_item,
448 playlist_item_t *p_parent )
450 return playlist_NodeInsert( p_playlist, i_view, p_item, p_parent, -1 );
453 int playlist_NodeInsert( playlist_t *p_playlist,
455 playlist_item_t *p_item,
456 playlist_item_t *p_parent,
460 vlc_bool_t b_found = VLC_FALSE;
461 if( !p_parent || p_parent->i_children == -1 )
463 msg_Err( p_playlist, "invalid node" );
467 if( i_position == -1 ) i_position = p_parent->i_children ;
469 INSERT_ELEM( p_parent->pp_children,
470 p_parent->i_children,
474 /* Add the parent to the array */
475 for( i= 0; i< p_item->i_parents ; i++ )
477 if( p_item->pp_parents[i]->i_view == i_view )
483 if( b_found == VLC_FALSE )
485 struct item_parent_t *p_ip = (struct item_parent_t *)
486 malloc(sizeof(struct item_parent_t) );
487 p_ip->i_view = i_view;
488 p_ip->p_parent = p_parent;
490 INSERT_ELEM( p_item->pp_parents,
491 p_item->i_parents, p_item->i_parents,
495 /* Let the interface know this has been updated */
496 p_parent->i_serial++;
501 * Deletes a parent from the parent list of a node
503 * \param p_playlist the playlist
504 * \param p_item the item to remove
505 * \param p_parent the parent node
506 * \return VLC_SUCCESS or an error
508 int playlist_NodeRemoveParent( playlist_t *p_playlist,
509 playlist_item_t *p_item,
510 playlist_item_t *p_parent )
513 if( !p_parent || p_parent->i_children == -1 )
515 msg_Err( p_playlist, "invalid node" );
518 for( i = 0; i < p_item->i_parents; i++ )
520 if( p_item->pp_parents[i]->p_parent == p_parent )
522 if( p_item->pp_parents[i] )
524 free( p_item->pp_parents[i] );
526 REMOVE_ELEM( p_item->pp_parents, p_item->i_parents, i );
534 * Deletes an item from the children of a node
536 * \param p_playlist the playlist
537 * \param p_item the item to remove
538 * \param p_parent the parent node
539 * \return VLC_SUCCESS or an error
541 int playlist_NodeRemoveItem( playlist_t *p_playlist,
542 playlist_item_t *p_item,
543 playlist_item_t *p_parent )
546 for( i= 0; i< p_parent->i_children ; i++ )
548 if( p_parent->pp_children[i] == p_item )
550 REMOVE_ELEM( p_parent->pp_children, p_parent->i_children, i );
554 /* Let the interface know this has been updated */
555 p_parent->i_serial++;
562 * Count the children of a node
564 * \param p_playlist the playlist
565 * \param p_node the node
566 * \return the number of children
568 int playlist_NodeChildrenCount( playlist_t *p_playlist, playlist_item_t*p_node)
572 if( p_node->i_children == -1 )
577 for( i=0 ; i< p_node->i_children;i++ )
579 if( p_node->pp_children[i]->i_children == -1 )
585 i_nb += playlist_NodeChildrenCount( p_playlist,
586 p_node->pp_children[i] );
593 * Search a child of a node by its name
595 * \param p_node the node
596 * \param psz_search the name of the child to search
597 * \return the child item or NULL if not found or error
599 playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node,
600 const char *psz_search )
604 if( p_node->i_children < 0 )
608 for( i = 0 ; i< p_node->i_children; i++ )
610 if( !strcmp( p_node->pp_children[i]->input.psz_name, psz_search ) )
612 return p_node->pp_children[i];
619 /**********************************************************************
621 **********************************************************************/
624 * Finds the next item to play
626 * \param p_playlist the playlist
627 * \param i_view the view
628 * \param p_root the root node
629 * \param p_node the node we are playing from
630 * \param p_item the item we were playing (NULL if none )
631 * \return the next item to play, or NULL if none found
633 playlist_item_t *playlist_FindNextFromParent( playlist_t *p_playlist,
634 int i_view, /* FIXME: useless */
635 playlist_item_t *p_root,
636 playlist_item_t *p_node,
637 playlist_item_t *p_item )
639 playlist_item_t *p_search, *p_next;
641 #ifdef PLAYLIST_DEBUG
644 msg_Dbg( p_playlist, "finding next of %s within %s - root %s",
645 p_item->input.psz_name, p_node->input.psz_name,
646 p_root->input.psz_name );
650 msg_Dbg( p_playlist, "finding something to play within %s -root %s",
651 p_node->input.psz_name, p_root->input.psz_name );
656 if( !p_node || p_node->i_children == -1 )
658 msg_Err( p_playlist,"invalid arguments for FindNextFromParent" );
662 /* Find the parent node of the item */
665 p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
666 if( p_search == NULL )
668 msg_Err( p_playlist, "parent node not found" );
677 /* Now, go up the tree until we find a suitable next item */
678 p_next = playlist_RecursiveFindNext( p_playlist,i_view,
679 p_node, p_item, p_search );
681 /* Not found, do we go past p_node ? */
684 if( p_playlist->b_go_next )
686 #ifdef PLAYLIST_DEBUG
687 msg_Dbg( p_playlist, "Moving on to next node: search from %s",
688 p_root->input.psz_name );
690 p_next = playlist_RecursiveFindNext( p_playlist, i_view,
691 p_root, p_item, p_search );
696 /* OK, we could continue, so set our current node to the root */
697 p_playlist->status.p_node = p_root;
701 #ifdef PLAYLIST_DEBUG
702 msg_Dbg( p_playlist, "Not moving on to next node: you loose" );
711 * Finds the previous item to play
713 * \param p_playlist the playlist
714 * \param i_view the view
715 * \param p_root the root node
716 * \param p_node the node we are playing from
717 * \param p_item the item we were playing (NULL if none )
718 * \return the next item to play, or NULL if none found
720 playlist_item_t *playlist_FindPrevFromParent( playlist_t *p_playlist,
722 playlist_item_t *p_root,
723 playlist_item_t *p_node,
724 playlist_item_t *p_item )
726 playlist_item_t *p_search, *p_next;
728 #ifdef PLAYLIST_DEBUG
731 msg_Dbg( p_playlist, "Finding prev of %s within %s",
732 p_item->input.psz_name, p_node->input.psz_name );
736 msg_Dbg( p_playlist, "Finding prev from %s",p_node->input.psz_name );
740 if( !p_node || p_node->i_children == -1 )
742 msg_Err( p_playlist,"invalid arguments for FindPrevFromParent" );
746 /* Find the parent node of the item */
749 p_search = playlist_FindDirectParent( p_playlist, p_item, i_view );
750 if( p_search == NULL )
752 msg_Err( p_playlist, "parent node not found" );
761 /* Now, go up the tree until we find a suitable next item */
762 p_next = playlist_RecursiveFindPrev( p_playlist,i_view,
763 p_node, p_item, p_search );
767 if( p_playlist->b_go_next )
769 p_next = playlist_RecursiveFindPrev( p_playlist, i_view,
770 p_root, p_item, p_search );
775 /* OK, we could continue, so set our current node to the root */
776 p_playlist->status.p_node = p_root;
786 /************************************************************************
787 * Following functions are local
788 ***********************************************************************/
791 /* Recursively search the tree for next item */
792 playlist_item_t *playlist_RecursiveFindNext( playlist_t *p_playlist,
794 playlist_item_t *p_root,
795 playlist_item_t *p_item,
796 playlist_item_t *p_parent )
799 playlist_item_t *p_parent_parent;
801 for( i= 0 ; i < p_parent->i_children ; i++ )
803 if( p_parent->pp_children[i] == p_item || p_item == NULL )
809 #ifdef PLAYLIST_DEBUG
810 msg_Dbg( p_playlist,"Current item found, child %i of %s",
811 i , p_parent->input.psz_name );
813 /* We found our item */
814 if( i+1 >= p_parent->i_children )
817 #ifdef PLAYLIST_DEBUG
818 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
819 p_parent->input.psz_name );
821 if( p_parent == p_root )
823 #ifdef PLAYLIST_DEBUG
824 msg_Dbg( p_playlist, "At root item (%s)",
825 p_root->input.psz_name );
827 /* Hmm, seems it's the end for you, guy ! */
831 /* Go up one level */
832 p_parent_parent = playlist_FindDirectParent( p_playlist,
834 if( p_parent_parent == NULL )
836 msg_Warn( p_playlist, "Unable to find parent !");
839 return playlist_RecursiveFindNext( p_playlist, i_view,p_root,
840 p_parent, p_parent_parent );
844 if( p_parent->pp_children[i+1]->i_children == -1 )
846 /* Cool, we have found a real item to play */
847 #ifdef PLAYLIST_DEBUG
848 msg_Dbg( p_playlist, "Playing child %i of %s",
849 i+1 , p_parent->input.psz_name );
851 return p_parent->pp_children[i+1];
853 else if( p_parent->pp_children[i+1]->i_children > 0 )
855 /* Select the first child of this node */
856 #ifdef PLAYLIST_DEBUG
857 msg_Dbg( p_playlist, "%s is a node with children, "
859 p_parent->pp_children[i+1]->input.psz_name);
861 if( p_parent->pp_children[i+1]->pp_children[0]
864 /* first child is a node ! */
865 return playlist_RecursiveFindNext( p_playlist, i_view,
867 p_parent->pp_children[i+1]->pp_children[0]);
869 return p_parent->pp_children[i+1]->pp_children[0];
873 /* This node has no child... We must continue */
874 #ifdef PLAYLIST_DEBUG
875 msg_Dbg( p_playlist, "%s is a node with no children",
876 p_parent->pp_children[i+1]->input.psz_name);
878 p_item = p_parent->pp_children[i+1];
883 /* Just in case :) */
887 /* Recursively search the tree for previous item */
888 playlist_item_t *playlist_RecursiveFindPrev( playlist_t *p_playlist,
890 playlist_item_t *p_root,
891 playlist_item_t *p_item,
892 playlist_item_t *p_parent )
895 playlist_item_t *p_parent_parent;
897 for( i= p_parent->i_children - 1 ; i >= 0 ; i-- )
899 if( p_parent->pp_children[i] == p_item || p_item == NULL )
905 #ifdef PLAYLIST_DEBUG
906 msg_Dbg( p_playlist,"Current item found, child %i of %s",
907 i , p_parent->input.psz_name );
909 /* We found our item */
913 #ifdef PLAYLIST_DEBUG
914 msg_Dbg( p_playlist, "Going up the tree,at parent of %s",
915 p_parent->input.psz_name );
917 if( p_parent == p_root )
919 #ifdef PLAYLIST_DEBUG
920 msg_Dbg( p_playlist, "At root item (%s)",
921 p_root->input.psz_name );
923 /* Hmm, seems it's the end for you, guy ! */
926 /* Go up one level */
927 p_parent_parent = playlist_FindDirectParent( p_playlist,
929 if( p_parent_parent == NULL )
931 #ifdef PLAYLIST_DEBUG
932 msg_Dbg( p_playlist, "Mmmh, couldn't find parent" );
936 return playlist_RecursiveFindPrev( p_playlist, i_view,p_root,
937 p_parent, p_parent_parent );
941 if( p_parent->pp_children[i-1]->i_children == -1 )
943 /* Cool, we have found a real item to play */
944 #ifdef PLAYLIST_DEBUG
945 msg_Dbg( p_playlist, "Playing child %i of %s",
946 i-1, p_parent->input.psz_name );
948 return p_parent->pp_children[i-1];
950 else if( p_parent->pp_children[i-1]->i_children > 0 )
952 /* Select the last child of this node */
953 #ifdef PLAYLIST_DEBUG
954 msg_Dbg( p_playlist, "%s is a node with children,"
956 p_parent->pp_children[i-1]->input.psz_name);
958 if( p_parent->pp_children[i-1]->pp_children[p_parent->
959 pp_children[i-1]->i_children-1]->i_children >= 0 )
961 /* Last child is a node */
962 return playlist_RecursiveFindPrev( p_playlist, i_view,
964 p_parent->pp_children[i-1]->pp_children[
965 p_parent->pp_children[i-1]->i_children-1]);
967 return p_parent->pp_children[i-1]->pp_children[
968 p_parent->pp_children[i-1]->i_children-1];
972 /* This node has no child... We must continue */
973 #ifdef PLAYLIST_DEBUG
974 msg_Dbg( p_playlist, "%s is a node with no children",
975 p_parent->pp_children[i-1]->input.psz_name);
977 p_item = p_parent->pp_children[i-1];
985 /* This function returns the parent of an item in a view */
986 playlist_item_t *playlist_FindDirectParent( playlist_t *p_playlist,
987 playlist_item_t *p_item,
991 for( i= 0; i< p_item->i_parents ; i++ )
993 if( p_item->pp_parents[i]->i_view == i_view )
995 return p_item->pp_parents[i]->p_parent;
1002 #ifdef PLAYLIST_DEBUG
1003 /* This function dumps a node : to be used only for debug*/
1004 void playlist_NodeDump( playlist_t *p_playlist, playlist_item_t *p_item,
1012 msg_Dbg( p_playlist, "%s (%i)",
1013 p_item->input.psz_name, p_item->i_children );
1016 if( p_item->i_children == -1 )
1021 for( i = 0; i< p_item->i_children; i++ )
1023 memset( str, 32, 512 );
1024 sprintf( str + 2 * i_level , "%s (%i)",
1025 p_item->pp_children[i]->input.psz_name,
1026 p_item->pp_children[i]->i_children );
1027 msg_Dbg( p_playlist, "%s",str );
1028 if( p_item->pp_children[i]->i_children >= 0 )
1030 playlist_NodeDump( p_playlist, p_item->pp_children[i],