Skip to content

Instantly share code, notes, and snippets.

@cfsamson
Last active February 21, 2023 23:48
Show Gist options
  • Save cfsamson/1cc74144fdf316bde8eebb84595f864e to your computer and use it in GitHub Desktop.
Save cfsamson/1cc74144fdf316bde8eebb84595f864e to your computer and use it in GitHub Desktop.
Fantocini file downloader - when you can't manually issua a GET request
// Cargo.toml
//
// [dependencies]
// serde_json = "1.0.61"
// notify = "4.0.15"
// anyhow = "1.0.38"
// fantoccini = "0.17.1"
// tokio = {version="1.1.1", features= ["full"]}
// log = "0.4.14"
// env_logger = "0.8.2"
use std::{
sync::mpsc::{self, Receiver},
time::Duration,
};
use anyhow::{bail, Result};
use fantoccini::{ClientBuilder, Locator};
use log::{error, info};
use notify::{DebouncedEvent, ReadDirectoryChangesWatcher, RecursiveMode, Watcher};
use serde_json::{json, Map, Value};
#[tokio::main]
async fn main() {
env_logger::init();
let download_path_absolute = "C:\\temp\\downloads";
match download_report(download_path_absolute).await {
Ok(()) => info!("Downloaded is completed!"),
Err(e) => error!("{}", e),
};
}
async fn download_report(download_dir: &str) -> Result<()> {
let capabilities = create_capabilities(download_dir);
let mut c = ClientBuilder::native()
.capabilities(capabilities)
.connect("http://localhost:4444")
.await
.expect("Failed to connect to webdriver");
c.goto("https://www.someplace.com").await?;
// Start to watch for new files in the download folder
let download_watcher = DownloadWatcher::init(download_dir)?;
let download = c.find(Locator::Id("download_btn")).await?;
download.click().await?;
// Watch folder for new files, timeout if the download hasn't finished in 10 minutes
download_watcher.wait(Duration::from_secs(60*10))?;
c.close().await?;
Ok(())
}
struct DownloadWatcher {
rx: Receiver<DebouncedEvent>,
_watcher: ReadDirectoryChangesWatcher,
}
impl DownloadWatcher {
fn init(path_to_watch: &str) -> Result<Self> {
let (tx, rx) = mpsc::channel();
let mut _watcher = notify::watcher(tx, Duration::from_secs(0))?;
_watcher.watch(path_to_watch, RecursiveMode::NonRecursive)?;
Ok(Self { rx, _watcher })
}
fn wait(self, timeout: Duration) -> Result<()> {
loop {
match self.rx.recv_timeout(timeout) {
Ok(DebouncedEvent::Create(_)) => break,
Ok(DebouncedEvent::Error(e, _)) => bail!("Notify error: {}", e),
Ok(_event) => (),
Err(mpsc::RecvTimeoutError::Timeout) => bail!("Download timeout"),
Err(e) => bail!("{}", e),
}
}
Ok(())
}
}
fn create_capabilities(download_dir: &str) -> Map<String, Value> {
let capabilities = json!({
"moz:firefoxOptions": {
"prefs": {
"browser.download.manager.showWhenStarting": false,
"browser.helperApps.neverAsk.saveToDisk": "text/csv,application/vnd.ms-excel,application/csv", // change to the contenttype of your video file
"browser.download.folderList": 2,
"browser.download.dir": download_dir
},
"args": ["-headless"] // if you want a headless browser
},
"timeouts": {
"pageLoad": 1000*60*5,
"implicit": 1000*60*5,
"script": 1000*60*5,
}
});
let map= capabilities.as_object().unwrap().to_owned();
map
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment