Instalá qBittorent: sudo apt install -y qbittorrent
Abrí qbittorrent, view -> search engine.
Ahora movete a la pestaña Search y abajo a la derecha dale a search plugins -> install a new one -> web link y pegá: https://raw.githubusercontent.com/qbittorrent/search-plugins/master/nova3/engines/jackett.py
Luego andá a ~/.local/share/qBittorrent/nova3/engines/ o seguí este tutorial: https://github.com/qbittorrent/search-plugins/wiki/How-to-configure-Jackett-plugin
{
"api_key": "YOUR_API_KEY_HERE",
"url": "http://127.0.0.1:9117",
"tracker_first": false,
"thread_count": 20
}
La API key lo copias de https://localhost:9117
Abrí Jackett y agregá todos los indexers publicos seleccionando uno por uno el checkbox de la izquierda.
Finalmente abrí qbittorrent andate a Search y buscá el torrent que quieras.
Podés descargarte qbittorrent-nox para y agregar .config/qBittorrent/qBittorrent.conf debajo de [Preferences]:
WebUI\Password_PBKDF2="@ByteArray(ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ==)"
Que establece la contraseña por defecto como adminadmin para no tener que estar copiando y pegando la contraseña temporal que te genera cada vez que abrís el programa.
AUR:yay -S jackett-bin
sudo systemctl start jackett
Y sumado a eso usá opencode para complementar con jackett y qbitorrent-nox ;)
Programame un script de bash
Chequea que qbittorrent-nox e jackett esten instalados. De no estarlo adverti al usuario.
inicia qbittorrent-nox en 2do plano ejecutando qbittorrent-nox (configura la contraseña adminadmin),
agregar $HOME/.config/qBittorrent/qBittorrent.conf debajo de [Preferences]: (si es que ya no está)
WebUI\Password_PBKDF2="@ByteArray(ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ==)"
Usa curl -s -X POST “http://127.0.0.1:8080/api/v2/auth/login” -d “username=admin&password=adminadmin” -c /tmp/qbt_cookies.txt && echo "" para comunicarte con qbittorrent-nox
y el servicio de jackett con systemd usando sudo systemctl start jackett descargame el torrent de jason vs freddie tenga muchos seeds hace una busqueda generica y despues filtra los resultados, bajame la calidad de video mas baja que encuentres y abrilo con mpv :) el servicio ya esta activo localmente en
el puerto 9117 y la api key esta en $HOME/.local/share/qBittorrent/nova3/engines/jackett.json usa qbittorrent-nox adminadmin es la contraseña agrega el torrent usando qbittorrent-nox con con curl -s -X POST “http://127.0.0.1:8080/api/v2.0/torrents/add” -d “urls=magnet:?xt=XXXXXXXXX” usa la guid que empieza con magnet:? al buscar en jackett These are magnet links stored in the Guid field. Let me use those magnet links directly with qBittorrent. busca en todos los indexers. http://127.0.0.1:9117/api/v2.0/indexers/all/
para buscar algo:
$ curl -s “http://127.0.0.1:9117/api/v2.0/indexers/all/results?Query=formula+1&apikey=xxxx” 2>&1 | jq ‘.’ 2>/dev/null | head -80
Y luego escribime un script de bash para automatizar todo esto.
Si se pone estúpido reinicialo.
0magnet: Needs FlareSolverr 1337x: Needs FlareSolverr 52bt: Needs FlareSolverr
El script que me generó opencode todavía tengo que arreglarlo cuando lo tenga, lo publico acá.
#!/usr/bin/env bash
# =============================================================================
# torrent_streamer.sh — "El nuevo Ares"
# https://imlauera.github.io/post/el_nuevo_ares/
#
# Uso:
# ./torrent_streamer.sh "Jason vs Freddy"
# ./torrent_streamer.sh --reset
# ./torrent_streamer.sh --setup
# =============================================================================
# IMPORTANTE: NO usar set -e porque grep/jq/curl devuelven exit!=0 legítimamente
set -uo pipefail
RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m'
CYAN='\033[0;36m'; MAGENTA='\033[0;35m'; BOLD='\033[1m'; RESET='\033[0m'
info() { echo -e "${CYAN}[INFO]${RESET} $*"; }
ok() { echo -e "${GREEN}[OK]${RESET} $*"; }
warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; }
step() { echo -e "\n${MAGENTA}[STEP]${RESET} ${BOLD}$*${RESET}"; }
error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
die() { error "$*"; exit 1; }
# ─── Config ──────────────────────────────────────────────────────────────────
QBT_HOST="http://127.0.0.1:8080"
QBT_USER="admin"
QBT_PASS="adminadmin"
QBT_COOKIES="/tmp/qbt_cookies.txt"
QBT_CONF="$HOME/.config/qBittorrent/qBittorrent.conf"
QBT_LOG="/tmp/qbt.log"
JACKETT_HOST="http://127.0.0.1:9117"
JACKETT_ENGINES_DIR="$HOME/.local/share/qBittorrent/nova3/engines"
JACKETT_KEY_FILE="$JACKETT_ENGINES_DIR/jackett.json"
JACKETT_PLUGIN_URL="https://raw.githubusercontent.com/qbittorrent/search-plugins/master/nova3/engines/jackett.py"
# Directorio de descarga: tiene que ser escribible por el usuario 'qbt' (systemd)
# /var/lib/qbittorrent es el home del usuario qbt → siempre tiene permisos
DOWNLOAD_DIR="/var/lib/qbittorrent/Downloads"
# Si preferís descargar en tu home, corré:
# sudo chown -R qbt:qbt ~/Downloads/torrents
# DOWNLOAD_DIR="$HOME/Downloads/torrents"
FLARESOLVERR_INDEXERS=("0magnet" "1337x" "52bt")
# ─── Args ────────────────────────────────────────────────────────────────────
MODE="stream"
QUERY="Jason vs Freddy"
usage() {
echo -e "${BOLD}Uso:${RESET}"
echo " $0 \"nombre\" Buscar y reproducir"
echo " $0 --setup Instalar y configurar todo"
echo " $0 --reset Reiniciar cuando algo falla"
echo " $0 --help"
exit 0
}
for arg in "$@"; do
case "$arg" in
--help|-h) usage ;;
--reset) MODE="reset" ;;
--setup) MODE="setup" ;;
*) QUERY="$arg" ;;
esac
done
banner() {
echo -e "\n${BOLD}${CYAN}╔══════════════════════════════════════════════╗${RESET}"
echo -e "${BOLD}${CYAN}║ 🎬 El Nuevo Ares — Torrent Streamer ║${RESET}"
echo -e "${BOLD}${CYAN}║ qBittorrent-nox + Jackett + mpv ║${RESET}"
echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════════╝${RESET}"
echo -e " ${CYAN}https://imlauera.github.io/post/el_nuevo_ares/${RESET}\n"
echo -e "${YELLOW}╔══════════════════════════════════════════════╗${RESET}"
echo -e "${YELLOW}║ ⚠ REQUISITO PREVIO: configurar Jackett ║${RESET}"
echo -e "${YELLOW}╠══════════════════════════════════════════════╣${RESET}"
echo -e "${YELLOW}║${RESET} 1. Abrí ${CYAN}http://localhost:9117${RESET}"
echo -e "${YELLOW}║${RESET} 2. Hacé click en ${BOLD}"+ Add indexer"${RESET}"
echo -e "${YELLOW}║${RESET} 3. Marcá los indexers que querés usar"
echo -e "${YELLOW}║${RESET} (cada fuente = un indexer, ej: 1337x,"
echo -e "${YELLOW}║${RESET} YTS, EZTV, Knaben, TorrentGalaxy...)"
echo -e "${YELLOW}║${RESET} 4. Hacé click en ${BOLD}"Add selected"${RESET} y listo"
echo -e "${YELLOW}║${RESET} ${RED}Sin indexers configurados no hay resultados${RESET}"
echo -e "${YELLOW}╚══════════════════════════════════════════════╝${RESET}\n"
}
# ─── Reset ───────────────────────────────────────────────────────────────────
do_reset() {
step "Reiniciando todo..."
pkill -f qbittorrent-nox 2>/dev/null && ok "qBittorrent-nox detenido." || info "No había proceso activo."
rm -f "$QBT_COOKIES" "$QBT_LOG"
sudo systemctl restart jackett 2>/dev/null && ok "Jackett reiniciado." || warn "No se pudo reiniciar Jackett."
rm -f /tmp/qbt_*.txt /tmp/qbt*.log
ok "Reset completo. Corré el script nuevamente."
exit 0
}
# ─── Setup ───────────────────────────────────────────────────────────────────
do_setup() {
step "Instalando dependencias..."
if command -v apt &>/dev/null; then
sudo apt update -qq && sudo apt install -y qbittorrent-nox jackett curl jq mpv python3
elif command -v yay &>/dev/null; then
yay -S --noconfirm qbittorrent-nox jackett-bin curl jq mpv
elif command -v pacman &>/dev/null; then
sudo pacman -Sy --noconfirm qbittorrent-nox curl jq mpv
warn "Instalá jackett: yay -S jackett-bin"
else
warn "Instalá manualmente: qbittorrent-nox jackett curl jq mpv"
fi
step "Configurando jackett.json..."
setup_jackett_json
step "Descargando plugin jackett.py..."
mkdir -p "$JACKETT_ENGINES_DIR"
if curl -sL "$JACKETT_PLUGIN_URL" -o "$JACKETT_ENGINES_DIR/jackett.py"; then
ok "Plugin descargado: $JACKETT_ENGINES_DIR/jackett.py"
else
warn "No se pudo descargar. URL: $JACKETT_PLUGIN_URL"
fi
step "Configurando qBittorrent..."
configure_qbt
echo ""
ok "Setup completo."
echo -e "\n ${BOLD}Próximos pasos:${RESET}"
echo -e " 1. Abrí Jackett en ${CYAN}http://localhost:9117${RESET}"
echo -e " 2. Copiá tu API key y pegala en: ${CYAN}$JACKETT_KEY_FILE${RESET}"
echo -e " 3. Corré: ${BOLD}./torrent_streamer.sh \"nombre pelicula\"${RESET}"
echo ""
warn "Indexers que necesitan FlareSolverr: ${FLARESOLVERR_INDEXERS[*]}"
exit 0
}
# ─── Deps ────────────────────────────────────────────────────────────────────
check_deps() {
step "Verificando dependencias..."
local missing=0
for cmd in qbittorrent-nox curl jq mpv python3; do
if command -v "$cmd" &>/dev/null; then
ok " ✓ $cmd"
else
warn " ✗ '$cmd' no encontrado."
missing=1
fi
done
# Jackett: puede estar en /usr/lib/jackett/ sin binario en PATH (AUR jackett-bin)
local jackett_found=0
command -v jackett &>/dev/null && jackett_found=1
[[ -f /usr/lib/jackett/jackett ]] && jackett_found=1
[[ -f /usr/lib/jackett/Jackett ]] && jackett_found=1
systemctl list-unit-files --type=service 2>/dev/null | grep -qi jackett && jackett_found=1
if [[ $jackett_found -eq 1 ]]; then
ok " ✓ jackett"
else
warn " ✗ jackett no encontrado. En Arch: yay -S jackett-bin | En Debian: apt install jackett"
missing=1
fi
if [[ $missing -eq 1 ]]; then
die "Faltan dependencias. Corré: $0 --setup"
fi
}
# ─── jackett.json ────────────────────────────────────────────────────────────
setup_jackett_json() {
mkdir -p "$JACKETT_ENGINES_DIR"
if [[ ! -f "$JACKETT_KEY_FILE" ]]; then
info "Creando $JACKETT_KEY_FILE..."
cat > "$JACKETT_KEY_FILE" <<'EOF'
{
"api_key": "YOUR_API_KEY_HERE",
"url": "http://127.0.0.1:9117",
"tracker_first": false,
"thread_count": 20
}
EOF
warn "IMPORTANTE: Editá $JACKETT_KEY_FILE con tu API key de http://localhost:9117"
else
# Completar campos faltantes (campos del artículo)
local updated
updated=$(jq '. + {
"url": (.url // "http://127.0.0.1:9117"),
"tracker_first": (.tracker_first // false),
"thread_count": (.thread_count // 20)
}' "$JACKETT_KEY_FILE" 2>/dev/null) || true
if [[ -n "$updated" ]]; then
echo "$updated" > "$JACKETT_KEY_FILE"
ok "jackett.json verificado."
fi
fi
}
# ─── qBittorrent.conf ────────────────────────────────────────────────────────
configure_qbt() {
info "Configurando qBittorrent.conf..."
mkdir -p "$(dirname "$QBT_CONF")"
local PASS_LINE
PASS_LINE='WebUI\Password_PBKDF2="@ByteArray(ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ==)"'
if [[ ! -f "$QBT_CONF" ]]; then
cat > "$QBT_CONF" <<EOF
[Preferences]
${PASS_LINE}
WebUI\Port=8080
WebUI\Username=admin
WebUI\LocalHostAuth=false
EOF
ok "qBittorrent.conf creado."
return
fi
# grep devuelve 1 si no encuentra → no usar set -e aquí
if grep -qF 'Password_PBKDF2' "$QBT_CONF" 2>/dev/null; then
ok "Contraseña ya configurada en qBittorrent.conf."
elif grep -q '^\[Preferences\]' "$QBT_CONF" 2>/dev/null; then
sed -i "/^\[Preferences\]/a ${PASS_LINE}" "$QBT_CONF"
ok "Contraseña insertada en [Preferences]."
else
printf '\n[Preferences]\n%s\n' "$PASS_LINE" >> "$QBT_CONF"
ok "Sección [Preferences] creada con contraseña."
fi
}
# ─── qBittorrent-nox ─────────────────────────────────────────────────────────
# El servicio systemd corre como usuario 'qbt', con su propia config en
# /var/lib/qbittorrent/.config/qBittorrent/qBittorrent.conf
# La contraseña debe estar ahí, no en $HOME del usuario actual.
QBT_CONF_SYS="/var/lib/qbittorrent/.config/qBittorrent/qBittorrent.conf"
configure_qbt_system() {
# Usar single quotes para evitar problemas con los caracteres especiales
local PASS_LINE
PASS_LINE='WebUI\Password_PBKDF2="@ByteArray(ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ==)"'
if [[ ! -f "$QBT_CONF_SYS" ]]; then
info "Creando config del servicio en $QBT_CONF_SYS..."
sudo mkdir -p "$(dirname "$QBT_CONF_SYS")"
printf '[Preferences]\n%s\nWebUI\\Port=8080\nWebUI\\Username=admin\nWebUI\\LocalHostAuth=false\n' \
"$PASS_LINE" | sudo tee "$QBT_CONF_SYS" > /dev/null
sudo chown -R qbt:qbt "$(dirname "$QBT_CONF_SYS")" 2>/dev/null || true
ok "Config del servicio creada en $QBT_CONF_SYS."
elif sudo grep -qF 'Password_PBKDF2' "$QBT_CONF_SYS" 2>/dev/null; then
ok "Contraseña ya configurada en config del servicio."
else
sudo sed -i "/^\[Preferences\]/a ${PASS_LINE}" "$QBT_CONF_SYS" 2>/dev/null \
&& ok "Contraseña insertada en config del servicio." \
|| warn "No se pudo escribir en $QBT_CONF_SYS. La contraseña será temporal (se leerá del journal)."
fi
}
start_qbt() {
if curl -sf "$QBT_HOST/api/v2/app/version" &>/dev/null; then
ok "qBittorrent-nox ya está corriendo."
return
fi
# Configurar la contraseña en la config del servicio (usuario qbt)
configure_qbt_system
# Crear directorio de descarga con permisos para el usuario qbt
sudo mkdir -p "$DOWNLOAD_DIR"
sudo chown qbt:qbt "$DOWNLOAD_DIR" 2>/dev/null || true
sudo chmod 775 "$DOWNLOAD_DIR" 2>/dev/null || true
# Agregar tu usuario al grupo qbt para que también puedas leer los archivos
sudo usermod -aG qbt "$USER" 2>/dev/null || true
info "Iniciando qBittorrent-nox via systemd..."
if sudo systemctl start qbittorrent-nox; then
ok "Comando systemctl start ejecutado."
else
die "sudo systemctl start qbittorrent-nox falló. Revisá el servicio."
fi
info "Esperando que la WebUI responda en $QBT_HOST..."
local tries=0
while true; do
if curl -sf "$QBT_HOST/api/v2/app/version" &>/dev/null; then
ok "qBittorrent-nox corriendo."
break
fi
tries=$(( tries + 1 ))
if [[ $tries -ge 30 ]]; then
error "No responde tras 30s. Status:"
sudo systemctl status qbittorrent-nox --no-pager -l >&2
die "qBittorrent-nox no responde en $QBT_HOST"
fi
sleep 1
done
}
# ─── Login ───────────────────────────────────────────────────────────────────
login_qbt() {
info "Login en qBittorrent WebUI (user: $QBT_USER / pass: $QBT_PASS)..."
local result
result=$(curl -sf -X POST "$QBT_HOST/api/v2/auth/login" \
-d "username=${QBT_USER}&password=${QBT_PASS}" \
-c "$QBT_COOKIES" 2>/dev/null) || result=""
case "$result" in
"Ok.")
ok "Login exitoso."
;;
"Fails.")
# El servicio systemd puede haber generado una contraseña temporal.
# Leerla del journal y reintentar.
warn "Contraseña incorrecta. Buscando contraseña temporal en el journal..."
local tmp_pass
tmp_pass=$(sudo journalctl -u qbittorrent-nox -n 50 --no-pager 2>/dev/null \
| grep -oP "temporary password is provided for this session: \K\S+" | tail -1)
if [[ -n "$tmp_pass" ]]; then
warn "Contraseña temporal encontrada: $tmp_pass"
warn "Para evitar esto en el futuro, el script intentará fijarla via API..."
result=$(curl -sf -X POST "$QBT_HOST/api/v2/auth/login" \
-d "username=admin&password=${tmp_pass}" \
-c "$QBT_COOKIES" 2>/dev/null) || result=""
if [[ "$result" == "Ok." ]]; then
ok "Login con contraseña temporal exitoso."
# Fijar la contraseña adminadmin para la próxima vez
curl -sf -X POST "$QBT_HOST/api/v2/app/setPreferences" \
-b "$QBT_COOKIES" \
-d "json={"web_ui_password":"adminadmin"}" &>/dev/null || true
ok "Contraseña fijada a 'adminadmin' para próximas sesiones."
else
die "Login fallido incluso con contraseña temporal. Corré --reset."
fi
else
die "Login fallido y no se encontró contraseña temporal. Corré --reset."
fi
;;
*)
warn "Respuesta login: '${result:-vacía}' (asumiendo sesión activa)."
;;
esac
}
# ─── Jackett ─────────────────────────────────────────────────────────────────
start_jackett() {
info "Iniciando Jackett (systemd)..."
if systemctl is-active --quiet jackett 2>/dev/null; then
ok "Jackett ya está activo."
else
sudo systemctl start jackett 2>/dev/null \
&& ok "Jackett iniciado." \
|| warn "No se pudo iniciar. ¿Instalado? (yay -S jackett-bin)"
fi
info "Esperando que Jackett responda en $JACKETT_HOST..."
local tries=0
while true; do
if curl -sf "$JACKETT_HOST" &>/dev/null; then
ok "Jackett disponible."
break
fi
tries=$(( tries + 1 ))
[[ $tries -ge 25 ]] && die "Jackett no responde en $JACKETT_HOST tras 25s."
sleep 1
done
}
get_jackett_key() {
setup_jackett_json
JACKETT_API_KEY=$(jq -r '.api_key // empty' "$JACKETT_KEY_FILE" 2>/dev/null) || JACKETT_API_KEY=""
if [[ -z "$JACKETT_API_KEY" || "$JACKETT_API_KEY" == "YOUR_API_KEY_HERE" ]]; then
die "API key no configurada. Editá $JACKETT_KEY_FILE con la key de http://localhost:9117"
fi
ok "API key lista."
}
warn_flaresolverr() {
echo ""
warn "Si no hay resultados, algunos indexers necesitan FlareSolverr: ${FLARESOLVERR_INDEXERS[*]}"
echo ""
}
# ─── Búsqueda ────────────────────────────────────────────────────────────────
search_torrent() {
local query_encoded
query_encoded=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$QUERY")
info "Buscando '${BOLD}${QUERY}${RESET}' en todos los indexers de Jackett..."
SEARCH_RESULTS=$(curl -sf \
"${JACKETT_HOST}/api/v2.0/indexers/all/results?Query=${query_encoded}&apikey=${JACKETT_API_KEY}" \
2>/dev/null) || SEARCH_RESULTS=""
if [[ -z "$SEARCH_RESULTS" ]]; then
warn_flaresolverr
die "Sin respuesta de Jackett. ¿API key correcta? ¿Jackett corriendo?"
fi
local total
total=$(echo "$SEARCH_RESULTS" | jq '.Results | length' 2>/dev/null) || total=0
if [[ "$total" -eq 0 ]]; then
warn_flaresolverr
die "Sin resultados para '${QUERY}'. Agregá más indexers en http://localhost:9117"
fi
info "Resultados encontrados: ${BOLD}$total${RESET}"
echo ""
info "Top 5 por seeds:"
echo "$SEARCH_RESULTS" | jq -r '
.Results
| map(select(.Seeders > 0))
| sort_by(-.Seeders)
| .[0:5][]
| " \(.Seeders) seeds | \(.Title[:65])"
' 2>/dev/null || true
echo ""
}
# ─── Filtrar ─────────────────────────────────────────────────────────────────
pick_torrent() {
info "Seleccionando: menor calidad + más seeds + magnet link..."
# Intento 1: sin 4K/1080p
MAGNET=$(echo "$SEARCH_RESULTS" | jq -r '
.Results
| map(select(
.Seeders > 0 and
(.Guid | startswith("magnet:?")) and
(.Title | test("4K|2160p|UHD|1080p|FHD|REMUX"; "i") | not)
))
| sort_by(-.Seeders)
| .[0].Guid // empty
' 2>/dev/null) || MAGNET=""
# Intento 2: sin 4K solamente
if [[ -z "$MAGNET" || "$MAGNET" == "null" ]]; then
warn "No hay baja calidad. Probando 1080p..."
MAGNET=$(echo "$SEARCH_RESULTS" | jq -r '
.Results
| map(select(
.Seeders > 0 and
(.Guid | startswith("magnet:?")) and
(.Title | test("4K|2160p|UHD"; "i") | not)
))
| sort_by(-.Seeders)
| .[0].Guid // empty
' 2>/dev/null) || MAGNET=""
fi
# Intento 3: cualquier magnet con seeds
if [[ -z "$MAGNET" || "$MAGNET" == "null" ]]; then
warn "Usando cualquier calidad disponible..."
MAGNET=$(echo "$SEARCH_RESULTS" | jq -r '
.Results
| map(select(.Seeders > 0 and (.Guid | startswith("magnet:?"))))
| sort_by(-.Seeders)
| .[0].Guid // empty
' 2>/dev/null) || MAGNET=""
fi
if [[ -z "$MAGNET" || "$MAGNET" == "null" ]]; then
warn_flaresolverr
die "No se encontró magnet link. Revisá los indexers activos en Jackett."
fi
local title seeders
title=$(echo "$SEARCH_RESULTS" | jq -r --arg g "$MAGNET" \
'.Results | map(select(.Guid == $g)) | .[0].Title // "desconocido"' 2>/dev/null) || title="?"
seeders=$(echo "$SEARCH_RESULTS" | jq -r --arg g "$MAGNET" \
'.Results | map(select(.Guid == $g)) | .[0].Seeders // 0' 2>/dev/null) || seeders="?"
ok "Elegido: ${BOLD}${title}${RESET}"
ok "Seeds: ${BOLD}${seeders}${RESET}"
info "Magnet: ${MAGNET:0:90}..."
}
# ─── Agregar torrent ─────────────────────────────────────────────────────────
add_torrent() {
info "Agregando torrent a qBittorrent-nox..."
local resp
resp=$(curl -sf -X POST "$QBT_HOST/api/v2/torrents/add" \
-b "$QBT_COOKIES" \
--data-urlencode "urls=$MAGNET" \
-d "savepath=$DOWNLOAD_DIR" \
-d "category=streamed" \
-d "sequentialDownload=true" \
-d "firstLastPiecePrio=true" \
2>/dev/null) || resp="(sin respuesta)"
ok "qBittorrent: ${resp}"
info "Descargando en: $DOWNLOAD_DIR"
}
# ─── Esperar y reproducir ────────────────────────────────────────────────────
# ─── Esperar y reproducir ────────────────────────────────────────────────────
wait_and_play() {
info "Esperando datos para reproducir... (Ctrl+C para cancelar)"
# Extraer hash del magnet para consultar qBittorrent directamente
local torrent_hash state="" found_file="" tries=0 max_wait=360 min_size=52428800 metadl_count=0
torrent_hash=$(echo "$MAGNET" | grep -oE 'btih:[a-fA-F0-9]+' | cut -d: -f2 | tr '[:upper:]' '[:lower:]')
info "Hash: $torrent_hash"
while [[ $tries -lt $max_wait ]]; do
# Consultar estado y path via API — funciona aunque ya esté descargado
local torrent_info content_path save_path
torrent_info=$(curl -sf "$QBT_HOST/api/v2/torrents/info?hashes=$torrent_hash" \
-b "$QBT_COOKIES" 2>/dev/null) || torrent_info=""
if [[ -n "$torrent_info" && "$torrent_info" != "[]" ]]; then
state=$(echo "$torrent_info" | jq -r '.[0].state // empty' 2>/dev/null) || state=""
content_path=$(echo "$torrent_info" | jq -r '.[0].content_path // empty' 2>/dev/null) || content_path=""
save_path=$(echo "$torrent_info" | jq -r '.[0].save_path // empty' 2>/dev/null) || save_path=""
local pct
pct=$(echo "$torrent_info" | jq -r '.[0].progress // 0' 2>/dev/null) || pct=0
pct=$(awk "BEGIN{printf \"%.1f\", $pct * 100}" 2>/dev/null) || pct="?"
info "Estado: ${state:-?} | Progreso: ${pct}%"
# metaDL por más de 30s = sin seeds reales → cancelar y reintentar
if [[ "$state" == "metaDL" ]]; then
metadl_count=$(( metadl_count + 1 ))
if [[ $metadl_count -ge 6 ]]; then
warn "Sin seeds después de 30s (metaDL). Cancelando este torrent..."
curl -sf -X POST "$QBT_HOST/api/v2/torrents/delete" -b "$QBT_COOKIES" -d "hashes=$torrent_hash&deleteFiles=false" &>/dev/null || true
warn "Buscando otro resultado con más seeds..."
# Reintentar pick_torrent excluyendo este magnet
SEARCH_RESULTS=$(echo "$SEARCH_RESULTS" | jq --arg g "$MAGNET" '[.Results[] | select(.Guid != $g)]' | jq '{Results: .}' 2>/dev/null) || true
pick_torrent
add_torrent
# Reiniciar contadores
torrent_hash=$(echo "$MAGNET" | grep -oE 'btih:[a-fA-F0-9]+' | cut -d: -f2 | tr '[:upper:]' '[:lower:]')
metadl_count=0
tries=0
info "Nuevo hash: $torrent_hash"
fi
else
metadl_count=0
fi
fi
# Encontrar el archivo de video: content_path puede ser fichero o carpeta
if [[ -n "$content_path" && -e "$content_path" ]]; then
if [[ -d "$content_path" ]]; then
found_file=$(find "$content_path" -type f \
\( -iname "*.mkv" -o -iname "*.mp4" -o -iname "*.avi" \
-o -iname "*.mov" -o -iname "*.webm" -o -iname "*.ts" \
-o -iname "*.m4v" -o -iname "*.m2ts" \) \
2>/dev/null | while read -r f; do
stat -c "%s %n" "$f" 2>/dev/null
done | sort -rn | head -1 | cut -d' ' -f2-) || found_file=""
else
# Verificar que sea video por extensión
case "${content_path,,}" in
*.mkv|*.mp4|*.avi|*.mov|*.webm|*.ts|*.m4v|*.m2ts)
found_file="$content_path" ;;
esac
fi
fi
# Fallback: buscar el video más grande en DOWNLOAD_DIR (sin filtro de fecha)
if [[ -z "$found_file" && -d "$DOWNLOAD_DIR" ]]; then
found_file=$(find "$DOWNLOAD_DIR" -type f \
\( -iname "*.mkv" -o -iname "*.mp4" -o -iname "*.avi" \
-o -iname "*.mov" -o -iname "*.webm" -o -iname "*.ts" \
-o -iname "*.m4v" -o -iname "*.m2ts" \) \
2>/dev/null | while read -r f; do
stat -c "%s %n" "$f" 2>/dev/null
done | sort -rn | head -1 | cut -d' ' -f2-) || found_file=""
fi
if [[ -n "$found_file" && -f "$found_file" ]]; then
local size size_mb
size=$(stat -c%s "$found_file" 2>/dev/null) || size=0
size_mb=$(( size / 1048576 ))
# Si ya terminó de bajar → reproducir de inmediato
case "$state" in
uploading|stalledUP|pausedUP|forcedUP|checkingUP|completed)
ok "Descarga completa (${size_mb} MB) — reproduciendo."
break ;;
esac
# Si está bajando y tiene suficientes datos para bufferear
if [[ $size -gt $min_size ]]; then
ok "Suficientes datos (${size_mb} MB) — reproduciendo."
break
fi
info "Descargando... ${size_mb} MB (mínimo 50 MB para reproducir)"
else
info "Esperando inicio de descarga... (${tries}s)"
fi
sleep 5
tries=$(( tries + 5 ))
done
if [[ -z "$found_file" || ! -f "$found_file" ]]; then
warn "No se encontró archivo de video."
warn "Buscá en: $DOWNLOAD_DIR"
warn "O en la WebUI: $QBT_HOST"
exit 0
fi
echo ""
ok "▶ Reproduciendo: $(basename "$found_file")"
mpv \
--cache=yes \
--cache-secs=120 \
--demuxer-readahead-secs=60 \
--force-seekable=yes \
--title="$(basename "$found_file")" \
"$found_file"
}
# ═══════════════════════════════════════════════════════════════════════════════
banner
case "$MODE" in
reset) do_reset ;;
setup) do_setup ;;
stream)
info "Búsqueda: '${BOLD}${QUERY}${RESET}'"
echo ""
check_deps
configure_qbt
start_qbt
login_qbt
start_jackett
get_jackett_key
search_torrent
pick_torrent
add_torrent
wait_and_play
echo ""
ok "¡Disfrutá la película! 🍿"
;;
esac
Los torrents se guardan en /var/lib/qbittorrent/Downloads
Despues de que Claude AI me genere ese script fenomenal le pedi esto:
Me encanto, aun asi cuando abria el video con mpv parece que abria el directorio entero en vez de abrir el video que habia descargado, fuera de eso que no es tanta complicacion. Lo que me gustaria es que me de a elegir ahora quiero que sea mas como un buscador pero ordenalos por los que tienen mas seeds primero y que sean recientes, y eso de 720 o 1080 no lo tomes en consideracion porque a veces yo quiero descargar archivos o audios que no son videos. No se si me entendes, ahora quiero algo mas como un buscador eficiente quiero descargar algo que tenga muchos seeds sino no funciona y quiero poder elegir que me de una lista de torrents y yo elijo cual quiero descargar.