@weirdfingers/baseboards 0.9.5 → 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +561 -469
- package/dist/index.js.map +1 -1
- package/package.json +2 -5
- package/templates/README.md +0 -122
- package/templates/api/.env.example +0 -65
- package/templates/api/ARTIFACT_RESOLUTION_GUIDE.md +0 -148
- package/templates/api/Dockerfile +0 -32
- package/templates/api/README.md +0 -264
- package/templates/api/alembic/env.py +0 -114
- package/templates/api/alembic/script.py.mako +0 -28
- package/templates/api/alembic/versions/20250101_000000_initial_schema.py +0 -506
- package/templates/api/alembic/versions/20251022_174729_remove_provider_name_from_generations.py +0 -75
- package/templates/api/alembic/versions/20251023_165852_switch_to_declarative_base_and_mapping.py +0 -467
- package/templates/api/alembic/versions/20251202_000000_add_artifact_lineage.py +0 -134
- package/templates/api/alembic/versions/2025925_62735_add_seed_data_for_default_tenant.py +0 -88
- package/templates/api/alembic.ini +0 -36
- package/templates/api/config/generators.yaml +0 -237
- package/templates/api/config/storage_config.yaml +0 -26
- package/templates/api/docs/ADDING_GENERATORS.md +0 -409
- package/templates/api/docs/GENERATORS_API.md +0 -502
- package/templates/api/docs/MIGRATIONS.md +0 -472
- package/templates/api/docs/TESTING_LIVE_APIS.md +0 -417
- package/templates/api/docs/storage_providers.md +0 -337
- package/templates/api/pyproject.toml +0 -205
- package/templates/api/src/boards/__init__.py +0 -10
- package/templates/api/src/boards/api/app.py +0 -172
- package/templates/api/src/boards/api/auth.py +0 -75
- package/templates/api/src/boards/api/endpoints/__init__.py +0 -3
- package/templates/api/src/boards/api/endpoints/jobs.py +0 -76
- package/templates/api/src/boards/api/endpoints/setup.py +0 -505
- package/templates/api/src/boards/api/endpoints/sse.py +0 -129
- package/templates/api/src/boards/api/endpoints/storage.py +0 -155
- package/templates/api/src/boards/api/endpoints/tenant_registration.py +0 -296
- package/templates/api/src/boards/api/endpoints/uploads.py +0 -149
- package/templates/api/src/boards/api/endpoints/webhooks.py +0 -13
- package/templates/api/src/boards/auth/__init__.py +0 -15
- package/templates/api/src/boards/auth/adapters/__init__.py +0 -27
- package/templates/api/src/boards/auth/adapters/auth0.py +0 -220
- package/templates/api/src/boards/auth/adapters/base.py +0 -73
- package/templates/api/src/boards/auth/adapters/clerk.py +0 -172
- package/templates/api/src/boards/auth/adapters/jwt.py +0 -122
- package/templates/api/src/boards/auth/adapters/none.py +0 -102
- package/templates/api/src/boards/auth/adapters/oidc.py +0 -284
- package/templates/api/src/boards/auth/adapters/supabase.py +0 -110
- package/templates/api/src/boards/auth/context.py +0 -35
- package/templates/api/src/boards/auth/factory.py +0 -129
- package/templates/api/src/boards/auth/middleware.py +0 -221
- package/templates/api/src/boards/auth/provisioning.py +0 -129
- package/templates/api/src/boards/auth/tenant_extraction.py +0 -278
- package/templates/api/src/boards/cli.py +0 -354
- package/templates/api/src/boards/config.py +0 -131
- package/templates/api/src/boards/database/__init__.py +0 -7
- package/templates/api/src/boards/database/cli.py +0 -110
- package/templates/api/src/boards/database/connection.py +0 -292
- package/templates/api/src/boards/database/models.py +0 -19
- package/templates/api/src/boards/database/seed_data.py +0 -182
- package/templates/api/src/boards/dbmodels/__init__.py +0 -441
- package/templates/api/src/boards/generators/__init__.py +0 -57
- package/templates/api/src/boards/generators/artifact_resolution.py +0 -405
- package/templates/api/src/boards/generators/artifacts.py +0 -53
- package/templates/api/src/boards/generators/base.py +0 -144
- package/templates/api/src/boards/generators/implementations/__init__.py +0 -14
- package/templates/api/src/boards/generators/implementations/fal/__init__.py +0 -25
- package/templates/api/src/boards/generators/implementations/fal/audio/__init__.py +0 -23
- package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_music_generation.py +0 -171
- package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_sound_effect_generation.py +0 -167
- package/templates/api/src/boards/generators/implementations/fal/audio/chatterbox_text_to_speech.py +0 -176
- package/templates/api/src/boards/generators/implementations/fal/audio/chatterbox_tts_turbo.py +0 -195
- package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_sound_effects_v2.py +0 -194
- package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_tts_eleven_v3.py +0 -209
- package/templates/api/src/boards/generators/implementations/fal/audio/fal_elevenlabs_tts_turbo_v2_5.py +0 -206
- package/templates/api/src/boards/generators/implementations/fal/audio/fal_minimax_speech_26_hd.py +0 -237
- package/templates/api/src/boards/generators/implementations/fal/audio/minimax_music_v2.py +0 -173
- package/templates/api/src/boards/generators/implementations/fal/audio/minimax_speech_2_6_turbo.py +0 -221
- package/templates/api/src/boards/generators/implementations/fal/image/__init__.py +0 -63
- package/templates/api/src/boards/generators/implementations/fal/image/bytedance_seedream_v45_edit.py +0 -219
- package/templates/api/src/boards/generators/implementations/fal/image/clarity_upscaler.py +0 -220
- package/templates/api/src/boards/generators/implementations/fal/image/crystal_upscaler.py +0 -173
- package/templates/api/src/boards/generators/implementations/fal/image/fal_ideogram_character.py +0 -227
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2.py +0 -203
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2_edit.py +0 -230
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro.py +0 -204
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro_edit.py +0 -221
- package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_kontext.py +0 -216
- package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_ultra.py +0 -197
- package/templates/api/src/boards/generators/implementations/fal/image/gemini_25_flash_image.py +0 -177
- package/templates/api/src/boards/generators/implementations/fal/image/gemini_25_flash_image_edit.py +0 -208
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_15_edit.py +0 -216
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_5.py +0 -177
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_edit_image.py +0 -182
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_mini.py +0 -167
- package/templates/api/src/boards/generators/implementations/fal/image/ideogram_character_edit.py +0 -299
- package/templates/api/src/boards/generators/implementations/fal/image/ideogram_v2.py +0 -190
- package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview.py +0 -191
- package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview_fast.py +0 -179
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana.py +0 -183
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_edit.py +0 -212
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_pro.py +0 -179
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_pro_edit.py +0 -226
- package/templates/api/src/boards/generators/implementations/fal/image/qwen_image.py +0 -249
- package/templates/api/src/boards/generators/implementations/fal/image/qwen_image_edit.py +0 -244
- package/templates/api/src/boards/generators/implementations/fal/image/reve_edit.py +0 -178
- package/templates/api/src/boards/generators/implementations/fal/image/reve_text_to_image.py +0 -155
- package/templates/api/src/boards/generators/implementations/fal/image/seedream_v45_text_to_image.py +0 -180
- package/templates/api/src/boards/generators/implementations/fal/utils.py +0 -61
- package/templates/api/src/boards/generators/implementations/fal/video/__init__.py +0 -77
- package/templates/api/src/boards/generators/implementations/fal/video/bytedance_seedance_v1_pro_text_to_video.py +0 -209
- package/templates/api/src/boards/generators/implementations/fal/video/creatify_lipsync.py +0 -161
- package/templates/api/src/boards/generators/implementations/fal/video/fal_bytedance_seedance_v1_pro_image_to_video.py +0 -222
- package/templates/api/src/boards/generators/implementations/fal/video/fal_minimax_hailuo_02_standard_text_to_video.py +0 -152
- package/templates/api/src/boards/generators/implementations/fal/video/fal_pixverse_lipsync.py +0 -197
- package/templates/api/src/boards/generators/implementations/fal/video/fal_sora_2_text_to_video.py +0 -173
- package/templates/api/src/boards/generators/implementations/fal/video/infinitalk.py +0 -221
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_ai_avatar_v2_pro.py +0 -168
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_ai_avatar_v2_standard.py +0 -159
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_image_to_video.py +0 -175
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_text_to_video.py +0 -168
- package/templates/api/src/boards/generators/implementations/fal/video/minimax_hailuo_2_3_pro_image_to_video.py +0 -153
- package/templates/api/src/boards/generators/implementations/fal/video/sora2_image_to_video.py +0 -172
- package/templates/api/src/boards/generators/implementations/fal/video/sora_2_image_to_video_pro.py +0 -175
- package/templates/api/src/boards/generators/implementations/fal/video/sora_2_text_to_video_pro.py +0 -163
- package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2.py +0 -167
- package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2_pro.py +0 -155
- package/templates/api/src/boards/generators/implementations/fal/video/veed_fabric_1_0.py +0 -180
- package/templates/api/src/boards/generators/implementations/fal/video/veed_lipsync.py +0 -174
- package/templates/api/src/boards/generators/implementations/fal/video/veo3.py +0 -194
- package/templates/api/src/boards/generators/implementations/fal/video/veo31.py +0 -190
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_fast.py +0 -190
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_fast_image_to_video.py +0 -191
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_first_last_frame_to_video.py +0 -187
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_image_to_video.py +0 -183
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_reference_to_video.py +0 -172
- package/templates/api/src/boards/generators/implementations/fal/video/wan_25_preview_image_to_video.py +0 -212
- package/templates/api/src/boards/generators/implementations/fal/video/wan_25_preview_text_to_video.py +0 -208
- package/templates/api/src/boards/generators/implementations/fal/video/wan_pro_image_to_video.py +0 -158
- package/templates/api/src/boards/generators/implementations/kie/__init__.py +0 -11
- package/templates/api/src/boards/generators/implementations/kie/base.py +0 -316
- package/templates/api/src/boards/generators/implementations/kie/image/__init__.py +0 -3
- package/templates/api/src/boards/generators/implementations/kie/image/nano_banana_edit.py +0 -190
- package/templates/api/src/boards/generators/implementations/kie/utils.py +0 -98
- package/templates/api/src/boards/generators/implementations/kie/video/__init__.py +0 -8
- package/templates/api/src/boards/generators/implementations/kie/video/veo3.py +0 -161
- package/templates/api/src/boards/generators/implementations/openai/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/openai/audio/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/openai/audio/whisper.py +0 -69
- package/templates/api/src/boards/generators/implementations/openai/image/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/openai/image/dalle3.py +0 -96
- package/templates/api/src/boards/generators/implementations/replicate/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/replicate/image/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/replicate/image/flux_pro.py +0 -88
- package/templates/api/src/boards/generators/implementations/replicate/video/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/replicate/video/lipsync.py +0 -73
- package/templates/api/src/boards/generators/loader.py +0 -253
- package/templates/api/src/boards/generators/registry.py +0 -114
- package/templates/api/src/boards/generators/resolution.py +0 -632
- package/templates/api/src/boards/generators/testmods/class_gen.py +0 -34
- package/templates/api/src/boards/generators/testmods/import_side_effect.py +0 -35
- package/templates/api/src/boards/graphql/__init__.py +0 -7
- package/templates/api/src/boards/graphql/access_control.py +0 -136
- package/templates/api/src/boards/graphql/mutations/root.py +0 -148
- package/templates/api/src/boards/graphql/queries/root.py +0 -116
- package/templates/api/src/boards/graphql/resolvers/__init__.py +0 -8
- package/templates/api/src/boards/graphql/resolvers/auth.py +0 -12
- package/templates/api/src/boards/graphql/resolvers/board.py +0 -1053
- package/templates/api/src/boards/graphql/resolvers/generation.py +0 -666
- package/templates/api/src/boards/graphql/resolvers/generator.py +0 -50
- package/templates/api/src/boards/graphql/resolvers/lineage.py +0 -381
- package/templates/api/src/boards/graphql/resolvers/upload.py +0 -463
- package/templates/api/src/boards/graphql/resolvers/user.py +0 -25
- package/templates/api/src/boards/graphql/schema.py +0 -81
- package/templates/api/src/boards/graphql/types/board.py +0 -102
- package/templates/api/src/boards/graphql/types/generation.py +0 -166
- package/templates/api/src/boards/graphql/types/generator.py +0 -17
- package/templates/api/src/boards/graphql/types/user.py +0 -47
- package/templates/api/src/boards/jobs/repository.py +0 -153
- package/templates/api/src/boards/logging.py +0 -195
- package/templates/api/src/boards/middleware.py +0 -339
- package/templates/api/src/boards/progress/__init__.py +0 -4
- package/templates/api/src/boards/progress/models.py +0 -25
- package/templates/api/src/boards/progress/publisher.py +0 -64
- package/templates/api/src/boards/py.typed +0 -0
- package/templates/api/src/boards/redis_pool.py +0 -118
- package/templates/api/src/boards/storage/__init__.py +0 -52
- package/templates/api/src/boards/storage/base.py +0 -363
- package/templates/api/src/boards/storage/config.py +0 -187
- package/templates/api/src/boards/storage/factory.py +0 -288
- package/templates/api/src/boards/storage/implementations/__init__.py +0 -27
- package/templates/api/src/boards/storage/implementations/gcs.py +0 -340
- package/templates/api/src/boards/storage/implementations/local.py +0 -201
- package/templates/api/src/boards/storage/implementations/s3.py +0 -294
- package/templates/api/src/boards/storage/implementations/supabase.py +0 -218
- package/templates/api/src/boards/tenant_isolation.py +0 -446
- package/templates/api/src/boards/validation.py +0 -262
- package/templates/api/src/boards/workers/__init__.py +0 -1
- package/templates/api/src/boards/workers/actors.py +0 -274
- package/templates/api/src/boards/workers/cli.py +0 -125
- package/templates/api/src/boards/workers/context.py +0 -348
- package/templates/api/src/boards/workers/middleware.py +0 -58
- package/templates/api/src/py.typed +0 -0
- package/templates/compose.web.yaml +0 -35
- package/templates/compose.yaml +0 -116
- package/templates/docker/env.example +0 -23
- package/templates/web/.env.example +0 -28
- package/templates/web/Dockerfile +0 -51
- package/templates/web/components.json +0 -22
- package/templates/web/imageLoader.js +0 -18
- package/templates/web/next-env.d.ts +0 -5
- package/templates/web/next.config.js +0 -36
- package/templates/web/package.json +0 -41
- package/templates/web/postcss.config.mjs +0 -7
- package/templates/web/public/favicon.ico +0 -0
- package/templates/web/src/app/boards/[boardId]/page.tsx +0 -353
- package/templates/web/src/app/globals.css +0 -123
- package/templates/web/src/app/layout.tsx +0 -31
- package/templates/web/src/app/lineage/[generationId]/page.tsx +0 -235
- package/templates/web/src/app/page.tsx +0 -35
- package/templates/web/src/app/providers.tsx +0 -18
- package/templates/web/src/components/boards/ArtifactInputSlots.tsx +0 -206
- package/templates/web/src/components/boards/ArtifactPreview.tsx +0 -466
- package/templates/web/src/components/boards/GenerationGrid.tsx +0 -282
- package/templates/web/src/components/boards/GenerationInput.tsx +0 -370
- package/templates/web/src/components/boards/GeneratorSelector.tsx +0 -272
- package/templates/web/src/components/boards/UploadArtifact.tsx +0 -563
- package/templates/web/src/components/header.tsx +0 -32
- package/templates/web/src/components/theme-provider.tsx +0 -10
- package/templates/web/src/components/theme-toggle.tsx +0 -75
- package/templates/web/src/components/ui/alert-dialog.tsx +0 -157
- package/templates/web/src/components/ui/button.tsx +0 -58
- package/templates/web/src/components/ui/card.tsx +0 -92
- package/templates/web/src/components/ui/dropdown-menu.tsx +0 -200
- package/templates/web/src/components/ui/navigation-menu.tsx +0 -168
- package/templates/web/src/components/ui/toast.tsx +0 -128
- package/templates/web/src/components/ui/toaster.tsx +0 -35
- package/templates/web/src/components/ui/use-toast.ts +0 -187
- package/templates/web/src/hooks/useGeneratorMRU.ts +0 -57
- package/templates/web/src/lib/utils.ts +0 -6
- package/templates/web/tsconfig.json +0 -41
package/templates/api/src/boards/generators/implementations/fal/video/veo31_fast_image_to_video.py
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Google Veo 3.1 Fast image-to-video generator.
|
|
3
|
-
|
|
4
|
-
Converts static images into animated videos based on text prompts using
|
|
5
|
-
Google's Veo 3.1 Fast technology via fal.ai. This is a faster version
|
|
6
|
-
with per-second pricing.
|
|
7
|
-
|
|
8
|
-
Based on Fal AI's fal-ai/veo3.1/fast/image-to-video model.
|
|
9
|
-
See: https://fal.ai/models/fal-ai/veo3.1/fast/image-to-video
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
import os
|
|
13
|
-
from typing import Literal
|
|
14
|
-
|
|
15
|
-
from pydantic import BaseModel, Field
|
|
16
|
-
|
|
17
|
-
from ....artifacts import ImageArtifact
|
|
18
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class Veo31FastImageToVideoInput(BaseModel):
|
|
22
|
-
"""Input schema for Veo 3.1 Fast image-to-video generation.
|
|
23
|
-
|
|
24
|
-
Artifact fields (image) are automatically detected via type introspection
|
|
25
|
-
and resolved from generation IDs to ImageArtifact objects.
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
prompt: str = Field(description="Text prompt describing the desired video content and motion")
|
|
29
|
-
image: ImageArtifact = Field(
|
|
30
|
-
description="Input image to animate. Should be 720p or higher in 16:9 or 9:16 aspect ratio"
|
|
31
|
-
)
|
|
32
|
-
aspect_ratio: Literal["auto", "9:16", "16:9"] = Field(
|
|
33
|
-
default="auto",
|
|
34
|
-
description="Aspect ratio of the generated video. "
|
|
35
|
-
"'auto' automatically detects from input image",
|
|
36
|
-
)
|
|
37
|
-
duration: Literal["4s", "6s", "8s"] = Field(
|
|
38
|
-
default="8s",
|
|
39
|
-
description="Duration of the generated video in seconds",
|
|
40
|
-
)
|
|
41
|
-
generate_audio: bool = Field(
|
|
42
|
-
default=True,
|
|
43
|
-
description="Whether to generate audio for the video. Disabling reduces cost by ~33%",
|
|
44
|
-
)
|
|
45
|
-
resolution: Literal["720p", "1080p"] = Field(
|
|
46
|
-
default="720p",
|
|
47
|
-
description="Resolution of the generated video",
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class FalVeo31FastImageToVideoGenerator(BaseGenerator):
|
|
52
|
-
"""Generator for creating videos from static images using Google Veo 3.1 Fast."""
|
|
53
|
-
|
|
54
|
-
name = "fal-veo31-fast-image-to-video"
|
|
55
|
-
description = "Fal: Veo 3.1 Fast - Convert images to videos with text-guided animation"
|
|
56
|
-
artifact_type = "video"
|
|
57
|
-
|
|
58
|
-
def get_input_schema(self) -> type[Veo31FastImageToVideoInput]:
|
|
59
|
-
"""Return the input schema for this generator."""
|
|
60
|
-
return Veo31FastImageToVideoInput
|
|
61
|
-
|
|
62
|
-
async def generate(
|
|
63
|
-
self, inputs: Veo31FastImageToVideoInput, context: GeneratorExecutionContext
|
|
64
|
-
) -> GeneratorResult:
|
|
65
|
-
"""Generate video using fal.ai veo3.1/fast/image-to-video."""
|
|
66
|
-
# Check for API key
|
|
67
|
-
if not os.getenv("FAL_KEY"):
|
|
68
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
69
|
-
|
|
70
|
-
# Import fal_client
|
|
71
|
-
try:
|
|
72
|
-
import fal_client
|
|
73
|
-
except ImportError as e:
|
|
74
|
-
raise ImportError(
|
|
75
|
-
"fal.ai SDK is required for FalVeo31FastImageToVideoGenerator. "
|
|
76
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
77
|
-
) from e
|
|
78
|
-
|
|
79
|
-
# Upload image artifact to Fal's public storage
|
|
80
|
-
# Fal API requires publicly accessible URLs, but our storage_url might be:
|
|
81
|
-
# - Localhost URLs (not publicly accessible)
|
|
82
|
-
# - Private S3 buckets (not publicly accessible)
|
|
83
|
-
# So we upload to Fal's temporary storage first
|
|
84
|
-
from ..utils import upload_artifacts_to_fal
|
|
85
|
-
|
|
86
|
-
image_urls = await upload_artifacts_to_fal([inputs.image], context)
|
|
87
|
-
|
|
88
|
-
# Prepare arguments for fal.ai API
|
|
89
|
-
arguments = {
|
|
90
|
-
"prompt": inputs.prompt,
|
|
91
|
-
"image_url": image_urls[0],
|
|
92
|
-
"aspect_ratio": inputs.aspect_ratio,
|
|
93
|
-
"duration": inputs.duration,
|
|
94
|
-
"generate_audio": inputs.generate_audio,
|
|
95
|
-
"resolution": inputs.resolution,
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
# Submit async job
|
|
99
|
-
handler = await fal_client.submit_async(
|
|
100
|
-
"fal-ai/veo3.1/fast/image-to-video",
|
|
101
|
-
arguments=arguments,
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
# Store external job ID
|
|
105
|
-
await context.set_external_job_id(handler.request_id)
|
|
106
|
-
|
|
107
|
-
# Stream progress updates
|
|
108
|
-
from .....progress.models import ProgressUpdate
|
|
109
|
-
|
|
110
|
-
event_count = 0
|
|
111
|
-
async for event in handler.iter_events(with_logs=True):
|
|
112
|
-
event_count += 1
|
|
113
|
-
# Sample every 3rd event to avoid spam
|
|
114
|
-
if event_count % 3 == 0:
|
|
115
|
-
# Extract logs if available
|
|
116
|
-
logs = getattr(event, "logs", None)
|
|
117
|
-
if logs:
|
|
118
|
-
# Join log entries into a single message
|
|
119
|
-
if isinstance(logs, list):
|
|
120
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
121
|
-
else:
|
|
122
|
-
message = str(logs)
|
|
123
|
-
|
|
124
|
-
if message:
|
|
125
|
-
await context.publish_progress(
|
|
126
|
-
ProgressUpdate(
|
|
127
|
-
job_id=handler.request_id,
|
|
128
|
-
status="processing",
|
|
129
|
-
progress=50.0,
|
|
130
|
-
phase="processing",
|
|
131
|
-
message=message,
|
|
132
|
-
)
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
# Get final result
|
|
136
|
-
result = await handler.get()
|
|
137
|
-
|
|
138
|
-
# Extract video from result
|
|
139
|
-
# Expected structure: {"video": {"url": "...", "content_type": "...", ...}}
|
|
140
|
-
video_data = result.get("video")
|
|
141
|
-
if not video_data:
|
|
142
|
-
raise ValueError("No video returned from fal.ai API")
|
|
143
|
-
|
|
144
|
-
video_url = video_data.get("url")
|
|
145
|
-
if not video_url:
|
|
146
|
-
raise ValueError("Video missing URL in fal.ai response")
|
|
147
|
-
|
|
148
|
-
# Calculate video dimensions based on resolution and aspect ratio
|
|
149
|
-
# For "auto" aspect ratio, assume 16:9 as the most common format
|
|
150
|
-
effective_aspect_ratio = inputs.aspect_ratio if inputs.aspect_ratio != "auto" else "16:9"
|
|
151
|
-
|
|
152
|
-
if inputs.resolution == "720p":
|
|
153
|
-
if effective_aspect_ratio == "16:9":
|
|
154
|
-
width, height = 1280, 720
|
|
155
|
-
else: # 9:16
|
|
156
|
-
width, height = 720, 1280
|
|
157
|
-
else: # 1080p
|
|
158
|
-
if effective_aspect_ratio == "16:9":
|
|
159
|
-
width, height = 1920, 1080
|
|
160
|
-
else: # 9:16
|
|
161
|
-
width, height = 1080, 1920
|
|
162
|
-
|
|
163
|
-
# Parse duration from "Xs" format
|
|
164
|
-
duration_seconds = int(inputs.duration.rstrip("s"))
|
|
165
|
-
|
|
166
|
-
artifact = await context.store_video_result(
|
|
167
|
-
storage_url=video_url,
|
|
168
|
-
format="mp4",
|
|
169
|
-
width=width,
|
|
170
|
-
height=height,
|
|
171
|
-
duration=duration_seconds,
|
|
172
|
-
output_index=0,
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
return GeneratorResult(outputs=[artifact])
|
|
176
|
-
|
|
177
|
-
async def estimate_cost(self, inputs: Veo31FastImageToVideoInput) -> float:
|
|
178
|
-
"""Estimate cost for this generation in USD.
|
|
179
|
-
|
|
180
|
-
Pricing: $0.10 per second (audio off) or $0.15 per second (audio on).
|
|
181
|
-
"""
|
|
182
|
-
# Parse duration from "Xs" format
|
|
183
|
-
duration_seconds = int(inputs.duration.rstrip("s"))
|
|
184
|
-
|
|
185
|
-
# Per-second pricing
|
|
186
|
-
if inputs.generate_audio:
|
|
187
|
-
cost_per_second = 0.15
|
|
188
|
-
else:
|
|
189
|
-
cost_per_second = 0.10
|
|
190
|
-
|
|
191
|
-
return duration_seconds * cost_per_second
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Google Veo 3.1 first-last frame to video generator.
|
|
3
|
-
|
|
4
|
-
Generates videos by interpolating between first and last frame images using
|
|
5
|
-
Google's Veo 3.1 technology via fal.ai.
|
|
6
|
-
|
|
7
|
-
Based on Fal AI's fal-ai/veo3.1/first-last-frame-to-video model.
|
|
8
|
-
See: https://fal.ai/models/fal-ai/veo3.1/first-last-frame-to-video
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import os
|
|
12
|
-
from typing import Literal
|
|
13
|
-
|
|
14
|
-
from pydantic import BaseModel, Field
|
|
15
|
-
|
|
16
|
-
from ....artifacts import ImageArtifact
|
|
17
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class Veo31FirstLastFrameToVideoInput(BaseModel):
|
|
21
|
-
"""Input schema for Veo 3.1 first-last frame to video generation.
|
|
22
|
-
|
|
23
|
-
Artifact fields (first_frame, last_frame) are automatically detected via type
|
|
24
|
-
introspection and resolved from generation IDs to ImageArtifact objects.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
first_frame: ImageArtifact = Field(description="The first frame of the video (input image)")
|
|
28
|
-
last_frame: ImageArtifact = Field(description="The last frame of the video (input image)")
|
|
29
|
-
prompt: str = Field(description="Text prompt describing the desired video content and motion")
|
|
30
|
-
duration: Literal["4s", "6s", "8s"] = Field(
|
|
31
|
-
default="8s",
|
|
32
|
-
description="Duration of the generated video in seconds",
|
|
33
|
-
)
|
|
34
|
-
aspect_ratio: Literal["auto", "9:16", "16:9", "1:1"] = Field(
|
|
35
|
-
default="auto",
|
|
36
|
-
description=(
|
|
37
|
-
"Aspect ratio of the generated video. 'auto' uses the aspect ratio from input images"
|
|
38
|
-
),
|
|
39
|
-
)
|
|
40
|
-
resolution: Literal["720p", "1080p"] = Field(
|
|
41
|
-
default="720p",
|
|
42
|
-
description="Resolution of the generated video",
|
|
43
|
-
)
|
|
44
|
-
generate_audio: bool = Field(
|
|
45
|
-
default=True,
|
|
46
|
-
description="Whether to generate audio for the video. Disabling uses 50% fewer credits",
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class FalVeo31FirstLastFrameToVideoGenerator(BaseGenerator):
|
|
51
|
-
"""Generator for creating videos from first and last frame images using Google Veo 3.1."""
|
|
52
|
-
|
|
53
|
-
name = "fal-veo31-first-last-frame-to-video"
|
|
54
|
-
description = "Fal: Veo 3.1 - Generate videos by interpolating between first and last frames"
|
|
55
|
-
artifact_type = "video"
|
|
56
|
-
|
|
57
|
-
def get_input_schema(self) -> type[Veo31FirstLastFrameToVideoInput]:
|
|
58
|
-
"""Return the input schema for this generator."""
|
|
59
|
-
return Veo31FirstLastFrameToVideoInput
|
|
60
|
-
|
|
61
|
-
async def generate(
|
|
62
|
-
self, inputs: Veo31FirstLastFrameToVideoInput, context: GeneratorExecutionContext
|
|
63
|
-
) -> GeneratorResult:
|
|
64
|
-
"""Generate video using fal.ai veo3.1/first-last-frame-to-video."""
|
|
65
|
-
# Check for API key
|
|
66
|
-
if not os.getenv("FAL_KEY"):
|
|
67
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
68
|
-
|
|
69
|
-
# Import fal_client
|
|
70
|
-
try:
|
|
71
|
-
import fal_client
|
|
72
|
-
except ImportError as e:
|
|
73
|
-
raise ImportError(
|
|
74
|
-
"fal.ai SDK is required for FalVeo31FirstLastFrameToVideoGenerator. "
|
|
75
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
76
|
-
) from e
|
|
77
|
-
|
|
78
|
-
# Upload image artifacts to Fal's public storage
|
|
79
|
-
# Fal API requires publicly accessible URLs, but our storage_url might be:
|
|
80
|
-
# - Localhost URLs (not publicly accessible)
|
|
81
|
-
# - Private S3 buckets (not publicly accessible)
|
|
82
|
-
# So we upload to Fal's temporary storage first
|
|
83
|
-
from ..utils import upload_artifacts_to_fal
|
|
84
|
-
|
|
85
|
-
first_frame_urls = await upload_artifacts_to_fal([inputs.first_frame], context)
|
|
86
|
-
last_frame_urls = await upload_artifacts_to_fal([inputs.last_frame], context)
|
|
87
|
-
|
|
88
|
-
# Prepare arguments for fal.ai API
|
|
89
|
-
arguments = {
|
|
90
|
-
"first_frame_url": first_frame_urls[0],
|
|
91
|
-
"last_frame_url": last_frame_urls[0],
|
|
92
|
-
"prompt": inputs.prompt,
|
|
93
|
-
"duration": inputs.duration,
|
|
94
|
-
"aspect_ratio": inputs.aspect_ratio,
|
|
95
|
-
"resolution": inputs.resolution,
|
|
96
|
-
"generate_audio": inputs.generate_audio,
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
# Submit async job
|
|
100
|
-
handler = await fal_client.submit_async(
|
|
101
|
-
"fal-ai/veo3.1/first-last-frame-to-video",
|
|
102
|
-
arguments=arguments,
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
# Store external job ID
|
|
106
|
-
await context.set_external_job_id(handler.request_id)
|
|
107
|
-
|
|
108
|
-
# Stream progress updates
|
|
109
|
-
from .....progress.models import ProgressUpdate
|
|
110
|
-
|
|
111
|
-
event_count = 0
|
|
112
|
-
async for event in handler.iter_events(with_logs=True):
|
|
113
|
-
event_count += 1
|
|
114
|
-
# Sample every 3rd event to avoid spam
|
|
115
|
-
if event_count % 3 == 0:
|
|
116
|
-
# Extract logs if available
|
|
117
|
-
logs = getattr(event, "logs", None)
|
|
118
|
-
if logs:
|
|
119
|
-
# Join log entries into a single message
|
|
120
|
-
if isinstance(logs, list):
|
|
121
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
122
|
-
else:
|
|
123
|
-
message = str(logs)
|
|
124
|
-
|
|
125
|
-
if message:
|
|
126
|
-
await context.publish_progress(
|
|
127
|
-
ProgressUpdate(
|
|
128
|
-
job_id=handler.request_id,
|
|
129
|
-
status="processing",
|
|
130
|
-
progress=50.0,
|
|
131
|
-
phase="processing",
|
|
132
|
-
message=message,
|
|
133
|
-
)
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
# Get final result
|
|
137
|
-
result = await handler.get()
|
|
138
|
-
|
|
139
|
-
# Extract video from result
|
|
140
|
-
# Expected structure: {"video": {"url": "...", "content_type": "...", ...}}
|
|
141
|
-
video_data = result.get("video")
|
|
142
|
-
if not video_data:
|
|
143
|
-
raise ValueError("No video returned from fal.ai API")
|
|
144
|
-
|
|
145
|
-
video_url = video_data.get("url")
|
|
146
|
-
if not video_url:
|
|
147
|
-
raise ValueError("Video missing URL in fal.ai response")
|
|
148
|
-
|
|
149
|
-
# Store video result
|
|
150
|
-
# Note: Fal API doesn't provide video dimensions/duration in the response,
|
|
151
|
-
# so we'll use defaults based on input parameters
|
|
152
|
-
width = 1280 if inputs.resolution == "720p" else 1920
|
|
153
|
-
height = 720 if inputs.resolution == "720p" else 1080
|
|
154
|
-
|
|
155
|
-
# Parse duration from "8s" format
|
|
156
|
-
duration_seconds = int(inputs.duration.rstrip("s"))
|
|
157
|
-
|
|
158
|
-
artifact = await context.store_video_result(
|
|
159
|
-
storage_url=video_url,
|
|
160
|
-
format="mp4",
|
|
161
|
-
width=width,
|
|
162
|
-
height=height,
|
|
163
|
-
duration=duration_seconds,
|
|
164
|
-
output_index=0,
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
return GeneratorResult(outputs=[artifact])
|
|
168
|
-
|
|
169
|
-
async def estimate_cost(self, inputs: Veo31FirstLastFrameToVideoInput) -> float:
|
|
170
|
-
"""Estimate cost for this generation in USD.
|
|
171
|
-
|
|
172
|
-
Note: Pricing information not available in Fal documentation.
|
|
173
|
-
Using placeholder value that should be updated with actual pricing.
|
|
174
|
-
"""
|
|
175
|
-
# TODO: Update with actual pricing from Fal when available
|
|
176
|
-
# Parse duration from "8s" format
|
|
177
|
-
duration_seconds = int(inputs.duration.rstrip("s"))
|
|
178
|
-
|
|
179
|
-
# Base cost per 8 seconds, scaled by actual duration
|
|
180
|
-
base_cost_8s = 0.15 # Placeholder estimate for 8s
|
|
181
|
-
duration_multiplier = duration_seconds / 8.0
|
|
182
|
-
cost = base_cost_8s * duration_multiplier
|
|
183
|
-
|
|
184
|
-
# 50% reduction if audio is disabled
|
|
185
|
-
if not inputs.generate_audio:
|
|
186
|
-
return cost * 0.5
|
|
187
|
-
return cost
|
package/templates/api/src/boards/generators/implementations/fal/video/veo31_image_to_video.py
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Google Veo 3.1 image-to-video generator.
|
|
3
|
-
|
|
4
|
-
Converts static images into animated videos based on text prompts using
|
|
5
|
-
Google's Veo 3.1 technology via fal.ai.
|
|
6
|
-
|
|
7
|
-
Based on Fal AI's fal-ai/veo3.1/image-to-video model.
|
|
8
|
-
See: https://fal.ai/models/fal-ai/veo3.1/image-to-video
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import os
|
|
12
|
-
from typing import Literal
|
|
13
|
-
|
|
14
|
-
from pydantic import BaseModel, Field
|
|
15
|
-
|
|
16
|
-
from ....artifacts import ImageArtifact
|
|
17
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class Veo31ImageToVideoInput(BaseModel):
|
|
21
|
-
"""Input schema for Veo 3.1 image-to-video generation.
|
|
22
|
-
|
|
23
|
-
Artifact fields (image) are automatically detected via type introspection
|
|
24
|
-
and resolved from generation IDs to ImageArtifact objects.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
prompt: str = Field(description="Text prompt describing the desired video content and motion")
|
|
28
|
-
image: ImageArtifact = Field(
|
|
29
|
-
description="Input image to animate. Should be 720p or higher in 16:9 or 9:16 aspect ratio"
|
|
30
|
-
)
|
|
31
|
-
aspect_ratio: Literal["9:16", "16:9"] = Field(
|
|
32
|
-
default="16:9",
|
|
33
|
-
description="Aspect ratio of the generated video",
|
|
34
|
-
)
|
|
35
|
-
duration: Literal["4s", "6s", "8s"] = Field(
|
|
36
|
-
default="8s",
|
|
37
|
-
description="Duration of the generated video in seconds",
|
|
38
|
-
)
|
|
39
|
-
generate_audio: bool = Field(
|
|
40
|
-
default=True,
|
|
41
|
-
description="Whether to generate audio for the video. Disabling uses 50% fewer credits",
|
|
42
|
-
)
|
|
43
|
-
resolution: Literal["720p", "1080p"] = Field(
|
|
44
|
-
default="720p",
|
|
45
|
-
description="Resolution of the generated video",
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class FalVeo31ImageToVideoGenerator(BaseGenerator):
|
|
50
|
-
"""Generator for creating videos from static images using Google Veo 3.1."""
|
|
51
|
-
|
|
52
|
-
name = "fal-veo31-image-to-video"
|
|
53
|
-
description = "Fal: Veo 3.1 - Convert images to videos with text-guided animation"
|
|
54
|
-
artifact_type = "video"
|
|
55
|
-
|
|
56
|
-
def get_input_schema(self) -> type[Veo31ImageToVideoInput]:
|
|
57
|
-
"""Return the input schema for this generator."""
|
|
58
|
-
return Veo31ImageToVideoInput
|
|
59
|
-
|
|
60
|
-
async def generate(
|
|
61
|
-
self, inputs: Veo31ImageToVideoInput, context: GeneratorExecutionContext
|
|
62
|
-
) -> GeneratorResult:
|
|
63
|
-
"""Generate video using fal.ai veo3.1/image-to-video."""
|
|
64
|
-
# Check for API key
|
|
65
|
-
if not os.getenv("FAL_KEY"):
|
|
66
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
67
|
-
|
|
68
|
-
# Import fal_client
|
|
69
|
-
try:
|
|
70
|
-
import fal_client
|
|
71
|
-
except ImportError as e:
|
|
72
|
-
raise ImportError(
|
|
73
|
-
"fal.ai SDK is required for FalVeo31ImageToVideoGenerator. "
|
|
74
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
75
|
-
) from e
|
|
76
|
-
|
|
77
|
-
# Upload image artifact to Fal's public storage
|
|
78
|
-
# Fal API requires publicly accessible URLs, but our storage_url might be:
|
|
79
|
-
# - Localhost URLs (not publicly accessible)
|
|
80
|
-
# - Private S3 buckets (not publicly accessible)
|
|
81
|
-
# So we upload to Fal's temporary storage first
|
|
82
|
-
from ..utils import upload_artifacts_to_fal
|
|
83
|
-
|
|
84
|
-
image_urls = await upload_artifacts_to_fal([inputs.image], context)
|
|
85
|
-
|
|
86
|
-
# Prepare arguments for fal.ai API
|
|
87
|
-
arguments = {
|
|
88
|
-
"prompt": inputs.prompt,
|
|
89
|
-
"image_url": image_urls[0],
|
|
90
|
-
"aspect_ratio": inputs.aspect_ratio,
|
|
91
|
-
"duration": inputs.duration,
|
|
92
|
-
"generate_audio": inputs.generate_audio,
|
|
93
|
-
"resolution": inputs.resolution,
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
# Submit async job
|
|
97
|
-
handler = await fal_client.submit_async(
|
|
98
|
-
"fal-ai/veo3.1/image-to-video",
|
|
99
|
-
arguments=arguments,
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
# Store external job ID
|
|
103
|
-
await context.set_external_job_id(handler.request_id)
|
|
104
|
-
|
|
105
|
-
# Stream progress updates
|
|
106
|
-
from .....progress.models import ProgressUpdate
|
|
107
|
-
|
|
108
|
-
event_count = 0
|
|
109
|
-
async for event in handler.iter_events(with_logs=True):
|
|
110
|
-
event_count += 1
|
|
111
|
-
# Sample every 3rd event to avoid spam
|
|
112
|
-
if event_count % 3 == 0:
|
|
113
|
-
# Extract logs if available
|
|
114
|
-
logs = getattr(event, "logs", None)
|
|
115
|
-
if logs:
|
|
116
|
-
# Join log entries into a single message
|
|
117
|
-
if isinstance(logs, list):
|
|
118
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
119
|
-
else:
|
|
120
|
-
message = str(logs)
|
|
121
|
-
|
|
122
|
-
if message:
|
|
123
|
-
await context.publish_progress(
|
|
124
|
-
ProgressUpdate(
|
|
125
|
-
job_id=handler.request_id,
|
|
126
|
-
status="processing",
|
|
127
|
-
progress=50.0,
|
|
128
|
-
phase="processing",
|
|
129
|
-
message=message,
|
|
130
|
-
)
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
# Get final result
|
|
134
|
-
result = await handler.get()
|
|
135
|
-
|
|
136
|
-
# Extract video from result
|
|
137
|
-
# Expected structure: {"video": {"url": "...", "content_type": "...", ...}}
|
|
138
|
-
video_data = result.get("video")
|
|
139
|
-
if not video_data:
|
|
140
|
-
raise ValueError("No video returned from fal.ai API")
|
|
141
|
-
|
|
142
|
-
video_url = video_data.get("url")
|
|
143
|
-
if not video_url:
|
|
144
|
-
raise ValueError("Video missing URL in fal.ai response")
|
|
145
|
-
|
|
146
|
-
# Calculate video dimensions based on resolution and aspect ratio
|
|
147
|
-
if inputs.resolution == "720p":
|
|
148
|
-
if inputs.aspect_ratio == "16:9":
|
|
149
|
-
width, height = 1280, 720
|
|
150
|
-
else: # 9:16
|
|
151
|
-
width, height = 720, 1280
|
|
152
|
-
else: # 1080p
|
|
153
|
-
if inputs.aspect_ratio == "16:9":
|
|
154
|
-
width, height = 1920, 1080
|
|
155
|
-
else: # 9:16
|
|
156
|
-
width, height = 1080, 1920
|
|
157
|
-
|
|
158
|
-
# Parse duration from "Xs" format
|
|
159
|
-
duration_seconds = int(inputs.duration.rstrip("s"))
|
|
160
|
-
|
|
161
|
-
artifact = await context.store_video_result(
|
|
162
|
-
storage_url=video_url,
|
|
163
|
-
format="mp4",
|
|
164
|
-
width=width,
|
|
165
|
-
height=height,
|
|
166
|
-
duration=duration_seconds,
|
|
167
|
-
output_index=0,
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
return GeneratorResult(outputs=[artifact])
|
|
171
|
-
|
|
172
|
-
async def estimate_cost(self, inputs: Veo31ImageToVideoInput) -> float:
|
|
173
|
-
"""Estimate cost for this generation in USD.
|
|
174
|
-
|
|
175
|
-
Note: Pricing information not available in Fal documentation.
|
|
176
|
-
Using placeholder value that should be updated with actual pricing.
|
|
177
|
-
"""
|
|
178
|
-
# TODO: Update with actual pricing from Fal when available
|
|
179
|
-
# Base cost, with 50% reduction if audio is disabled
|
|
180
|
-
base_cost = 0.15 # Placeholder estimate
|
|
181
|
-
if not inputs.generate_audio:
|
|
182
|
-
return base_cost * 0.5
|
|
183
|
-
return base_cost
|