diff --git a/include/libvideo2x/fsutils.h b/include/libvideo2x/fsutils.h index dd5ec33..6edfd84 100644 --- a/include/libvideo2x/fsutils.h +++ b/include/libvideo2x/fsutils.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace video2x { @@ -20,9 +21,9 @@ typedef std::wstring StringType; typedef std::string StringType; #endif -bool filepath_is_readable(const std::filesystem::path& path); +bool file_is_readable(const std::filesystem::path& path); -std::filesystem::path find_resource_file(const std::filesystem::path& path); +std::optional find_resource(const std::filesystem::path& resource); std::string path_to_u8string(const std::filesystem::path& path); diff --git a/src/filter_libplacebo.cpp b/src/filter_libplacebo.cpp index 84a6328..7987f61 100644 --- a/src/filter_libplacebo.cpp +++ b/src/filter_libplacebo.cpp @@ -42,20 +42,20 @@ FilterLibplacebo::~FilterLibplacebo() { int FilterLibplacebo::init(AVCodecContext* dec_ctx, AVCodecContext* enc_ctx, AVBufferRef*) { // Construct the shader path - std::filesystem::path shader_full_path; - if (fsutils::filepath_is_readable(shader_path_)) { + std::optional shader_full_path = std::nullopt; + if (fsutils::file_is_readable(shader_path_)) { // If the shader path is directly readable, use it shader_full_path = shader_path_; } else { // Construct the fallback path using std::filesystem - shader_full_path = fsutils::find_resource_file( + shader_full_path = fsutils::find_resource( std::filesystem::path(STR("models")) / STR("libplacebo") / (fsutils::path_to_string_type(shader_path_) + STR(".glsl")) ); } // Check if the shader file exists - if (!std::filesystem::exists(shader_full_path)) { + if (!shader_full_path.has_value()) { logger()->error("libplacebo shader file not found: '{}'", shader_path_.u8string()); return -1; } @@ -73,7 +73,7 @@ int FilterLibplacebo::init(AVCodecContext* dec_ctx, AVCodecContext* enc_ctx, AVB width_, height_, vk_device_index_, - shader_full_path + shader_full_path.value() ); // Set these resources to nullptr since they are already freed by `avfilter_graph_free` diff --git a/src/filter_realcugan.cpp b/src/filter_realcugan.cpp index 9bde037..57c16f4 100644 --- a/src/filter_realcugan.cpp +++ b/src/filter_realcugan.cpp @@ -66,20 +66,22 @@ int FilterRealcugan::init(AVCodecContext* dec_ctx, AVCodecContext* enc_ctx, AVBu std::filesystem::path(STR("models")) / STR("realcugan") / model_name_ / bin_file_name; // Get the full paths using a function that possibly modifies or validates the path - std::filesystem::path model_param_full_path = fsutils::find_resource_file(model_param_path); - std::filesystem::path model_bin_full_path = fsutils::find_resource_file(model_bin_path); + std::optional model_param_full_path = + fsutils::find_resource(model_param_path); + std::optional model_bin_full_path = + fsutils::find_resource(model_bin_path); // Check if the model files exist - if (!std::filesystem::exists(model_param_full_path)) { - logger()->error("RealCUGAN model param file not found: {}", model_param_path.u8string()); + if (!model_param_full_path.has_value()) { + logger()->error("Real-CUGAN model param file not found: {}", model_param_path.u8string()); return -1; } - if (!std::filesystem::exists(model_bin_full_path)) { - logger()->error("RealCUGAN model bin file not found: {}", model_bin_path.u8string()); + if (!model_bin_full_path.has_value()) { + logger()->error("Real-CUGAN model bin file not found: {}", model_bin_path.u8string()); return -1; } - // Create a new RealCUGAN instance + // Create a new Real-CUGAN instance realcugan_ = new RealCUGAN(gpuid_, tta_mode_, num_threads_); // Store the time bases @@ -88,8 +90,8 @@ int FilterRealcugan::init(AVCodecContext* dec_ctx, AVCodecContext* enc_ctx, AVBu out_pix_fmt_ = enc_ctx->pix_fmt; // Load the model - if (realcugan_->load(model_param_full_path, model_bin_full_path) != 0) { - logger()->error("Failed to load RealCUGAN model"); + if (realcugan_->load(model_param_full_path.value(), model_bin_full_path.value()) != 0) { + logger()->error("Failed to load Real-CUGAN model"); return -1; } @@ -176,7 +178,7 @@ int FilterRealcugan::filter(AVFrame* in_frame, AVFrame** out_frame) { ret = realcugan_->process(in_mat, out_mat); if (ret != 0) { - logger()->error("RealCUGAN processing failed"); + logger()->error("Real-CUGAN processing failed"); return ret; } diff --git a/src/filter_realesrgan.cpp b/src/filter_realesrgan.cpp index 389f726..df0febf 100644 --- a/src/filter_realesrgan.cpp +++ b/src/filter_realesrgan.cpp @@ -45,20 +45,22 @@ int FilterRealesrgan::init(AVCodecContext* dec_ctx, AVCodecContext* enc_ctx, AVB model_bin_path = std::filesystem::path(STR("models")) / STR("realesrgan") / bin_file_name; // Get the full paths using a function that possibly modifies or validates the path - std::filesystem::path model_param_full_path = fsutils::find_resource_file(model_param_path); - std::filesystem::path model_bin_full_path = fsutils::find_resource_file(model_bin_path); + std::optional model_param_full_path = + fsutils::find_resource(model_param_path); + std::optional model_bin_full_path = + fsutils::find_resource(model_bin_path); // Check if the model files exist - if (!std::filesystem::exists(model_param_full_path)) { - logger()->error("RealESRGAN model param file not found: {}", model_param_path.u8string()); + if (!model_param_full_path.has_value()) { + logger()->error("Real-ESRGAN model param file not found: {}", model_param_path.u8string()); return -1; } - if (!std::filesystem::exists(model_bin_full_path)) { - logger()->error("RealESRGAN model bin file not found: {}", model_bin_path.u8string()); + if (!model_bin_full_path.has_value()) { + logger()->error("Real-ESRGAN model bin file not found: {}", model_bin_path.u8string()); return -1; } - // Create a new RealESRGAN instance + // Create a new Real-ESRGAN instance realesrgan_ = new RealESRGAN(gpuid_, tta_mode_); // Store the time bases @@ -67,12 +69,12 @@ int FilterRealesrgan::init(AVCodecContext* dec_ctx, AVCodecContext* enc_ctx, AVB out_pix_fmt_ = enc_ctx->pix_fmt; // Load the model - if (realesrgan_->load(model_param_full_path, model_bin_full_path) != 0) { - logger()->error("Failed to load RealESRGAN model"); + if (realesrgan_->load(model_param_full_path.value(), model_bin_full_path.value()) != 0) { + logger()->error("Failed to load Real-ESRGAN model"); return -1; } - // Set RealESRGAN parameters + // Set Real-ESRGAN parameters realesrgan_->scale = scaling_factor_; realesrgan_->prepadding = 10; @@ -108,7 +110,7 @@ int FilterRealesrgan::filter(AVFrame* in_frame, AVFrame** out_frame) { ret = realesrgan_->process(in_mat, out_mat); if (ret != 0) { - logger()->error("RealESRGAN processing failed"); + logger()->error("Real-ESRGAN processing failed"); return ret; } diff --git a/src/fsutils.cpp b/src/fsutils.cpp index 1706df9..38c3731 100644 --- a/src/fsutils.cpp +++ b/src/fsutils.cpp @@ -54,13 +54,13 @@ static std::filesystem::path get_executable_directory() { } #endif // _WIN32 -bool filepath_is_readable(const std::filesystem::path& path) { +bool file_is_readable(const std::filesystem::path& path) { #if _WIN32 FILE* fp = _wfopen(path.c_str(), L"rb"); -#else // _WIN32 +#else FILE* fp = fopen(path.c_str(), "rb"); -#endif // _WIN32 - if (!fp) { +#endif + if (fp == nullptr) { return false; } @@ -68,16 +68,40 @@ bool filepath_is_readable(const std::filesystem::path& path) { return true; } -std::filesystem::path find_resource_file(const std::filesystem::path& path) { - if (filepath_is_readable(path)) { - return path; +std::optional find_resource(const std::filesystem::path& resource) { + // Build a list of candidate directories + std::vector candidates; + + // 1. The resource's path as provided + candidates.push_back(resource); + +#ifndef _WIN32 + // 2. AppImage's mounted directory + if (const char* appdir = std::getenv("APPDIR")) { + candidates.push_back( + std::filesystem::path(appdir) / "usr" / "share" / "video2x" / resource + ); } - if (filepath_is_readable(std::filesystem::path("/usr/share/video2x/") / path)) { - return std::filesystem::path("/usr/share/video2x/") / path; + // 3. The Linux standard local data directory + candidates.push_back(std::filesystem::path("/usr/local/share/video2x") / resource); + + // 4. The Linux standard data directory + candidates.push_back(std::filesystem::path("/usr/share/video2x") / resource); +#endif + + // 5. The executable's parent directory + candidates.push_back(get_executable_directory() / resource); + + // Iterate over the candidate directories and return the first readable file + for (const auto& candidate : candidates) { + if (file_is_readable(candidate) || std::filesystem::is_directory(candidate)) { + return candidate; + } } - return get_executable_directory() / path; + // Return nullopt if the resource was not found + return std::nullopt; } std::string path_to_u8string(const std::filesystem::path& path) { @@ -119,11 +143,11 @@ std::string wstring_to_u8string(const std::wstring& wstr) { ); return converted_str; } -#else +#else // _WIN32 std::string wstring_to_u8string(const std::string& str) { return str; } -#endif +#endif // _WIN32 fsutils::StringType path_to_string_type(const std::filesystem::path& path) { #if _WIN32 diff --git a/src/interpolator_rife.cpp b/src/interpolator_rife.cpp index 2c0f28f..6f67a69 100644 --- a/src/interpolator_rife.cpp +++ b/src/interpolator_rife.cpp @@ -35,17 +35,15 @@ InterpolatorRIFE::~InterpolatorRIFE() { int InterpolatorRIFE::init(AVCodecContext* dec_ctx, AVCodecContext* enc_ctx, AVBufferRef*) { // Construct the model directory path using std::filesystem - std::filesystem::path model_param_dir; - - // Find the model paths by model name if provided - model_param_dir = std::filesystem::path(STR("models")) / STR("rife") / model_name_; + std::filesystem::path model_dir = + std::filesystem::path(STR("models")) / STR("rife") / model_name_; // Get the full paths using a function that possibly modifies or validates the path - std::filesystem::path model_param_full_path = fsutils::find_resource_file(model_param_dir); + std::optional model_dir_full_path = fsutils::find_resource(model_dir); // Check if the model files exist - if (!std::filesystem::exists(model_param_full_path)) { - logger()->error("RIFE model param directory not found: {}", model_param_dir.u8string()); + if (!model_dir_full_path.has_value()) { + logger()->error("RIFE model param directory not found: {}", model_dir.u8string()); return -1; } @@ -73,7 +71,7 @@ int InterpolatorRIFE::init(AVCodecContext* dec_ctx, AVCodecContext* enc_ctx, AVB out_pix_fmt_ = enc_ctx->pix_fmt; // Load the model - if (rife_->load(model_param_full_path) != 0) { + if (rife_->load(model_dir_full_path.value()) != 0) { logger()->error("Failed to load RIFE model"); return -1; }