Hace rato que estoy experimentando con inteligencia artificial, empezando con ChatGPT, y luego con Stable Diffusion. Este último lo tuve instalado en la laptop que tiene unas buenas especificaciones técnicas y se banca la generación de imágenes, pero calienta que da calambre y ahora con modelos mas grandes, los 6GB de VRAM no me estaría andando muy bien que digamos.

El Servicio gratuito

Así que salí a buscar servicios gratuitos, la diferencia que estos servicios no tienen tanta libertad para hacer cosas como tenerlo de forma local, pero bueno, depende de los usos y ya no estaba experimentando con cosas muy raras, solo necesitaba generar imágenes.

Asi que ahi entra el servicio de Playground AI, un servicio completo, a pesar de sus limitaciones por usar la version gratuita, con un modelo muy bueno si sabes que instrucciones darle, pueden salir cosas muy buenas.

La cuestión que al día de hoy genere mas de 1000 imágenes, y por algún lado lei como que el servicio va a caducar o se restringe mas, la verdad no se, esto no lo tengo claro, pero a pesar de esa situación dije -Ok, quiero descargarme todas las imágenes que hice y también los prompts-, para volver a generar imágenes similares, es muy importante esto último.

El backup

Ahora, Playground no tiene una opción para descargar imágenes y prompts en bulk, osea de a monotones. Solo te deja descargar las imágenes de a 100, que previamente tenes que ir seleccionando una por una. Un fastidio realmente, de hecho lo hice, seleccione todas las imágenes que tenia y cuando le di a descargar me entero que solo puede descargar de a 100, no me puso contento eso.

Y dije bueno a ver como bajo todo esto sin sufrir la odisea de seleccionar todo de nuevo y en cuotas, y además también bajar los prompts.

El script

Entonces básicamente hice un script en JavaScript para ejecutar en la consola de Google Chrome para que lo haga por mi. A ver, no es de lo mas elegante, pero es funcional y cumple con lo que quiero.

Básicamente usando el código que carga el sitio por atrás vi que no cambiaba el codigo de la imagen y el prompt. Lo hice y anduvo hermoso.

El script descarga el archivo de imagen y un .txt con el prompt y lo nombra igual que la imagen.

luego dije, ok, tengo que darle next para que me muestre la otra imagen y volver a ejecutar el código, NO WAY JOSE.

Así que me fije si el código del botón de "siguiente" variaba en algo en cada cambio y era el mismo, así que agregue al script para que lo haga solo y que se ejecutara en loop. Y ahi si solito lo hacia.

El Código

// Función para descargar un archivo
function descargarArchivo(nombre, contenido) {
    const enlace = document.createElement("a");
    enlace.href = contenido;
    enlace.download = nombre;
    document.body.appendChild(enlace);
    enlace.click();
    document.body.removeChild(enlace);
}

