122 lines
3.9 KiB
Rust
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
|
|
)
|
|
}
|