use std::{env, path::Path}; use thirtyfour::{session::handle::SessionHandle, By, DesiredCapabilities, WebDriver}; pub struct BrowserSession { driver: WebDriver, } const DATA_DIR_ARG: &str = "--user-data-dir"; #[cfg(target_os = "linux")] const DATA_DIR_CHROMIUM: &str = "/.config/chromium"; #[cfg(target_os = "linux")] const DATA_DIR_CHROME: &str = "~/.config/google-chrome"; #[cfg(target_os = "linux")] const DATA_DIR_CHROME_BETA: &str = "~/.config/google-chrome-beta"; #[cfg(target_os = "linux")] const HOME_VARIABLE: &str = "HOME"; #[cfg(target_os = "windows")] const DATA_DIR_CHROME: &str = "\\Google\\Chrome\\User Data"; #[cfg(target_os = "windows")] const DATA_DIR_CHROME_BETA: &str = "\\Google\\Chrome Beta\\User Data"; #[cfg(target_os = "windows")] const DATA_DIR_CHROMIUM: &str = "\\Google\\Chromium\\User Data"; #[cfg(target_os = "windows")] const HOME_VARIABLE: &str = "LOCALAPPDATA"; #[derive(Clone)] pub struct BrowserHandle { handle: SessionHandle, } fn get_data_dir() -> String { let mut home = String::new(); for (key, val) in env::vars() { if key == HOME_VARIABLE { home = val; } } if home.is_empty() { panic!("cannot find home env variable"); } let by_preference = vec![DATA_DIR_CHROMIUM, DATA_DIR_CHROME, DATA_DIR_CHROME_BETA]; for data_dir in by_preference { if Path::new(format!("{}{}", home, data_dir).as_str()).exists() { return format!("{}{}", home, data_dir); } } // Default to chrome anyway I guess return format!("{}{}", home, DATA_DIR_CHROME); } impl BrowserSession { pub async fn new() -> Result { // TODO: error if chrome is already running let mut caps = DesiredCapabilities::chrome(); // Use the normal data directory (for extensions and logins) let data_dir = get_data_dir(); println!("starting chrome using data directory: {}", data_dir); #[cfg(target_os = "windows")] let data_dir = format!("\"{}\"", data_dir); caps.add_chrome_arg(format!("{}={}", DATA_DIR_ARG, data_dir).as_str())?; let driver = WebDriver::new("http://localhost:6444", caps).await?; println!("started browser"); Ok(Self { driver: driver }) } pub fn handle(&self) -> BrowserHandle { BrowserHandle { handle: self.driver.clone(), } } pub async fn stop(self) -> Result<(), anyhow::Error> { self.driver.quit().await?; Ok(()) } } impl BrowserHandle { pub async fn start(&self, start_url: &str) -> Result<(), anyhow::Error> { self.handle.fullscreen_window().await?; self.handle .get(format!("http://{}", start_url)) .await .unwrap_or(()); // Ignore errors ig? idfk it errors out in some weirdo way but it works Ok(()) } async fn fullscreen_youtube(&self) -> Result<(), anyhow::Error> { let elems = self .handle .find_elements(By::ClassName("ytp-fullscreen-button")) .await?; if elems.len() != 0 { if let Some(button) = elems.first() { button.click().await?; } } Ok(()) } async fn youtube(&self) -> Result<(), anyhow::Error> { self.fullscreen_youtube().await?; Ok(()) } pub async fn go_to(&self, url: &str) -> Result<(), anyhow::Error> { self.handle.get(url).await?; if let Some(domain) = self.handle.current_url().await?.domain() { if domain == "www.youtube.com" { self.youtube().await?; } } Ok(()) } }