// Función principal para descargar imagen y texto, y avanzar al siguiente
async function procesarImagenYTexto() {
    const imagenSelector = 'body > div.ReactModalPortal > div > div > div > div > div > div.font-pg-medium.flex.flex-col.lg\\:flex-row.pt-4.p-6.md\\:pt-16.lg\\:pt-6.gap-2.lg\\:gap-8.w-full.relative.text-white.overflow-auto.transition-all.duration-150.image-post-page > div.flex.flex-col.gap-4.flex-none.md\\:max-w-\\[min\\(60vw\\)\\] > div:nth-child(2) > img';
    const textoSelector = 'body > div.ReactModalPortal > div > div > div > div > div > div.font-pg-medium.flex.flex-col.lg\\:flex-row.pt-4.p-6.md\\:pt-16.lg\\:pt-6.gap-2.lg\\:gap-8.w-full.relative.text-white.overflow-auto.transition-all.duration-150.image-post-page > div.flex.flex-col.justify-between.md\\:pl-0.flex-grow.py-2.md\\:py-8 > div > div > div.space-y-5 > div.space-y-1 > div';
    const botonSiguienteSelector = '#__next > button.bg-gray-95.rounded-full.items-center.justify-center.w-10.h-10.text-gray-10.border.border-solid.border-gray-85.hover\\:bg-gray-90.hover\\:border-gray-80.active\\:scale-105.fixed.z-\\[1000\\].hidden.lg\\:flex.right-4.top-1\\/2.-translate-y-1\\/2';

    const imagenElemento = document.querySelector(imagenSelector);
    const textoElemento = document.querySelector(textoSelector);

    if (imagenElemento && textoElemento) {
        // Descargar imagen
        const urlImagen = imagenElemento.src;
        const nombreImagen = urlImagen.split('/').pop();
        const blob = await fetch(urlImagen).then(res => res.blob());
        const urlBlob = URL.createObjectURL(blob);
        descargarArchivo(nombreImagen, urlBlob);
        URL.revokeObjectURL(urlBlob);

        // Descargar texto
        const texto = textoElemento.innerText.replace(/\n/g, " ");
        const contenidoTxt = `data:text/plain;charset=utf-8,${encodeURIComponent(texto)}`;
        const nombreTxt = nombreImagen.replace(/\.[^/.]+$/, ".txt");
        descargarArchivo(nombreTxt, contenidoTxt);

        // Avanzar al siguiente
        const botonSiguiente = document.querySelector(botonSiguienteSelector);
        if (botonSiguiente) {
            botonSiguiente.click();
            // Esperar un tiempo para que la siguiente imagen y texto carguen
            setTimeout(procesarImagenYTexto, 2000); // Ajustá el tiempo si es necesario
        } else {
            console.log("No se encontró el botón de siguiente. Finalizó el proceso.");
        }
    } else {
        console.error("No se encontró la imagen o el texto. Finalizó el proceso.");
    }
}

// Iniciar el proceso
procesarImagenYTexto();

Lo que hay que tener en cuenta.

Detalle importante, la verdad no me fijé si hay alguna manera de hacerlo por script, así que configure el Chrome para que guarde sin preguntar, porque sino tenes que estar dándole al Enter o a la combinación de teclas Alt+S para darle al SAVE de cada archivo.

Para Windows

Si por alguna razón no querés cambiar la configuración, no veo porque no hacerlo, te dejo un script en PowerShell para simular que estas manteniendo apretado Alt+S y listo. El tema que no vas a poder sacar el foco de la ventana de guardado.

# Cargar el espacio de nombres necesario
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;

public class KeyboardSimulator {
    [DllImport("user32.dll", SetLastError = true)]
    public static extern short GetAsyncKeyState(int vKey);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern int keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);

    // Códigos de las teclas en el teclado (alt y s)
    public const int VK_MENU = 0x12; // ALT
    public const int VK_S = 0x53;    // S

    // Definir las acciones para simular la presionar las teclas
    public const int KEYEVENTF_KEYDOWN = 0x0000;
    public const int KEYEVENTF_KEYUP = 0x0002;

    public static void HoldAltS() {
        // Simular presionar ALT
        keybd_event(VK_MENU, 0, KEYEVENTF_KEYDOWN, 0);
        // Simular presionar S
        keybd_event(VK_S, 0, KEYEVENTF_KEYDOWN, 0);

        // Mantener las teclas presionadas por un breve intervalo
        System.Threading.Thread.Sleep(50); // 50ms de duración

        // Simular soltar S
        keybd_event(VK_S, 0, KEYEVENTF_KEYUP, 0);
        // Simular soltar ALT
        keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
    }
}
"@

# Bucle infinito para simular la pulsación de Alt + S de manera continua
while ($true) {
    [KeyboardSimulator]::HoldAltS()
    # Pausa para evitar sobrecargar la CPU, puedes ajustar este valor
    Start-Sleep -Milliseconds 50
}

Como diría Tusam "Puede fallar" pero las cosas se logran reintentando e insistiendo, así es la vida.

Éxitos!