From 287e1ab0f41999417ef39a7fa0c9e622bf5e0f6d Mon Sep 17 00:00:00 2001 From: "Phyks (Lucas Verney)" Date: Wed, 6 Apr 2016 18:57:11 +0200 Subject: [PATCH] Store errors in db and allow to pass them again --- include/cmdline.h | 3 + src/args.ggo | 1 + src/cmdline.c | 27 +++- src/main.c | 310 +++++++++++++++++++++++++++++++++------------- 4 files changed, 253 insertions(+), 88 deletions(-) diff --git a/include/cmdline.h b/include/cmdline.h index 46ff095..2e990ee 100644 --- a/include/cmdline.h +++ b/include/cmdline.h @@ -41,6 +41,8 @@ struct gengetopt_args_info const char *version_help; /**< @brief Print version and exit help description. */ int rescan_flag; /**< @brief Rescan the whole MPD database. (default=off). */ const char *rescan_help; /**< @brief Rescan the whole MPD database. help description. */ + int rescan_errors_flag; /**< @brief Rescan the errored files from the MPD database. (default=off). */ + const char *rescan_errors_help; /**< @brief Rescan the errored files from the MPD database. help description. */ int update_flag; /**< @brief Trigger an update. (default=off). */ const char *update_help; /**< @brief Trigger an update. help description. */ char * mpd_root_arg; /**< @brief MPD library base path.. */ @@ -56,6 +58,7 @@ struct gengetopt_args_info unsigned int help_given ; /**< @brief Whether help was given. */ unsigned int version_given ; /**< @brief Whether version was given. */ unsigned int rescan_given ; /**< @brief Whether rescan was given. */ + unsigned int rescan_errors_given ; /**< @brief Whether rescan-errors was given. */ unsigned int update_given ; /**< @brief Whether update was given. */ unsigned int mpd_root_given ; /**< @brief Whether mpd_root was given. */ unsigned int host_given ; /**< @brief Whether host was given. */ diff --git a/src/args.ggo b/src/args.ggo index 1f6e85a..116682a 100644 --- a/src/args.ggo +++ b/src/args.ggo @@ -3,6 +3,7 @@ version "VERSION" purpose "Binding MPD and Bliss." option "rescan" r "Rescan the whole MPD database." flag off +option "rescan-errors" e "Rescan the errored files from the MPD database." flag off option "update" u "Trigger an update." flag off option "mpd_root" - "MPD library base path." string option "host" - "MPD host." string default="" optional diff --git a/src/cmdline.c b/src/cmdline.c index c99fa0f..de41202 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -37,6 +37,7 @@ const char *gengetopt_args_info_help[] = { " -h, --help Print help and exit", " -V, --version Print version and exit", " -r, --rescan Rescan the whole MPD database. (default=off)", + " -e, --rescan-errors Rescan the errored files from the MPD database.\n (default=off)", " -u, --update Trigger an update. (default=off)", " --mpd_root=STRING MPD library base path.", " --host=STRING MPD host. (default=`')", @@ -71,6 +72,7 @@ void clear_given (struct gengetopt_args_info *args_info) args_info->help_given = 0 ; args_info->version_given = 0 ; args_info->rescan_given = 0 ; + args_info->rescan_errors_given = 0 ; args_info->update_given = 0 ; args_info->mpd_root_given = 0 ; args_info->host_given = 0 ; @@ -82,6 +84,7 @@ void clear_args (struct gengetopt_args_info *args_info) { FIX_UNUSED (args_info); args_info->rescan_flag = 0; + args_info->rescan_errors_flag = 0; args_info->update_flag = 0; args_info->mpd_root_arg = NULL; args_info->mpd_root_orig = NULL; @@ -100,10 +103,11 @@ void init_args_info(struct gengetopt_args_info *args_info) args_info->help_help = gengetopt_args_info_help[0] ; args_info->version_help = gengetopt_args_info_help[1] ; args_info->rescan_help = gengetopt_args_info_help[2] ; - args_info->update_help = gengetopt_args_info_help[3] ; - args_info->mpd_root_help = gengetopt_args_info_help[4] ; - args_info->host_help = gengetopt_args_info_help[5] ; - args_info->port_help = gengetopt_args_info_help[6] ; + args_info->rescan_errors_help = gengetopt_args_info_help[3] ; + args_info->update_help = gengetopt_args_info_help[4] ; + args_info->mpd_root_help = gengetopt_args_info_help[5] ; + args_info->host_help = gengetopt_args_info_help[6] ; + args_info->port_help = gengetopt_args_info_help[7] ; } @@ -228,6 +232,8 @@ cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) write_into_file(outfile, "version", 0, 0 ); if (args_info->rescan_given) write_into_file(outfile, "rescan", 0, 0 ); + if (args_info->rescan_errors_given) + write_into_file(outfile, "rescan-errors", 0, 0 ); if (args_info->update_given) write_into_file(outfile, "update", 0, 0 ); if (args_info->mpd_root_given) @@ -521,6 +527,7 @@ cmdline_parser_internal ( { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, { "rescan", 0, NULL, 'r' }, + { "rescan-errors", 0, NULL, 'e' }, { "update", 0, NULL, 'u' }, { "mpd_root", 1, NULL, 0 }, { "host", 1, NULL, 0 }, @@ -528,7 +535,7 @@ cmdline_parser_internal ( { 0, 0, 0, 0 } }; - c = getopt_long (argc, argv, "hVru", long_options, &option_index); + c = getopt_long (argc, argv, "hVreu", long_options, &option_index); if (c == -1) break; /* Exit from `while (1)' loop. */ @@ -553,6 +560,16 @@ cmdline_parser_internal ( additional_error)) goto failure; + break; + case 'e': /* Rescan the errored files from the MPD database.. */ + + + if (update_arg((void *)&(args_info->rescan_errors_flag), 0, &(args_info->rescan_errors_given), + &(local_args_info.rescan_errors_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "rescan-errors", 'e', + additional_error)) + goto failure; + break; case 'u': /* Trigger an update.. */ diff --git a/src/main.c b/src/main.c index 61c965f..142df74 100644 --- a/src/main.c +++ b/src/main.c @@ -57,6 +57,151 @@ void strip_trailing_slash(char* str) } + +int _parse_music_helper( + sqlite3* dbh, + const char *mpd_base_path, + const char *song_uri) +{ + sqlite3_stmt *res; + + // Compute full uri + printf("\nAdding new song to db: %s\n", song_uri); + char song_full_uri[DEFAULT_STRING_LENGTH] = ""; + strncat(song_full_uri, mpd_base_path, DEFAULT_STRING_LENGTH); + strncat(song_full_uri, song_uri, DEFAULT_STRING_LENGTH); + + // Pass it to bliss + struct bl_song song_analysis; + if (BL_UNEXPECTED == bl_analyze(song_full_uri, &song_analysis)) { + fprintf(stderr, "Error while parsing song: %s.\n\n", song_full_uri); + // Store error in db + sqlite3_prepare_v2(dbh, + "INSERT INTO errors(filename) VALUES(?)", + -1, &res, 0); + sqlite3_bind_text(res, 1, song_uri, strlen(song_uri), SQLITE_STATIC); + sqlite3_step(res); + sqlite3_finalize(res); + // Pass file + return 1; + } + // Insert into db + // Begin transaction + int dberr = sqlite3_exec(dbh, "BEGIN TRANSACTION", NULL, NULL, NULL); + if (SQLITE_OK != dberr) { + fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh)); + sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL); + // Store error in db + sqlite3_prepare_v2(dbh, + "INSERT INTO errors(filename) VALUES(?)", + -1, &res, 0); + sqlite3_bind_text(res, 1, song_uri, strlen(song_uri), SQLITE_STATIC); + sqlite3_step(res); + sqlite3_finalize(res); + // Pass file + return 1; + } + // Insert song analysis in database + dberr = sqlite3_prepare_v2(dbh, + "INSERT INTO songs(tempo, amplitude, frequency, attack, filename) VALUES(?, ?, ?, ?, ?)", + -1, &res, 0); + if (SQLITE_OK != dberr) { + fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh)); + sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL); + // Store error in db + sqlite3_prepare_v2(dbh, + "INSERT INTO errors(filename) VALUES(?)", + -1, &res, 0); + sqlite3_bind_text(res, 1, song_uri, strlen(song_uri), SQLITE_STATIC); + sqlite3_step(res); + sqlite3_finalize(res); + // Pass file + return 1; + } + sqlite3_bind_double(res, 1, song_analysis.force_vector.tempo); + sqlite3_bind_double(res, 2, song_analysis.force_vector.amplitude); + sqlite3_bind_double(res, 3, song_analysis.force_vector.frequency); + sqlite3_bind_double(res, 4, song_analysis.force_vector.attack); + sqlite3_bind_text(res, 5, song_uri, strlen(song_uri), SQLITE_STATIC); + sqlite3_step(res); + sqlite3_finalize(res); + int last_id = sqlite3_last_insert_rowid(dbh); + // Insert updated distances + dberr = sqlite3_prepare_v2(dbh, "SELECT id, tempo, amplitude, frequency, attack FROM songs", -1, &res, 0); + if (SQLITE_OK != dberr) { + fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh)); + sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL); + // Store error in db + sqlite3_prepare_v2(dbh, + "INSERT INTO errors(filename) VALUES(?)", + -1, &res, 0); + sqlite3_bind_text(res, 1, song_uri, strlen(song_uri), SQLITE_STATIC); + sqlite3_step(res); + sqlite3_finalize(res); + // Pass file + return 1; + } + int dberr2 = SQLITE_OK; + while (sqlite3_step(res) == SQLITE_ROW) { + int id = sqlite3_column_int(res, 0); + if (id == last_id) { + // Skip last inserted item + return 1; + } + struct force_vector_s song_db; + song_db.tempo = sqlite3_column_double(res, 1); + song_db.amplitude = sqlite3_column_double(res, 2); + song_db.frequency = sqlite3_column_double(res, 3); + song_db.attack = sqlite3_column_double(res, 4); + float distance = bl_distance(song_analysis.force_vector, song_db); + + sqlite3_stmt *res2; + dberr2 = sqlite3_prepare_v2(dbh, + "INSERT INTO distances(song1, song2, distance) VALUES(?, ?, ?)", + -1, &res2, 0); + if (SQLITE_OK != dberr2) { + fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh)); + break; + } + sqlite3_bind_int(res2, 1, last_id); + sqlite3_bind_int(res2, 2, id); + sqlite3_bind_double(res2, 3, distance); + sqlite3_step(res2); + sqlite3_finalize(res2); + } + if (SQLITE_OK != dberr2) { + sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL); + // Store error in db + sqlite3_prepare_v2(dbh, + "INSERT INTO errors(filename) VALUES(?)", + -1, &res, 0); + sqlite3_bind_text(res, 1, song_uri, strlen(song_uri), SQLITE_STATIC); + sqlite3_step(res); + sqlite3_finalize(res); + // Pass file + return 1; + } + sqlite3_finalize(res); + // Commit transaction + dberr = sqlite3_exec(dbh, "COMMIT", NULL, NULL, NULL); + if (SQLITE_OK != dberr) { + fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh)); + sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL); + // Store error in db + sqlite3_prepare_v2(dbh, + "INSERT INTO errors(filename) VALUES(?)", + -1, &res, 0); + sqlite3_bind_text(res, 1, song_uri, strlen(song_uri), SQLITE_STATIC); + sqlite3_step(res); + sqlite3_finalize(res); + // Pass file + return 1; + } + + return 0; +} + + /** * Update the database. * @@ -67,7 +212,7 @@ void strip_trailing_slash(char* str) void update_database( struct mpd_connection *conn, time_t initial_mtime, - char *mpd_base_path + const char *mpd_base_path ) { // Store latest mtime seen @@ -111,86 +256,7 @@ void update_database( // Compute bl_analyze and store it const char *song_uri = mpd_song_get_uri(song); - printf("\nAdding new song to db: %s\n", song_uri); - struct bl_song song_analysis; - char song_full_uri[DEFAULT_STRING_LENGTH] = ""; - strncat(song_full_uri, mpd_base_path, DEFAULT_STRING_LENGTH); - strncat(song_full_uri, song_uri, DEFAULT_STRING_LENGTH); - if (BL_UNEXPECTED == bl_analyze(song_full_uri, &song_analysis)) { - fprintf(stderr, "Error while parsing song: %s.\n\n", song_uri); - continue; - } - // Insert into db - sqlite3_stmt *res; - // Begin transaction - int dberr = sqlite3_exec(dbh, "BEGIN TRANSACTION", NULL, NULL, NULL); - if (SQLITE_OK != dberr) { - fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh)); - sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL); - continue; - } - // Insert song analysis in database - dberr = sqlite3_prepare_v2(dbh, - "INSERT INTO songs(tempo, amplitude, frequency, attack, filename) VALUES(?, ?, ?, ?, ?)", - -1, &res, 0); - if (SQLITE_OK != dberr) { - fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh)); - sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL); - continue; - } - sqlite3_bind_double(res, 1, song_analysis.force_vector.tempo); - sqlite3_bind_double(res, 2, song_analysis.force_vector.amplitude); - sqlite3_bind_double(res, 3, song_analysis.force_vector.frequency); - sqlite3_bind_double(res, 4, song_analysis.force_vector.attack); - sqlite3_bind_text(res, 5, song_uri, strlen(song_uri), SQLITE_STATIC); - sqlite3_step(res); - sqlite3_finalize(res); - int last_id = sqlite3_last_insert_rowid(dbh); - // Insert updated distances - dberr = sqlite3_prepare_v2(dbh, "SELECT id, tempo, amplitude, frequency, attack FROM songs", -1, &res, 0); - if (SQLITE_OK != dberr) { - fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh)); - sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL); - continue; - } - int dberr2 = SQLITE_OK; - while (sqlite3_step(res) == SQLITE_ROW) { - int id = sqlite3_column_int(res, 0); - if (id == last_id) { - // Skip last inserted item - continue; - } - struct force_vector_s song_db; - song_db.tempo = sqlite3_column_double(res, 1); - song_db.amplitude = sqlite3_column_double(res, 2); - song_db.frequency = sqlite3_column_double(res, 3); - song_db.attack = sqlite3_column_double(res, 4); - float distance = bl_distance(song_analysis.force_vector, song_db); - - sqlite3_stmt *res2; - dberr2 = sqlite3_prepare_v2(dbh, - "INSERT INTO distances(song1, song2, distance) VALUES(?, ?, ?)", - -1, &res2, 0); - if (SQLITE_OK != dberr2) { - fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh)); - break; - } - sqlite3_bind_int(res2, 1, last_id); - sqlite3_bind_int(res2, 2, id); - sqlite3_bind_double(res2, 3, distance); - sqlite3_step(res2); - sqlite3_finalize(res2); - } - if (SQLITE_OK != dberr2) { - sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL); - continue; - } - sqlite3_finalize(res); - // Commit transaction - dberr = sqlite3_exec(dbh, "COMMIT", NULL, NULL, NULL); - if (SQLITE_OK != dberr) { - fprintf(stderr, "Error while inserting data in db: %s\n\n", sqlite3_errmsg(dbh)); - sqlite3_exec(dbh, "ROLLBACK", NULL, NULL, NULL); + if (1 == _parse_music_helper(dbh, mpd_base_path, song_uri)) { continue; } @@ -220,6 +286,57 @@ void update_database( } +/** + * Rescan errored files + * + * @param mpd_base_path Root directory of the MPD library. + */ +void rescan_errored(const char *mpd_base_path) +{ + // Connect to SQLite db + sqlite3 *dbh; + if (0 != sqlite3_open(mpdbliss_data_db, &dbh)) { + fprintf(stderr, "Unable to open SQLite db.\n"); + return; + } + + // Get the list of all the files to process + sqlite3_stmt *res; + int dberr = sqlite3_exec(dbh, "SELECT filename FROM errors", NULL, NULL, NULL); + if (SQLITE_OK != dberr) { + fprintf(stderr, "Error while fetching data in db: %s\n\n", sqlite3_errmsg(dbh)); + sqlite3_close(dbh); + return; + } + // Handle the files + while (sqlite3_step(res) == SQLITE_ROW) { + const char* filename = sqlite3_column_text(res, 1); + + // Delete it from errors list + sqlite3_stmt *res2; + int dberr2 = sqlite3_prepare_v2(dbh, + "DELETE FROM errors WHERE filename=?", + -1, &res2, 0); + if (SQLITE_OK != dberr2) { + fprintf(stderr, "Error while deleting error from db: %s\n\n", sqlite3_errmsg(dbh)); + continue; + } + sqlite3_bind_text(res2, 1, filename, strlen(filename), SQLITE_STATIC); + sqlite3_step(res2); + sqlite3_finalize(res2); + + // Try to import it back + if (1 == _parse_music_helper(dbh, mpd_base_path, filename)) { + continue; + } + } + sqlite3_finalize(res); + + // Close SQLite connection + sqlite3_close(dbh); +} + + int main(int argc, char** argv) { struct gengetopt_args_info args_info; @@ -233,14 +350,29 @@ int main(int argc, char** argv) { if (strlen(args_info.host_arg) > 0) { mpd_host = args_info.host_arg; } - conn = mpd_connection_new( + struct mpd_settings* conn_settings = mpd_settings_new( mpd_host, args_info.port_arg, - 30000); + 0, + NULL, + NULL); + // Connect + conn = mpd_connection_new( + mpd_settings_get_host(conn_settings), + mpd_settings_get_port(conn_settings), + mpd_settings_get_timeout_ms(conn_settings)); if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { fprintf(stderr, "Unable to connect to the MPD server.\n"); exit(EXIT_FAILURE); } + // Handle passwords + const char* mpd_password = mpd_settings_get_password(conn_settings); + if (NULL != mpd_password) { + if (!mpd_run_password(conn, mpd_password)) { + fprintf(stderr, "Unable to send password to the MPD server.\n"); + exit(EXIT_FAILURE); + } + } char *mpd_base_path = args_info.mpd_root_arg; @@ -312,6 +444,14 @@ int main(int argc, char** argv) { sqlite3_close(dbh); return EXIT_FAILURE; } + dberr = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS errors( \ + id INTEGER PRIMARY KEY, \ + filename TEXT UNIQUE)", NULL, NULL, NULL); + if (SQLITE_OK != dberr) { + fprintf(stderr, "Error creating db: %s.\n", sqlite3_errmsg(dbh)); + sqlite3_close(dbh); + return EXIT_FAILURE; + } // Purge db if a rescan is needed if (1 == args_info.rescan_flag) { dberr = sqlite3_exec(dbh, "BEGIN TRANSACTION; DELETE FROM distances; DELETE FROM songs; COMMIT", NULL, NULL, NULL); @@ -327,6 +467,10 @@ int main(int argc, char** argv) { if (1 == args_info.rescan_flag) { update_database(conn, last_mtime, mpd_base_path); } + // Else, if we want to rescan errored files + if (1 == args_info.rescan_errors_flag) { + rescan_errored(mpd_base_path); + } // Else, if we requested an update of the db else if (true == args_info.update_flag) { // Rescan from last known mtime