use serde::Serialize;
use std::path::PathBuf;
use std::time::Duration;
use crate::error::AppError;
use crate::output::{self, Ctx};
const HOST: &str = "https://tmpfiles.org/api/v1/upload";
#[derive(Serialize)]
struct UploadResult {
input: String,
url: String,
size_bytes: u64,
host: &'static str,
}
pub fn run(ctx: Ctx, input: PathBuf) -> Result<(), AppError> {
if !input.exists() {
return Err(AppError::InvalidInput(format!(
"file not found: {}",
input.display()
)));
}
let meta = std::fs::metadata(&input)?;
output::info(
ctx,
&format!(
"uploading {} ({} bytes) to tmpfiles.org",
input.display(),
meta.len()
),
);
let http = reqwest::blocking::Client::builder()
.timeout(Duration::from_secs(300))
.build()
.map_err(AppError::from)?;
let form = reqwest::blocking::multipart::Form::new()
.file("file", &input)
.map_err(|e| AppError::Transient(e.to_string()))?;
let resp = http.post(HOST).multipart(form).send()?;
let status = resp.status();
let body = resp.text().unwrap_or_default();
if !status.is_success() {
return Err(AppError::Transient(format!(
"tmpfiles upload failed: HTTP {status} body={body}"
)));
}
#[derive(serde::Deserialize)]
struct Envelope {
status: String,
data: Option<Data>,
}
#[derive(serde::Deserialize)]
struct Data {
url: String,
}
let env: Envelope = serde_json::from_str(&body)
.map_err(|e| AppError::Transient(format!("tmpfiles response not JSON: {e} -- {body}")))?;
if env.status != "success" {
return Err(AppError::Transient(format!(
"tmpfiles returned status={} body={body}",
env.status
)));
}
let raw_url = env.data.map(|d| d.url).unwrap_or_default();
let direct_url = raw_url
.replacen("http://tmpfiles.org/", "https://tmpfiles.org/dl/", 1)
.replacen("https://tmpfiles.org/", "https://tmpfiles.org/dl/", 1)
.replace("/dl/dl/", "/dl/");
let result = UploadResult {
input: input.display().to_string(),
url: direct_url.clone(),
size_bytes: meta.len(),
host: "tmpfiles.org",
};
output::print_success_or(ctx, &result, |r| {
use owo_colors::OwoColorize;
println!("{} {}", "url:".bold(), r.url.cyan());
println!(
" pass to: seedance generate --video {} ...",
r.url.as_str().dimmed()
);
});
Ok(())
}