package videogen import "context" // LipsyncRequest asks a talking-head backend (SadTalker style) to animate a // still portrait so it speaks the given audio. Zero values mean "backend // default" (ADR-0025). type LipsyncRequest struct { // Image is the portrait to animate. Required. Image Image // Audio is the encoded speech the head lip-syncs to. Required. Carried // as bytes (never a URL), mirroring audio.TranscriptionRequest. Audio []byte // AudioMIME is the audio MIME type (e.g. "audio/wav"); "" = let the // backend sniff it. AudioMIME string // AudioFilename is the multipart filename hint some backends key their // format detection on; "" derives one from AudioMIME or falls back to // "audio". AudioFilename string // Still reduces head motion to blinks and lip movement (less uncanny on // formal portraits); false = backend default motion. Still bool // Enhance runs the backend's face enhancer over the output frames. Enhance bool // Preprocess selects how the backend frames the face: "crop" (animate // the face crop) or "full" (paste the animated face back into the whole // image); "" = backend default. Preprocess string } // LipsyncOption mutates a LipsyncRequest before it is sent. type LipsyncOption func(*LipsyncRequest) // WithLipsyncStill reduces head motion to blinks and lip movement. func WithLipsyncStill() LipsyncOption { return func(r *LipsyncRequest) { r.Still = true } } // WithLipsyncEnhance runs the backend's face enhancer over the output. func WithLipsyncEnhance() LipsyncOption { return func(r *LipsyncRequest) { r.Enhance = true } } // WithLipsyncPreprocess selects the face framing ("crop" or "full"). func WithLipsyncPreprocess(p string) LipsyncOption { return func(r *LipsyncRequest) { r.Preprocess = p } } // Apply returns a copy of the request with all options applied. func (r LipsyncRequest) Apply(opts ...LipsyncOption) LipsyncRequest { for _, opt := range opts { opt(&r) } return r } // LipSyncer animates still portraits into talking-head clips. Its own small // interface rather than a method on Model: lip-syncers are not text-to-video // generators — they bind to a different backend id entirely. type LipSyncer interface { // Lipsync returns the talking-head clip. Generation is slow (minutes); // bound the call with a context deadline. Lipsync(ctx context.Context, req LipsyncRequest, opts ...LipsyncOption) (*Result, error) } // LipsyncModelOption configures a LipSyncer at construction time. Reserved // for future per-model settings. type LipsyncModelOption func(*LipsyncModelConfig) // LipsyncModelConfig carries per-model construction settings. type LipsyncModelConfig struct{} // ApplyLipsyncModelOptions folds options into a config. func ApplyLipsyncModelOptions(opts []LipsyncModelOption) LipsyncModelConfig { var cfg LipsyncModelConfig for _, opt := range opts { opt(&cfg) } return cfg } // LipsyncProvider mints LipSyncers bound to one backend. type LipsyncProvider interface { // Name is the registry identifier for the provider. Name() string // LipsyncModel returns a LipSyncer bound to the given id (passed through // to the backend verbatim; no catalog validation). LipsyncModel(id string, opts ...LipsyncModelOption) (LipSyncer, error) }