Improve Catalog
ci / checks (push) Successful in 46m42s

This commit is contained in:
khodowany
2026-09-27 11:03:43 -05:00
parent a416a80dd9
commit 142d843fac
418 changed files with 440 additions and 324488 deletions
+12 -81
View File
@@ -93,13 +93,9 @@ auto valid_title_image(noto::sqlite_value const &value) -> std::bool_t {
return text.size() == 64U && std::all_of(text.begin(), text.end(), [](std::char_t c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); });
}
auto compatible_table(noto::connection &connection, std::string const &table) -> boost::asio::awaitable<std::bool_t> {
auto columns = std::vector<std::string_view>{"id", "title", "composer", "key_signature", "source_path", "source_json", "measure_count", "total_pages", "title_image"};
if (table == "practice_exercises") {
columns.push_back("exercise_id");
}
columns.push_back("part");
auto parameters = std::vector<noto::sqlite_value>{noto::sqlite_value{table}};
auto compatible_catalog(noto::connection &connection) -> boost::asio::awaitable<std::bool_t> {
auto columns = std::vector<std::string_view>{"id", "title", "composer", "key_signature", "source_path", "source_json", "measure_count", "total_pages", "title_image", "part"};
auto parameters = std::vector<noto::sqlite_value>{noto::sqlite_value{"songs"}};
auto selected = co_await connection.async_execute("SELECT name, type, \"notnull\", pk FROM pragma_table_info(?1) "
"WHERE EXISTS (SELECT 1 FROM pragma_table_list WHERE schema = 'main' AND name = ?1 AND type = 'table' AND strict = 1) ORDER BY cid",
std::move(parameters));
@@ -138,14 +134,12 @@ auto vamp_zero::async_validate_database(boost::asio::any_io_executor executor, v
if (!application || *application != application_id) {
status = std::unexpected(vamp_zero::make_error(vamp_zero::error_code::PERSISTENCE, "invalid or incompatible catalog: " + config.path + "; rebuild or reinstall Product Vamp"));
}
for (auto table : {"songs", "practice_exercises"}) {
if (status && !(co_await compatible_table(connection, table))) {
status = std::unexpected(vamp_zero::make_error(vamp_zero::error_code::PERSISTENCE, "incompatible catalog table " + std::string{table} + ": " + config.path + "; rebuild or reinstall Product Vamp"));
}
if (status && !(co_await compatible_catalog(connection))) {
status = std::unexpected(vamp_zero::make_error(vamp_zero::error_code::PERSISTENCE, "incompatible songs catalog: " + config.path + "; rebuild or reinstall Product Vamp"));
}
if (status && integrity) {
auto checked = co_await connection.async_execute("PRAGMA quick_check(1)");
if (!checked || checked->rows.size() != 1U || checked->rows[0].size() != 1U || text_value(checked->rows[0][0]) != "ok") {
auto checked = co_await connection.async_quick_check();
if (!checked || *checked != std::vector<std::string>{"ok"}) {
status = std::unexpected(vamp_zero::make_error(vamp_zero::error_code::PERSISTENCE, "corrupt catalog: " + config.path + "; rebuild or reinstall Product Vamp"));
}
}
@@ -156,8 +150,8 @@ auto vamp_zero::async_validate_database(boost::asio::any_io_executor executor, v
co_return closed;
}
auto vamp_zero::async_load_performance_library(boost::asio::any_io_executor executor, vamp_zero::database_config config, std::string assets_path)
-> boost::asio::awaitable<std::expected<vamp_zero::library_state, modulus::error>> {
auto vamp_zero::async_load_catalog(boost::asio::any_io_executor executor, vamp_zero::database_config config, std::string assets_path)
-> boost::asio::awaitable<std::expected<vamp_zero::catalog, modulus::error>> {
auto connection = noto::connection{executor, connection_config(config)};
auto connected = co_await connection.async_connect();
if (!connected) {
@@ -165,7 +159,7 @@ auto vamp_zero::async_load_performance_library(boost::asio::any_io_executor exec
}
auto selected =
co_await connection.async_prepare("SELECT id, title, composer, part, source_path, measure_count, key_signature, total_pages, title_image FROM "
"songs ORDER BY title COLLATE NOCASE, part COLLATE NOCASE");
"songs ORDER BY title, composer, part, id");
if (!selected) {
auto close = co_await connection.async_close();
if (!close) {
@@ -210,69 +204,7 @@ auto vamp_zero::async_load_performance_library(boost::asio::any_io_executor exec
if (!closed) {
co_return std::unexpected(closed.error());
}
co_return vamp_zero::group_library_parts(std::move(parts));
}
auto vamp_zero::async_load_practice_library(boost::asio::any_io_executor executor, vamp_zero::database_config config, std::string assets_path)
-> boost::asio::awaitable<std::expected<vamp_zero::practice_library, modulus::error>> {
auto connection = noto::connection{executor, connection_config(config)};
auto connected = co_await connection.async_connect();
if (!connected) {
co_return std::unexpected(connected.error());
}
auto selected = co_await connection.async_prepare("SELECT id, title, composer, key_signature, source_path, measure_count, total_pages, part, exercise_id, title_image "
"FROM practice_exercises ORDER BY exercise_id, id");
if (!selected) {
auto close = co_await connection.async_close();
if (!close) {
modulus::log_error("database close failed after practice load error: " + close.error().describe());
}
co_return std::unexpected(selected.error());
}
auto exercises = std::vector<vamp_zero::practice_exercise>{};
auto status = std::expected<void, modulus::error>{};
for (;;) {
auto next = co_await selected->async_step();
if (!next) {
status = std::unexpected(next.error());
break;
}
if (!*next) {
break;
}
auto const &row = **next;
if (row.size() != 10U || count_value(row[5]) == 0U || count_value(row[6]) == 0U || text_value(row[8]).empty() || text_value(row[3]).empty() || !valid_title_image(row[9])) {
status = std::unexpected(vamp_zero::make_error(vamp_zero::error_code::PERSISTENCE, "invalid practice metadata"));
break;
}
auto source = resolve_source_path(assets_path, text_value(row[4]));
if (!source) {
status = std::unexpected(source.error());
break;
}
auto practice_part = vamp_zero::library_part{
.id = text_value(row[0]),
.title = text_value(row[1]),
.composer = text_value(row[2]),
.key_signature = text_value(row[3]),
.part = text_value(row[7]),
.source_path = std::move(*source),
.measure_count = count_value(row[5]),
.total_pages = count_value(row[6]),
.title_image = text_value(row[9]),
};
exercises.push_back({
.id = text_value(row[8]),
.title = text_value(row[1]),
.composer = text_value(row[2]),
.parts = {std::move(practice_part)},
});
}
auto closed = co_await close_catalog_query(connection, *selected, std::move(status));
if (!closed) {
co_return std::unexpected(closed.error());
}
co_return vamp_zero::group_practice_exercises(std::move(exercises));
co_return vamp_zero::group_ordered_parts(std::move(parts));
}
auto vamp_zero::async_load_score(boost::asio::any_io_executor executor, vamp_zero::database_config config, vamp_zero::score_reference reference, std::string assets_path) -> boost::asio::awaitable<std::expected<vamp_zero::score_document, modulus::error>> {
@@ -283,8 +215,7 @@ auto vamp_zero::async_load_score(boost::asio::any_io_executor executor, vamp_zer
if (!connected) {
co_return std::unexpected(connected.error());
}
auto table = reference.category == vamp_zero::score_category::PERFORMANCE ? "songs" : "practice_exercises";
auto selected = co_await connection.async_prepare("SELECT source_json, source_path FROM " + std::string{table} + " WHERE id = ?1");
auto selected = co_await connection.async_prepare("SELECT source_json, source_path FROM songs WHERE id = ?1");
if (!selected) {
static_cast<void>(co_await connection.async_close());
co_return std::unexpected(selected.error());