View on GitHub

KGet

A modern, lightweight wget clone written in Rust for fast and reliable file downloads from the command line.

Usando KGet como Biblioteca Rust

KGet es un gestor de descargas y también un motor Rust reutilizable para HTTP/HTTPS, FTP, SFTP, WebDAV, magnet links, callbacks de progreso, reanudación de descargas, proxy y verificación de checksum multi-algoritmo.

English Português Español

Instalación

[dependencies]
Kget = "1.7.0"

# Opcional: cliente torrent nativo
Kget = { version = "1.7.0", features = ["torrent-native"] }

# Opcional: API async
Kget = { version = "1.7.0", features = ["async"] }

Quick Start — API Builder (recomendada)

use kget::KgetError;

fn main() -> Result<(), KgetError> {
    kget::builder("https://example.com/archivo.zip")
        .output("./downloads/")
        .connections(8)
        .sha256("abc123def456...")
        .download()?;
    Ok(())
}

Métodos del Builder

kget::builder(url) retorna un DownloadBuilder. Todos los métodos son encadenables:

Método Descripción
.output(ruta) Guardar en archivo o directorio
.connections(n) Conexiones paralelas (modo turbo)
.speed_limit(bps) Máximo de bytes/s (token bucket global)
.proxy(url) URL del proxy HTTP o SOCKS5
.quiet(bool) Suprimir salida de progreso
.sha256(hash) Verificar SHA-256 después de la descarga
.sha512(hash) Verificar SHA-512 después de la descarga
.sha1(hash) Verificar SHA-1 después de la descarga
.md5(hash) Verificar MD5 después de la descarga
.blake3(hash) Verificar BLAKE3 después de la descarga
.verify_from(url) Descargar y analizar archivo de checksum sidecar GNU/BSD
.header(nombre, valor) Agregar header HTTP
.retry(config) Política de reintentos personalizada (RetryConfig)
.range(inicio, fin) Solicitar rango de bytes específico

Métodos terminales:

Método Retorna Descripción
.download() Result<DownloadResult, KgetError> Descarga a disco
.download_to_bytes() Result<Vec<u8>, KgetError> Descarga en memoria
.download_to_reader() Result<impl Read, KgetError> Reader de streaming
.spawn() Result<(JoinHandle, Receiver<DownloadEvent>), KgetError> Hilo en background con canal de eventos
.download_async() impl Future<…> API async (feature async)

Descargas en Lote

let results = kget::batch([
    "https://mirror1.example.com/archivo.iso",
    "https://mirror2.example.com/otro.tar.gz",
])
.concurrency(4)
.output_dir("./downloads/")
.download_all();

for r in results {
    match r.result {
        Ok(d) => println!("✓ {} — {:.1} MB/s", r.url, d.avg_speed_bps / 1e6),
        Err(e) => eprintln!("✗ {}: {}", r.url, e),
    }
}

Canal de Eventos

use kget::{DownloadEvent, KgetError};

let (handle, rx) = kget::builder("https://example.com/grande.iso")
    .connections(4)
    .spawn()?;

for event in rx {
    match event {
        DownloadEvent::Progress { percent, speed_bps, eta_secs } => {
            print!("\r{:.1}%  {:.1} MB/s  eta {}s", percent, speed_bps / 1e6, eta_secs.unwrap_or(0));
        }
        DownloadEvent::Completed { path, bytes, .. } => {
            println!("\nGuardados {bytes} bytes en {path}");
        }
        DownloadEvent::Error(e) => eprintln!("Error: {e}"),
        _ => {}
    }
}
handle.join().ok();
# Ok::<(), KgetError>(())

Errores Tipados

pub enum KgetError {
    Network(reqwest::Error),
    Io(std::io::Error),
    ChecksumMismatch { algorithm: String, expected: String, got: String },
    Protocol(String),
    Cancelled,
    NotFound(String),
    SidecarError(String),
    Other(String),
}

Los errores permanentes (Cancelled, NotFound, ChecksumMismatch) nunca se reintentan.

Checksums

use kget::checksum::{compute_checksum, ChecksumAlgorithm};

let hash = compute_checksum(ChecksumAlgorithm::Blake3, std::path::Path::new("archivo.bin"))?;
println!("BLAKE3: {hash}");
# Ok::<(), Box<dyn std::error::Error>>(())

Algoritmos soportados: Sha256, Sha512, Sha1, Md5, Blake3.

Verificación por archivo sidecar:

kget::builder("https://example.com/release.tar.gz")
    .verify_from("https://example.com/release.tar.gz.sha256sum")
    .download()?;
# Ok::<(), kget::KgetError>(())

ResumePolicy

use kget::{AdvancedDownloader, ResumePolicy, Optimizer, ProxyConfig};

let mut dl = AdvancedDownloader::new(
    "https://example.com/imagen.iso".to_string(),
    "imagen.iso".to_string(),
    true,
    ProxyConfig::default(),
    Optimizer::new(),
)?;
dl.set_resume_policy(ResumePolicy::AlwaysResume); // nunca bloquea en stdin
dl.download()?;
# Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
Variante Comportamiento
Ask (defecto) Solicita via stdin en sesiones interactivas
AlwaysResume Ignora el prompt, procede sin preguntar
AlwaysRestart Ignora el prompt, reinicia desde cero

