From b92b5e2e91ed4d316c6e50973bc4202ded09986b Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 19 Feb 2025 11:50:18 -0600 Subject: [PATCH] refactor --- monitarr/monitarr/include/run_cmd.hpp | 13 ++ monitarr/monitarr/main.cpp | 236 ++++---------------------- monitarr/monitarr/src/run_cmd.cpp | 197 +++++++++++++++++++++ 3 files changed, 246 insertions(+), 200 deletions(-) create mode 100644 monitarr/monitarr/include/run_cmd.hpp create mode 100644 monitarr/monitarr/src/run_cmd.cpp diff --git a/monitarr/monitarr/include/run_cmd.hpp b/monitarr/monitarr/include/run_cmd.hpp new file mode 100644 index 0000000..b414c65 --- /dev/null +++ b/monitarr/monitarr/include/run_cmd.hpp @@ -0,0 +1,13 @@ +#ifndef LIBMONITARR_INCLUDE_RUN_CMD_HPP_ +#define LIBMONITARR_INCLUDE_RUN_CMD_HPP_ + +#include "utils/config.hpp" + +namespace monitarr { +struct app_config; + +[[nodiscard]] auto run_cmd(const app_config &cfg, std::string_view cfg_file) + -> int; +} // namespace monitarr + +#endif // LIBMONITARR_INCLUDE_RUN_CMD_HPP_ diff --git a/monitarr/monitarr/main.cpp b/monitarr/monitarr/main.cpp index 2e57d7a..dd035ee 100644 --- a/monitarr/monitarr/main.cpp +++ b/monitarr/monitarr/main.cpp @@ -2,110 +2,25 @@ #include "backward.hpp" #endif // defined(PROJECT_ENABLE_BACKWARD_CPP) -#include - #include "initialize.hpp" #include "actions.hpp" #include "args.hpp" #include "block_cmd.hpp" #include "config_cmd.hpp" -#include "data_db.hpp" #include "list_cmd.hpp" +#include "run_cmd.hpp" #include "settings.hpp" #include "show_cmd.hpp" #include "utils/common.hpp" -#include "utils/config.hpp" #include "utils/file.hpp" -#include "utils/path.hpp" -#include "utils/string.hpp" -#include "utils/time.hpp" #include "utils/unix.hpp" #include "utils/windows.hpp" namespace monitarr { -static void check_server(const server_cfg &server, data_db &state_db) { +[[nodiscard]] static auto load_config(std::string &cfg_file) -> app_config { MONITARR_USES_FUNCTION_NAME(); - fmt::println("checking server|{}|{}", server.id, server.url); - - auto cli = create_client(server); - - std::uint16_t page{0U}; - while (++page != 0U) { - httplib::Params params; - params.emplace("page", std::to_string(page)); - params.emplace("pageSize", "100"); - - auto response = - cli.Get(fmt::format("/api/{}/queue", server.api_version), params, {}); - if (response->status != httplib::StatusCode::OK_200) { - utils::error::handle_error( - function_name, fmt::format("check server request failed|{}|{}|{}", - server.id, server.url, response->status)); - break; - } - - auto json_data = nlohmann::json::parse(response->body); - if (json_data.at("page").get() != page) { - break; - } - - auto now = utils::time::get_time_now(); - - for (const auto &record : json_data.at("records")) { - auto download_id = - fmt::format("{}/{}", server.id, record.at("id").get()); - auto episode_id = record.contains("episodeId") - ? record["episodeId"].get() - : std::uint64_t{0U}; - auto movie_id = record.contains("movieId") - ? record["movieId"].get() - : std::uint64_t{0U}; - auto size_left = record.at("sizeleft").get(); - auto title = record.at("title").get(); - auto is_downloading = utils::string::contains( - record.at("status").get(), "downloading"); - - auto data = state_db.get(download_id); - const auto update_entry = [&download_id, &now, &size_left, &state_db, - &title, url = server.url]() { - if (size_left == 0U) { - state_db.remove(download_id); - return; - } - - fmt::println("updating {}|{}|{}|{}", download_id, title, now, - size_left); - state_db.set(data_entry{ - download_id, - now, - size_left, - }); - }; - - if (data.has_value()) { - if (std::chrono::nanoseconds(now - data->last_check) >= - server.timeout) { - if (size_left == data->size_left) { - remove_stalled(download_id, title, episode_id, movie_id, server, - &state_db); - } else if (size_left == 0U || not is_downloading) { - state_db.remove(download_id); - } else { - update_entry(); - } - } else if (size_left == 0U || not is_downloading) { - state_db.remove(download_id); - } - } else if (is_downloading && size_left > 0U) { - update_entry(); - } - } - } -} - -[[nodiscard]] static auto load_config(std::string &cfg_file) -> app_config { auto cfg_dir = utils::get_environment_variable("MONITARR_CFG_DIR"); if (cfg_dir.empty()) { cfg_dir = utils::path::combine(".", {"config"}); @@ -119,28 +34,12 @@ static void check_server(const server_cfg &server, data_db &state_db) { cfg_file = utils::path::combine(cfg_dir, {"monitarr.json"}); - fmt::println("loading config|{}", cfg_file); + utils::error::handle_info(function_name, + fmt::format("loading config|{}", cfg_file)); app_config cfg{}; cfg.load(cfg_file); return cfg; } - -[[nodiscard]] static auto load_db() -> data_db { - auto data_dir = utils::get_environment_variable("MONITARR_DATA_DIR"); - if (data_dir.empty()) { - data_dir = utils::path::combine(".", {"data"}); - } - - if (not utils::file::directory{data_dir}.create_directory()) { - throw std::runtime_error(fmt::format("failed to create data dir|{}", - data_dir, - utils::get_last_error_code())); - } - - data_db state_db{}; - state_db.open(data_dir); - return state_db; -} } // namespace monitarr using namespace monitarr; @@ -157,102 +56,39 @@ auto main(int argc, char **argv) -> int { } auto ret{0}; - if (argc > 1) { - try { - std::string cfg_file; - auto cfg{load_config(cfg_file)}; - if (has_arg("-b", argc, argv)) { - ret = block_cmd(argc, argv, cfg); - } else if (has_arg("-c", argc, argv)) { - ret = config_cmd(cfg); - } else if (has_arg("-h", argc, argv)) { - fmt::println("usage:"); - fmt::println("monitarr -b -i -id "); - fmt::println("\tblocklist and search record id at configuration index"); - fmt::println("monitarr -c"); - fmt::println("\tdisplay configuration"); - fmt::println("monitarr -l -i "); - fmt::println("\tdisplay server queue at configuration index"); - fmt::println("monitarr -s -i -id "); - fmt::println("\tshow record id details at configuration index"); - } else if (has_arg("-l", argc, argv)) { - ret = list_cmd(argc, argv, cfg); - } else if (has_arg("-s", argc, argv)) { - ret = show_cmd(argc, argv, cfg); - } - } catch (const std::exception &ex) { - utils::error::handle_exception(function_name, ex); - ret = 2; - } catch (...) { - utils::error::handle_exception(function_name); - ret = 2; + + try { + std::string cfg_file; + auto cfg{load_config(cfg_file)}; + if (has_arg("-b", argc, argv)) { + ret = block_cmd(argc, argv, cfg); + } else if (has_arg("-c", argc, argv)) { + ret = config_cmd(cfg); + } else if (argc == 1 || has_arg("-h", argc, argv)) { + fmt::println("usage:"); + fmt::println(" monitarr -b -i -id "); + fmt::println(" blocklist and search record id at configuration index"); + fmt::println(" monitarr -c"); + fmt::println(" display configuration"); + fmt::println(" monitarr -l -i "); + fmt::println(" display server queue at configuration index"); + fmt::println(" monitarr -r"); + fmt::println(" run monitarr server"); + fmt::println(" monitarr -s -i -id "); + fmt::println(" show record id details at configuration index"); + } else if (has_arg("-l", argc, argv)) { + ret = list_cmd(argc, argv, cfg); + } else if (has_arg("-r", argc, argv)) { + ret = run_cmd(cfg, cfg_file); + } else if (has_arg("-s", argc, argv)) { + ret = show_cmd(argc, argv, cfg); } - } else { - static std::mutex mtx; - static std::condition_variable notify; - static stop_type stop_requested{false}; - - static const auto quit_handler = [](int sig) { - fmt::println("stop requested|{}", sig); - stop_requested = true; - - mutex_lock lock(mtx); - notify.notify_all(); - }; - - std::signal(SIGINT, quit_handler); -#if !defined(_WIN32) - std::signal(SIGQUIT, quit_handler); -#endif // !defined(_WIN32) - std::signal(SIGTERM, quit_handler); - - try { - std::string cfg_file; - auto cfg{load_config(cfg_file)}; - auto state_db{load_db()}; - - if (cfg.server_list.empty()) { - utils::error::handle_error(function_name, - "no servers have been configured"); - ret = 3; - } else { - while (not stop_requested) { - std::for_each(std::execution::par, cfg.server_list.begin(), - cfg.server_list.end(), - [&state_db](const server_cfg &server) { - if (stop_requested) { - return; - } - - try { - check_server(server, state_db); - } catch (const std::exception &ex) { - utils::error::handle_exception(function_name, ex); - } catch (...) { - utils::error::handle_exception(function_name); - } - }); - unique_mutex_lock lock(mtx); - if (stop_requested) { - continue; - } - - fmt::println("waiting for next check|{}", cfg.check_interval); - notify.wait_for(lock, cfg.check_interval); - } - } - - cfg.save(cfg_file); - state_db.close(); - } catch (const std::exception &ex) { - utils::error::handle_exception(function_name, ex); - ret = 2; - } catch (...) { - utils::error::handle_exception(function_name); - ret = 2; - } - - fmt::println("terminating application|{}", ret); + } catch (const std::exception &ex) { + utils::error::handle_exception(function_name, ex); + ret = 2; + } catch (...) { + utils::error::handle_exception(function_name); + ret = 2; } monitarr::project_cleanup(); diff --git a/monitarr/monitarr/src/run_cmd.cpp b/monitarr/monitarr/src/run_cmd.cpp new file mode 100644 index 0000000..11481e0 --- /dev/null +++ b/monitarr/monitarr/src/run_cmd.cpp @@ -0,0 +1,197 @@ +#include + +#include "run_cmd.hpp" + +#include "actions.hpp" +#include "data_db.hpp" +#include "settings.hpp" +#include "utils/common.hpp" +#include "utils/config.hpp" +#include "utils/error.hpp" +#include "utils/file.hpp" +#include "utils/path.hpp" +#include "utils/string.hpp" +#include "utils/time.hpp" +#include "utils/unix.hpp" +#include "utils/windows.hpp" + +namespace { +std::mutex mtx; +std::condition_variable notify; +monitarr::stop_type stop_requested{false}; + +void quit_handler(int sig) { + MONITARR_USES_FUNCTION_NAME(); + + monitarr::utils::error::handle_info(function_name, + fmt::format("stop requested|{}", sig)); + stop_requested = true; + + monitarr::mutex_lock lock(mtx); + notify.notify_all(); +}; +} // namespace + +namespace monitarr { +static void check_server(const server_cfg &server, data_db &state_db) { + MONITARR_USES_FUNCTION_NAME(); + + utils::error::handle_info(function_name, fmt::format("checking server|{}|{}", + server.id, server.url)); + + auto cli = create_client(server); + + std::uint16_t page{0U}; + while (++page != 0U) { + httplib::Params params; + params.emplace("page", std::to_string(page)); + params.emplace("pageSize", "100"); + + auto response = + cli.Get(fmt::format("/api/{}/queue", server.api_version), params, {}); + if (response->status != httplib::StatusCode::OK_200) { + utils::error::handle_error( + function_name, fmt::format("check server request failed|{}|{}|{}", + server.id, server.url, response->status)); + break; + } + + auto json_data = nlohmann::json::parse(response->body); + if (json_data.at("page").get() != page) { + break; + } + + auto now = utils::time::get_time_now(); + + for (const auto &record : json_data.at("records")) { + auto download_id = + fmt::format("{}/{}", server.id, record.at("id").get()); + auto episode_id = record.contains("episodeId") + ? record["episodeId"].get() + : std::uint64_t{0U}; + auto movie_id = record.contains("movieId") + ? record["movieId"].get() + : std::uint64_t{0U}; + auto size_left = record.at("sizeleft").get(); + auto title = record.at("title").get(); + auto is_downloading = utils::string::contains( + record.at("status").get(), "downloading"); + + auto data = state_db.get(download_id); + const auto update_entry = [&download_id, &now, &size_left, &state_db, + &title, url = server.url]() { + if (size_left == 0U) { + state_db.remove(download_id); + return; + } + + utils::error::handle_info( + function_name, fmt::format("updating {}|{}|{}|{}", download_id, + title, now, size_left)); + state_db.set(data_entry{ + download_id, + now, + size_left, + }); + }; + + if (data.has_value()) { + if (std::chrono::nanoseconds(now - data->last_check) >= + server.timeout) { + if (size_left == data->size_left) { + remove_stalled(download_id, title, episode_id, movie_id, server, + &state_db); + } else if (size_left == 0U || not is_downloading) { + state_db.remove(download_id); + } else { + update_entry(); + } + } else if (size_left == 0U || not is_downloading) { + state_db.remove(download_id); + } + } else if (is_downloading && size_left > 0U) { + update_entry(); + } + } + } +} + +[[nodiscard]] static auto load_db() -> data_db { + auto data_dir = utils::get_environment_variable("MONITARR_DATA_DIR"); + if (data_dir.empty()) { + data_dir = utils::path::combine(".", {"data"}); + } + + if (not utils::file::directory{data_dir}.create_directory()) { + throw std::runtime_error(fmt::format("failed to create data dir|{}", + data_dir, + utils::get_last_error_code())); + } + + data_db state_db{}; + state_db.open(data_dir); + return state_db; +} + +auto run_cmd(const app_config &cfg, std::string_view cfg_file) -> int { + MONITARR_USES_FUNCTION_NAME(); + auto ret{0}; + std::signal(SIGINT, quit_handler); +#if !defined(_WIN32) + std::signal(SIGQUIT, quit_handler); +#endif // !defined(_WIN32) + std::signal(SIGTERM, quit_handler); + + try { + if (cfg.server_list.empty()) { + utils::error::handle_error(function_name, + "no servers have been configured"); + ret = 3; + } else { + auto state_db{load_db()}; + + while (not stop_requested) { + std::for_each(std::execution::par, cfg.server_list.begin(), + cfg.server_list.end(), + [&state_db](const server_cfg &server) { + if (stop_requested) { + return; + } + + try { + check_server(server, state_db); + } catch (const std::exception &ex) { + utils::error::handle_exception(function_name, ex); + } catch (...) { + utils::error::handle_exception(function_name); + } + }); + unique_mutex_lock lock(mtx); + if (stop_requested) { + continue; + } + + utils::error::handle_error( + function_name, + fmt::format("waiting for next check|{}", cfg.check_interval)); + notify.wait_for(lock, cfg.check_interval); + } + + state_db.close(); + } + + cfg.save(cfg_file); + } catch (const std::exception &ex) { + utils::error::handle_exception(function_name, ex); + ret = 2; + } catch (...) { + utils::error::handle_exception(function_name); + ret = 2; + } + + utils::error::handle_info(function_name, + fmt::format("exiting application|{}", ret)); + + return ret; +} +} // namespace monitarr