package geminiweb import ( "fmt" "os" "path/filepath" "sync" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/launcher" "github.com/go-rod/rod/lib/proto" ) // BrowserManager 管理瀏覽器實例的生命週期 type BrowserManager struct { mu sync.Mutex browser *rod.Browser userDataDir string page *rod.Page visible bool isRunning bool currentModel string } var ( globalManager *BrowserManager globalMu sync.Mutex ) // GetBrowserManager 獲取全域瀏覽器管理器(單例) func GetBrowserManager(userDataDir string, visible bool) (*BrowserManager, error) { globalMu.Lock() defer globalMu.Unlock() if globalManager != nil { return globalManager, nil } manager, err := NewBrowserManager(userDataDir, visible) if err != nil { return nil, err } globalManager = manager return globalManager, nil } // NewBrowserManager 建立新的瀏覽器管理器 func NewBrowserManager(userDataDir string, visible bool) (*BrowserManager, error) { cleanLockFiles(userDataDir) if err := os.MkdirAll(userDataDir, 0755); err != nil { return nil, fmt.Errorf("failed to create user data dir: %w", err) } return &BrowserManager{ userDataDir: userDataDir, visible: visible, }, nil } // cleanLockFiles 清理 Chrome 的殘留鎖檔案 func cleanLockFiles(userDataDir string) { lockFiles := []string{ "SingletonLock", "SingletonCookie", "SingletonSocket", "Default/SingletonLock", "Default/SingletonCookie", "Default/SingletonSocket", } for _, file := range lockFiles { path := filepath.Join(userDataDir, file) os.Remove(path) } } // Launch 啟動瀏覽器(如果尚未啟動) func (m *BrowserManager) Launch() error { m.mu.Lock() defer m.mu.Unlock() if m.isRunning && m.browser != nil { return nil } l := launcher.New() if m.visible { l = l.Headless(false) } else { l = l.Headless(true) } l = l.UserDataDir(m.userDataDir) url, err := l.Launch() if err != nil { return fmt.Errorf("failed to launch browser: %w", err) } b := rod.New().ControlURL(url) if err := b.Connect(); err != nil { return fmt.Errorf("failed to connect browser: %w", err) } m.browser = b page, err := b.Page(proto.TargetCreateTarget{URL: "about:blank"}) if err != nil { _ = b.Close() return fmt.Errorf("failed to create page: %w", err) } m.page = page m.isRunning = true return nil } // GetPage 獲取頁面 func (m *BrowserManager) GetPage() (*rod.Page, error) { m.mu.Lock() defer m.mu.Unlock() if !m.isRunning || m.browser == nil { return nil, fmt.Errorf("browser not running") } return m.page, nil } // Close 關閉瀏覽器 func (m *BrowserManager) Close() error { m.mu.Lock() defer m.mu.Unlock() if !m.isRunning { return nil } var err error if m.browser != nil { err = m.browser.Close() m.browser = nil } m.page = nil m.isRunning = false return err } // IsRunning 檢查瀏覽器是否正在運行 func (m *BrowserManager) IsRunning() bool { m.mu.Lock() defer m.mu.Unlock() return m.isRunning } // SetCurrentModel 設定當前模型 func (m *BrowserManager) SetCurrentModel(model string) { m.mu.Lock() defer m.mu.Unlock() m.currentModel = model } // GetCurrentModel 獲取當前模型 func (m *BrowserManager) GetCurrentModel() string { m.mu.Lock() defer m.mu.Unlock() return m.currentModel }