@@ -4,7 +4,12 @@ import {
44 directVideoModels ,
55 getDirectVideoModel ,
66} from '@/lib/media/direct-video-catalog'
7- import { falVideoModels , getFalVideoModel } from '@/lib/media/fal-video-catalog'
7+ import {
8+ type FalVideoSettings ,
9+ falVideoModels ,
10+ getFalVideoModel ,
11+ getFalVideoVariant ,
12+ } from '@/lib/media/fal-video-catalog'
813import { AuthMode , type BlockConfig , IntegrationType , type SubBlockConfig } from '@/blocks/types'
914import { normalizeFileInput , parseOptionalBooleanInput } from '@/blocks/utils'
1015
@@ -42,40 +47,55 @@ const FALAI_AUDIO_DEFAULT_OFF_MODELS = ['kling-o3-pro', 'kling-o3-4k']
4247const FAL_MODEL_DEFAULT = { id : 'default' , label : 'Model default' } as const
4348const endpointModels = falVideoModels . filter ( ( model ) => ! model . legacyId )
4449
45- function tuningField (
46- id : string ,
47- title : string ,
48- key : 'durations' | 'aspectRatios' | 'resolutions' | 'audioField' | 'promptOptimizer'
49- ) : SubBlockConfig {
50+ const START_FRAME_FIELDS = [ 'visualReferenceInput' , 'visualReferenceUpload' ]
51+ const HIDDEN = { field : 'provider' , value : [ ] }
52+
53+ function hasStartFrame ( values ?: Record < string , unknown > ) {
54+ return [
55+ values ?. visualReference ,
56+ values ?. visualReferenceInput ,
57+ values ?. visualReferenceUpload ,
58+ ] . some ( ( reference ) => ( Array . isArray ( reference ) ? reference . length > 0 : Boolean ( reference ) ) )
59+ }
60+
61+ /** Settings of the endpoint a fal run will call: image-to-video once a start frame is attached. */
62+ function selectedFalSettings ( values ?: Record < string , unknown > ) {
63+ const model = getFalVideoModel ( values ?. model )
64+ return getFalVideoVariant ( values ?. model , Boolean ( model ?. image ) && hasStartFrame ( values ) )
65+ }
66+
67+ type FalSettingKey = 'durations' | 'aspectRatios' | 'resolutions' | 'audioField' | 'promptOptimizer'
68+
69+ function tuningField ( id : string , title : string , key : FalSettingKey ) : SubBlockConfig {
5070 return {
5171 id,
5272 title,
5373 type : 'dropdown' ,
5474 value : ( ) => FAL_MODEL_DEFAULT . id ,
55- condition : {
56- field : 'provider' ,
57- value : 'falai' ,
58- and : {
59- field : 'model' ,
60- value : endpointModels . filter ( ( model ) => model [ key ] ) . map ( ( model ) => model . modelId ) ,
61- } ,
75+ condition : ( values ) => {
76+ const model = getFalVideoModel ( values ?. model )
77+ return model && ! model . legacyId && selectedFalSettings ( values ) ?. [ key ]
78+ ? { field : 'provider' , value : 'falai' }
79+ : HIDDEN
6280 } ,
63- dependsOn : [ 'model' ] ,
81+ dependsOn : [ 'model' , ... START_FRAME_FIELDS ] ,
6482 options : ( params ) => {
6583 if ( ! params ) {
66- const values = endpointModels . flatMap ( ( model ) => {
67- const choices = model [ key ]
68- return Array . isArray ( choices )
69- ? choices . map ( ( value ) =>
70- key === 'durations' ? String ( Number . parseFloat ( String ( value ) ) ) : String ( value )
71- )
72- : choices
73- ? [ 'true' , 'false' ]
74- : [ ]
75- } )
84+ const values = endpointModels . flatMap ( ( model ) =>
85+ [ model , model . image ] . flatMap ( ( settings ) => {
86+ const choices = settings ?. [ key ]
87+ return Array . isArray ( choices )
88+ ? choices . map ( ( value ) =>
89+ key === 'durations' ? String ( Number . parseFloat ( String ( value ) ) ) : String ( value )
90+ )
91+ : choices
92+ ? [ 'true' , 'false' ]
93+ : [ ]
94+ } )
95+ )
7696 return [ FAL_MODEL_DEFAULT , ...[ ...new Set ( values ) ] . map ( ( id ) => ( { id, label : id } ) ) ]
7797 }
78- const model = getFalVideoModel ( params ?. values . model )
98+ const model = selectedFalSettings ( params ?. values )
7999 if ( ! model ?. [ key ] ) return [ ]
80100 if ( key === 'audioField' || key === 'promptOptimizer' ) {
81101 return [ FAL_MODEL_DEFAULT , { id : 'true' , label : 'On' } , { id : 'false' , label : 'Off' } ]
@@ -109,6 +129,15 @@ const falVideoFields = [
109129 tuningField ( 'falPromptOptimizer' , 'Prompt Optimizer' , 'promptOptimizer' ) ,
110130]
111131
132+ /** Settings fields that models with a legacy ID keep, and the catalog setting each one sets. */
133+ const LEGACY_FAL_SETTING_KEYS : Record < string , keyof FalVideoSettings > = {
134+ duration : 'durations' ,
135+ aspectRatio : 'aspectRatios' ,
136+ resolution : 'resolutions' ,
137+ generateAudio : 'audioField' ,
138+ promptOptimizer : 'promptOptimizer' ,
139+ }
140+
112141function withFalVideoCatalog ( subBlocks : SubBlockConfig [ ] ) : SubBlockConfig [ ] {
113142 return subBlocks . flatMap ( ( subBlock ) => {
114143 const condition = subBlock . condition
@@ -141,17 +170,27 @@ function withFalVideoCatalog(subBlocks: SubBlockConfig[]): SubBlockConfig[] {
141170 ]
142171 }
143172 if ( subBlock . id === 'apiKey' && ! condition . not ) return [ ...falVideoFields , subBlock ]
173+ const key = LEGACY_FAL_SETTING_KEYS [ subBlock . id ]
174+ if ( key )
175+ return [
176+ {
177+ ...subBlock ,
178+ condition : ( values ) => {
179+ const image = getFalVideoModel ( values ?. model ) ?. image
180+ return image && ! image [ key ] && hasStartFrame ( values ) ? HIDDEN : condition
181+ } ,
182+ dependsOn : [
183+ ...( Array . isArray ( subBlock . dependsOn ) ? subBlock . dependsOn : [ ] ) ,
184+ ...START_FRAME_FIELDS ,
185+ ] ,
186+ } ,
187+ ]
144188 return [ subBlock ]
145189 } )
146190}
147191
148192function selectedDirectModel ( values ?: Record < string , unknown > ) {
149- const withImage = [
150- values ?. visualReference ,
151- values ?. visualReferenceInput ,
152- values ?. visualReferenceUpload ,
153- ] . some ( ( reference ) => ( Array . isArray ( reference ) ? reference . length > 0 : Boolean ( reference ) ) )
154- return getDirectVideoModel ( values ?. provider , values ?. model , withImage )
193+ return getDirectVideoModel ( values ?. provider , values ?. model , hasStartFrame ( values ) )
155194}
156195
157196const directVideoFields = (
@@ -168,7 +207,7 @@ const directVideoFields = (
168207 title,
169208 type : key === 'promptOptimizer' ? 'switch' : 'dropdown' ,
170209 value : key === 'promptOptimizer' ? undefined : ( ) => FAL_MODEL_DEFAULT . id ,
171- dependsOn : [ 'provider' , 'model' , 'visualReferenceInput' , 'visualReferenceUpload' ] ,
210+ dependsOn : [ 'provider' , 'model' , ... START_FRAME_FIELDS ] ,
172211 condition : ( values ) => ( {
173212 field : 'provider' ,
174213 value : selectedDirectModel ( values ) ?. settings ?. [ key ] ? [ String ( values ?. provider ) ] : [ ] ,
@@ -301,9 +340,18 @@ function withDirectVideoCatalog(subBlocks: SubBlockConfig[]): SubBlockConfig[] {
301340 dependsOn : [ 'provider' , 'model' ] ,
302341 } ,
303342 ]
304- if ( field . id === 'visualReferenceUpload' || field . id === 'visualReferenceInput' )
343+ if ( START_FRAME_FIELDS . includes ( field . id ) )
305344 return [
306- { ...field , required : { field : 'model' , value : [ 'gen4_turbo' , 'gen-4' , 'gen-4-turbo' ] } } ,
345+ {
346+ ...field ,
347+ condition : ( values ) =>
348+ values ?. provider === 'runway' ||
349+ ( values ?. provider === 'falai' && getFalVideoModel ( values . model ) ?. image )
350+ ? { field : 'provider' , value : [ 'runway' , 'falai' ] }
351+ : HIDDEN ,
352+ dependsOn : [ 'provider' , 'model' ] ,
353+ required : { field : 'model' , value : [ 'gen4_turbo' , 'gen-4' , 'gen-4-turbo' ] } ,
354+ } ,
307355 ]
308356 return [ field ]
309357 } )
@@ -1928,8 +1976,20 @@ export const VideoGeneratorV3Block: BlockConfig = {
19281976 config : {
19291977 tool : ( params ) => VideoGeneratorV2Block . tools . config ?. tool ( params ) ?? 'video_runway' ,
19301978 params : ( params ) => {
1931- if ( params . provider === 'falai' )
1932- return VideoGeneratorV2Block . tools . config ?. params ?.( params ) ?? params
1979+ if ( params . provider === 'falai' ) {
1980+ const falParams = VideoGeneratorV2Block . tools . config ?. params ?.( params ) ?? params
1981+ if ( ! getFalVideoModel ( params . model ) ?. image || ! falParams . visualReference )
1982+ return { ...falParams , visualReference : undefined }
1983+ const image = getFalVideoVariant ( params . model , true )
1984+ return {
1985+ ...falParams ,
1986+ duration : image ?. durations ? falParams . duration : undefined ,
1987+ aspectRatio : image ?. aspectRatios ? falParams . aspectRatio : undefined ,
1988+ resolution : image ?. resolutions ? falParams . resolution : undefined ,
1989+ generateAudio : image ?. audioField ? falParams . generateAudio : undefined ,
1990+ promptOptimizer : image ?. promptOptimizer ? falParams . promptOptimizer : undefined ,
1991+ }
1992+ }
19331993 const visualReference =
19341994 params . provider === 'runway'
19351995 ? normalizeFileInput ( params . visualReference , { single : true } )
0 commit comments