app
Adom Video Post-Production
Public Made by Adomby adom
AI-driven studio for finishing demo videos: review every captured clip, flag and re-record the weak ones, speed up dead air, narrate, mux and auto-level, validate the final cut, and publish straight to the wiki, driven end to end by your AI.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
//! `impl Routes` for the unified `adom-video-post app` subcommand.
//!
//! Dispatches incoming requests across the storyboard and voiceover
//! sub-route tables behind prefix-based URL rewriting:
//!
//! /manifest, /state, /video/*, /poster/*, /image/*,
//! /console-png/*, /final-video/* → `StoryboardRoutes`
//! /voiceover/<rest> → `VoiceoverRoutes` with
//! `<rest>` passed as the
//! stripped URL
//!
//! Voiceover's /video endpoint serves the configured input (the
//! final_fast_path from the manifest) — it's accessible via
//! `/voiceover/video` in the unified app. The UI's Narrate panel
//! points a `<video>` element at that URL.
use crate::storyboard::routes::StoryboardRoutes;
use crate::voiceover::server::VoiceoverRoutes;
use crate::webapp::{video as webapp_video, RouteOutcome, Routes};
use std::sync::Arc;
use tiny_http::{Method, Request};
const UI_HTML: &str = include_str!("ui.html");
const FAVICON_SVG: &str = include_str!("icon.svg");
pub struct AppRoutes {
pub storyboard: Arc<StoryboardRoutes>,
pub voiceover: Arc<VoiceoverRoutes>,
}
impl Routes for AppRoutes {
fn ui_html(&self) -> &str {
UI_HTML
}
fn favicon_svg(&self) -> &str {
FAVICON_SVG
}
fn handle(&self, request: Request, method: &Method, url: &str) -> RouteOutcome {
// Special-case /voiceover/video: VoiceoverRoutes's own
// /video handler reads a hardcoded `cfg.input` path that was
// captured at app startup. In the unified app, the user may
// run `POST /concat` after startup to produce the final fast
// concat, and we want the Narrate phase's video player to
// pick up the newly-set manifest.final_fast_path without
// needing a process restart. So intercept the request here,
// read the current final_fast_path out of the shared
// manifest, and serve it directly. Fall back to the raw
// concat if final_fast_path is unset.
if matches!(method, Method::Get) && url == "/voiceover/video" {
let path = {
let m = self.storyboard.manifest.read().unwrap();
m.final_fast_path
.clone()
.or_else(|| m.final_raw_path.clone())
};
return match path {
Some(p) if p.exists() => RouteOutcome::Handled(
webapp_video::serve_file_range(request, &p, "video/webm"),
),
_ => RouteOutcome::Handled(
request
.respond(tiny_http::Response::from_string("no final video available — run POST /concat first")
.with_status_code(404))
.map_err(|e| e.to_string()),
),
};
}
// /voiceover/* → strip the prefix and hand off to the
// voiceover route table, which thinks it's running alone.
if let Some(rest) = url.strip_prefix("/voiceover") {
let sub_url = if rest.is_empty() { "/" } else { rest };
return self.voiceover.handle(request, method, sub_url);
}
// Everything else falls through to the storyboard routes.
// This includes /manifest, /state, /video/:id/*, /poster/:id,
// /image/:id, /console-png/:id, /final-video/raw|fast.
self.storyboard.handle(request, method, url)
}
}