#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <curl/curl.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#define MAX_THREADS 10
#define MAX_URL_LENGTH 2048
#define MAX_FILENAME_LENGTH 256
typedef struct {
char url[MAX_URL_LENGTH];
char filename[MAX_FILENAME_LENGTH];
double progress;
pthread_mutex_t mutex;
} DownloadInfo;
DownloadInfo downloads[MAX_THREADS];
int active_downloads = 0;
pthread_mutex_t console_mutex = PTHREAD_MUTEX_INITIALIZER;
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
return fwrite(ptr, size, nmemb, stream);
}
int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal,
double ulnow) {
DownloadInfo *info = (DownloadInfo *)clientp;
pthread_mutex_lock(&info->mutex);
info->progress = (dltotal > 0) ? (dlnow / dltotal * 100) : 0;
pthread_mutex_unlock(&info->mutex);
return 0;
}
void *download_thread(void *arg) {
DownloadInfo *info = (DownloadInfo *)arg;
CURL *curl;
FILE *fp;
CURLcode res;
curl = curl_easy_init();
if (curl) {
fp = fopen(info->filename, "wb");
curl_easy_setopt(curl, CURLOPT_URL, info->url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, info);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
pthread_mutex_lock(&console_mutex);
active_downloads--;
pthread_mutex_unlock(&console_mutex);
return NULL;
}
void print_progress() {
pthread_mutex_lock(&console_mutex);
printf("\033[2J\033[H"); // Clear screen and move cursor to top-left
for (int i = 0; i < MAX_THREADS; i++) {
if (downloads[i].filename[0] != '\0') {
printf("%s: %.2f%%\n", downloads[i].filename, downloads[i].progress);
}
}
pthread_mutex_unlock(&console_mutex);
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Uso: %s <URL>\n", argv[0]);
return 1;
}
CURL *curl = curl_easy_init();
if (!curl) {
fprintf(stderr, "Error al inicializar cURL\n");
return 1;
}
// Crear carpeta con la fecha actual
time_t t = time(NULL);
struct tm *tm = localtime(&t);
char foldername[20];
strftime(foldername, sizeof(foldername), "%Y-%m-%d--%H%M%S", tm);
mkdir(foldername, 0777);
// Obtener lista de archivos
char *url = argv[1];
char *buffer = NULL;
size_t size = 0;
FILE *stream = open_memstream(&buffer, &size);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream);
CURLcode res = curl_easy_perform(curl);
fclose(stream);
if (res != CURLE_OK) {
fprintf(stderr, "Error al obtener la lista de archivos: %s\n",
curl_easy_strerror(res));
free(buffer);
curl_easy_cleanup(curl);
return 1;
}
// Procesar la lista de archivos (esto es una simplificación, necesitarías un
parser HTML adecuado)
char *line = strtok(buffer, "\n");
pthread_t threads[MAX_THREADS];
int thread_count = 0;
while (line != NULL) {
char *href = strstr(line, "href=\"");
if (href) {
href += 6;
char *end = strchr(href, '"');
if (end) {
*end = '\0';
if (href[0] != '/' && strstr(href, "://") == NULL) {
char full_url[MAX_URL_LENGTH];
snprintf(full_url, sizeof(full_url), "%s/%s", url, href);
while (active_downloads >= MAX_THREADS) {
print_progress();
sleep(1);
}
pthread_mutex_lock(&console_mutex);
int idx = thread_count % MAX_THREADS;
active_downloads++;
strcpy(downloads[idx].url, full_url);
snprintf(downloads[idx].filename,
sizeof(downloads[idx].filename), "%s/%s", foldername, href);
downloads[idx].progress = 0;
pthread_mutex_init(&downloads[idx].mutex, NULL);
pthread_create(&threads[thread_count], NULL, download_thread,
&downloads[idx]);
thread_count++;
pthread_mutex_unlock(&console_mutex);
}
}
}
line = strtok(NULL, "\n");
}
free(buffer);
curl_easy_cleanup(curl);
// Esperar a que terminen todas las descargas
while (active_downloads > 0) {
print_progress();
sleep(1);
}
for (int i = 0; i < thread_count; i++) {
pthread_join(threads[i], NULL);
}
printf("Todas las descargas han sido completadas.\n");
return 0;
}