package http import ( "runtime" "sync" "apigo.cc/gojs" "apigo.cc/gojs/goja" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/launcher" "github.com/go-rod/rod/lib/launcher/flags" "github.com/ssgo/u" ) type Chrome struct { id string chromeURL string chromePath string launcher *launcher.Launcher browser *rod.Browser } var chromes = map[string]*Chrome{} var chromesLock sync.Mutex func (ch *Chrome) Close(vm *goja.Runtime) { // logger := gojs.GetLogger(vm) if ch.browser != nil { // ver, _ := ch.browser.Version() // logger.Info("关闭Chrome浏览器", "id", ch.id, "browser", ver.Product, "userAgent", ver.UserAgent, "chromeURL", ch.chromeURL, "chromePath", ch.chromePath) ch.browser.Close() ch.browser = nil } if ch.launcher != nil { ch.launcher.Cleanup() ch.launcher = nil } chromesLock.Lock() delete(chromes, ch.id) chromesLock.Unlock() } func CloseAllChrome(vm *goja.Runtime) { n := len(chromes) if n > 0 { for _, ch := range chromes { ch.Close(vm) } logger := gojs.GetLogger(vm) logger.Info("关闭所有未主动关闭的Chrome浏览器", "count", n) } } type ChromeInfo struct { ProtocolVersion string Product string Revision string UserAgent string JsVersion string ChromeURL string ChromePath string } type ChromeOption struct { ChromeURL string ChromeOption []string ShowWindow bool ChromeHtpProxy string } func (ch *Chrome) Info(showWindow *bool, vm *goja.Runtime) (ChromeInfo, error) { ver, err := ch.browser.Version() if err != nil { return ChromeInfo{ ChromeURL: ch.chromeURL, ChromePath: ch.chromePath, }, err } return ChromeInfo{ ProtocolVersion: ver.ProtocolVersion, Product: ver.Product, Revision: ver.Revision, UserAgent: ver.UserAgent, JsVersion: ver.JsVersion, ChromeURL: ch.chromeURL, ChromePath: ch.chromePath, }, nil } func StartChrome(opt *ChromeOption, vm *goja.Runtime) (*Chrome, error) { if opt == nil { opt = &ChromeOption{} } if opt.ChromeURL == "" { opt.ChromeURL = conf.ChromeURL } if opt.ChromeHtpProxy == "" { opt.ChromeHtpProxy = conf.ChromeHtpProxy } if opt.ChromeOption == nil { opt.ChromeOption = conf.ChromeOption } // logger := gojs.GetLogger(vm) ch := &Chrome{} ch.id = u.UniqueId() ch.browser = rod.New() ch.chromeURL = opt.ChromeURL if opt.ChromeURL == "" { // 使用本地Chrome ch.launcher = launcher.New() if localBrowserPath, hasLocalBrowser := launcher.LookPath(); hasLocalBrowser { ch.launcher.Bin(localBrowserPath) ch.chromePath = localBrowserPath } // "--headless=new", "--no-sandbox", "--disable-dev-shm-usage", "--hide-scrollbars", "--font-render-hinting=none", "--disable-blink-features=AutomationControlled", "--disable-infobars", "--lang=zh-CN,zh", "--disable-extensions", "--disable-gpu", "--use-gl=swiftshader", "--ignore-gpu-blocklist", "--use-angle=swiftshader", "--disable-features=Translate" ch.launcher.Headless(!opt.ShowWindow).Set("disable-dev-shm-usage").Set("single-process").Set("disable-blink-features", "AutomationControlled") ch.launcher.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0") if opt.ChromeHtpProxy != "" { ch.launcher.Proxy(opt.ChromeHtpProxy) } switch runtime.GOOS { case "linux": ch.launcher.Set("disable-setuid-sandbox") case "windows": ch.launcher.Set("disable-features=RendererCodeIntegrity") } if opt.ChromeOption != nil { for _, opt := range opt.ChromeOption { a := u.SplitTrimN(opt, "=", 2) if len(a) == 2 { ch.launcher.Set(flags.Flag(a[0]), a[1]) } else { ch.launcher.Set(flags.Flag(opt)) } } } if localChromeURL, err := ch.launcher.Launch(); err != nil { ch.Close(vm) return nil, gojs.Err(err) } else { ch.chromeURL = localChromeURL if ch.chromePath == "" { ch.chromePath = ch.launcher.Get(flags.Bin) } } } ch.browser.ControlURL(ch.chromeURL) if err := ch.browser.Connect(); err != nil { return nil, gojs.Err(err) } // ver, _ := ch.browser.Version() // logger.Info("启动Chrome浏览器", "id", ch.id, "browser", ver.Product, "userAgent", ver.UserAgent, "chromeURL", ch.chromeURL, "chromePath", ch.chromePath) chromesLock.Lock() chromes[ch.id] = ch chromesLock.Unlock() return ch, nil }