v2.9.0: menú de catálogo en dos niveles — categoría → ISOs

download_from_catalog() refactorizada en 4 funciones:
  _catalog_load()             — parsea isos.conf agrupando por sección # ──
  _catalog_menu_categorias()  — muestra las categorías con conteo de ISOs
  _catalog_menu_isos(cat)     — muestra solo las ISOs de la categoría elegida
  _catalog_do_download()      — ejecuta la descarga (sin cambios funcionales)

El usuario elige primero la categoría (rm-ISOs, Debian, Ubuntu, etc.)
y luego ve solo las ISOs de esa categoría, evitando la lista larga.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ricardo Monla
2026-06-07 00:08:37 -03:00
co-authored by Claude Sonnet 4.6
parent ae898d7a4d
commit c2cd1e4240
+133 -85
View File
@@ -5,7 +5,7 @@
# Autor: Lic. Ricardo MONLA # Autor: Lic. Ricardo MONLA
# Email: rmonla@gmail.com # Email: rmonla@gmail.com
# GitHub: https://github.com/ricardomonla/rm-MULTIBOOT # GitHub: https://github.com/ricardomonla/rm-MULTIBOOT
# Versión: 2.8.0 # Versión: 2.9.0
# Licencia: MIT # Licencia: MIT
# #
# Uso: sudo ./rm-multiboot.sh # Uso: sudo ./rm-multiboot.sh
@@ -19,7 +19,7 @@
set -euo pipefail set -euo pipefail
# ─── Constantes ─────────────────────────────────────────────────────────────── # ─── Constantes ───────────────────────────────────────────────────────────────
readonly SCRIPT_VERSION="2.8.0" readonly SCRIPT_VERSION="2.9.0"
readonly SCRIPT_AUTHOR="Lic. Ricardo MONLA" readonly SCRIPT_AUTHOR="Lic. Ricardo MONLA"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly ISOS_CONF="$SCRIPT_DIR/isos.conf" readonly ISOS_CONF="$SCRIPT_DIR/isos.conf"
@@ -985,107 +985,130 @@ fetch_catalog() {
wget -q --timeout=8 -O "$tmp" "$ISOS_CONF_URL" 2>/dev/null wget -q --timeout=8 -O "$tmp" "$ISOS_CONF_URL" 2>/dev/null
fi fi
# Si el archivo descargado tiene contenido válido, lo usamos
if [[ -s "$tmp" ]] && grep -q '|' "$tmp" 2>/dev/null; then if [[ -s "$tmp" ]] && grep -q '|' "$tmp" 2>/dev/null; then
echo "$tmp" echo "$tmp"; return 0
return 0
fi fi
rm -f "$tmp" rm -f "$tmp"
[[ -f "$ISOS_CONF" ]] && { echo "$ISOS_CONF"; return 0; }
# Fallback: archivo local junto al script
if [[ -f "$ISOS_CONF" ]]; then
echo "$ISOS_CONF"
return 0
fi
return 1 return 1
} }
download_from_catalog() { # Parsea el catálogo agrupado por categorías.
# Popula los arrays globales: _CAT_NAMES _CAT_URLS _CAT_CATS _CAT_LABELS
_catalog_load() {
local catalog_file="$1"
_CAT_NAMES=(); _CAT_URLS=(); _CAT_CATS=(); _CAT_LABELS=()
local current_cat="Sin categoría"
while IFS= read -r line; do
# Detectar línea de categoría: "# ── Nombre ────"
if [[ "$line" == *'# ──'* ]]; then
local cat_name
cat_name=$(echo "$line" | sed 's/.*# ── //' | sed 's/[[:space:]]*─*[[:space:]]*$//')
[[ -n "$cat_name" ]] && current_cat="$cat_name"
local found=false
local l; for l in "${_CAT_LABELS[@]:-}"; do
[[ "$l" == "$current_cat" ]] && { found=true; break; }
done
[[ "$found" == false ]] && _CAT_LABELS+=("$current_cat")
continue
fi
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ -z "${line// }" ]] && continue
local name url
IFS='|' read -r name url <<< "$line"
name="${name#"${name%%[! ]*}"}"; name="${name%"${name##*[! ]}"}"
url="${url#"${url%%[! ]*}"}"; url="${url%"${url##*[! ]}"}"
[[ -z "$url" ]] && continue
_CAT_NAMES+=("$name")
_CAT_URLS+=("$url")
_CAT_CATS+=("$current_cat")
done < "$catalog_file"
}
# Muestra el menú de categorías y navega a la elegida.
_catalog_menu_categorias() {
banner banner
step "Descargar ISO desde el catálogo" step "Descargar ISO — Elegí una categoría"
echo "" echo ""
# Obtener catálogo
local catalog_src catalog_file origin_label
printf " Cargando catálogo desde GitHub..."
if catalog_file=$(fetch_catalog); then
if [[ "$catalog_file" == "$ISOS_CONF" ]]; then
origin_label="${Y}(sin red — usando archivo local)${N}"
else
origin_label="${G}(GitHub — actualizado)${N}"
fi
printf "\r ${G}${N} Catálogo cargado %b\n\n" "$origin_label"
else
printf "\r ${R}${N} No se pudo obtener el catálogo.\n"
echo -e " Verificá la conexión a internet o colocá ${W}isos.conf${N} junto al script.\n"
pause; banner; detect_system; menu_principal; return
fi
# Leer entradas válidas del catálogo (ignorar comentarios y líneas vacías)
local -a names urls
while IFS='|' read -r name url; do
[[ "$name" =~ ^[[:space:]]*# ]] && continue
[[ -z "${name// }" ]] && continue
name="${name#"${name%%[! ]*}"}"; name="${name%"${name##*[! ]}"}"
url="${url#"${url%%[! ]*}"}"; url="${url%"${url##*[! ]}"}"
[[ -z "$url" ]] && continue
names+=("$name")
urls+=("$url")
done < "$catalog_file"
# Limpiar archivo temporal si vino de GitHub
[[ "$catalog_file" != "$ISOS_CONF" ]] && rm -f "$catalog_file"
if [[ "${#names[@]}" -eq 0 ]]; then
err "El catálogo está vacío o no tiene entradas válidas."
pause; banner; detect_system; menu_principal; return
fi
# Mostrar catálogo
echo -e " ${W}Catálogo disponible:${N}\n"
local i=1 local i=1
for name in "${names[@]}"; do for cat in "${_CAT_LABELS[@]}"; do
local iso_filename local n=0
iso_filename=$(basename "${urls[$((i-1))]%%\?*}") local c; for c in "${_CAT_CATS[@]}"; do [[ "$c" == "$cat" ]] && n=$(( n + 1 )); done
local estado="" local s=""; [[ $n -gt 1 ]] && s="s"
[[ -f "$ISO_DIR/$iso_filename" ]] && estado=" ${G}[ya descargada]${N}" printf " ${C}%d)${N} %-42s ${W}%d ISO%s${N}\n" "$i" "$cat" "$n" "$s"
printf " ${C}%2d)${N} %-42s%b\n" "$i" "$name" "$estado" i=$(( i + 1 ))
i=$((i+1))
done done
echo " 0) Volver" echo " 0) Volver al menú principal"
echo ""
local opt
opt=$(ask "¿Qué categoría? [0-${#_CAT_LABELS[@]}]")
[[ "$opt" == "0" ]] && { banner; detect_system; menu_principal; return; }
if ! [[ "$opt" =~ ^[0-9]+$ ]] || \
[[ "$opt" -lt 1 ]] || [[ "$opt" -gt "${#_CAT_LABELS[@]}" ]]; then
err "Opción inválida."; pause; _catalog_menu_categorias; return
fi
_catalog_menu_isos "${_CAT_LABELS[$((opt-1))]}"
}
# Muestra las ISOs de una categoría y permite seleccionar una para descargar.
_catalog_menu_isos() {
local cat="$1"
banner
step "Descargar ISO — $cat"
echo ""
local -a names urls
local idx; for idx in "${!_CAT_NAMES[@]}"; do
[[ "${_CAT_CATS[$idx]}" == "$cat" ]] || continue
names+=("${_CAT_NAMES[$idx]}")
urls+=("${_CAT_URLS[$idx]}")
done
local j=1
for name in "${names[@]}"; do
local iso_filename estado=""
iso_filename=$(basename "${urls[$((j-1))]%%\?*}")
[[ -f "$ISO_DIR/$iso_filename" ]] && estado=" ${G}[ya descargada]${N}"
printf " ${C}%2d)${N} %-42s%b\n" "$j" "$name" "$estado"
j=$(( j + 1 ))
done
echo " 0) ← Categorías"
echo "" echo ""
local opt local opt
opt=$(ask "¿Cuál descargamos? [0-${#names[@]}]") opt=$(ask "¿Cuál descargamos? [0-${#names[@]}]")
[[ "$opt" == "0" ]] && { _catalog_menu_categorias; return; }
if [[ "$opt" == "0" ]]; then
banner; detect_system; menu_principal; return
fi
if ! [[ "$opt" =~ ^[0-9]+$ ]] || \ if ! [[ "$opt" =~ ^[0-9]+$ ]] || \
[[ "$opt" -lt 1 ]] || [[ "$opt" -gt "${#names[@]}" ]]; then [[ "$opt" -lt 1 ]] || [[ "$opt" -gt "${#names[@]}" ]]; then
err "Opción inválida."; pause; download_from_catalog; return err "Opción inválida."; pause; _catalog_menu_isos "$cat"; return
fi fi
local idx=$((opt - 1)) _catalog_do_download "${names[$((opt-1))]}" "${urls[$((opt-1))]}" "$cat"
local chosen_name="${names[$idx]}" }
local chosen_url="${urls[$idx]}"
# Ejecuta la descarga de una ISO seleccionada del catálogo.
_catalog_do_download() {
local chosen_name="$1" chosen_url="$2" chosen_cat="$3"
local iso_filename gdrive_id="" local iso_filename gdrive_id=""
# ── Resolver filename y origen de descarga ────────────────────────────────
if is_gdrive_url "$chosen_url"; then if is_gdrive_url "$chosen_url"; then
gdrive_id=$(gdrive_file_id "$chosen_url") gdrive_id=$(gdrive_file_id "$chosen_url")
if [[ -z "$gdrive_id" ]]; then if [[ -z "$gdrive_id" ]]; then
err "No se pudo extraer el File ID de la URL de Google Drive." err "No se pudo extraer el File ID de la URL de Google Drive."
pause; banner; detect_system; menu_principal; return pause; _catalog_menu_categorias; return
fi fi
# Intentar obtener el nombre real del archivo desde headers
printf " Obteniendo nombre del archivo desde Google Drive..." printf " Obteniendo nombre del archivo desde Google Drive..."
iso_filename=$(gdrive_real_filename "$gdrive_id") iso_filename=$(gdrive_real_filename "$gdrive_id")
if [[ -z "$iso_filename" ]]; then if [[ -z "$iso_filename" ]]; then
# Fallback: generar nombre desde el nombre del catálogo
iso_filename=$(echo "$chosen_name" | tr '[:upper:]' '[:lower:]' \ iso_filename=$(echo "$chosen_name" | tr '[:upper:]' '[:lower:]' \
| tr ' ' '-' | sed 's/[^a-z0-9._-]//g').iso | tr ' ' '-' | sed 's/[^a-z0-9._-]//g').iso
fi fi
@@ -1098,20 +1121,18 @@ download_from_catalog() {
echo "" echo ""
echo -e " ${W}ISO seleccionada:${N}" echo -e " ${W}ISO seleccionada:${N}"
ok "Nombre: ${W}$chosen_name${N}" ok "Categoría: ${W}$chosen_cat${N}"
ok "Archivo: $iso_filename" ok "Nombre: ${W}$chosen_name${N}"
if [[ -n "$gdrive_id" ]]; then ok "Archivo: $iso_filename"
ok "Origen: ${C}Google Drive${N} (ID: $gdrive_id)" [[ -n "$gdrive_id" ]] \
else && ok "Origen: ${C}Google Drive${N} (ID: $gdrive_id)" \
ok "URL: ${C}$chosen_url${N}" || ok "URL: ${C}$chosen_url${N}"
fi
echo "" echo ""
# Verificar si ya existe
if [[ -f "$dest" ]]; then if [[ -f "$dest" ]]; then
warn "Esta ISO ya está descargada en la partición MULTIBOOT." warn "Esta ISO ya está descargada en la partición MULTIBOOT."
if ! confirm "¿Descargar de nuevo y sobreescribir?"; then if ! confirm "¿Descargar de nuevo y sobreescribir?"; then
banner; detect_system; menu_principal; return _catalog_menu_categorias; return
fi fi
fi fi
@@ -1120,10 +1141,9 @@ download_from_catalog() {
echo -e " Espacio libre en MULTIBOOT: $(( avail_k / 1024 / 1024 )) GB\n" echo -e " Espacio libre en MULTIBOOT: $(( avail_k / 1024 / 1024 )) GB\n"
if ! confirm "¿Iniciar descarga?"; then if ! confirm "¿Iniciar descarga?"; then
banner; detect_system; menu_principal; return _catalog_menu_categorias; return
fi fi
# ── Descargar ─────────────────────────────────────────────────────────────
echo "" echo ""
step "Descargando $iso_filename..." step "Descargando $iso_filename..."
echo "" echo ""
@@ -1141,11 +1161,9 @@ download_from_catalog() {
ok "Descarga completada: $iso_filename" ok "Descarga completada: $iso_filename"
# Generar entrada GRUB
step "Generando entrada de arranque..." step "Generando entrada de arranque..."
local distro local distro
distro=$(detect_distro "$iso_filename") distro=$(detect_distro "$iso_filename")
# Preferir el nombre del catálogo si es más descriptivo
[[ "$distro" == "Linux" ]] && distro="$chosen_name" [[ "$distro" == "Linux" ]] && distro="$chosen_name"
generate_grub_entry "$iso_filename" "$distro" generate_grub_entry "$iso_filename" "$distro"
@@ -1158,6 +1176,36 @@ download_from_catalog() {
pause; banner; detect_system; menu_principal pause; banner; detect_system; menu_principal
} }
# Punto de entrada: carga el catálogo y muestra el menú de categorías.
download_from_catalog() {
banner
step "Descargar ISO desde el catálogo"
echo ""
local catalog_file origin_label
printf " Cargando catálogo desde GitHub..."
if catalog_file=$(fetch_catalog); then
[[ "$catalog_file" == "$ISOS_CONF" ]] \
&& origin_label="${Y}(sin red — usando archivo local)${N}" \
|| origin_label="${G}(GitHub — actualizado)${N}"
printf "\r ${G}${N} Catálogo cargado %b\n\n" "$origin_label"
else
printf "\r ${R}${N} No se pudo obtener el catálogo.\n"
echo -e " Verificá la conexión a internet o colocá ${W}isos.conf${N} junto al script.\n"
pause; banner; detect_system; menu_principal; return
fi
_catalog_load "$catalog_file"
[[ "$catalog_file" != "$ISOS_CONF" ]] && rm -f "$catalog_file"
if [[ "${#_CAT_NAMES[@]}" -eq 0 ]]; then
err "El catálogo está vacío o no tiene entradas válidas."
pause; banner; detect_system; menu_principal; return
fi
_catalog_menu_categorias
}
# ============================================================================= # =============================================================================
# AGREGAR ISO DESDE RUTA LOCAL # AGREGAR ISO DESDE RUTA LOCAL