2024-09-30 05:50:59 -04:00
|
|
|
use std::{collections::HashMap, time::Duration};
|
|
|
|
|
|
|
|
use chrono::Timelike;
|
|
|
|
use tokio::task::JoinHandle;
|
|
|
|
|
|
|
|
use tracing::{Instrument, Level};
|
|
|
|
use tracing_subscriber::FmtSubscriber;
|
|
|
|
|
|
|
|
use serde::Deserialize;
|
|
|
|
use toml;
|
|
|
|
|
|
|
|
mod guac;
|
|
|
|
mod shotter;
|
|
|
|
|
|
|
|
fn duration_until_next_minute() -> Duration {
|
|
|
|
let now = chrono::Utc::now();
|
|
|
|
let interval = (60 - now.second()) * 1000;
|
|
|
|
|
|
|
|
// not sure if this ever triggers
|
|
|
|
if interval == 0 {
|
|
|
|
return Duration::from_secs(59);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Duration::from_millis(interval as u64);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize, Debug, Clone)]
|
|
|
|
struct Node {
|
|
|
|
url: String,
|
|
|
|
origin: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct Config {
|
|
|
|
root_path: std::path::PathBuf,
|
|
|
|
|
|
|
|
webp_quality: f32,
|
|
|
|
|
|
|
|
vms: HashMap<String, Node>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
async fn main() -> anyhow::Result<()> {
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
let subscriber = FmtSubscriber::builder()
|
|
|
|
.with_max_level(Level::TRACE)
|
|
|
|
.compact()
|
|
|
|
.finish();
|
|
|
|
|
|
|
|
#[cfg(not(debug_assertions))]
|
|
|
|
let subscriber = FmtSubscriber::builder()
|
|
|
|
.with_max_level(Level::INFO)
|
|
|
|
.compact()
|
|
|
|
.finish();
|
|
|
|
|
|
|
|
tracing::subscriber::set_global_default(subscriber)?;
|
|
|
|
|
|
|
|
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
|
|
|
|
|
|
|
|
let config: Config = toml::from_str(std::fs::read_to_string("./config.toml")?.as_str())?;
|
|
|
|
|
|
|
|
// Essentially this is meant to be a sentinel for "SCREENSHOT NOW!"
|
|
|
|
let (tx, _) = tokio::sync::broadcast::channel::<Option<()>>(10);
|
|
|
|
|
|
|
|
for (id, node) in config.vms.iter() {
|
|
|
|
tracing::info!("Adding node {id} : {:?}", node);
|
|
|
|
|
|
|
|
let mut clone = tx.subscribe();
|
|
|
|
let path = config.root_path.join(id);
|
|
|
|
let id_clone = id.clone();
|
|
|
|
let node_clone = node.clone();
|
|
|
|
|
|
|
|
let _: JoinHandle<anyhow::Result<()>> = tokio::spawn(async move {
|
|
|
|
while let Some(_) = clone.recv().await? {
|
|
|
|
let span = tracing::span!(
|
|
|
|
Level::INFO,
|
|
|
|
"node screenshot",
|
|
|
|
// FIXME: This should NOT be hardcoded
|
|
|
|
node = id_clone.as_str()
|
|
|
|
);
|
|
|
|
|
2024-09-30 06:53:36 -04:00
|
|
|
match shotter::take_one_screenshot(
|
2024-09-30 05:50:59 -04:00
|
|
|
&node_clone.url,
|
|
|
|
&node_clone.origin,
|
|
|
|
&id_clone,
|
|
|
|
path.clone(),
|
|
|
|
config.webp_quality,
|
|
|
|
)
|
|
|
|
.instrument(span)
|
2024-09-30 06:53:36 -04:00
|
|
|
.await
|
|
|
|
{
|
|
|
|
Ok(_) => {}
|
|
|
|
|
|
|
|
Err(error) => {
|
|
|
|
// FIXME: On WebSocket errors, should we just try again?
|
|
|
|
tracing::error!("Error taking screenshot: {error}");
|
|
|
|
}
|
|
|
|
};
|
2024-09-30 05:50:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
loop {
|
|
|
|
tx.send(Some(()))?;
|
|
|
|
|
|
|
|
let dur = duration_until_next_minute();
|
|
|
|
tracing::info!("Waiting {:?}", dur);
|
|
|
|
tokio::time::sleep(dur).await;
|
|
|
|
}
|
|
|
|
}
|