/* 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 ) { PL_ASSERT_LOCKED; if( (i_mode & PLAYLIST_GO ) ) { pl_priv(p_playlist)->request.b_request = true; pl_priv(p_playlist)->request.i_skip = 0; 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 no artist/album info, and hasn't been preparsed allready and if user has some preparsing option (auto-preparse variable) enabled*/ 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 && input_item_IsPreparsed( p_item->p_input ) == false && ( EMPTY_STR( psz_artist ) || ( EMPTY_STR( psz_album ) ) ) ) playlist_PreparseEnqueue( p_playlist, p_item->p_input ); free( psz_artist ); free( psz_album ); }
static int media_parse(libvlc_media_t *media) { /* TODO: fetcher and parser independent of playlist */ playlist_t *playlist = libvlc_priv (media->p_libvlc_instance->p_libvlc_int)->p_playlist; /* TODO: Fetch art on need basis. But how not to break compatibility? */ playlist_AskForArtEnqueue(playlist, media->p_input_item ); return playlist_PreparseEnqueue(playlist, media->p_input_item); }
/************************************************************************** * Preparse if not already done (Private) **************************************************************************/ static void preparse_if_needed( libvlc_media_t *p_md ) { /* XXX: need some locking here */ if (!p_md->has_asked_preparse) { playlist_PreparseEnqueue( libvlc_priv (p_md->p_libvlc_instance->p_libvlc_int)->p_playlist, p_md->p_input_item ); p_md->has_asked_preparse = true; } }
/** * Add a playlist item to a given node (in the category view ) * * \param p_playlist the playlist to insert into * \param p_item the playlist item to insert * \param i_view the view for which to add or TODO: ALL_VIEWS * \param p_parent the parent node * \param i_mode the mode used when adding * \param i_pos the possition in the node where to add. If this is * PLAYLIST_END the item will be added at the end of the node ** \return The id of the playlist item */ int playlist_NodeAddItem( playlist_t *p_playlist, playlist_item_t *p_item, int i_view,playlist_item_t *p_parent, int i_mode, int i_pos) { vlc_value_t val; int i_position; playlist_view_t *p_view; playlist_add_t *p_add = (playlist_add_t *)malloc(sizeof( playlist_add_t)); vlc_mutex_lock( &p_playlist->object_lock ); if ( i_pos == PLAYLIST_END ) i_pos = -1; /* Sanity checks */ if( !p_parent || p_parent->i_children == -1 ) { msg_Err( p_playlist, "invalid node" ); } /* * CHECK_INSERT : checks if the item is already enqued before * enqueing it */ if ( i_mode & PLAYLIST_CHECK_INSERT ) { int j; if ( p_playlist->pp_items ) { for ( j = 0; j < p_playlist->i_size; j++ ) { if ( !strcmp( p_playlist->pp_items[j]->input.psz_uri, p_item->input.psz_uri ) ) { playlist_ItemDelete( p_item ); vlc_mutex_unlock( &p_playlist->object_lock ); free( p_add ); return -1; } } } i_mode &= ~PLAYLIST_CHECK_INSERT; i_mode |= PLAYLIST_APPEND; } msg_Dbg( p_playlist, "adding playlist item `%s' ( %s )", p_item->input.psz_name, p_item->input.psz_uri ); p_item->input.i_id = ++p_playlist->i_last_id; /* First, add the item at the right position in the item bank */ /* WHY THAT ? */ //i_position = p_playlist->i_index == -1 ? 0 : p_playlist->i_index; i_position = p_playlist->i_size ; INSERT_ELEM( p_playlist->pp_items, p_playlist->i_size, i_position, p_item ); INSERT_ELEM( p_playlist->pp_all_items, p_playlist->i_all_size, p_playlist->i_all_size, p_item ); p_playlist->i_enabled ++; /* TODO: Handle modes */ playlist_NodeInsert( p_playlist, i_view, p_item, p_parent, i_pos ); p_add->i_item = p_item->input.i_id; p_add->i_node = p_parent->input.i_id; p_add->i_view = i_view; val.p_address = p_add; var_Set( p_playlist, "item-append", val ); /* We update the ALL view directly */ p_view = playlist_ViewFind( p_playlist, VIEW_ALL ); playlist_ItemAddParent( p_item, VIEW_ALL, p_view->p_root ); playlist_ViewUpdate( p_playlist, VIEW_ALL ); /* TODO : Update sorted views*/ if( i_mode & PLAYLIST_GO ) { p_playlist->request.b_request = VLC_TRUE; p_playlist->request.i_view = VIEW_CATEGORY; p_playlist->request.p_node = p_parent; p_playlist->request.p_item = p_item; if( p_playlist->p_input ) { input_StopThread( p_playlist->p_input ); } p_playlist->status.i_status = PLAYLIST_RUNNING; } if( i_mode & PLAYLIST_PREPARSE && var_CreateGetBool( p_playlist, "auto-preparse" ) ) { playlist_PreparseEnqueue( p_playlist, &p_item->input ); } vlc_mutex_unlock( &p_playlist->object_lock ); val.b_bool = VLC_TRUE; // var_Set( p_playlist, "intf-change", val ); // free( p_add ); return p_item->input.i_id; }
/** * Add a playlist item into a playlist * * \param p_playlist the playlist to insert into * \param p_item the playlist item to insert * \param i_mode the mode used when adding * \param i_pos the possition in the playlist where to add. If this is * PLAYLIST_END the item will be added at the end of the playlist * regardless of it's size * \return The id of the playlist item */ int playlist_AddItem( playlist_t *p_playlist, playlist_item_t *p_item, int i_mode, int i_pos) { vlc_value_t val; vlc_bool_t b_end = VLC_FALSE; playlist_view_t *p_view = NULL; playlist_add_t *p_add = (playlist_add_t *)malloc(sizeof( playlist_add_t)); vlc_mutex_lock( &p_playlist->object_lock ); /* * CHECK_INSERT : checks if the item is already enqued before * enqueing it */ /* That should not change */ if ( i_mode & PLAYLIST_CHECK_INSERT ) { int j; if ( p_playlist->pp_items ) { for ( j = 0; j < p_playlist->i_size; j++ ) { if ( !strcmp( p_playlist->pp_items[j]->input.psz_uri, p_item->input.psz_uri ) ) { playlist_ItemDelete( p_item ); vlc_mutex_unlock( &p_playlist->object_lock ); return -1; } } } i_mode &= ~PLAYLIST_CHECK_INSERT; i_mode |= PLAYLIST_APPEND; } msg_Dbg( p_playlist, "adding playlist item `%s' ( %s )", p_item->input.psz_name, p_item->input.psz_uri ); p_item->input.i_id = ++p_playlist->i_last_id; /* Do a few boundary checks and allocate space for the item */ if( i_pos == PLAYLIST_END ) { b_end = VLC_TRUE; if( i_mode & PLAYLIST_INSERT ) { i_mode &= ~PLAYLIST_INSERT; i_mode |= PLAYLIST_APPEND; } i_pos = p_playlist->i_size - 1; } if( !(i_mode & PLAYLIST_REPLACE) || i_pos < 0 || i_pos >= p_playlist->i_size ) { /* Additional boundary checks */ if( i_mode & PLAYLIST_APPEND ) { i_pos++; } if( i_pos < 0 ) { i_pos = 0; } else if( i_pos > p_playlist->i_size ) { i_pos = p_playlist->i_size; } INSERT_ELEM( p_playlist->pp_items, p_playlist->i_size, i_pos, p_item ); INSERT_ELEM( p_playlist->pp_all_items, p_playlist->i_all_size, p_playlist->i_all_size, p_item ); p_playlist->i_enabled ++; /* We update the ALL view directly */ playlist_ViewUpdate( p_playlist, VIEW_ALL ); /* Add the item to the General category */ if( b_end == VLC_TRUE ) { playlist_NodeAppend( p_playlist, VIEW_CATEGORY, p_item, p_playlist->p_general ); p_add->i_item = p_item->input.i_id; p_add->i_node = p_playlist->p_general->input.i_id; p_add->i_view = VIEW_CATEGORY; val.p_address = p_add; var_Set( p_playlist, "item-append", val ); } else { playlist_NodeInsert( p_playlist, VIEW_CATEGORY, p_item, p_playlist->p_general, i_pos ); } p_view = playlist_ViewFind( p_playlist, VIEW_ALL ); playlist_ItemAddParent( p_item, VIEW_ALL, p_view->p_root ); /* FIXME : Update sorted views */ if( p_playlist->i_index >= i_pos ) { p_playlist->i_index++; } } else { msg_Err( p_playlist, "Insert mode not implemented" ); } if( (i_mode & PLAYLIST_GO ) && p_view ) { p_playlist->request.b_request = VLC_TRUE; /* FIXME ... */ p_playlist->request.i_view = VIEW_CATEGORY; p_playlist->request.p_node = p_view->p_root; p_playlist->request.p_item = p_item; if( p_playlist->p_input ) { input_StopThread( p_playlist->p_input ); } p_playlist->status.i_status = PLAYLIST_RUNNING; } if( i_mode & PLAYLIST_PREPARSE && var_CreateGetBool( p_playlist, "auto-preparse" ) ) { playlist_PreparseEnqueue( p_playlist, &p_item->input ); } vlc_mutex_unlock( &p_playlist->object_lock ); if( b_end == VLC_FALSE ) { val.b_bool = VLC_TRUE; var_Set( p_playlist, "intf-change", val ); } free( p_add ); return p_item->input.i_id; }
/** * @brief Scan files in a particular directory */ static void ScanFiles( monitoring_thread_t *p_mon, int i_dir_id, bool b_recursive, stat_list_t *stparent ) { int i_rows, i_cols, i_dir_content, i, i_mon_rows, i_mon_cols; char **ppsz_monitored_files; char **pp_results, *psz_dir; char **pp_dir_content; bool *pb_processed; input_item_t *p_input; struct stat s_stat; media_library_t *p_ml = (media_library_t *)p_mon->p_ml; Query( p_ml, &pp_results, &i_rows, &i_cols, "SELECT uri AS directory_uri FROM directories WHERE id = '%d'", i_dir_id ); if( i_rows < 1 ) { msg_Dbg( p_mon, "query returned no directory for dir_id: %d (%s:%d)", i_dir_id, __FILE__, __LINE__ ); return; } psz_dir = strdup( pp_results[1] ); FreeSQLResult( p_ml, pp_results ); struct stat_list_t stself; if( vlc_stat( psz_dir, &stself.st ) == -1 ) { msg_Err( p_ml, "Cannot stat `%s': %m", psz_dir ); free( psz_dir ); return; } #ifndef WIN32 for( stat_list_t *stats = stparent; stats != NULL; stats = stats->parent ) { if( ( stself.st.st_ino == stats->st.st_ino ) && ( stself.st.st_dev == stats->st.st_dev ) ) { msg_Warn( p_ml, "Ignoring infinitely recursive directory `%s'", psz_dir ); free( psz_dir ); return; } } #else /* Windows has st_dev (driver letter - 'A'), but it zeroes st_ino, * so that the test above will always incorrectly succeed. * Besides, Windows does not have dirfd(). */ #endif stself.parent = stparent; QuerySimple( p_ml, "UPDATE directories SET timestamp=%d WHERE id = %d", stself.st.st_mtime, i_dir_id ); Query( p_ml, &ppsz_monitored_files, &i_mon_rows, &i_mon_cols, "SELECT id AS media_id, timestamp AS media_ts, uri AS media_uri " "FROM media WHERE directory_id = %d", i_dir_id ); pb_processed = malloc(sizeof(bool) * i_mon_rows); for( i = 0; i < i_mon_rows ; i++) pb_processed[i] = false; i_dir_content = vlc_scandir( psz_dir, &pp_dir_content, NULL, Sort ); if( i_dir_content == -1 ) { msg_Err( p_mon, "Cannot read `%s': %m", psz_dir ); free( pb_processed ); free( psz_dir ); return; } else if( i_dir_content == 0 ) { msg_Dbg( p_mon, "Nothing in directory `%s'", psz_dir ); free( pb_processed ); free( psz_dir ); return; } for( i = 0; i < i_dir_content; i++ ) { const char *psz_entry = pp_dir_content[i]; if( psz_entry[0] != '.' ) { /* 7 is the size of "file://" */ char psz_uri[strlen(psz_dir) + strlen(psz_entry) + 2 + 7]; sprintf( psz_uri, "%s/%s", psz_dir, psz_entry ); if( vlc_stat( psz_uri, &s_stat ) == -1 ) { msg_Err( p_mon, "%s: %m", psz_uri ); free( pb_processed ); free( psz_dir ); return; } if( S_ISREG( s_stat.st_mode ) ) { const char *psz_dot = strrchr( psz_uri, '.' ); if( psz_dot++ && *psz_dot ) { int i_is_media = 0; for( int a = 0; ppsz_MediaExtensions[a]; a++ ) { if( !strcasecmp( psz_dot, ppsz_MediaExtensions[a] ) ) { i_is_media = 1; break; } } if( !i_is_media ) { msg_Dbg( p_mon, "ignoring file %s", psz_uri ); continue; } } char * psz_tmp = encode_URI_component( psz_uri ); char * psz_encoded_uri = ( char * )calloc( strlen( psz_tmp ) + 9, 1 ); strcpy( psz_encoded_uri, "file:///" ); strcat( psz_encoded_uri, psz_tmp ); free( psz_tmp ); /* Check if given media is already in DB and it has been updated */ bool b_skip = false; bool b_update = false; int j = 1; for( j = 1; j <= i_mon_rows; j++ ) { if( strcasecmp( ppsz_monitored_files[ j * i_mon_cols + 2 ], psz_encoded_uri ) != 0 ) continue; b_update = true; pb_processed[ j - 1 ] = true; if( atoi( ppsz_monitored_files[ j * i_mon_cols + 1 ] ) < s_stat.st_mtime ) { b_skip = false; break; } else { b_skip = true; break; } } msg_Dbg( p_ml , "Checking if %s is in DB. Found: %d", psz_encoded_uri, b_skip? 1 : 0 ); if( b_skip ) continue; p_input = input_item_New( psz_encoded_uri, psz_entry ); playlist_t* p_pl = pl_Get( p_mon ); preparsed_item_t* p_itemobject; p_itemobject = malloc( sizeof( preparsed_item_t ) ); p_itemobject->i_dir_id = i_dir_id; p_itemobject->psz_uri = psz_encoded_uri; p_itemobject->i_mtime = s_stat.st_mtime; p_itemobject->p_mon = p_mon; p_itemobject->b_update = b_update; p_itemobject->i_update_id = b_update ? atoi( ppsz_monitored_files[ j * i_mon_cols + 0 ] ) : 0 ; vlc_event_manager_t *p_em = &p_input->event_manager; vlc_event_attach( p_em, vlc_InputItemPreparsedChanged, PreparseComplete, p_itemobject ); playlist_PreparseEnqueue( p_pl, p_input ); } else if( S_ISDIR( s_stat.st_mode ) && b_recursive ) { Query( p_ml, &pp_results, &i_rows, &i_cols, "SELECT id AS directory_id FROM directories " "WHERE uri=%Q", psz_uri ); FreeSQLResult( p_ml, pp_results ); if( i_rows <= 0 ) { msg_Dbg( p_mon, "New directory `%s' in dir of id %d", psz_uri, i_dir_id ); QuerySimple( p_ml, "INSERT INTO directories (uri, timestamp, " "recursive) VALUES(%Q, 0, 1)", psz_uri ); // We get the id of the directory we've just added Query( p_ml, &pp_results, &i_rows, &i_cols, "SELECT id AS directory_id FROM directories WHERE uri=%Q", psz_uri ); if( i_rows <= 0 ) { msg_Err( p_mon, "Directory `%s' was not sucessfully" " added to the database", psz_uri ); FreeSQLResult( p_ml, pp_results ); continue; } ScanFiles( p_mon, atoi( pp_results[1] ), b_recursive, &stself ); FreeSQLResult( p_ml, pp_results ); } } } } vlc_array_t* delete_ids = vlc_array_new(); for( i = 0; i < i_mon_rows; i++ ) { if( !pb_processed[i] ) { /* This file doesn't exist anymore. Let's...urm...delete it. */ ml_element_t* find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) ); find->criteria = ML_ID; find->value.i = atoi( ppsz_monitored_files[ (i + 1) * i_mon_cols ] ); vlc_array_append( delete_ids, find ); } } /* Delete the unfound media */ if( Delete( p_ml, delete_ids ) != VLC_SUCCESS ) msg_Dbg( p_ml, "Something went wrong in multi delete" ); for( i = 0; i < vlc_array_count( delete_ids ); i++ ) { free( vlc_array_item_at_index( delete_ids, i ) ); } vlc_array_destroy( delete_ids ); FreeSQLResult( p_ml, ppsz_monitored_files ); for( i = 0; i < i_dir_content; i++ ) free( pp_dir_content[i] ); free( pp_dir_content ); free( psz_dir ); free( pb_processed ); }