feat(videogen): LastImage — pin the trailing keyframe (first-last-frame-to-video) #26

Merged
steve merged 4 commits from feat/videogen-last-frame into main 2026-08-08 07:10:27 +00:00
3 changed files with 6 additions and 11 deletions
Showing only changes of commit 5994d96921 - Show all commits
+5 -3
View File
@@ -207,10 +207,12 @@ func parseSwapReport(header string) []imagegen.SwappedFace {
// imageFilename picks a multipart filename for an image part. The shim reads
// bytes, not names, but a plausible extension keeps server-side sniffing and
// request logs honest. base distinguishes the parts of a multi-file form
// ("target"/"source") so a log line says which one was malformed.
// ("target"/"source", "frame"/"frame_last") so a log line says which one was
// malformed — and, for the video keyframes, so a backend that stages uploads
// by filename cannot have the second overwrite the first.
//
// initImageFilename (video.go) is this function with base fixed to "frame"
// and delegates here — two copies of one extension table is how they drift.
// Every caller routes through here: two copies of one extension table is how
// they drift.
func imageFilename(mimeType, base string) string {
if base == "" {
base = "image"
+1 -1
View File
@@ -56,7 +56,7 @@ func (m *lipsyncModel) Lipsync(ctx context.Context, req videogen.LipsyncRequest,
// hand (mirrors videoModel.Generate).
var buf bytes.Buffer
w := multipart.NewWriter(&buf)
fw, err := w.CreateFormFile("image", initImageFilename(req.Image.MIME))
fw, err := w.CreateFormFile("image", imageFilename(req.Image.MIME, "frame"))
if err != nil {
return nil, fmt.Errorf("llama-swap: build lipsync form: %w", err)
}
-7
View File
1
@@ -147,13 +147,6 @@ func singleVideoResult(provider, model, verb string, raw []byte, contentType str
return &videogen.Result{Video: videogen.Video{Data: raw, MIME: mimeType}}, nil
}
// initImageFilename picks the multipart filename hint for the conditioning
// frame from its MIME subtype. The name is provider-chosen (never
// caller-supplied), so no sanitization is needed.
func initImageFilename(mimeType string) string {
return imageFilename(mimeType, "frame")
}
// writeImagePart attaches one conditioning frame under the given field name,
// with a filename derived from nameStem. Shared by the first- and last-frame
// parts so the two cannot drift in how they encode, which is the usual way a