Cortesía de opencode XD.
#!/bin/bash
USERNAME="${1:-}"
OUTPUT_DIR="${2:-./instagram_stories}"
if [ -z "$USERNAME" ]; then
echo "Uso: $0 <nombre_usuario> [directorio_salida]"
exit 1
fi
URL="https://iqsaved.com/viewer/$USERNAME"
echo "Descargando historias de: $URL"
mkdir -p "$OUTPUT_DIR"
cd "$OUTPUT_DIR"
echo "Iniciando chromium..."
chromium --headless=new --disable-gpu --no-sandbox --remote-debugging-port=9222 "$URL" > /dev/null 2>&1 &
CHROME_PID=$!
sleep 6
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"
WS_DIR="/tmp/ws_cdp"
mkdir -p "$WS_DIR"
cd "$WS_DIR"
npm init -y > /dev/null 2>&1
npm install ws > /dev/null 2>&1
cat > "$WS_DIR/get_stories.js" << 'SCRIPT_EOF'
const WebSocket = require('ws');
const ws = new WebSocket(process.argv[2]);
let cmdId = 0;
function send(method, params) {
return new Promise((resolve) => {
const id = ++cmdId;
ws.send(JSON.stringify({id, method, params}));
ws.once('message', (data) => resolve(JSON.parse(data)));
});
}
ws.on('open', async function() {
console.error('Cargando pagina...');
await new Promise(r => setTimeout(r, 5000));
console.error('Haciendo scroll...');
for (let i = 0; i < 20; i++) {
await send('Runtime.evaluate', {
expression: 'window.scrollBy(0, 500);'
});
await new Promise(r => setTimeout(r, 500));
}
console.error('Extrayendo imagenes y videos...');
const r = await send('Runtime.evaluate', {
expression: 'var imgs = document.querySelectorAll("img"); var results = []; for (var i = 0; i < imgs.length; i++) { var src = imgs[i].dataset.src || imgs[i].src; if (src && src.indexOf("cdn.iqsaved") > -1) { results.push({href: src, type: "img"}); } } var links = document.querySelectorAll("a[href*=\\"cdn.iqsaved\\"]"); for (var j = 0; j < links.length; j++) { if (links[j].href.indexOf(".mp4") > -1) { results.push({href: links[j].href, type: "video"}); } } JSON.stringify(results)'
});
if (r.result && r.result.result) {
console.log(r.result.result.value);
}
ws.close();
});
ws.on('error', function(e) {
console.error('Error:', e.message);
process.exit(1);
});
SCRIPT_EOF
JSON_RESULT=$(cd "$WS_DIR" && node get_stories.js "$WS_URL" 2>/dev/null)
kill $CHROME_PID 2>/dev/null
cd "$OUTPUT_DIR"
echo "$JSON_RESULT" | python3 -c "
import sys, json, subprocess, os, urllib.request
try:
data = json.load(sys.stdin)
downloads = data if isinstance(data, list) else 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']
item_type = item.get('type', '')
if item_type == 'video' or '.mp4' in url:
ext = 'mp4'
else:
try:
req = urllib.request.Request(url, method='HEAD')
req.add_header('User-Agent', 'Mozilla/5.0')
resp = urllib.request.urlopen(req, timeout=10)
content_type = resp.headers.get('Content-Type', '').lower()
if 'jpeg' in content_type or 'jpg' in content_type:
ext = 'jpg'
elif 'png' in content_type:
ext = 'png'
elif 'video' in content_type or 'mp4' in content_type:
ext = 'mp4'
else:
ext = 'bin'
except:
ext = 'jpg'
outfile = f'{i}.{ext}'
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', '-U', 'Mozilla/5.0', '-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