screencap-bot/src/main.rs

122 lines
3.9 KiB
Rust

use log::{debug, error, info};
use rand::{distributions::Standard, seq::IteratorRandom, Rng};
mod config;
mod shows;
mod video;
use crate::shows::EpisodeNumber;
#[tokio::main]
async fn main() {
dotenvy::dotenv().ok();
env_logger::init();
ffmpeg_next::init().unwrap();
let mut rng = rand::thread_rng();
let conf = config::load();
info!("Loading shows from {}", conf.shows_file.display());
let shows = shows::load(conf.shows_file);
info!("Logging into cohost as {}", conf.cohost_email);
let session = eggbug::Session::login(&conf.cohost_email, &conf.cohost_password)
.await
.expect("Failed to login to cohost");
loop {
let (title, show) = shows.iter().choose(&mut rng).expect("No shows found!");
let episodes = match show.episodes() {
Ok(eps) => eps,
Err(e) => {
error!("Failed to get episodes for {}: {}", title, e);
continue;
}
};
let (num, file) = episodes.iter().choose(&mut rng).unwrap();
let descriptor = format!(
"{}{}",
title,
match num {
EpisodeNumber::Standalone => String::new(),
EpisodeNumber::SingleSeason(n) => format!(" episode {}", n),
EpisodeNumber::MultiSeason(season, ep) =>
format!(" season {} episode {}", season, ep),
}
);
info!("Selected: {} - {}", descriptor, file.display());
let video_info = video::get_video_info(file, Some("eng")).unwrap();
debug!(
"Video duration: {}",
format_timestamp(video_info.duration_secs, None)
);
debug!(
"Subtitle stream index: {:?}",
video_info.subtitle_stream_index
);
let timestamp = video_info.duration_secs * rng.sample::<f64, _>(Standard);
let formatted_timestamp = format_timestamp(timestamp, Some(video_info.duration_secs));
info!("Taking screencap at {}", formatted_timestamp);
match video::take_screencap(file, timestamp, video_info.subtitle_stream_index).await {
Err(e) => {
error!("Failed to take screencap: {}", e);
}
Ok(img_data) => {
let attachment = eggbug::Attachment::new(
img_data,
format!("{} @{}.png", descriptor, formatted_timestamp),
String::from("image/png"),
)
.with_alt_text(format!(
"Screencap of {} at {}",
descriptor, formatted_timestamp
));
let mut tags = show.tags.clone();
tags.extend_from_slice(&conf.global_tags);
match session
.create_post(
&conf.cohost_page,
&mut eggbug::Post {
content_warnings: vec![descriptor],
attachments: vec![attachment],
tags,
draft: false,
adult_content: false,
headline: String::new(),
markdown: String::new(),
},
)
.await
{
Ok(id) => info!("Created post {}", id),
Err(e) => error!("Failed to create post: {}", e),
}
}
}
tokio::time::sleep(conf.post_interval).await;
}
}
fn format_timestamp(timestamp: f64, total_duration: Option<f64>) -> String {
let total_duration = total_duration.unwrap_or(timestamp);
format!(
"{}{:02}:{:05.2}",
if total_duration >= 3600.0 {
format!("{}:", (timestamp / 3600.0) as u32)
} else {
String::new()
},
((timestamp % 3600.0) / 60.0) as u32,
timestamp % 60.0
)
}