@weirdfingers/baseboards 0.9.6 → 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 +560 -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/sync_lipsync_v2_pro.py
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
fal.ai sync-lipsync v2 pro video generator.
|
|
3
|
-
|
|
4
|
-
Generate high-quality realistic lipsync animations from audio while preserving
|
|
5
|
-
unique details like natural teeth and unique facial features.
|
|
6
|
-
|
|
7
|
-
Based on Fal AI's fal-ai/sync-lipsync/v2/pro model.
|
|
8
|
-
See: https://fal.ai/models/fal-ai/sync-lipsync/v2/pro
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import os
|
|
12
|
-
from typing import Literal
|
|
13
|
-
|
|
14
|
-
from pydantic import BaseModel, Field
|
|
15
|
-
|
|
16
|
-
from ....artifacts import AudioArtifact, VideoArtifact
|
|
17
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class SyncLipsyncV2ProInput(BaseModel):
|
|
21
|
-
"""Input schema for sync-lipsync v2 pro.
|
|
22
|
-
|
|
23
|
-
Artifact fields are automatically detected via type introspection
|
|
24
|
-
and resolved from generation IDs to artifact objects.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
video: VideoArtifact = Field(description="Input video for lip-sync animation")
|
|
28
|
-
audio: AudioArtifact = Field(description="Audio to synchronize with the video")
|
|
29
|
-
sync_mode: Literal["cut_off", "loop", "bounce", "silence", "remap"] = Field(
|
|
30
|
-
default="cut_off",
|
|
31
|
-
description="Lipsync synchronization approach when media durations differ",
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class FalSyncLipsyncV2ProGenerator(BaseGenerator):
|
|
36
|
-
"""Generator for high-quality realistic lip-synchronization animations."""
|
|
37
|
-
|
|
38
|
-
name = "fal-sync-lipsync-v2-pro"
|
|
39
|
-
description = "Fal: sync-lipsync v2 pro - High-quality lipsync preserving facial features"
|
|
40
|
-
artifact_type = "video"
|
|
41
|
-
|
|
42
|
-
def get_input_schema(self) -> type[SyncLipsyncV2ProInput]:
|
|
43
|
-
"""Return the input schema for this generator."""
|
|
44
|
-
return SyncLipsyncV2ProInput
|
|
45
|
-
|
|
46
|
-
async def generate(
|
|
47
|
-
self, inputs: SyncLipsyncV2ProInput, context: GeneratorExecutionContext
|
|
48
|
-
) -> GeneratorResult:
|
|
49
|
-
"""Generate lip-synced video using fal.ai sync-lipsync/v2/pro."""
|
|
50
|
-
# Check for API key
|
|
51
|
-
if not os.getenv("FAL_KEY"):
|
|
52
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
53
|
-
|
|
54
|
-
# Import fal_client
|
|
55
|
-
try:
|
|
56
|
-
import fal_client
|
|
57
|
-
except ImportError as e:
|
|
58
|
-
raise ImportError(
|
|
59
|
-
"fal.ai SDK is required for FalSyncLipsyncV2ProGenerator. "
|
|
60
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
61
|
-
) from e
|
|
62
|
-
|
|
63
|
-
# Upload video and audio artifacts to Fal's public storage
|
|
64
|
-
# Fal API requires publicly accessible URLs
|
|
65
|
-
from ..utils import upload_artifacts_to_fal
|
|
66
|
-
|
|
67
|
-
# Upload video and audio separately
|
|
68
|
-
video_urls = await upload_artifacts_to_fal([inputs.video], context)
|
|
69
|
-
audio_urls = await upload_artifacts_to_fal([inputs.audio], context)
|
|
70
|
-
|
|
71
|
-
# Prepare arguments for fal.ai API
|
|
72
|
-
arguments = {
|
|
73
|
-
"video_url": video_urls[0],
|
|
74
|
-
"audio_url": audio_urls[0],
|
|
75
|
-
"sync_mode": inputs.sync_mode,
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
# Submit async job
|
|
79
|
-
handler = await fal_client.submit_async(
|
|
80
|
-
"fal-ai/sync-lipsync/v2/pro",
|
|
81
|
-
arguments=arguments,
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
# Store external job ID
|
|
85
|
-
await context.set_external_job_id(handler.request_id)
|
|
86
|
-
|
|
87
|
-
# Stream progress updates
|
|
88
|
-
from .....progress.models import ProgressUpdate
|
|
89
|
-
|
|
90
|
-
event_count = 0
|
|
91
|
-
async for event in handler.iter_events(with_logs=True):
|
|
92
|
-
event_count += 1
|
|
93
|
-
# Sample every 3rd event to avoid spam
|
|
94
|
-
if event_count % 3 == 0:
|
|
95
|
-
# Extract logs if available
|
|
96
|
-
logs = getattr(event, "logs", None)
|
|
97
|
-
if logs:
|
|
98
|
-
# Join log entries into a single message
|
|
99
|
-
if isinstance(logs, list):
|
|
100
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
101
|
-
else:
|
|
102
|
-
message = str(logs)
|
|
103
|
-
|
|
104
|
-
if message:
|
|
105
|
-
await context.publish_progress(
|
|
106
|
-
ProgressUpdate(
|
|
107
|
-
job_id=handler.request_id,
|
|
108
|
-
status="processing",
|
|
109
|
-
progress=50.0, # Approximate mid-point progress
|
|
110
|
-
phase="processing",
|
|
111
|
-
message=message,
|
|
112
|
-
)
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
# Get final result
|
|
116
|
-
result = await handler.get()
|
|
117
|
-
|
|
118
|
-
# Extract video from result
|
|
119
|
-
# fal.ai returns: {"video": {"url": "...", "content_type": "video/mp4", ...}}
|
|
120
|
-
video_data = result.get("video")
|
|
121
|
-
|
|
122
|
-
if not video_data:
|
|
123
|
-
raise ValueError("No video returned from fal.ai API")
|
|
124
|
-
|
|
125
|
-
video_url = video_data.get("url")
|
|
126
|
-
if not video_url:
|
|
127
|
-
raise ValueError("Video missing URL in fal.ai response")
|
|
128
|
-
|
|
129
|
-
# Extract format from content_type (e.g., "video/mp4" -> "mp4")
|
|
130
|
-
content_type = video_data.get("content_type", "video/mp4")
|
|
131
|
-
video_format = content_type.split("/")[-1] if "/" in content_type else "mp4"
|
|
132
|
-
|
|
133
|
-
# Store the video result
|
|
134
|
-
# Note: The API doesn't return width/height/duration/fps, so we use input values
|
|
135
|
-
# The actual dimensions will be the same as the input video
|
|
136
|
-
artifact = await context.store_video_result(
|
|
137
|
-
storage_url=video_url,
|
|
138
|
-
format=video_format,
|
|
139
|
-
width=inputs.video.width,
|
|
140
|
-
height=inputs.video.height,
|
|
141
|
-
duration=inputs.audio.duration,
|
|
142
|
-
fps=inputs.video.fps,
|
|
143
|
-
output_index=0,
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
return GeneratorResult(outputs=[artifact])
|
|
147
|
-
|
|
148
|
-
async def estimate_cost(self, inputs: SyncLipsyncV2ProInput) -> float:
|
|
149
|
-
"""Estimate cost for sync-lipsync v2 pro generation in USD.
|
|
150
|
-
|
|
151
|
-
Pricing not specified in documentation, using estimate based on
|
|
152
|
-
typical video processing costs for pro models.
|
|
153
|
-
"""
|
|
154
|
-
# Estimated cost per generation for pro model
|
|
155
|
-
return 0.10
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
VEED Fabric 1.0 image-to-video generator.
|
|
3
|
-
|
|
4
|
-
Generate talking videos from any image using VEED Fabric 1.0.
|
|
5
|
-
This generator turns a static image into a talking video with synchronized
|
|
6
|
-
lip movements based on provided audio.
|
|
7
|
-
|
|
8
|
-
Based on Fal AI's veed/fabric-1.0 model.
|
|
9
|
-
See: https://fal.ai/models/veed/fabric-1.0
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
import os
|
|
13
|
-
from typing import Literal
|
|
14
|
-
|
|
15
|
-
from pydantic import BaseModel, Field
|
|
16
|
-
|
|
17
|
-
from ....artifacts import AudioArtifact, ImageArtifact
|
|
18
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
19
|
-
from ..utils import upload_artifacts_to_fal
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class VeedFabric10Input(BaseModel):
|
|
23
|
-
"""Input schema for VEED Fabric 1.0.
|
|
24
|
-
|
|
25
|
-
Artifact fields are automatically detected via type introspection
|
|
26
|
-
and resolved from generation IDs to artifact objects.
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
image_url: ImageArtifact = Field(description="Image to turn into a talking video")
|
|
30
|
-
audio_url: AudioArtifact = Field(description="Audio to synchronize with the image")
|
|
31
|
-
resolution: Literal["720p", "480p"] = Field(
|
|
32
|
-
default="720p", description="Output video resolution"
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class FalVeedFabric10Generator(BaseGenerator):
|
|
37
|
-
"""Generator for turning images into talking videos using VEED Fabric 1.0."""
|
|
38
|
-
|
|
39
|
-
name = "veed-fabric-1.0"
|
|
40
|
-
description = "VEED: Fabric 1.0 - Turn any image into a talking video"
|
|
41
|
-
artifact_type = "video"
|
|
42
|
-
|
|
43
|
-
def get_input_schema(self) -> type[VeedFabric10Input]:
|
|
44
|
-
"""Return the input schema for this generator."""
|
|
45
|
-
return VeedFabric10Input
|
|
46
|
-
|
|
47
|
-
async def generate(
|
|
48
|
-
self, inputs: VeedFabric10Input, context: GeneratorExecutionContext
|
|
49
|
-
) -> GeneratorResult:
|
|
50
|
-
"""Generate talking video using VEED Fabric 1.0."""
|
|
51
|
-
# Check for API key
|
|
52
|
-
if not os.getenv("FAL_KEY"):
|
|
53
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
54
|
-
|
|
55
|
-
# Import fal_client
|
|
56
|
-
try:
|
|
57
|
-
import fal_client
|
|
58
|
-
except ImportError as e:
|
|
59
|
-
raise ImportError(
|
|
60
|
-
"fal.ai SDK is required for FalVeedFabric10Generator. "
|
|
61
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
62
|
-
) from e
|
|
63
|
-
|
|
64
|
-
# Upload image and audio artifacts to Fal's public storage
|
|
65
|
-
# Fal API requires publicly accessible URLs
|
|
66
|
-
image_urls = await upload_artifacts_to_fal([inputs.image_url], context)
|
|
67
|
-
audio_urls = await upload_artifacts_to_fal([inputs.audio_url], context)
|
|
68
|
-
|
|
69
|
-
# Prepare arguments for fal.ai API
|
|
70
|
-
arguments = {
|
|
71
|
-
"image_url": image_urls[0],
|
|
72
|
-
"audio_url": audio_urls[0],
|
|
73
|
-
"resolution": inputs.resolution,
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
# Submit async job
|
|
77
|
-
handler = await fal_client.submit_async(
|
|
78
|
-
"veed/fabric-1.0",
|
|
79
|
-
arguments=arguments,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
# Store external job ID
|
|
83
|
-
await context.set_external_job_id(handler.request_id)
|
|
84
|
-
|
|
85
|
-
# Stream progress updates
|
|
86
|
-
from .....progress.models import ProgressUpdate
|
|
87
|
-
|
|
88
|
-
event_count = 0
|
|
89
|
-
async for event in handler.iter_events(with_logs=True):
|
|
90
|
-
event_count += 1
|
|
91
|
-
# Sample every 3rd event to avoid spamming progress updates
|
|
92
|
-
if event_count % 3 == 0:
|
|
93
|
-
logs = getattr(event, "logs", None)
|
|
94
|
-
if logs:
|
|
95
|
-
if isinstance(logs, list):
|
|
96
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
97
|
-
else:
|
|
98
|
-
message = str(logs)
|
|
99
|
-
|
|
100
|
-
if message:
|
|
101
|
-
await context.publish_progress(
|
|
102
|
-
ProgressUpdate(
|
|
103
|
-
job_id=handler.request_id,
|
|
104
|
-
status="processing",
|
|
105
|
-
progress=50.0,
|
|
106
|
-
phase="processing",
|
|
107
|
-
message=message,
|
|
108
|
-
)
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
# Get final result
|
|
112
|
-
result = await handler.get()
|
|
113
|
-
|
|
114
|
-
# Extract video from result
|
|
115
|
-
# Fabric 1.0 API returns: {"video": {"url": "...", "content_type": "video/mp4", ...}}
|
|
116
|
-
video_data = result.get("video")
|
|
117
|
-
|
|
118
|
-
if not video_data:
|
|
119
|
-
raise ValueError(
|
|
120
|
-
"No video returned from VEED Fabric 1.0 API. "
|
|
121
|
-
f"Response structure: {list(result.keys())}"
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
video_url = video_data.get("url")
|
|
125
|
-
if not video_url:
|
|
126
|
-
raise ValueError(
|
|
127
|
-
f"Video missing URL in VEED response. Video data keys: {list(video_data.keys())}"
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
# Determine video format with fallback strategy:
|
|
131
|
-
# 1. Try to extract from URL extension
|
|
132
|
-
# 2. Parse content_type only if it's a video/* MIME type
|
|
133
|
-
# 3. Default to mp4
|
|
134
|
-
video_format = "mp4" # Default
|
|
135
|
-
|
|
136
|
-
if video_url:
|
|
137
|
-
url_parts = video_url.split(".")
|
|
138
|
-
if len(url_parts) > 1:
|
|
139
|
-
ext = url_parts[-1].split("?")[0].lower()
|
|
140
|
-
if ext in ["mp4", "webm", "mov", "avi"]:
|
|
141
|
-
video_format = ext
|
|
142
|
-
|
|
143
|
-
if video_format == "mp4":
|
|
144
|
-
content_type = video_data.get("content_type", "")
|
|
145
|
-
if content_type.startswith("video/"):
|
|
146
|
-
video_format = content_type.split("/")[-1]
|
|
147
|
-
|
|
148
|
-
# Determine output dimensions based on resolution
|
|
149
|
-
# 720p = 1280x720, 480p = 854x480 (16:9 aspect ratio)
|
|
150
|
-
if inputs.resolution == "720p":
|
|
151
|
-
output_width = 1280
|
|
152
|
-
output_height = 720
|
|
153
|
-
else:
|
|
154
|
-
output_width = 854
|
|
155
|
-
output_height = 480
|
|
156
|
-
|
|
157
|
-
# Store the video result
|
|
158
|
-
artifact = await context.store_video_result(
|
|
159
|
-
storage_url=video_url,
|
|
160
|
-
format=video_format,
|
|
161
|
-
width=output_width,
|
|
162
|
-
height=output_height,
|
|
163
|
-
duration=inputs.audio_url.duration,
|
|
164
|
-
fps=30.0, # Standard frame rate for talking videos
|
|
165
|
-
output_index=0,
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
return GeneratorResult(outputs=[artifact])
|
|
169
|
-
|
|
170
|
-
async def estimate_cost(self, inputs: VeedFabric10Input) -> float:
|
|
171
|
-
"""Estimate cost for VEED Fabric 1.0 generation in USD.
|
|
172
|
-
|
|
173
|
-
Pricing not specified in documentation, using estimate based on
|
|
174
|
-
typical video lipsync processing costs.
|
|
175
|
-
"""
|
|
176
|
-
# Fixed cost estimate of $0.08 per generation
|
|
177
|
-
# Based on typical AI video processing costs
|
|
178
|
-
# This is a conservative estimate and should be updated when official
|
|
179
|
-
# pricing information becomes available
|
|
180
|
-
return 0.08
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
VEED Lipsync video generator.
|
|
3
|
-
|
|
4
|
-
Generate realistic lipsync from any audio using VEED's latest model.
|
|
5
|
-
This generator synchronizes lip movements in video with provided audio.
|
|
6
|
-
|
|
7
|
-
Based on Fal AI's veed/lipsync model.
|
|
8
|
-
See: https://fal.ai/models/veed/lipsync
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import os
|
|
12
|
-
|
|
13
|
-
from pydantic import BaseModel, Field
|
|
14
|
-
|
|
15
|
-
from ....artifacts import AudioArtifact, VideoArtifact
|
|
16
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
17
|
-
from ..utils import upload_artifacts_to_fal
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class VeedLipsyncInput(BaseModel):
|
|
21
|
-
"""Input schema for VEED lipsync.
|
|
22
|
-
|
|
23
|
-
Artifact fields are automatically detected via type introspection
|
|
24
|
-
and resolved from generation IDs to artifact objects.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
video_url: VideoArtifact = Field(description="Video to apply lip-sync animation to")
|
|
28
|
-
audio_url: AudioArtifact = Field(description="Audio to synchronize with the video")
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class FalVeedLipsyncGenerator(BaseGenerator):
|
|
32
|
-
"""Generator for realistic lip-synchronization using VEED's model."""
|
|
33
|
-
|
|
34
|
-
name = "veed-lipsync"
|
|
35
|
-
description = "VEED: Lipsync - Generate realistic lipsync from any audio"
|
|
36
|
-
artifact_type = "video"
|
|
37
|
-
|
|
38
|
-
def get_input_schema(self) -> type[VeedLipsyncInput]:
|
|
39
|
-
"""Return the input schema for this generator."""
|
|
40
|
-
return VeedLipsyncInput
|
|
41
|
-
|
|
42
|
-
async def generate(
|
|
43
|
-
self, inputs: VeedLipsyncInput, context: GeneratorExecutionContext
|
|
44
|
-
) -> GeneratorResult:
|
|
45
|
-
"""Generate lip-synced video using VEED lipsync."""
|
|
46
|
-
# Check for API key
|
|
47
|
-
if not os.getenv("FAL_KEY"):
|
|
48
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
49
|
-
|
|
50
|
-
# Import fal_client
|
|
51
|
-
try:
|
|
52
|
-
import fal_client
|
|
53
|
-
except ImportError as e:
|
|
54
|
-
raise ImportError(
|
|
55
|
-
"fal.ai SDK is required for FalVeedLipsyncGenerator. "
|
|
56
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
57
|
-
) from e
|
|
58
|
-
|
|
59
|
-
# Upload video and audio artifacts to Fal's public storage
|
|
60
|
-
# Fal API requires publicly accessible URLs
|
|
61
|
-
# Upload video and audio separately
|
|
62
|
-
video_urls = await upload_artifacts_to_fal([inputs.video_url], context)
|
|
63
|
-
audio_urls = await upload_artifacts_to_fal([inputs.audio_url], context)
|
|
64
|
-
|
|
65
|
-
# Prepare arguments for fal.ai API
|
|
66
|
-
arguments = {
|
|
67
|
-
"video_url": video_urls[0],
|
|
68
|
-
"audio_url": audio_urls[0],
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
# Submit async job
|
|
72
|
-
handler = await fal_client.submit_async(
|
|
73
|
-
"veed/lipsync",
|
|
74
|
-
arguments=arguments,
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
# Store external job ID
|
|
78
|
-
await context.set_external_job_id(handler.request_id)
|
|
79
|
-
|
|
80
|
-
# Stream progress updates
|
|
81
|
-
from .....progress.models import ProgressUpdate
|
|
82
|
-
|
|
83
|
-
event_count = 0
|
|
84
|
-
async for event in handler.iter_events(with_logs=True):
|
|
85
|
-
event_count += 1
|
|
86
|
-
# Sample every 3rd event to avoid spamming progress updates
|
|
87
|
-
# This provides regular feedback without overwhelming the system
|
|
88
|
-
if event_count % 3 == 0:
|
|
89
|
-
# Extract logs if available
|
|
90
|
-
logs = getattr(event, "logs", None)
|
|
91
|
-
if logs:
|
|
92
|
-
# Join log entries into a single message
|
|
93
|
-
if isinstance(logs, list):
|
|
94
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
95
|
-
else:
|
|
96
|
-
message = str(logs)
|
|
97
|
-
|
|
98
|
-
if message:
|
|
99
|
-
await context.publish_progress(
|
|
100
|
-
ProgressUpdate(
|
|
101
|
-
job_id=handler.request_id,
|
|
102
|
-
status="processing",
|
|
103
|
-
# Using fixed 50% since API doesn't provide granular progress
|
|
104
|
-
# This indicates processing is underway without false precision
|
|
105
|
-
progress=50.0,
|
|
106
|
-
phase="processing",
|
|
107
|
-
message=message,
|
|
108
|
-
)
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
# Get final result
|
|
112
|
-
result = await handler.get()
|
|
113
|
-
|
|
114
|
-
# Extract video from result
|
|
115
|
-
# VEED API returns: {"video": {"url": "...", "content_type": "video/mp4", ...}}
|
|
116
|
-
video_data = result.get("video")
|
|
117
|
-
|
|
118
|
-
if not video_data:
|
|
119
|
-
raise ValueError(
|
|
120
|
-
f"No video returned from VEED API. Response structure: {list(result.keys())}"
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
video_url = video_data.get("url")
|
|
124
|
-
if not video_url:
|
|
125
|
-
raise ValueError(
|
|
126
|
-
f"Video missing URL in VEED response. Video data keys: {list(video_data.keys())}"
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
# Determine video format with fallback strategy:
|
|
130
|
-
# 1. Try to extract from URL extension (most reliable)
|
|
131
|
-
# 2. Parse content_type only if it's a video/* MIME type
|
|
132
|
-
# 3. Default to mp4 (most common format for this API)
|
|
133
|
-
video_format = "mp4" # Default
|
|
134
|
-
|
|
135
|
-
# Try extracting extension from URL
|
|
136
|
-
if video_url:
|
|
137
|
-
url_parts = video_url.split(".")
|
|
138
|
-
if len(url_parts) > 1:
|
|
139
|
-
ext = url_parts[-1].split("?")[0].lower() # Remove query params
|
|
140
|
-
if ext in ["mp4", "webm", "mov", "avi"]: # Common video formats
|
|
141
|
-
video_format = ext
|
|
142
|
-
|
|
143
|
-
# If no valid extension found, try content_type (only if it's video/*)
|
|
144
|
-
if video_format == "mp4": # Still using default
|
|
145
|
-
content_type = video_data.get("content_type", "")
|
|
146
|
-
if content_type.startswith("video/"):
|
|
147
|
-
video_format = content_type.split("/")[-1]
|
|
148
|
-
|
|
149
|
-
# Store the video result
|
|
150
|
-
# Note: The API doesn't return width/height/duration/fps in documentation
|
|
151
|
-
# Using input video dimensions and audio duration
|
|
152
|
-
artifact = await context.store_video_result(
|
|
153
|
-
storage_url=video_url,
|
|
154
|
-
format=video_format,
|
|
155
|
-
width=inputs.video_url.width,
|
|
156
|
-
height=inputs.video_url.height,
|
|
157
|
-
duration=inputs.audio_url.duration,
|
|
158
|
-
fps=inputs.video_url.fps,
|
|
159
|
-
output_index=0,
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
return GeneratorResult(outputs=[artifact])
|
|
163
|
-
|
|
164
|
-
async def estimate_cost(self, inputs: VeedLipsyncInput) -> float:
|
|
165
|
-
"""Estimate cost for VEED lipsync generation in USD.
|
|
166
|
-
|
|
167
|
-
Pricing not specified in documentation, using estimate based on
|
|
168
|
-
typical video lipsync processing costs.
|
|
169
|
-
"""
|
|
170
|
-
# Fixed cost estimate of $0.05 per generation
|
|
171
|
-
# Based on typical AI video processing costs (~$0.03-0.07 per minute)
|
|
172
|
-
# This is a conservative estimate and should be updated when official
|
|
173
|
-
# pricing information becomes available from VEED/FAL
|
|
174
|
-
return 0.05
|