/* SampleHive * Copyright (C) 2021 Apoorv Singh * A simple, modern audio sample browser/manager for GNU/Linux. * * This file is a part of SampleHive * * SampleHive is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SampleHive is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "Database/Database.hpp" #include "Utility/Log.hpp" #include "Utility/Paths.hpp" #include "Utility/Serialize.hpp" #include "Utility/Utils.hpp" #include #include #include #include #include #include #include #include #include #include #include void throw_on_sqlite3_error(int rc) { if (rc != SQLITE_OK) { throw std::runtime_error(sqlite3_errstr(rc)); } } void debug_log_on_error(int rc) { if (rc != SQLITE_OK) { sqlite3_errstr(rc); } } void show_modal_dialog_and_log(const std::string &message, const std::string &title, const std::string &error_msg) { std::stringstream ss; ss << message << " : " << error_msg; const auto msg = ss.str(); SH_LOG_ERROR(msg.c_str()); wxMessageDialog msgDialog(NULL, _(msg), _(title), wxOK | wxICON_ERROR); msgDialog.ShowModal(); } class Sqlite3Statement { public: Sqlite3Statement(sqlite3 *database, const std::string &query) { throw_on_sqlite3_error(sqlite3_prepare_v2(database, query.c_str(), query.size(), &stmt, NULL)); } ~Sqlite3Statement() { throw_on_sqlite3_error(sqlite3_finalize(stmt)); } sqlite3_stmt* stmt = nullptr; }; cDatabase::cDatabase() { SampleHive::cSerializer serializer; if (!serializer.DeserializeDemoMode()) OpenDatabase(); else OpenTemporaryDatabase(); } cDatabase::~cDatabase() { CloseDatabase(); } void cDatabase::CreateTableSamples() { /* Create SQL statement */ const auto samples = "CREATE TABLE IF NOT EXISTS SAMPLES(" "FAVORITE INT NOT NULL," "FILENAME TEXT NOT NULL," "EXTENSION TEXT NOT NULL," "SAMPLEPACK TEXT NOT NULL," "TYPE TEXT NOT NULL," "CHANNELS INT NOT NULL," "LENGTH INT NOT NULL," "SAMPLERATE INT NOT NULL," "BITRATE INT NOT NULL," "PATH TEXT NOT NULL," "TRASHED INT NOT NULL," "HIVE TEXT NOT NULL);"; try { throw_on_sqlite3_error(sqlite3_exec(m_pDatabase, samples, NULL, 0, &m_pErrMsg)); SH_LOG_INFO("SAMPLES table created successfully."); } catch (const std::exception& e) { show_modal_dialog_and_log("Error! Cannot create SAMPLES table", "Error", e.what()); } const auto indices = "CREATE INDEX IF NOT EXISTS idx_filename_path ON SAMPLES(FILENAME, PATH);"; try { throw_on_sqlite3_error(sqlite3_exec(m_pDatabase, indices, NULL, 0, &m_pErrMsg)); SH_LOG_INFO("FILENAME PATH index created successfully."); } catch (const std::exception& e) { show_modal_dialog_and_log("Error! Cannot create INDEX Filename Path", "Error", e.what()); } } void cDatabase::CreateTableHives() { /* Create SQL statement */ const auto hives = "CREATE TABLE IF NOT EXISTS HIVES(HIVE TEXT NOT NULL);"; try { throw_on_sqlite3_error(sqlite3_exec(m_pDatabase, hives, NULL, 0, &m_pErrMsg)); SH_LOG_INFO("HIVES table created successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot create HIVES table", "Error", e.what()); } } //Loops through a Sample array and adds them to the database void cDatabase::InsertIntoSamples(const std::vector &samples) { try { const auto sql = "INSERT INTO SAMPLES (FAVORITE, FILENAME, \ EXTENSION, SAMPLEPACK, TYPE, CHANNELS, LENGTH, \ SAMPLERATE, BITRATE, PATH, TRASHED, HIVE) \ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_exec(m_pDatabase, "BEGIN TRANSACTION", NULL, NULL, &m_pErrMsg)); Sample sample; std::string filename; std::string file_extension; std::string sample_pack; std::string type; std::string path; for (unsigned int i = 0; i < samples.size(); i++) { sample = samples[i]; filename = sample.GetFilename(); file_extension = sample.GetFileExtension(); sample_pack = sample.GetSamplePack(); type = sample.GetType(); path = sample.GetPath(); std::string hive = "Favorites"; throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 1, sample.GetFavorite())); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 2, filename.c_str(), filename.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 3, file_extension.c_str(), file_extension.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 4, sample_pack.c_str(), sample_pack.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 5, type.c_str(), type.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 6, sample.GetChannels())); throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 7, sample.GetLength())); throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 8, sample.GetSampleRate())); throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 9, sample.GetBitrate())); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 10, path.c_str(), path.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 11, sample.GetTrashed())); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 12, hive.c_str(), hive.size(), SQLITE_STATIC)); sqlite3_step(statement.stmt); throw_on_sqlite3_error(sqlite3_clear_bindings(statement.stmt)); throw_on_sqlite3_error(sqlite3_reset(statement.stmt)); } throw_on_sqlite3_error(sqlite3_exec(m_pDatabase, "END TRANSACTION", NULL, NULL, &m_pErrMsg)); SH_LOG_INFO("Data inserted successfully into SAMPLES."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot insert data into SAMPLES", "Error", e.what()); } } void cDatabase::InsertIntoHives(const std::string &hiveName) { try { const auto sql = "INSERT INTO HIVES(HIVE) VALUES(?);"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, hiveName.c_str(), hiveName.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_step(statement.stmt)); SH_LOG_INFO("Data inserted successfully into HIVES."); } catch (const std::exception& e) { show_modal_dialog_and_log("Error! Cannot insert data into HIVES", "Error", e.what()); } } void cDatabase::UpdateHive(const std::string &hiveOldName, const std::string &hiveNewName) { try { const auto sql = "UPDATE HIVES SET HIVE = ? WHERE HIVE = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, hiveNewName.c_str(), hiveNewName.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 2, hiveOldName.c_str(), hiveOldName.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) != SQLITE_DONE) { SH_LOG_INFO("Updating hive {} to {}", hiveOldName, hiveNewName); } else { SH_LOG_INFO("Updated hive successfully."); } } catch (const std::exception& e) { show_modal_dialog_and_log("Error! Cannot update hive", "Error", e.what()); } } void cDatabase::UpdateHiveName(const std::string &filename, const std::string &hiveName) { try { const auto sql = "UPDATE SAMPLES SET HIVE = ? WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, hiveName.c_str(), hiveName.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 2, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_ROW) { SH_LOG_INFO("Updating hive to {} for {}", hiveName, filename); } SH_LOG_INFO("Updated hive name successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot update hive name", "Error", e.what()); } } void cDatabase::UpdateFavoriteColumn(const std::string &filename, int value) { try { const auto sql = "UPDATE SAMPLES SET FAVORITE = ? WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 1, value)); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 2, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_ROW) { SH_LOG_INFO("Updating favorite value of {} to {}", filename, value); } SH_LOG_INFO("Updated favorite column successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot update favorite column", "Error", e.what()); } } void cDatabase::UpdateSamplePack(const std::string &filename, const std::string &samplePack) { try { const auto sql = "UPDATE SAMPLES SET SAMPLEPACK = ? WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, samplePack.c_str(), samplePack.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 2, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_ROW) { SH_LOG_INFO("Updating sample pack of {} to {}", filename, samplePack); } SH_LOG_INFO("Updated sample pack successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot update sample pack", "Error", e.what()); } } void cDatabase::UpdateSampleType(const std::string &filename, const std::string &type) { try { const auto sql = "UPDATE SAMPLES SET TYPE = ? WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, type.c_str(), type.size(), SQLITE_STATIC)); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 2, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_ROW) { SH_LOG_INFO("Updating sample type of {} to {}", filename, type); } SH_LOG_INFO("Updated sample type successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot update sample type", "Error", e.what()); } } std::string cDatabase::GetSampleType(const std::string &filename) { std::string type; try { const auto sql = "SELECT TYPE FROM SAMPLES WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_ROW) { SH_LOG_INFO("Record found, fetching sample type for {}", filename); type = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 0))); } SH_LOG_INFO("Selected sample type from table successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot get sample type from table", "Error", e.what()); } return type; } int cDatabase::GetFavoriteColumnValueByFilename(const std::string &filename) { int value = 0; try { const auto sql = "SELECT FAVORITE FROM SAMPLES WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_ROW) { value = sqlite3_column_int(statement.stmt, 0); SH_LOG_INFO("Record found, fetching favorite column value for {}", filename); } SH_LOG_INFO("Selected favorite column from table successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot get favorite column value from table", "Error", e.what()); } return value; } std::string cDatabase::GetHiveByFilename(const std::string &filename) { std::string hive; try { const auto sql = "SELECT HIVE FROM SAMPLES WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_ROW) { hive = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 0))); SH_LOG_INFO("Record found, fetching hive for {}", filename); } SH_LOG_INFO("Selected hive from table successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot get hive value from table", "Error", e.what()); } return hive; } void cDatabase::RemoveSampleFromDatabase(const std::string &filename) { try { const auto sql = "DELETE FROM SAMPLES WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_DONE) { SH_LOG_INFO("Record found, Deleting {} from table", filename); } SH_LOG_INFO("Deleted sample from table successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot delete sample from table", "Error", e.what()); } } void cDatabase::RemoveHiveFromDatabase(const std::string &hiveName) { try { const auto sql = "DELETE FROM HIVES WHERE HIVE = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, hiveName.c_str(), hiveName.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_DONE) { SH_LOG_INFO("Record found, Deleting hive {} from table", hiveName); } SH_LOG_INFO("Deleted hive from table successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot delete hive from table", "Error", e.what()); } } void cDatabase::DeleteAllSamples() { try { const auto sql = "DELETE FROM SAMPLES;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_exec(m_pDatabase, sql, NULL, 0, &m_pErrMsg)); SH_LOG_INFO("All Samples deleted."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Could not delete samples.", "Error!", e.what()); } } std::string cDatabase::GetSamplePathByFilename(const std::string &filename) { std::string path; try { const auto sql = "SELECT PATH FROM SAMPLES WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_ROW) { path = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 0))); SH_LOG_INFO("Record found, fetching sample path for {}", filename); } SH_LOG_INFO("Selected sample path from table successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot select sample path from table", "Error", e.what()); } return path; } std::string cDatabase::GetSampleFileExtension(const std::string &filename) { std::string extension; try { const auto sql = "SELECT EXTENSION FROM SAMPLES WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_ROW) { extension = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 0))); SH_LOG_INFO("Record found, fetching file extension for {}", filename); } SH_LOG_INFO("Selected file extension from table successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot select sample extension from table", "Error", e.what()); } return extension; } wxVector> cDatabase::LoadSamplesDatabase(wxDataViewTreeCtrl &favorite_tree, wxDataViewItem &favorite_item, wxTreeCtrl &trash_tree, wxTreeItemId &trash_item, bool show_extension, const std::string &icon_star_filled, const std::string &icon_star_empty) { wxVector> vecSet; wxVariant icon_filled, icon_empty; icon_filled = wxVariant(wxBitmap(icon_star_filled, wxBITMAP_TYPE_PNG)); icon_empty = wxVariant(wxBitmap(icon_star_empty, wxBITMAP_TYPE_PNG)); try { int num_rows = 0; Sqlite3Statement statement1(m_pDatabase, "SELECT Count(*) FROM SAMPLES;"); if (SQLITE_ROW == sqlite3_step(statement1.stmt)) { num_rows = sqlite3_column_int(statement1.stmt, 0); SH_LOG_INFO("Loading {} samples..", num_rows); vecSet.reserve(num_rows); } Sqlite3Statement statement(m_pDatabase, "SELECT FAVORITE, FILENAME, EXTENSION, SAMPLEPACK, \ TYPE, CHANNELS, LENGTH, SAMPLERATE, BITRATE, PATH, \ TRASHED, HIVE FROM SAMPLES;"); int row = 0; while (SQLITE_ROW == sqlite3_step(statement.stmt)) { int favorite = sqlite3_column_int(statement.stmt, 0); wxString filename = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 1))); wxString file_extension = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 2))); wxString sample_pack = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 3))); wxString sample_type = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 4))); int channels = sqlite3_column_int(statement.stmt, 5); int length = sqlite3_column_int(statement.stmt, 6); int sample_rate = sqlite3_column_int(statement.stmt, 7); int bitrate = sqlite3_column_int(statement.stmt, 8); wxString path = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 9))); int trashed = sqlite3_column_int(statement.stmt, 10); wxString hive_name = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 11))); wxString len = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); wxVector vec; vec.reserve(12); if (trashed == 1) { if (show_extension) trash_tree.AppendItem(trash_item, wxString::Format("%s.%s", filename, file_extension)); else trash_tree.AppendItem(trash_item, filename); } else { if (favorite == 1) { vec.push_back(icon_filled); std::deque nodes; nodes.push_back(favorite_tree.GetNthChild(wxDataViewItem(wxNullPtr), 0)); wxDataViewItem current_item, found_item; int row = 0; int hive_count = favorite_tree.GetChildCount(wxDataViewItem(wxNullPtr)); while (!nodes.empty()) { current_item = nodes.front(); nodes.pop_front(); if (favorite_tree.GetItemText(current_item) == hive_name) { found_item = current_item; break; } wxDataViewItem child = favorite_tree.GetNthChild(wxDataViewItem(wxNullPtr), 0); while (row < (hive_count - 1)) { row++; child = favorite_tree.GetNthChild(wxDataViewItem(wxNullPtr), row); nodes.push_back(child); } } nodes.clear(); if (found_item.IsOk()) { if (show_extension) favorite_tree.AppendItem(found_item, wxString::Format("%s.%s", filename, file_extension)); else favorite_tree.AppendItem(found_item, filename); } } else vec.push_back(icon_empty); if (show_extension) { vec.push_back(path.AfterLast('/')); } else { vec.push_back(path.AfterLast('/').BeforeLast('.')); } vec.push_back(sample_pack); vec.push_back(sample_type); vec.push_back(wxString::Format("%d", channels)); vec.push_back(len); vec.push_back(wxString::Format("%d", sample_rate)); vec.push_back(wxString::Format("%d", bitrate)); vec.push_back(path); vecSet.push_back(vec); } row++; } } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot load data from SAMPLES", "Error", e.what()); } return vecSet; } wxVector>cDatabase::FilterDatabaseBySampleName(const std::string &sampleName, bool show_extension, const std::string &icon_star_filled, const std::string &icon_star_empty) { wxVector> sampleVec; wxVariant icon_filled, icon_empty; icon_filled = wxVariant(wxBitmap(icon_star_filled, wxBITMAP_TYPE_PNG)); icon_empty = wxVariant(wxBitmap(icon_star_empty, wxBITMAP_TYPE_PNG)); try { Sqlite3Statement statement(m_pDatabase, "SELECT FAVORITE, FILENAME, SAMPLEPACK, TYPE, \ CHANNELS, LENGTH, SAMPLERATE, BITRATE, PATH \ FROM SAMPLES WHERE FILENAME LIKE '%' || ? || '%' ;"); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, sampleName.c_str(), sampleName.size(), SQLITE_STATIC)); int row = 0; while (SQLITE_ROW == sqlite3_step(statement.stmt)) { SH_LOG_INFO("Record found, filtering db by {}", sampleName); int favorite = sqlite3_column_int(statement.stmt, 0); wxString filename = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 1)))); wxString sample_pack = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 2)))); wxString sample_type = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 3))); int channels = sqlite3_column_int(statement.stmt, 4); int length = sqlite3_column_int(statement.stmt, 5); int sample_rate = sqlite3_column_int(statement.stmt, 6); int bitrate = sqlite3_column_int(statement.stmt, 7); wxString path = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 8)))); wxString len = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); wxVector vec; if (favorite == 1) vec.push_back(icon_filled); else vec.push_back(icon_empty); if (show_extension) { vec.push_back(path.AfterLast('/')); } else { vec.push_back(path.AfterLast('/').BeforeLast('.')); } vec.push_back(sample_pack); vec.push_back(sample_type); vec.push_back(wxString::Format("%d", channels)); vec.push_back(len); vec.push_back(wxString::Format("%d", sample_rate)); vec.push_back(wxString::Format("%d", bitrate)); vec.push_back(path); sampleVec.push_back(vec); row++; } } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot filter data from table", "Error", e.what()); } return sampleVec; } wxVector>cDatabase::FilterDatabaseByHiveName(const std::string &hiveName, bool show_extension, const std::string &icon_star_filled, const std::string &icon_star_empty) { wxVector> sampleVec; wxVariant icon_filled, icon_empty; icon_filled = wxVariant(wxBitmap(icon_star_filled, wxBITMAP_TYPE_PNG)); icon_empty = wxVariant(wxBitmap(icon_star_empty, wxBITMAP_TYPE_PNG)); try { Sqlite3Statement statement(m_pDatabase, "SELECT FAVORITE, FILENAME, SAMPLEPACK, TYPE, \ CHANNELS, LENGTH, SAMPLERATE, BITRATE, PATH \ FROM SAMPLES WHERE HIVE = ? AND FAVORITE = 1;"); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, hiveName.c_str(), hiveName.size(), SQLITE_STATIC)); int row = 0; while (SQLITE_ROW == sqlite3_step(statement.stmt)) { SH_LOG_INFO("Record found, filtering db by {}", hiveName); int favorite = sqlite3_column_int(statement.stmt, 0); wxString filename = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 1)))); wxString sample_pack = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 2)))); wxString sample_type = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 3))); int channels = sqlite3_column_int(statement.stmt, 4); int length = sqlite3_column_int(statement.stmt, 5); int sample_rate = sqlite3_column_int(statement.stmt, 6); int bitrate = sqlite3_column_int(statement.stmt, 7); wxString path = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 8)))); wxString len = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); wxVector vec; if (favorite == 1) vec.push_back(icon_filled); else vec.push_back(icon_empty); if (show_extension) vec.push_back(path.AfterLast('/')); else vec.push_back(path.AfterLast('/').BeforeLast('.')); vec.push_back(sample_pack); vec.push_back(sample_type); vec.push_back(wxString::Format("%d", channels)); vec.push_back(len); vec.push_back(wxString::Format("%d", sample_rate)); vec.push_back(wxString::Format("%d", bitrate)); vec.push_back(path); sampleVec.push_back(vec); row++; } } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot filter data from table", "Error", e.what()); } return sampleVec; } void cDatabase::LoadHivesDatabase(wxDataViewTreeCtrl &treeCtrl) { try { const auto sql = "SELECT HIVE FROM HIVES;"; Sqlite3Statement statement(m_pDatabase, sql); while (SQLITE_ROW == sqlite3_step(statement.stmt)) { SH_LOG_INFO("Loading hives.."); const auto hive = wxString(std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 0)))); treeCtrl.AppendContainer(wxDataViewItem(wxNullPtr), hive); } } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot load data from HIVES", "Error", e.what()); } } // Compares the input array with the database and removes duplicates. wxArrayString cDatabase::CheckDuplicates(const wxArrayString &files) { wxArrayString sorted_files; std::string filename; std::string sample; try { const auto sql = "SELECT * FROM SAMPLES WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); for (unsigned int i = 0; i < files.size(); i++) { filename = files[i].AfterLast('/').BeforeLast('.').ToStdString(); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) != SQLITE_ROW) sorted_files.push_back(files[i]); else SH_LOG_INFO("Already added: {}, skipping..", files[i]); rc = sqlite3_clear_bindings(statement.stmt); rc = sqlite3_reset(statement.stmt); } } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot check duplicates from table", "Error", e.what()); } return sorted_files; } bool cDatabase::IsTrashed(const std::string &filename) { try { const auto sql = "SELECT TRASHED FROM SAMPLES WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_ROW) { SH_LOG_INFO("Record found, fetching {} trash status", filename); if (sqlite3_column_int(statement.stmt, 0) == 1) return true; } SH_LOG_INFO("Selected trash status from table successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot check for trash status from table", "Error", e.what()); } return false; } void cDatabase::UpdateTrashColumn(const std::string &filename, int value) { try { const auto sql = "UPDATE SAMPLES SET TRASHED = ? WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_int(statement.stmt, 1, value)); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 2, filename.c_str(), filename.size(), SQLITE_STATIC)); if (sqlite3_step(statement.stmt) == SQLITE_ROW) { SH_LOG_INFO("Record found, updating trash status for {}", filename); } SH_LOG_INFO("Updated trash status successfully."); } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot update trash status", "Error", e.what()); } } wxVector>cDatabase::RestoreFromTrashByFilename(const std::string &filename, wxVector> &vecSet, bool show_extension, const std::string &icon_star_filled, const std::string &icon_star_empty) { wxVariant icon_filled, icon_empty; icon_filled = wxVariant(wxBitmap(icon_star_filled, wxBITMAP_TYPE_PNG)); icon_empty = wxVariant(wxBitmap(icon_star_empty, wxBITMAP_TYPE_PNG)); try { const auto sql = "SELECT FAVORITE, FILENAME, EXTENSION, SAMPLEPACK, \ TYPE, CHANNELS, LENGTH, SAMPLERATE, BITRATE, PATH, \ TRASHED, HIVE FROM SAMPLES WHERE FILENAME = ?;"; Sqlite3Statement statement(m_pDatabase, sql); throw_on_sqlite3_error(sqlite3_bind_text(statement.stmt, 1, filename.c_str(), filename.size(), SQLITE_STATIC)); while (SQLITE_ROW == sqlite3_step(statement.stmt)) { int favorite = sqlite3_column_int(statement.stmt, 0); wxString filename = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 1))); wxString file_extension = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 2))); wxString sample_pack = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 3))); wxString sample_type = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 4))); int channels = sqlite3_column_int(statement.stmt, 5); int length = sqlite3_column_int(statement.stmt, 6); int sample_rate = sqlite3_column_int(statement.stmt, 7); int bitrate = sqlite3_column_int(statement.stmt, 8); wxString path = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 9))); int trashed = sqlite3_column_int(statement.stmt, 10); wxString hive_name = std::string(reinterpret_cast(sqlite3_column_text(statement.stmt, 11))); wxString len = SampleHive::cUtils::Get().CalculateAndGetISOStandardTime(length); wxVector vec; if (trashed == 0) { if (favorite == 1) vec.push_back(icon_filled); else vec.push_back(icon_empty); if (show_extension) vec.push_back(path.AfterLast('/')); else vec.push_back(path.AfterLast('/').BeforeLast('.')); vec.push_back(sample_pack); vec.push_back(sample_type); vec.push_back(wxString::Format("%d", channels)); vec.push_back(len); vec.push_back(wxString::Format("%d", sample_rate)); vec.push_back(wxString::Format("%d", bitrate)); vec.push_back(path); vecSet.push_back(vec); } } } catch (const std::exception &e) { show_modal_dialog_and_log("Error! Cannot restore trash data from table", "Error", e.what()); } return vecSet; } void cDatabase::OpenDatabase() { throw_on_sqlite3_error(sqlite3_open(static_cast(DATABASE_FILEPATH).c_str(), &m_pDatabase)); } void cDatabase::OpenTemporaryDatabase() { SH_LOG_WARN("Creating temporary in memory database, all samples will be deleted on application exit."); throw_on_sqlite3_error(sqlite3_open("tempdb.db", &m_pDatabase)); } void cDatabase::CloseDatabase() { throw_on_sqlite3_error(sqlite3_close(m_pDatabase)); }