\brief Download a file from URL to a local file \param url the URL to download from \param local_path the local path to save the file \param progress_cb the progress callback \return true if the file is downloaded, false otherwise
| 126 | /// \param progress_cb the progress callback |
| 127 | /// \return true if the file is downloaded, false otherwise |
| 128 | bool download_file(const std::string& url, const std::string& local_path, bool is_lfs, std::string remote_oid, |
| 129 | std::function<void(double)> progress_cb) { |
| 130 | // Reset progress bar tracking for this download |
| 131 | g_progress_bar_shown = false; |
| 132 | |
| 133 | CURL* curl = curl_easy_init(); |
| 134 | if (!curl) { |
| 135 | std::cerr << "Failed to initialize CURL" << std::endl; |
| 136 | return false; |
| 137 | } |
| 138 | |
| 139 | // Create directory if it doesn't exist |
| 140 | std::filesystem::path path(local_path); |
| 141 | std::filesystem::create_directories(path.parent_path()); |
| 142 | |
| 143 | FILE* fp = fopen(local_path.c_str(), "wb"); |
| 144 | if (!fp) { |
| 145 | std::cerr << "Failed to open file for writing: " << local_path << std::endl; |
| 146 | curl_easy_cleanup(curl); |
| 147 | return false; |
| 148 | } |
| 149 | |
| 150 | // Hide cursor before starting download |
| 151 | hide_cursor(); |
| 152 | |
| 153 | curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); |
| 154 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data_to_file); |
| 155 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); |
| 156 | curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); |
| 157 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); |
| 158 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); |
| 159 | curl_easy_setopt(curl, CURLOPT_USERAGENT, "FastFlowLM/1.0"); |
| 160 | curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3600L); // 1 hour timeout |
| 161 | |
| 162 | // Set progress callback if provided |
| 163 | if (progress_cb) { |
| 164 | curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); |
| 165 | curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback); |
| 166 | } |
| 167 | |
| 168 | CURLcode res = curl_easy_perform(curl); |
| 169 | |
| 170 | fclose(fp); |
| 171 | curl_easy_cleanup(curl); |
| 172 | |
| 173 | // Show cursor after download completes |
| 174 | show_cursor(); |
| 175 | |
| 176 | if (res != CURLE_OK) { |
| 177 | std::cerr << "CURL error: " << curl_easy_strerror(res) << std::endl; |
| 178 | std::filesystem::remove(local_path); // Remove partial download |
| 179 | return false; |
| 180 | } |
| 181 | |
| 182 | // Only add newline if progress bar was shown |
| 183 | if (g_progress_bar_shown) { |
| 184 | std::cout << std::endl; |
| 185 | } |
no test coverage detected