Cortesía de opencode XD.
#!/bin/bash
# Descargar historias de Instagram desde iqsaved.com usando chromium headless
USERNAME="${1:-}"
OUTPUT_DIR="${2:-./instagram_stories}"
if [ -z "$USERNAME" ]; then
echo "Uso: $0 <nombre_usuario> [directorio_salida]"
echo "Ejemplo: $0 male_leoncini /tmp/mis_historias"
exit 1
fi
URL="https://iqsaved.com/viewer/$USERNAME"
echo "Descargando historias de: $URL"
mkdir -p "$OUTPUT_DIR"
cd "$OUTPUT_DIR"
# Iniciar chromium con modo headless y debug remoto
echo "Iniciando chromium..."
chromium --headless --disable-gpu --no-sandbox --remote-debugging-port=9222 "$URL" > /dev/null 2>&1 &
CHROME_PID=$!
# Esperar a que chrome este listo
sleep 4
# Obtener WebSocket URL de la pagina
WS_URL=$(curl -s http://localhost:9222/json | python3 -c "
import sys, json
pages = json.load(sys.stdin)
target = [p for p in pages if '$USERNAME' in p.get('url','') or 'iqsaved' in p.get('url','')]
if target:
print(target[0]['webSocketDebuggerUrl'])
" 2>/dev/null)
if [ -z "$WS_URL" ]; then
echo "Error: No se pudo obtener WebSocket URL"
kill $CHROME_PID 2>/dev/null
exit 1
fi
echo "WebSocket: $WS_URL"
# Instalar ws si no existe
WS_DIR="/tmp/ws_cdp"
if [ ! -d "$WS_DIR" ]; then
mkdir -p "$WS_DIR"
cd "$WS_DIR"
npm init -y > /dev/null 2>&1
npm install ws > /dev/null 2>&1
fi
# Crear script para extraer URLs
cat > "$WS_DIR/get_stories.js" << 'SCRIPT_EOF'
const WebSocket = require('ws');
const ws = new WebSocket(process.argv[2]);
let cmdId = 0;
const send = (method, params={}) => {
return new Promise((resolve) => {
const id = ++cmdId;
ws.send(JSON.stringify({id, method, params}));
ws.once('message', (msg) => {
const data = JSON.parse(msg);
resolve(data.result || data);
});
});
};
ws.on('open', async () => {
console.error('Esperando que carguen las historias (12s)...');
await new Promise(r => setTimeout(r, 12000));
const r = await send('Runtime.evaluate', {
expression: `JSON.stringify({
downloads: [...document.querySelectorAll('a[download],a[href$=".mp4"],a[href$=".jpg"]')]
.map(a => ({href: a.href, text: a.getAttribute('download') || a.text.trim()}))
.filter(a => a.href.includes('cdn.iqsaved'))
})`
});
// El resultado viene anidado: r.result.value es un string JSON
const value = r.result ? r.result.value : r.value;
process.stdout.write(value);
ws.close();
});
ws.on('error', e => { console.error('Error:', e.message); process.exit(1); });
SCRIPT_EOF
# Ejecutar y extraer URLs (solo stdout, ignorar stderr)
JSON_RESULT=$(cd "$WS_DIR" && node get_stories.js "$WS_URL" 2>/dev/null)
# Cerrar chromium
kill $CHROME_PID 2>/dev/null
# Parsear y descargar
echo "$JSON_RESULT" | python3 -c "
import sys, json, subprocess, re, os
from urllib.parse import unquote
try:
data = json.load(sys.stdin)
downloads = data.get('downloads', [])
if not downloads:
print('No se encontraron descargas')
sys.exit(0)
print(f'Encontrados {len(downloads)} archivos')
for i, item in enumerate(downloads, 1):
url = item['href']
# Extraer nombre de archivo o generar uno
if 'filename=' in url:
fname = url.split('filename=')[1].split('&')[0]
fname = unquote(fname)
else:
fname = f'file_{i}'
# Determinar extension
if '.mp4' in fname:
ext = 'mp4'
elif '.jpg' in fname or '.jpeg' in fname:
ext = 'jpg'
else:
ext = 'bin'
outfile = f'{i}.{ext}'
# Skip if already exists and has content
if os.path.exists(outfile) and os.path.getsize(outfile) > 1000:
print(f'Omitiendo {outfile} (ya existe)')
continue
print(f'Descargando {outfile}...')
subprocess.run(['wget', '-q', '-O', outfile, url], check=True)
print('Descarga completa!')
except Exception as e:
print(f'Error: {e}')
sys.exit(1)
"
echo "Archivos en: $(pwd)"
ls -la