Los llamadores de la biblioteca siempre deben establecer AlwaysResume o AlwaysRestart.

Descarga HTTP Avanzada Paralela

use kget::{AdvancedDownloader, Optimizer, ProxyConfig};

let mut downloader = AdvancedDownloader::new(
    "https://example.com/grande.iso".to_string(),
    "grande.iso".to_string(),
    false,
    ProxyConfig::default(),
    Optimizer::new(),
)?;  // new() retorna Result — propaga con ?

downloader.set_resume_policy(kget::ResumePolicy::AlwaysResume);
downloader.set_progress_callback(|progress| {
    print!("\r{:.1}%", progress * 100.0);
});
downloader.download()?;
# Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())

Descarga HTTP Simple

use kget::{download, DownloadOptions, Optimizer, ProxyConfig};

let options = DownloadOptions {
    output_path: Some("archivo.zip".to_string()),
    ..Default::default()
};

download(
    "https://example.com/archivo.zip",
    ProxyConfig::default(),
    Optimizer::new(),
    options,
    None,
)?;
# Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())

Descarga en Memoria

let bytes: Vec<u8> = kget::builder("https://example.com/datos.json")
    .download_to_bytes()?;
println!("Recibidos {} bytes", bytes.len());
# Ok::<(), kget::KgetError>(())

FTP

use kget::ftp::FtpDownloader;
use kget::{Optimizer, ProxyConfig};

let dl = FtpDownloader::new(
    "ftp://ftp.gnu.org/gnu/emacs/emacs-28.2.tar.gz".to_string(),
    "emacs-28.2.tar.gz".to_string(),
    false,
    ProxyConfig::default(),
    Optimizer::new(),
);
dl.download()?;
# Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())

SFTP

use kget::sftp::SftpDownloader;
use kget::{Optimizer, ProxyConfig};

let dl = SftpDownloader::new(
    "sftp://usuario:clave@servidor.example.com/ruta/archivo.tar.gz".to_string(),
    "archivo.tar.gz".to_string(),
    false,
    ProxyConfig::default(),
    Optimizer::new(),
);
dl.download()?;
# Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())

Prioridad de autenticación:

  1. Contraseña en la URL (sftp://usuario:clave@host/ruta)
  2. SSH agent activo
  3. Archivos de clave por defecto (~/.ssh/id_ed25519, ~/.ssh/id_rsa, ~/.ssh/id_ecdsa)

Las host keys se verifican contra ~/.ssh/known_hosts; las divergencias generan error.

WebDAV

use kget::webdav::WebDavDownloader;
use kget::{Optimizer, ProxyConfig};

let dl = WebDavDownloader::new(
    "webdavs://usuario:clave@nas.local/backups/db.tar.gz".to_string(),
    "db.tar.gz".to_string(),
    false,
    ProxyConfig::default(),
    Optimizer::new(),
);
dl.download()?;
# Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())

yt-dlp

use kget::ytdlp::{download_video, VideoQuality, is_video_url};

if is_video_url("https://www.youtube.com/watch?v=dQw4w9WgXcQ") {
    download_video(
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        VideoQuality::Quality720p,
        "./downloads",
        None,
    )?;
}
# Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
use kget::{download_magnet, Optimizer, ProxyConfig, TorrentCallbacks};
use std::sync::Arc;

download_magnet(
    "magnet:?xt=urn:btih:0123456789abcdef0123456789abcdef01234567",
    "./downloads",
    true,
    ProxyConfig::default(),
    Optimizer::new(),
    TorrentCallbacks {
        status: Some(Arc::new(|msg| println!("{msg}"))),
        progress: Some(Arc::new(|p| println!("{:.1}%", p * 100.0))),
    },
)?;
# Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
use kget::metalink::download_metalink;
use kget::{Optimizer, ProxyConfig};

download_metalink(
    "ubuntu-24.04.meta4",
    "~/Downloads",
    false,
    ProxyConfig::default(),
    Optimizer::new(),
)?;
# Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())

Historial de Descargas

use kget::queue::{DownloadHistory, EntryStatus, HistoryEntry};

let mut history = DownloadHistory::load();
let entry = HistoryEntry::new("https://example.com/archivo.iso", "/home/user/Downloads", None);
history.record(entry, EntryStatus::Completed, None);
history.save()?;

for e in history.recent(10) {
    println!("{} {} {}", e.created_at_display(), e.status, e.filename);
}
history.clear_completed();
history.save()?;
# Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())

API Async

Con --features async:

#[tokio::main]
async fn main() -> Result<(), kget::KgetError> {
    kget::builder("https://example.com/archivo.zip")
        .output("./downloads/")
        .connections(4)
        .download_async()
        .await?;
    Ok(())
}

Garantías de la Biblioteca

Ver examples/lib_usage.rs para más ejemplos.