/** * @brief Update library if new files found or updated */ static void UpdateLibrary( monitoring_thread_t *p_mon ) { int i_rows, i_cols, i; char **pp_results; media_library_t *p_ml = p_mon->p_ml; struct stat s_stat; bool b_recursive = var_GetBool( p_mon, "ml-recursive-scan" ); msg_Dbg( p_mon, "Scanning directories" ); Query( p_ml, &pp_results, &i_rows, &i_cols, "SELECT id AS directory_id, uri AS directory_uri, " "timestamp AS directory_ts FROM directories" ); msg_Dbg( p_mon, "%d directories to scan", i_rows ); for( i = 1; i <= i_rows; i++ ) { int id = atoi( pp_results[i*i_cols] ); char *psz_dir = pp_results[i*i_cols+1]; int timestamp = atoi( pp_results[i*i_cols+2] ); if( vlc_stat( psz_dir, &s_stat ) == -1 ) { int err = errno; if( err == ENOTDIR || err == ENOENT ) { msg_Dbg( p_mon, "Removing `%s'", psz_dir ); RemoveDirToMonitor( p_ml, psz_dir ); } else { msg_Err( p_mon, "%s: %m", psz_dir ); FreeSQLResult( p_ml, pp_results ); return; } errno = err; } if( !S_ISDIR( s_stat.st_mode ) ) { msg_Dbg( p_mon, "Removing `%s'", psz_dir ); RemoveDirToMonitor( p_ml, psz_dir ); } if( timestamp < s_stat.st_mtime ) { msg_Dbg( p_mon, "Adding `%s'", psz_dir ); ScanFiles( p_mon, id, b_recursive, NULL ); } } FreeSQLResult( p_ml, pp_results ); }
int FindAdv( media_library_t *p_ml, vlc_array_t *p_result_array, ml_select_e selected_type, const char* psz_lvalue, ml_ftree_t *tree ) { int i_ret = VLC_SUCCESS; char *psz_query; ml_result_type_e result_type; char **pp_results = NULL; int i_cols, i_rows; if( !p_result_array ) return VLC_EGENERIC; i_ret = BuildSelect( p_ml, &psz_query, &result_type, psz_lvalue, selected_type, tree ); if( i_ret != VLC_SUCCESS ) return i_ret; if( Query( p_ml, &pp_results, &i_rows, &i_cols, "%s", psz_query ) != VLC_SUCCESS ) { msg_Err( p_ml, "Error occurred while making the query to the database" ); return VLC_EGENERIC; } i_ret = SQLToResultArray( p_ml, p_result_array, pp_results, i_rows, i_cols, result_type ); free( psz_query); FreeSQLResult( p_ml, pp_results ); return i_ret; }
/** * @brief Remove a directory to monitor * @param p_ml A media library object * @param psz_dir the directory to remove * @return VLC_SUCCESS or VLC_EGENERIC */ int RemoveDirToMonitor( media_library_t *p_ml, const char *psz_dir ) { assert( p_ml ); char **pp_results = NULL; int i_cols = 0, i_rows = 0, i_ret = VLC_SUCCESS; int i; bool b_recursive = var_CreateGetBool( p_ml, "ml-recursive-scan" ); if( b_recursive ) { i_ret = Query( p_ml, &pp_results, &i_rows, &i_cols, "SELECT media.id FROM media JOIN directories ON " "(media.directory_id = directories.id) WHERE " "directories.uri LIKE '%q%%'", psz_dir ); if( i_ret != VLC_SUCCESS ) { msg_Err( p_ml, "Error occured while making a query to the database" ); return i_ret; } QuerySimple( p_ml, "DELETE FROM directories WHERE uri LIKE '%q%%'", psz_dir ); } else { i_ret = Query( p_ml, &pp_results, &i_rows, &i_cols, "SELECT media.id FROM media JOIN directories ON " "(media.directory_id = directories.id) WHERE " "directories.uri = %Q", psz_dir ); if( i_ret != VLC_SUCCESS ) { msg_Err( p_ml, "Error occured while making a query to the database" ); return i_ret; } QuerySimple( p_ml, "DELETE FROM directories WHERE uri = %Q", psz_dir ); } vlc_array_t *p_where = vlc_array_new(); for( i = 1; i <= i_rows; i++ ) { int id = atoi( pp_results[i*i_cols] ); ml_element_t* p_find = ( ml_element_t * ) calloc( 1, sizeof( ml_element_t ) ); p_find->criteria = ML_ID; p_find->value.i = id; vlc_array_append( p_where, p_find ); } Delete( p_ml, p_where ); FreeSQLResult( p_ml, pp_results ); for( i = 0; i < vlc_array_count( p_where ); i++ ) { free( vlc_array_item_at_index( p_where, i ) ); } vlc_array_destroy( p_where ); return VLC_SUCCESS; }
/** * @brief Generic update in Media Library database * * @param p_ml the media library object * @param selected_type the type of the element we're selecting * @param where the list of ids or uri to change * @param changes list of changes to make in the entries * @return VLC_SUCCESS or VLC_EGENERIC * @note This function is transactional */ int Update( media_library_t *p_ml, ml_select_e selected_type, const char* psz_lvalue, ml_ftree_t *where, vlc_array_t *changes ) { int i_ret = VLC_EGENERIC; char *psz_query = NULL; char *psz_id_query = NULL; char **pp_results = NULL; int i_rows = 0, i_cols = 0; i_ret = BuildUpdate( p_ml, &psz_query, &psz_id_query, psz_lvalue, selected_type, where, changes ); if( i_ret != VLC_SUCCESS ) { msg_Err(p_ml,"Failed to generate update query" ); return i_ret; } i_ret = VLC_EGENERIC; Begin( p_ml ); if( QuerySimple( p_ml, "%s", psz_query ) != VLC_SUCCESS ) { msg_Err( p_ml, "Couldn't run the generated update query successfully" ); goto quitdelete; } /* Get the updated IDs to send events! */ if( Query( p_ml, &pp_results, &i_rows, &i_cols, psz_id_query ) != VLC_SUCCESS ) goto quitdelete; i_ret = VLC_SUCCESS; quitdelete: if( i_ret != VLC_SUCCESS ) Rollback( p_ml ); else { Commit( p_ml ); if( i_rows > 0 ) { for( int i = 0; i < i_rows; i++ ) { var_SetInteger( p_ml, "media-meta-change", atoi( pp_results[i*i_cols] ) ); } } } FreeSQLResult( p_ml, pp_results ); free( psz_id_query ); free( psz_query ); return i_ret; }
/** * @brief Get the list of the monitored directories * @param p_ml A media library object * @param p_array An initialized array where the list will be put in * @return VLC_SUCCESS or VLC_EGENERIC */ int ListMonitoredDirs( media_library_t *p_ml, vlc_array_t *p_array ) { char **pp_results; int i_cols, i_rows; int i; if( Query( p_ml, &pp_results, &i_rows, &i_cols, "SELECT uri AS directory_uri FROM directories WHERE recursive=0" ) != VLC_SUCCESS ) return VLC_EGENERIC; for( i = 1; i <= i_rows; i++ ) { vlc_array_append( p_array, strdup( pp_results[i] ) ); } FreeSQLResult( p_ml, pp_results ); return VLC_SUCCESS; }
/** * @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 ); }
/** * @brief Generic DELETE function for many medias * Delete a media and all its referencies which don't point * an anything else. * * @param p_ml This media_library_t object * @param p_array list of ids to delete * @return VLC_SUCCESS or VLC_EGENERIC * TODO: Expand to delete media/artist/album given any params */ int Delete( media_library_t *p_ml, vlc_array_t *p_array ) { char *psz_idlist = NULL, *psz_tmp = NULL; int i_return = VLC_ENOMEM; int i_rows = 0, i_cols = 0; char **pp_results = NULL; if( vlc_array_count( p_array ) <= 0 ) { i_return = VLC_SUCCESS; goto quit_delete_final; } for( int i = 0; i < vlc_array_count( p_array ); i++ ) { ml_element_t* find = ( ml_element_t * ) vlc_array_item_at_index( p_array, i ); assert( find->criteria == ML_ID ); if( !psz_idlist ) { if( asprintf( &psz_tmp, "( %d", find->value.i ) == -1) { goto quit_delete_final; } } else { if( asprintf( &psz_tmp, "%s, %d", psz_idlist, find->value.i ) == -1) { goto quit_delete_final; } } free( psz_idlist ); psz_idlist = psz_tmp; psz_tmp = NULL; } free( psz_tmp ); if( asprintf( &psz_tmp, "%s )", psz_idlist ? psz_idlist : "(" ) == -1 ) { goto quit_delete_final; } psz_idlist = psz_tmp; psz_tmp = NULL; msg_Dbg( p_ml, "Multi Delete id list: %s", psz_idlist ); /** * Below ensures you are emitting media-deleted only * for existant media */ Begin( p_ml ); i_return = Query( p_ml, &pp_results, &i_rows, &i_cols, "SELECT id FROM media WHERE id IN %s", psz_idlist ); if( i_return != VLC_SUCCESS ) goto quit; i_return = QuerySimple( p_ml, "DELETE FROM media WHERE media.id IN %s", psz_idlist ); if( i_return != VLC_SUCCESS ) goto quit; i_return = QuerySimple( p_ml, "DELETE FROM extra WHERE extra.id IN %s", psz_idlist ); if( i_return != VLC_SUCCESS ) goto quit; quit: if( i_return == VLC_SUCCESS ) { Commit( p_ml ); /* Emit delete on var media-deleted */ for( int i = 1; i <= i_rows; i++ ) { var_SetInteger( p_ml, "media-deleted", atoi( pp_results[i*i_cols] ) ); } } else Rollback( p_ml ); quit_delete_final: FreeSQLResult( p_ml, pp_results ); free( psz_tmp ); free( psz_idlist ); return i_return; }