-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b011ca4
Showing
8 changed files
with
457 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
name: Miru Release | ||
|
||
on: | ||
release: | ||
types: [created] | ||
|
||
jobs: | ||
build: | ||
name: Build on ${{ matrix.os }} | ||
runs-on: ${{ matrix.os }} | ||
strategy: | ||
matrix: | ||
os: [ubuntu-latest, macos-latest, windows-latest] | ||
include: | ||
- os: ubuntu-latest | ||
output_name: eru_linux | ||
- os: macos-latest | ||
output_name: eru_macos | ||
- os: windows-latest | ||
output_name: eru_windows.exe | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
|
||
- name: Install dependencies (Ubuntu) | ||
if: matrix.os == 'ubuntu-latest' | ||
run: | | ||
sudo apt-get update | ||
sudo apt-get install -y libcurl4-openssl-dev | ||
- name: Install dependencies (macOS) | ||
if: matrix.os == 'macos-latest' | ||
run: | | ||
brew install curl | ||
- name: Install dependencies (Windows) | ||
if: matrix.os == 'windows-latest' | ||
run: | | ||
vcpkg install curl:x64-windows | ||
vcpkg integrate install | ||
- name: Configure CMake | ||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release | ||
|
||
- name: Build | ||
run: cmake --build ${{github.workspace}}/build --config Release | ||
|
||
- name: Rename binary | ||
run: | | ||
if [ "${{ matrix.os }}" == "windows-latest" ]; then | ||
mv ${{github.workspace}}/build/src/Release/eru.exe ${{github.workspace}}/${{ matrix.output_name }} | ||
else | ||
mv ${{github.workspace}}/build/src/eru ${{github.workspace}}/${{ matrix.output_name }} | ||
fi | ||
shell: bash | ||
|
||
- name: Upload Release Asset | ||
uses: actions/upload-release-asset@v1 | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
with: | ||
upload_url: ${{ github.event.release.upload_url }} | ||
asset_path: ${{github.workspace}}/${{ matrix.output_name }} | ||
asset_name: ${{ matrix.output_name }} | ||
asset_content_type: application/octet-stream |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Prerequisites | ||
*.d | ||
|
||
# Compiled Object files | ||
*.slo | ||
*.lo | ||
*.o | ||
*.obj | ||
|
||
# Precompiled Headers | ||
*.gch | ||
*.pch | ||
|
||
# Compiled Dynamic libraries | ||
*.so | ||
*.dylib | ||
*.dll | ||
|
||
# Fortran module files | ||
*.mod | ||
*.smod | ||
|
||
# Compiled Static libraries | ||
*.lai | ||
*.la | ||
*.a | ||
*.lib | ||
|
||
# Executables | ||
*.exe | ||
*.out | ||
*.app | ||
|
||
|
||
/build/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
cmake_minimum_required(VERSION 3.14) | ||
project(file_downloader VERSION 1.0) | ||
|
||
set(CMAKE_CXX_STANDARD 17) | ||
set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||
|
||
include(FetchContent) | ||
|
||
FetchContent_Declare( | ||
cpr | ||
GIT_REPOSITORY https://github.com/libcpr/cpr.git | ||
GIT_TAG 1.10.x | ||
) | ||
FetchContent_MakeAvailable(cpr) | ||
|
||
FetchContent_Declare( | ||
cli11 | ||
GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git | ||
GIT_TAG v2.3.2 | ||
) | ||
FetchContent_MakeAvailable(cli11) | ||
|
||
FetchContent_Declare( | ||
indicators | ||
GIT_REPOSITORY https://github.com/p-ranav/indicators.git | ||
GIT_TAG v2.3 | ||
) | ||
FetchContent_MakeAvailable(indicators) | ||
|
||
add_subdirectory(src) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# Eru: Multi-threaded File Downloader | ||
|
||
Eru is a simple and efficient multi-threaded file downloader designed to accelerate your download speeds. Written in C++, Eru leverages multiple threads to download file chunks concurrently, potentially increasing download speeds significantly. I made this as I had a simple requirement and did not want to buy IDM or pirate it. | ||
|
||
## Features | ||
|
||
- Multi-threaded downloading | ||
- Progress bar with real-time updates | ||
- Automatic filename detection from URL | ||
- Customizable number of download threads | ||
- Supports both HTTP and HTTPS | ||
- Displays download speed and estimated time remaining | ||
- Automatic merging of downloaded chunks | ||
|
||
## Requirements | ||
|
||
- C++17 compatible compiler | ||
- CMake 3.14 or higher | ||
- libcurl | ||
- CLI11 | ||
- indicators | ||
|
||
## Building from Source | ||
|
||
1. Clone the repository: | ||
``` | ||
git clone https://github.com/Dank-del/eru.git | ||
cd eru | ||
``` | ||
|
||
2. Create a build directory and navigate to it: | ||
``` | ||
mkdir build && cd build | ||
``` | ||
|
||
3. Configure the project with CMake: | ||
``` | ||
cmake .. | ||
``` | ||
|
||
4. Build the project: | ||
``` | ||
cmake --build . | ||
``` | ||
|
||
## Usage | ||
|
||
After building, you can run Eru using the following command: | ||
|
||
``` | ||
./src/eru --url <download_url> [options] | ||
``` | ||
|
||
### Options | ||
|
||
- `-u, --url <url>`: Specify the URL of the file to download (required) | ||
- `-o, --output <filename>`: Set the output filename (optional, auto-detected if not specified) | ||
- `-t, --threads <number>`: Set the number of download threads (default: 4) | ||
- `-a, --about`: Display information about Eru | ||
- `-h, --help`: Show help message | ||
|
||
### Examples | ||
|
||
1. Download a file using default settings: | ||
``` | ||
./src/eru --url https://example.com/large_file.zip | ||
``` | ||
|
||
2. Download a file with a custom output name and 8 threads: | ||
``` | ||
./src/eru --url https://example.com/large_file.zip --output my_file.zip --threads 8 | ||
``` | ||
|
||
3. Display information about Eru: | ||
``` | ||
./src/eru --about | ||
``` | ||
|
||
## Contributing | ||
|
||
Contributions to Eru are welcome! Please feel free to submit a Pull Request. | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. | ||
|
||
## Author | ||
|
||
Sayan Biswas | ||
- Email: [email protected] | ||
|
||
## Acknowledgments | ||
|
||
- [CPR](https://github.com/libcpr/cpr) for HTTP requests | ||
- [CLI11](https://github.com/CLIUtils/CLI11) for command-line parsing | ||
- [indicators](https://github.com/p-ranav/indicators) for progress bars |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
add_library(downloader STATIC | ||
downloader.cpp | ||
downloader.h | ||
) | ||
|
||
target_link_libraries(downloader PUBLIC cpr::cpr indicators::indicators) | ||
|
||
add_executable(eru main.cpp) | ||
target_link_libraries(eru PRIVATE downloader CLI11::CLI11 indicators::indicators) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
#include "downloader.h" | ||
#include <iostream> | ||
#include <fstream> | ||
#include <vector> | ||
#include <thread> | ||
#include <mutex> | ||
#include <atomic> | ||
#include <cmath> | ||
#include <chrono> | ||
#include <iomanip> | ||
#include <cpr/cpr.h> | ||
#include <indicators/progress_bar.hpp> | ||
|
||
std::mutex console_mutex; | ||
std::atomic<size_t> total_progress(0); | ||
|
||
std::string get_filename_from_url(const std::string &url) | ||
{ | ||
size_t pos = url.find_last_of("/"); | ||
if (pos != std::string::npos) | ||
{ | ||
return url.substr(pos + 1); | ||
} | ||
return "downloaded_file"; | ||
} | ||
|
||
std::string format_size(size_t size) | ||
{ | ||
double size_mb = static_cast<double>(size) / (1024 * 1024); | ||
std::stringstream ss; | ||
ss << std::fixed << std::setprecision(2) << size_mb; | ||
return ss.str(); | ||
} | ||
|
||
void Downloader::download_file(const std::string &url, const std::optional<std::string> &output_file, int num_threads) | ||
{ | ||
cpr::Response r = cpr::Head(cpr::Url{url}); | ||
if (r.status_code != 200) | ||
{ | ||
std::cerr << "Failed to get file information. Status code: " << r.status_code << std::endl; | ||
return; | ||
} | ||
|
||
size_t file_size = std::stoull(r.header["Content-Length"]); | ||
size_t chunk_size = std::ceil(static_cast<double>(file_size) / num_threads); | ||
|
||
std::string filename = output_file.value_or(get_filename_from_url(url)); | ||
std::cout << "Downloading to: " << filename << std::endl; | ||
std::cout << "File size: " << format_size(file_size) << " MB" << std::endl; | ||
|
||
indicators::ProgressBar progress_bar{ | ||
indicators::option::BarWidth{50}, | ||
indicators::option::Start{"["}, | ||
indicators::option::Fill{"="}, | ||
indicators::option::Lead{">"}, | ||
indicators::option::Remainder{" "}, | ||
indicators::option::End{"]"}, | ||
indicators::option::ForegroundColor{indicators::Color::green}, | ||
indicators::option::ShowElapsedTime{true}, | ||
indicators::option::ShowRemainingTime{true}, | ||
indicators::option::FontStyles{std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}}; | ||
|
||
std::vector<std::thread> threads; | ||
for (int i = 0; i < num_threads; ++i) | ||
{ | ||
size_t start = i * chunk_size; | ||
size_t end = (i == num_threads - 1) ? file_size - 1 : (i + 1) * chunk_size - 1; | ||
threads.emplace_back(&Downloader::download_chunk, url, start, end, filename, i); | ||
} | ||
|
||
while (total_progress < file_size) | ||
{ | ||
size_t current_progress = total_progress.load(); | ||
progress_bar.set_progress(100.0 * current_progress / file_size); | ||
std::cout << "\rDownloaded: " << format_size(current_progress) << " MB / " << format_size(file_size) << " MB" << std::flush; | ||
std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
} | ||
|
||
for (auto &t : threads) | ||
{ | ||
t.join(); | ||
} | ||
|
||
progress_bar.set_progress(100); | ||
std::cout << "\nDownload complete. Merging chunks..." << std::endl; | ||
|
||
merge_chunks(filename, num_threads); | ||
|
||
std::cout << "File saved as: " << filename << std::endl; | ||
} | ||
|
||
void Downloader::download_chunk(const std::string &url, size_t start, size_t end, const std::string &output_file, int chunk_id) | ||
{ | ||
std::string chunk_file = output_file + ".part" + std::to_string(chunk_id); | ||
std::ofstream out(chunk_file, std::ios::binary); | ||
|
||
cpr::Response r = cpr::Get(cpr::Url{url}, | ||
cpr::Header{{"Range", "bytes=" + std::to_string(start) + "-" + std::to_string(end)}}, | ||
cpr::WriteCallback([&](const std::string &data, intptr_t) | ||
{ | ||
out.write(data.c_str(), data.length()); | ||
total_progress += data.length(); | ||
return true; })); | ||
|
||
out.close(); | ||
|
||
if (r.status_code != 206) | ||
{ | ||
std::lock_guard<std::mutex> lock(console_mutex); | ||
std::cerr << "Failed to download chunk " << chunk_id << ". Status code: " << r.status_code << std::endl; | ||
} | ||
} | ||
|
||
void Downloader::merge_chunks(const std::string &output_file, int num_chunks) | ||
{ | ||
std::ofstream out(output_file, std::ios::binary); | ||
|
||
for (int i = 0; i < num_chunks; ++i) | ||
{ | ||
std::string chunk_file = output_file + ".part" + std::to_string(i); | ||
std::ifstream in(chunk_file, std::ios::binary); | ||
out << in.rdbuf(); | ||
in.close(); | ||
std::remove(chunk_file.c_str()); | ||
} | ||
|
||
out.close(); | ||
} | ||
|
||
bool Downloader::test_connection(const std::string &url) | ||
{ | ||
cpr::Response r = cpr::Head(cpr::Url{url}); | ||
return r.status_code == 200; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#pragma once | ||
|
||
#include <string> | ||
#include <optional> | ||
|
||
class Downloader | ||
{ | ||
public: | ||
static void download_file(const std::string &url, const std::optional<std::string> &output_file, int num_threads); | ||
static bool test_connection(const std::string &url); | ||
|
||
private: | ||
static void download_chunk(const std::string &url, size_t start, size_t end, const std::string &output_file, int chunk_id); | ||
static void merge_chunks(const std::string &output_file, int num_chunks); | ||
}; |
Oops, something went wrong.