app
AI Flow: Video
Public Made by Adomby adom
Turn a board build into a narrated walkthrough video on your wiki page and YouTube.
main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
//! Adom's own logo sting, for official Adom videos only.
//! The brand kit lives on a wiki page only members of the Adom org can read, so being able to
//! read it is the test: an Adom employee gets the sting offered (and applied on request), and
//! everyone else's film stays free of Adom branding, because a logo on it would make it look
//! like an official Adom video.
use std::path::{Path, PathBuf};
use std::process::Command;
const KIT: &str = "adom/adom-video-brand";
const OVERLAY: &str = "media/alpha/logo-sting-teal-1080p.mov";
const CLOSER: &str = "media/dark-intro-16x9.mp4";
fn cache() -> PathBuf {
let home = std::env::var("HOME").unwrap_or_else(|_| ".".into());
PathBuf::from(home).join(".adom/adom-aiflow-video/brand")
}
/// True when this account can read the Adom brand kit (an Adom org member).
pub fn is_adom_member() -> bool {
Command::new("adom-wiki").args(["repo", "show", KIT, "page.json"]).output().map(|o| o.status.success()).unwrap_or(false)
}
fn kit() -> Result<PathBuf, String> {
let dir = cache();
let page = dir.join("adom-video-brand");
if page.join(OVERLAY).exists() && page.join(CLOSER).exists() { return Ok(page); }
std::fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
let _ = std::fs::remove_dir_all(&page);
let o = Command::new("adom-wiki").args(["repo", "clone", KIT]).current_dir(&dir).output().map_err(|e| e.to_string())?;
if !o.status.success() || !page.join(OVERLAY).exists() { return Err("could not fetch the Adom brand kit".into()); }
Ok(page)
}
fn has_audio(f: &str) -> bool {
Command::new("ffprobe").args(["-v", "error", "-select_streams", "a", "-show_entries", "stream=index", "-of", "csv=p=0", f]).output()
.map(|o| !String::from_utf8_lossy(&o.stdout).trim().is_empty()).unwrap_or(false)
}
pub fn status() -> (String, Vec<String>) {
if is_adom_member() {
("this account is in the Adom org: an official Adom film can carry the Adom logo sting".into(), vec![
"Ask the human once whether this is an official Adom video. If it is, plan the script so nobody speaks during the first 5 seconds, and finish with `brand --video <final.mp4> --out <branded.mp4>` after the voice is on.".into(),
"That puts the transparent logo sting over the film's own first 5 seconds (with its sound) and the black-background sting after the last line. Captions and chapters keep their times.".into(),
"A personal film by the same person gets no Adom branding: leave it out when they say it is not an Adom video.".into(),
])
} else {
("no Adom branding for this film".into(), vec![
"Adom's logo, sting, sound and end card are for official Adom videos made by Adom employees. Leave them out of this film; saying it was made in Adom Hydrogen is fine.".into(),
])
}
}
pub fn apply(video: &str, out: &str) -> Result<(String, Vec<String>), (String, Vec<String>)> {
if !is_adom_member() {
return Ok(("left the film as it is: no Adom branding".into(), vec!["Adom's logo sting is for official Adom videos made by Adom employees. Use the film without it.".into()]));
}
if !Path::new(video).exists() { return Err((format!("no film at {video}"), vec![])); }
let page = kit().map_err(|e| (e, vec!["Check `adom-wiki repo show adom/adom-video-brand page.json`, then try again.".to_string()]))?;
let (ov, cl) = (page.join(OVERLAY), page.join(CLOSER));
let a0 = if has_audio(video) { "[0:a]aresample=48000,aformat=channel_layouts=stereo[a0];".to_string() }
else { "anullsrc=r=48000:cl=stereo[an];[an]atrim=0:5[a0];".to_string() };
let amix = if has_audio(video) { "[a0][a1]amix=inputs=2:duration=first:normalize=0[ma];" } else { "[a0][a1]amix=inputs=2:duration=longest:normalize=0[ma];" };
let fc = format!(
"[0:v]fps=30,scale=1920:1080:flags=lanczos,setsar=1[m];[1:v]format=yuva444p10le,scale=1920:1080[s];\
[m][s]overlay=0:0:eof_action=pass:format=auto,format=yuv420p[mv];[2:v]scale=1920:1080,fps=30,setsar=1,format=yuv420p[e];\
[mv][e]concat=n=2:v=1:a=0[v];{a0}[1:a]aresample=48000,aformat=channel_layouts=stereo[a1];{amix}\
[2:a]aresample=48000,aformat=channel_layouts=stereo[ea];[ma][ea]concat=n=2:v=0:a=1[a]");
let o = Command::new("ffmpeg").args(["-nostdin", "-v", "error", "-y", "-i", video, "-i"]).arg(&ov).arg("-i").arg(&cl)
.args(["-filter_complex", &fc, "-map", "[v]", "-map", "[a]", "-c:v", "libx264", "-preset", "medium", "-crf", "18", "-g", "30",
"-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart", out]).output().map_err(|e| (format!("could not run ffmpeg: {e}"), vec![]))?;
if !o.status.success() {
return Err(("ffmpeg could not add the sting".into(), vec![String::from_utf8_lossy(&o.stderr).lines().last().unwrap_or("").to_string()]));
}
Ok((format!("Adom logo sting added: {out}"), vec![
"Overlay sting on the first 5 seconds, black-background sting after the last frame (5 seconds longer). Captions and chapters keep their times.".into(),
"Watch the first and last 10 seconds before showing the human: the logo should not sit on top of speech.".into(),
]))
}