9d3bfd2cd2
Nueva función cdp_wait_load para esperar carga completa de página. CdpConnect ahora soporta host remoto via CdpConnectHost (útil para WSL2 donde Chrome Windows escucha en IP distinta). Mejoras en chrome_launch para configuración más flexible. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
141 lines
3.8 KiB
Go
141 lines
3.8 KiB
Go
package infra
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
)
|
|
|
|
// ChromeLaunchOpts configura el lanzamiento de Chrome con CDP.
|
|
type ChromeLaunchOpts struct {
|
|
// Port es el puerto de remote debugging. Por defecto 9222.
|
|
Port int
|
|
// UserDataDir es el directorio de perfil de Chrome. Por defecto /tmp/chrome-cdp-profile.
|
|
UserDataDir string
|
|
// Headless activa el modo headless (--headless=new). Por defecto false.
|
|
Headless bool
|
|
// ChromePath es la ruta al ejecutable de Chrome. Si esta vacio, se busca automaticamente.
|
|
ChromePath string
|
|
// ExtraArgs permite pasar flags adicionales a Chrome.
|
|
ExtraArgs []string
|
|
}
|
|
|
|
// chromePaths lista los ejecutables de Chrome conocidos en WSL2/Linux.
|
|
var chromePaths = []string{
|
|
"chrome.exe",
|
|
"google-chrome",
|
|
"chromium-browser",
|
|
"chromium",
|
|
"/mnt/c/Program Files/Google/Chrome/Application/chrome.exe",
|
|
"/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
|
|
"/mnt/c/Users/Public/Desktop/chrome.exe",
|
|
}
|
|
|
|
// findChrome localiza el ejecutable de Chrome en el sistema.
|
|
func findChrome() (string, error) {
|
|
for _, p := range chromePaths {
|
|
if path, err := exec.LookPath(p); err == nil {
|
|
return path, nil
|
|
}
|
|
if _, err := os.Stat(p); err == nil {
|
|
return p, nil
|
|
}
|
|
}
|
|
return "", fmt.Errorf("chrome: ejecutable no encontrado en PATH ni en rutas conocidas de Windows")
|
|
}
|
|
|
|
// waitCDPReady espera hasta que el puerto CDP responda conexiones TCP.
|
|
// host puede estar vacio (usa "localhost").
|
|
func waitCDPReady(host string, port int, timeout time.Duration) error {
|
|
if host == "" {
|
|
host = "localhost"
|
|
}
|
|
deadline := time.Now().Add(timeout)
|
|
addr := fmt.Sprintf("%s:%d", host, port)
|
|
for time.Now().Before(deadline) {
|
|
conn, err := net.DialTimeout("tcp", addr, 200*time.Millisecond)
|
|
if err == nil {
|
|
conn.Close()
|
|
return nil
|
|
}
|
|
time.Sleep(200 * time.Millisecond)
|
|
}
|
|
return fmt.Errorf("chrome: puerto CDP %s:%d no disponible despues de %s", host, port, timeout)
|
|
}
|
|
|
|
// ChromeLaunch lanza Google Chrome con remote debugging habilitado en el puerto indicado.
|
|
// Retorna el PID del proceso Chrome. Espera hasta 15s a que el puerto CDP este listo.
|
|
// Busca chrome.exe en PATH (WSL2) o en rutas conocidas de Windows.
|
|
func ChromeLaunch(opts ChromeLaunchOpts) (int, error) {
|
|
if opts.Port == 0 {
|
|
opts.Port = 9222
|
|
}
|
|
if opts.UserDataDir == "" {
|
|
opts.UserDataDir = "/tmp/chrome-cdp-profile"
|
|
}
|
|
|
|
chromePath := opts.ChromePath
|
|
if chromePath == "" {
|
|
var err error
|
|
chromePath, err = findChrome()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
|
|
args := []string{
|
|
fmt.Sprintf("--remote-debugging-port=%d", opts.Port),
|
|
fmt.Sprintf("--user-data-dir=%s", opts.UserDataDir),
|
|
"--no-first-run",
|
|
"--no-default-browser-check",
|
|
"--disable-background-networking",
|
|
"--disable-client-side-phishing-detection",
|
|
"--disable-default-apps",
|
|
"--disable-extensions",
|
|
"--disable-hang-monitor",
|
|
"--disable-popup-blocking",
|
|
"--disable-prompt-on-repost",
|
|
"--disable-sync",
|
|
"--disable-translate",
|
|
"--metrics-recording-only",
|
|
"--safebrowsing-disable-auto-update",
|
|
}
|
|
|
|
if opts.Headless {
|
|
args = append(args, "--headless=new", "--disable-gpu")
|
|
}
|
|
|
|
args = append(args, opts.ExtraArgs...)
|
|
|
|
cmd := exec.Command(chromePath, args...)
|
|
// Chrome necesita que no haya stderr/stdout bloqueados
|
|
cmd.Stdout = nil
|
|
cmd.Stderr = nil
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
return 0, fmt.Errorf("chrome: arrancar proceso: %w", err)
|
|
}
|
|
|
|
pid := cmd.Process.Pid
|
|
|
|
// Esperar a que el puerto CDP este listo
|
|
// Si Chrome escucha en 0.0.0.0 (ej: WSL2 -> Windows), el caller se encarga del wait
|
|
skipWait := false
|
|
for _, a := range opts.ExtraArgs {
|
|
if a == "--remote-debugging-address=0.0.0.0" {
|
|
skipWait = true
|
|
break
|
|
}
|
|
}
|
|
if !skipWait {
|
|
if err := waitCDPReady("localhost", opts.Port, 15*time.Second); err != nil {
|
|
cmd.Process.Kill()
|
|
return 0, err
|
|
}
|
|
}
|
|
|
|
return pid, nil
|
|
}
|