@snahrup/notion-hero-image-mcp
Version:
AI-powered animated hero banner generator for Notion pages. Integrates DALL-E 3, Google Veo 3, Cloudinary CDN, and Notion API.
116 lines (112 loc) • 3.97 kB
JavaScript
import { VertexAI } from '@google-cloud/vertexai';
import { join } from 'path';
import { tmpdir } from 'os';
const vertexAI = new VertexAI({
project: process.env.GOOGLE_PROJECT,
location: process.env.GOOGLE_REGION || 'us-central1',
});
const definition = {
name: 'generate_video',
description: 'Animate a still image using Google Veo 3 with detailed animation script',
inputSchema: {
type: 'object',
properties: {
image_url: {
type: 'string',
description: 'URL of the still image to animate'
},
title: {
type: 'string',
description: 'Title for the animation'
},
scene_description: {
type: 'string',
description: 'Detailed scene description'
},
animation_beats: {
type: 'array',
description: 'Timestamped animation sequences',
items: {
type: 'object',
properties: {
timeRange: { type: 'string' },
description: { type: 'string' }
}
}
},
style_tags: {
type: 'string',
description: 'Style descriptors for the animation'
},
duration: {
type: 'number',
description: 'Duration in seconds (max 8)',
default: 8,
maximum: 8
}
},
required: ['image_url', 'title', 'scene_description']
}
};
const formatVeoPrompt = (params) => {
const { title, scene_description, animation_beats = [], style_tags = '', duration = 8 } = params;
// Format animation beats with timestamps
const beatsFormatted = animation_beats.map((beat) => `${beat.timeRange} ${beat.description}`).join('\n');
return `
TITLE: "${title}"
REFERENCE_IMAGE: [provided]
DURATION: ${duration} seconds, 30 fps, seamless loop
ASPECT_RATIO: 16 × 9 (banner-safe center band 1280 × 480)
AUDIO: none
SCENE DESCRIPTION:
${scene_description}
ANIMATION BEATS:
${beatsFormatted}
STYLE TAGS: ${style_tags}
NOTES:
- First and last frames must be identical for seamless loop
- Maintain consistent lighting and atmosphere
- Use smooth ease curves for all transitions
- Keep motion subtle and elegant for Notion banner use
`;
};
const handler = {
'tools/call': async (request) => {
if (request.params.name !== 'generate_video')
return null;
const params = request.params.arguments;
const veoPrompt = formatVeoPrompt(params);
const generativeModel = vertexAI.getGenerativeModel({
model: 'veo-003',
});
// Generate video using Veo 3
const result = await generativeModel.generateContent({
contents: [{
role: 'user',
parts: [{
text: veoPrompt
}, {
inlineData: {
mimeType: 'image/png',
data: params.image_url // This would need proper base64 encoding in production
}
}]
}]
});
// In production, this would return the actual video URL from Vertex AI
const videoPath = join(tmpdir(), `video-${Date.now()}.mp4`);
return {
content: [{
type: 'text',
text: JSON.stringify({
mp4_url: `file://${videoPath}`,
local_path: videoPath,
duration: params.duration || 8,
animation_script: veoPrompt
})
}]
};
}
};
export default { definition, handler };
//# sourceMappingURL=generate-video.js.map