@mixio-pro/kalaasetu-mcp 1.1.2 → 1.1.4

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.
@@ -1,160 +0,0 @@
1
- import { z } from "zod";
2
- import { callFalModel } from "../utils/fal.utils";
3
-
4
- /**
5
- * Calculate number of frames based on audio duration at 25 FPS
6
- * Adds 1 second buffer to ensure complete audio coverage
7
- */
8
- function calculateFramesFromAudioDuration(
9
- audioDurationSeconds: number
10
- ): number {
11
- const totalDuration = audioDurationSeconds + 1; // Add 1 second buffer
12
- const frames = Math.round(totalDuration * 25); // 25 FPS
13
-
14
- // Clamp to valid range (129-401 frames)
15
- return Math.max(129, Math.min(401, frames));
16
- }
17
-
18
- /**
19
- * FAL AI Hunyuan Avatar - High-Fidelity Audio-Driven Human Animation
20
- */
21
- export const hunyuanAvatar = {
22
- name: "hunyuan_avatar",
23
- description:
24
- "Generate high-fidelity audio-driven human animation videos using FAL AI Hunyuan Avatar. Creates realistic talking avatar animations from an image and audio file.",
25
- parameters: z.object({
26
- image_url: z
27
- .string()
28
- .describe("Public URL of the reference image for the avatar."),
29
- audio_url: z
30
- .string()
31
- .describe("Public URL of the audio file to drive the animation."),
32
- audio_duration_seconds: z
33
- .number()
34
- .optional()
35
- .describe(
36
- "Duration of the audio in seconds. If provided, will automatically calculate optimal frames (audio duration + 1 second buffer at 25 FPS)."
37
- ),
38
- text: z
39
- .string()
40
- .optional()
41
- .describe(
42
- "Text prompt describing the scene. Default: 'A cat is singing.'"
43
- ),
44
- num_frames: z
45
- .number()
46
- .optional()
47
- .describe(
48
- "Number of video frames to generate at 25 FPS. Range: 129 to 401. If not provided and audio_duration_seconds is given, will be calculated automatically. Default: 129"
49
- ),
50
- num_inference_steps: z
51
- .number()
52
- .optional()
53
- .describe(
54
- "Number of inference steps for sampling. Higher values give better quality but take longer. Range: 30 to 50. Default: 30"
55
- ),
56
- turbo_mode: z
57
- .boolean()
58
- .optional()
59
- .describe(
60
- "If true, the video will be generated faster with no noticeable degradation in visual quality. Default: true"
61
- ),
62
- seed: z.number().optional().describe("Random seed for generation."),
63
- fal_key: z
64
- .string()
65
- .optional()
66
- .describe(
67
- "FAL API key. If not provided, will use FAL_KEY environment variable."
68
- ),
69
- }),
70
- execute: async (args: {
71
- image_url: string;
72
- audio_url: string;
73
- audio_duration_seconds?: number;
74
- text?: string;
75
- num_frames?: number;
76
- num_inference_steps?: number;
77
- turbo_mode?: boolean;
78
- seed?: number;
79
- fal_key?: string;
80
- }) => {
81
- // Calculate frames from audio duration if provided and num_frames not specified
82
- let calculatedFrames = args.num_frames;
83
- if (
84
- args.audio_duration_seconds !== undefined &&
85
- args.num_frames === undefined
86
- ) {
87
- calculatedFrames = calculateFramesFromAudioDuration(
88
- args.audio_duration_seconds
89
- );
90
- }
91
-
92
- // Validate num_frames range if provided
93
- if (
94
- calculatedFrames !== undefined &&
95
- (calculatedFrames < 129 || calculatedFrames > 401)
96
- ) {
97
- throw new Error("num_frames must be between 129 and 401");
98
- }
99
-
100
- // Validate num_inference_steps range if provided
101
- if (
102
- args.num_inference_steps !== undefined &&
103
- (args.num_inference_steps < 30 || args.num_inference_steps > 50)
104
- ) {
105
- throw new Error("num_inference_steps must be between 30 and 50");
106
- }
107
-
108
- // Build input payload
109
- const input: any = {
110
- image_url: args.image_url,
111
- audio_url: args.audio_url,
112
- };
113
-
114
- // Add optional parameters if provided
115
- if (args.text !== undefined) {
116
- input.text = args.text;
117
- }
118
- if (calculatedFrames !== undefined) {
119
- input.num_frames = calculatedFrames;
120
- }
121
- if (args.num_inference_steps !== undefined) {
122
- input.num_inference_steps = args.num_inference_steps;
123
- }
124
- if (args.turbo_mode !== undefined) {
125
- input.turbo_mode = args.turbo_mode;
126
- }
127
- if (args.seed !== undefined) {
128
- input.seed = args.seed;
129
- }
130
-
131
- const result = await callFalModel("fal-ai/hunyuan-avatar", input, {
132
- falKey: args.fal_key,
133
- });
134
-
135
- // Extract video data from the response
136
- const videoData = result.data?.video;
137
-
138
- if (!videoData || !videoData.url) {
139
- throw new Error(
140
- `No video data in completed response: ${JSON.stringify(result.data)}`
141
- );
142
- }
143
-
144
- const videoUrl = videoData.url;
145
- const fileName = videoData.file_name || "hunyuan_avatar.mp4";
146
-
147
- return JSON.stringify({
148
- videos: [
149
- {
150
- url: videoUrl,
151
- filename: fileName,
152
- mimeType: "video/mp4",
153
- filesize: videoData.file_size,
154
- },
155
- ],
156
- message: "Hunyuan Avatar video generated successfully",
157
- requestId: result.requestId,
158
- });
159
- },
160
- };
@@ -1,156 +0,0 @@
1
- import { z } from "zod";
2
- import { callFalModel } from "../utils/fal.utils";
3
-
4
- /**
5
- * Calculate number of frames based on audio duration at 25 FPS
6
- * Adds 1 second buffer to ensure complete audio coverage
7
- */
8
- function calculateFramesFromAudioDuration(
9
- audioDurationSeconds: number
10
- ): number {
11
- const totalDuration = audioDurationSeconds + 1; // Add 1 second buffer
12
- const frames = Math.round(totalDuration * 25); // 25 FPS
13
-
14
- // Clamp to valid range (41-721 frames)
15
- return Math.max(41, Math.min(721, frames));
16
- }
17
-
18
- /**
19
- * FAL AI Infinitalk - Generate talking avatar video from image and audio
20
- */
21
- export const infinitalk = {
22
- name: "infinitalk",
23
- description:
24
- "Generate a talking avatar video from an image and audio file using FAL AI Infinitalk. The avatar lip-syncs to the provided audio with natural facial expressions.",
25
- parameters: z.object({
26
- image_url: z
27
- .string()
28
- .describe(
29
- "Public URL of the input image. If the input image does not match the chosen aspect ratio, it is resized and center cropped."
30
- ),
31
- audio_url: z
32
- .string()
33
- .describe("The Public URL of the audio file for lip-sync generation."),
34
- audio_duration_seconds: z
35
- .number()
36
- .optional()
37
- .describe(
38
- "Duration of the audio in seconds. If provided, will automatically calculate optimal frames (audio duration + 1 second buffer at 25 FPS)."
39
- ),
40
- prompt: z
41
- .string()
42
- .describe(
43
- "The text prompt to guide video generation (e.g., 'A woman with colorful hair talking on a podcast')"
44
- ),
45
- num_frames: z
46
- .number()
47
- .optional()
48
- .describe(
49
- "Number of frames to generate. Must be between 41 to 721. If not provided and audio_duration_seconds is given, will be calculated automatically. Default: 145"
50
- ),
51
- resolution: z
52
- .enum(["480p", "720p"])
53
- .optional()
54
- .describe("Resolution of the video to generate. Default: '480p'"),
55
- seed: z
56
- .number()
57
- .optional()
58
- .describe(
59
- "Random seed for reproducibility. If not provided, a random seed is chosen. Default: 42"
60
- ),
61
- acceleration: z
62
- .enum(["none", "regular", "high"])
63
- .optional()
64
- .describe(
65
- "The acceleration level to use for generation. Default: 'regular'"
66
- ),
67
- fal_key: z
68
- .string()
69
- .optional()
70
- .describe(
71
- "FAL API key. If not provided, will use FAL_KEY environment variable."
72
- ),
73
- }),
74
- execute: async (args: {
75
- image_url: string;
76
- audio_url: string;
77
- audio_duration_seconds?: number;
78
- prompt: string;
79
- num_frames?: number;
80
- resolution?: "480p" | "720p";
81
- seed?: number;
82
- acceleration?: "none" | "regular" | "high";
83
- fal_key?: string;
84
- }) => {
85
- // Calculate frames from audio duration if provided and num_frames not specified
86
- let calculatedFrames = args.num_frames;
87
- if (
88
- args.audio_duration_seconds !== undefined &&
89
- args.num_frames === undefined
90
- ) {
91
- calculatedFrames = calculateFramesFromAudioDuration(
92
- args.audio_duration_seconds
93
- );
94
- }
95
-
96
- // Validate num_frames range if provided
97
- if (
98
- calculatedFrames !== undefined &&
99
- (calculatedFrames < 41 || calculatedFrames > 721)
100
- ) {
101
- throw new Error("num_frames must be between 41 and 721");
102
- }
103
-
104
- // Build input payload
105
- const input: any = {
106
- image_url: args.image_url,
107
- audio_url: args.audio_url,
108
- prompt: args.prompt,
109
- };
110
-
111
- // Add optional parameters if provided
112
- if (calculatedFrames !== undefined) {
113
- input.num_frames = calculatedFrames;
114
- }
115
-
116
- input.resolution = args.resolution || "480p";
117
-
118
- if (args.seed !== undefined) {
119
- input.seed = args.seed;
120
- }
121
- if (args.acceleration !== undefined) {
122
- input.acceleration = args.acceleration;
123
- }
124
-
125
- const result = await callFalModel("fal-ai/infinitalk", input, {
126
- falKey: args.fal_key,
127
- });
128
-
129
- // Extract video data from the response
130
- const videoData = result.data?.video;
131
- const seed = result.data?.seed;
132
-
133
- if (!videoData || !videoData.url) {
134
- throw new Error(
135
- `No video data in completed response: ${JSON.stringify(result.data)}`
136
- );
137
- }
138
-
139
- const videoUrl = videoData.url;
140
- const fileName = videoData.file_name || "infinitalk.mp4";
141
-
142
- return JSON.stringify({
143
- videos: [
144
- {
145
- url: videoUrl,
146
- filename: fileName,
147
- mimeType: "video/mp4",
148
- filesize: videoData.file_size,
149
- },
150
- ],
151
- message: "Infinitalk video generated successfully",
152
- seed: seed,
153
- requestId: result.requestId,
154
- });
155
- },
156
- };
@@ -1,53 +0,0 @@
1
- import { fal } from "@fal-ai/client";
2
-
3
- export async function callFalModel(
4
- modelName: string,
5
- input: any,
6
- options: { falKey?: string; logs?: boolean } = {}
7
- ) {
8
- const { falKey, logs = true } = options;
9
- const key = falKey || process.env.FAL_KEY;
10
- if (!key) {
11
- throw new Error(
12
- "FAL_KEY is required. Provide it via fal_key parameter or FAL_KEY environment variable."
13
- );
14
- }
15
-
16
- fal.config({
17
- credentials: key,
18
- });
19
-
20
- console.error(`[${modelName}] Submitting request to FAL AI...`);
21
-
22
- try {
23
- const result = await fal.subscribe(modelName, {
24
- input,
25
- logs,
26
- onQueueUpdate: (update) => {
27
- if (update.status === "IN_PROGRESS") {
28
- console.error(`[${modelName}] Status: ${update.status}`);
29
- if (logs && "logs" in update && update.logs) {
30
- update.logs.forEach((log) => {
31
- console.error(`[${modelName}] ${log.message}`);
32
- });
33
- }
34
- } else if (update.status === "IN_QUEUE") {
35
- console.error(
36
- `[${modelName}] Status: ${update.status} - Waiting in queue...`
37
- );
38
- }
39
- },
40
- });
41
-
42
- console.error(`[${modelName}] Generation completed successfully`);
43
-
44
- return result;
45
- } catch (error: any) {
46
- console.error(`[${modelName}] Error:`, error);
47
- throw new Error(
48
- `FAL AI ${modelName} generation failed: ${
49
- error.message || JSON.stringify(error)
50
- }`
51
- );
52
- }
53
- }