port: check the five MODDING rules, and label the generated files in the asset tree
MODDING.md calls modding a constraint on the exporter TODAY and nothing verified it -- the same shape as the black hold, skipped[], stop_bed and --focus. All five rules pass, so check-modding is a guard rather than a fix, and it is proved able to fail: a stripped .cmd header, a bogus.bmp, and one orphaned PNG each exit 1. It found one thing: the .cmd encode-cache sidecars sat in the modder-facing tree with nothing saying what they were. They now carry a header. The header is excluded from the cache key so rewording it does not re-encode four minutes of video, and the sidecar is refreshed whenever its text differs rather than only on re-encode -- otherwise a header change could never reach an existing export. Also partly answers my own question to the Decoder: there is no general capture-path floor, because the port matches live-title-press-a at 0.00093% full-frame and 0.000% across the band. The 0.301% is specific to that pair. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
@@ -157,10 +157,35 @@ pub fn transcode(disc: &Path, out: &Path, m: &Movie) -> Result<Option<Transcoded
|
||||
let argv = args(&src, &ogv, ch);
|
||||
let command = format!("ffmpeg {}", argv.join(" "));
|
||||
let size = std::fs::metadata(&src)?.len();
|
||||
let want = format!("{command}\nsource-bytes: {size}\nsource-channels: {ch}\n");
|
||||
// The sidecar SAYS WHAT IT IS. It sits in the modder-facing asset tree next
|
||||
// to the `.ogv`, and MODDING rule 2's principle is that a generated file
|
||||
// should be tellable from a hand-made one by reading it -- a bare ffmpeg
|
||||
// line beside a video looks like something a modder should edit or delete.
|
||||
//
|
||||
// The header is NOT part of the cache key: `fresh` compares only the lines
|
||||
// that describe the encode. Otherwise rewording this comment would re-encode
|
||||
// four minutes of video to no purpose, which is a cache that punishes
|
||||
// documentation.
|
||||
let key = format!("{command}\nsource-bytes: {size}\nsource-channels: {ch}\n");
|
||||
let want = format!(
|
||||
"# Generated by sylpheed-export. NOT an asset and not hand-editable: this\n\
|
||||
# records how {}.ogv beside it was encoded, so a re-export can skip the\n\
|
||||
# encode when the source and the command are both unchanged. Deleting it\n\
|
||||
# only forces one re-encode. To change the video, override the .ogv under\n\
|
||||
# data/mods/ (MODDING rule 4) -- editing this file changes nothing.\n{key}",
|
||||
m.stem
|
||||
);
|
||||
|
||||
let cache_key = |s: &str| -> String {
|
||||
s.lines()
|
||||
.filter(|l| !l.starts_with('#'))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
};
|
||||
let fresh = ogv.exists()
|
||||
&& std::fs::read_to_string(&stamp).map(|s| s == want).unwrap_or(false);
|
||||
&& std::fs::read_to_string(&stamp)
|
||||
.map(|s| cache_key(&s) == cache_key(&want))
|
||||
.unwrap_or(false);
|
||||
if !fresh {
|
||||
// Encode to a temp name and rename on success. A reader that catches
|
||||
// this mid-write sees no file at all rather than a valid-looking one
|
||||
@@ -181,6 +206,17 @@ pub fn transcode(disc: &Path, out: &Path, m: &Movie) -> Result<Option<Transcoded
|
||||
bail!("ffmpeg failed on {}", m.src);
|
||||
}
|
||||
std::fs::rename(&partial, &ogv)?;
|
||||
}
|
||||
// Refresh the sidecar whenever its TEXT differs, encode or no encode.
|
||||
//
|
||||
// It used to be written only inside the `!fresh` branch, which is right for
|
||||
// the cache and wrong for the file: a change to the header alone -- the part
|
||||
// deliberately excluded from the key -- would then never reach an existing
|
||||
// export, because nothing that reads the header can trigger the write that
|
||||
// updates it. The explanation would be correct in the source and absent on
|
||||
// disc, which is the same shape as every other documented-but-unexercised
|
||||
// thing this port has had to find the hard way.
|
||||
if std::fs::read_to_string(&stamp).map(|s| s != want).unwrap_or(true) {
|
||||
std::fs::write(&stamp, &want)?;
|
||||
}
|
||||
Ok(Some(Transcoded {
|
||||
|
||||
Reference in New Issue
Block a user