Admin: show the voice used and split video cost from audio cost

Cost was a single number, which now hides two very different things: about
$0.40 a shot of video generation against roughly $0.015 of voice conversion.
Per shot, show which voice was actually dubbed in, so a conversion that failed
and silently fell back to Veo's own audio is visible rather than invisible.
This commit is contained in:
gator 2026-09-07 15:00:54 +00:00
parent b1cf3ef6f6
commit 79ba141ada

View file

@ -68,6 +68,11 @@ function shotHtml(s) {
+ '<p style="margin:0 0 6px">' + esc(s.line || "") + '</p>'
+ '<div class="kv"><span' + (over ? ' class="warnw"' : '') + '>' + s.words + ' words'
+ (over ? ' (over 18)' : '') + '</span> · camera: ' + esc(s.camera || "—") + '</div>'
// A redub that failed falls back to Veo's own audio on purpose. That is
// the right behaviour, but it must not be silent.
+ '<div class="kv">voice: ' + (s.voice_used
? esc(s.voice_used) + ' (redubbed)'
: '<span class="warnw">Veo original, not redubbed</span>') + '</div>'
+ (s.plate_url ? '<div class="kv">opening still: <a href="' + esc(s.plate_url) + '" target="_blank" rel="noopener">view</a></div>' : '')
+ (s.prompt_sent
? '<details><summary>Exact prompt sent</summary><pre>' + esc(s.prompt_sent) + '</pre></details>'
@ -86,10 +91,15 @@ function projHtml(p) {
+ esc(p.status || "") + ' · ' + esc(when(p.created_at)) + '</div>'
+ '<div class="kv">'
+ '<b>billed</b> ' + money(p.price_usd) + ' · <b>cost</b> ' + money(p.actual_cost_usd || p.cost_usd)
+ (p.audio_cost_usd
? ' <span style="opacity:.7">(video ' + money(p.video_cost_usd)
+ ' + voice ' + money(p.audio_cost_usd) + ', ' + (p.audio_chars || 0) + ' chars)</span>'
: '')
+ ' · <b>tokens</b> ' + (p.script_tokens || 0)
+ ' · <b>writes</b> ' + (p.script_writes || 0)
+ ' · <b>rewrites</b> ' + (p.revisions || 0) + '<br>'
+ '<b>voice</b> ' + esc(p.pet_voice_formula || "(none set)") + '<br>'
+ '<b>voice</b> ' + esc(p.voice_name ? p.voice_name + " — " : "")
+ esc(p.pet_voice_formula || "(none set)") + '<br>'
+ '<b>scene</b> ' + esc(p.scene || "(none)") + '<br>'
+ '<b>source</b> <a href="' + esc(p.source_url || "#") + '" target="_blank" rel="noopener nofollow">' + esc((p.source_url || "").slice(0, 70)) + '</a>'
+ (p.film_note ? '<br><span class="warnw"><b>note</b> ' + esc(p.film_note) + '</span>' : '')