Add custom browser path support

This commit is contained in:
Rustem Kamalov
2026-03-17 02:18:51 +03:00
parent 7cdd358cad
commit a4d459761e
5 changed files with 49 additions and 5 deletions

View File

@@ -13,7 +13,7 @@ import (
)
const (
version = "0.5.5"
version = "0.5.6"
defaultConfigFilename = "config"
envPrefix = "OPENSERP"
)
@@ -37,6 +37,7 @@ type AppConfig struct {
Port int `mapstructure:"port"`
Timeout int `mapstructure:"timeout"`
ConfigPath string `mapstructure:"config_path"`
BrowserPath string `mapstructure:"browser_path"`
IsBrowserHead bool `mapstructure:"head"`
IsLeaveHead bool `mapstructure:"leave_head"`
IsLeakless bool `mapstructure:"leakless"`
@@ -52,6 +53,7 @@ var config = Config{}
var flagToConfigKey = map[string]string{
"config": "app.config_path",
"browser-path": "app.browser_path",
"leave": "app.leave_head",
"raw": "app.raw_requests",
"2captcha_key": "2captcha.apikey",
@@ -163,6 +165,7 @@ func init() {
RootCmd.PersistentFlags().StringVarP(&config.App.Host, "host", "a", "127.0.0.1", "Host address to run server")
RootCmd.PersistentFlags().IntVarP(&config.App.Timeout, "timeout", "t", 30, "Timeout to fail request")
RootCmd.PersistentFlags().StringVarP(&config.App.ConfigPath, "config", "c", "", "Configuration file path")
RootCmd.PersistentFlags().StringVarP(&config.App.BrowserPath, "browser-path", "", "", "Custom browser binary path (Chrome/Chromium/Edge/Brave..)")
RootCmd.PersistentFlags().BoolVarP(&config.App.IsVerbose, "verbose", "v", false, "Use verbose output")
RootCmd.PersistentFlags().BoolVarP(&config.App.IsDebug, "debug", "d", false, "Use debug output. Disable headless browser")
RootCmd.PersistentFlags().BoolVarP(&config.App.IsBrowserHead, "head", "", false, "Enable browser UI")

View File

@@ -70,6 +70,7 @@ func searchBrowser(engineType string, query core.Query) ([]core.SearchResult, er
Timeout: time.Second * time.Duration(config.App.Timeout),
LeavePageOpen: config.App.IsLeaveHead,
CaptchaSolverApiKey: config.Config2Capcha.ApiKey,
BrowserPath: config.App.BrowserPath,
ProxyURL: config.App.ProxyURL,
Insecure: config.App.Insecure,
UseStealth: config.App.IsStealth,

View File

@@ -80,6 +80,7 @@ func serve(cmd *cobra.Command, args []string) {
Timeout: time.Second * time.Duration(config.App.Timeout),
LeavePageOpen: config.App.IsLeaveHead,
CaptchaSolverApiKey: config.Config2Capcha.ApiKey,
BrowserPath: config.App.BrowserPath,
ProxyURL: config.App.ProxyURL,
Insecure: config.App.Insecure,
UseStealth: config.App.IsStealth,

View File

@@ -10,6 +10,9 @@ app:
stealth: false
insecure: true
# Optional custom browser binary path (chrome/chromium/edge..)
#browser_path: "C:/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe"
2captcha:
apikey: "123123123123123"

View File

@@ -3,6 +3,7 @@ package core
import (
"fmt"
"net/url"
"os"
"strings"
"time"
@@ -23,6 +24,7 @@ type BrowserOpts struct {
LeavePageOpen bool // Leave pages and browser open
WaitLoadTime time.Duration // Time to wait till page loads
CaptchaSolverApiKey string // 2Captcha api key
BrowserPath string // Explicit browser executable path
ProxyURL string // Proxy URL
Insecure bool // Allow insecure TLS connections
UseStealth bool // Use go-rod stealth plugin
@@ -51,12 +53,18 @@ func NewBrowser(opts BrowserOpts) (*Browser, error) {
opts.Check()
logrus.Debugf("Browser options: %+v", opts)
path, has := launcher.LookPath()
logrus.Debug("Browser found: ", has)
path, err := resolveBrowserBinaryPath(opts.BrowserPath, launcher.LookPath)
if err != nil {
return nil, err
}
// Create launcher
l := launcher.New().Bin(path).Leakless(opts.IsLeakless).Headless(opts.IsHeadless).Set("disable-blink-features", "AutomationControlled").
l := launcher.New().Leakless(opts.IsLeakless).Headless(opts.IsHeadless).Set("disable-blink-features", "AutomationControlled").
Delete("enable-automation")
if path != "" {
logrus.Debugf("Using browser binary: %s", path)
l = l.Bin(path)
}
// Configure proxy if specified
if opts.ProxyURL != "" {
@@ -79,7 +87,6 @@ func NewBrowser(opts BrowserOpts) (*Browser, error) {
}
}
var err error
b := Browser{BrowserOpts: opts}
b.browserAddr, err = l.Launch()
@@ -91,6 +98,35 @@ func NewBrowser(opts BrowserOpts) (*Browser, error) {
return &b, err
}
func validateBrowserBinaryPath(path string) error {
info, err := os.Stat(path)
if err != nil {
return err
}
if info.IsDir() {
return fmt.Errorf("path points to a directory")
}
return nil
}
// resolveBrowserBinaryPath prefers an explicit browser path. If no explicit path is provided,
// it falls back to launcher autodiscovery and lets Rod handle auto-download when no binary is found.
func resolveBrowserBinaryPath(browserPath string, lookPath func() (string, bool)) (string, error) {
if browserPath != "" {
if err := validateBrowserBinaryPath(browserPath); err != nil {
return "", fmt.Errorf("invalid browser_path %q: %w", browserPath, err)
}
return browserPath, nil
}
path, has := lookPath()
if has {
return path, nil
}
return "", nil
}
// Check whether browser instance is already created
func (b *Browser) IsInitialized() bool {
if b.browserAddr != "" {