Hiya! having a persistent issue already for 3 Days :
I’m using Supabase Deno / Edge Functions:
- I’ve called the Veo 3.0 model with the prompt, done the longrun operations successfully 1of 1, 1of 2, etc… until 1 of 7 more or less. Then got the VideoFile object as follows:
“Generated video file: {\n uri: "https://generativelanguage.googleapis.com/v1beta/files/ojhax2nibn8k:download?alt=media\”,\n videoBytes: undefined,\n mimeType: undefined\n}\n"
But when I tried to downoad as per the documentation generatedVideo.video it nevers downloads anything. all the console logs below // Download the video file that you can see on the code below are undefied.
Also when I post the URL on a webBrowser this is what I see (Attached- I don’t recognise this project)
This is the whole generate-ai-vIdeos edge function code running on Supabase:
import { serve } from '…
import { createClient } from …
import { GoogleGenAI } from “npm:@google/genai”
const corsHeaders = {
‘Access-Control-Allow-Origin’: ‘*’,
‘Access-Control-Allow-Headers’: ‘authorization, x-client-info, apikey, content-type’,
‘Access-Control-Allow-Methods’: ‘POST, OPTIONS’,
}
interface VideoFormat {
count: number;
dimensions: {
width: number;
height: number;
};
aspectRatio: string;
}
interface VideoObject {
prompt: string;
postType: string;
videoFormats: {
landscape: VideoFormat;
portrait: VideoFormat;
square: VideoFormat;
};
}
interface VideoGenerationRequest {
videoObject: VideoObject;
}
serve(async (req) => {
if (req.method === ‘OPTIONS’) {
return new Response(null, { headers: corsHeaders })
}
try {
const authHeader = req.headers.get(‘Authorization’)
if (!authHeader) {
throw new Error(‘No authorization header’)
}
const supabaseClient = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? ''
)
const { data: { user }, error: authError } = await supabaseClient.auth.getUser(
authHeader.replace('Bearer ', '')
)
if (authError || !user) {
throw new Error('Unauthorized')
}
const supabaseAdmin = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
)
const { videoObject }: VideoGenerationRequest = await req.json()
if (!videoObject || !videoObject.prompt) {
throw new Error('Video object with prompt is required')
}
console.log('=== VIDEO GENERATION REQUEST ===')
console.log('User ID:', user.id)
console.log('Prompt:', videoObject.prompt)
console.log('Video formats requested:', {
landscape: videoObject.videoFormats.landscape.count,
portrait: videoObject.videoFormats.portrait.count,
square: videoObject.videoFormats.square.count
})
console.log('=== END VIDEO GENERATION REQUEST ===')
// Get the Gemini API key
const geminiApiKey = Deno.env.get('GEMINI_API_KEY')
if (!geminiApiKey) {
throw new Error('Gemini API key not configured')
}
// Initialize Google GenAI
const ai = new GoogleGenAI({
apiKey: geminiApiKey
})
const generatedVideos = []
const errors = []
// Process each video format
for (const [formatName, formatData] of Object.entries(videoObject.videoFormats)) {
if (formatData.count === 0) continue
console.log(`Generating ${formatData.count} ${formatName} videos...`)
// Generate the specified number of videos for this format
for (let i = 0; i < formatData.count; i++) {
try {
console.log(`Generating ${formatName} video ${i + 1}/${formatData.count}`)
// Create database record first
const { data: videoRecord, error: recordError } = await supabaseAdmin
.from('ai_generated_videos')
.insert({
user_id: user.id,
prompt: videoObject.prompt,
video_format: formatName,
aspect_ratio: formatData.aspectRatio,
width: formatData.dimensions.width,
height: formatData.dimensions.height,
model_used: 'veo-3.0-generate-preview',
status: 'pending'
})
.select()
.single()
if (recordError) {
console.error('Error creating video record:', recordError)
errors.push(`Failed to create database record for ${formatName} video ${i + 1}`)
continue
}
console.log('Created video record:', videoRecord.id)
// Update status to processing
await supabaseAdmin
.from('ai_generated_videos')
.update({ status: 'processing' })
.eq('id', videoRecord.id)
// Generate video using Veo 3
console.log(`Calling Veo 3 API for ${formatName} video...`)
let operation = await ai.models.generateVideos({
model: "veo-3.0-generate-preview",
prompt: videoObject.prompt,
config: {
aspectRatio: formatData.aspectRatio
},
})
console.log('Video generation operation started:', operation.name)
// Update database with operation ID
await supabaseAdmin
.from('ai_generated_videos')
.update({ operation_id: operation.name })
.eq('id', videoRecord.id)
// Poll the operation status until the video is ready
let pollCount = 0
const maxPolls = 60 // Maximum 10 minutes (60 * 10 seconds)
while (!operation.done && pollCount < maxPolls) {
console.log(`Waiting for video generation to complete... (poll ${pollCount + 1}/${maxPolls})`)
await new Promise((resolve) => setTimeout(resolve, 10000)) // Wait 10 seconds
operation = await ai.operations.getVideosOperation({
operation: operation,
})
pollCount++
}
if (!operation.done) {
throw new Error('Video generation timed out after 10 minutes')
}
if (operation.error) {
throw new Error(`Video generation failed: ${operation.error.message || 'Unknown error'}`)
}
console.log('Video generation completed successfully')
// Get the generated video
const generatedVideo = operation.response?.generatedVideos?.[0]
if (!generatedVideo?.video) {
throw new Error('No video data in response')
}
console.log('Generated video file:', generatedVideo.video)
// Get user's bucket for storage
const { data: userBucket, error: bucketError } = await supabaseAdmin
.from('content_buckets')
.select('bucket_name')
.eq('user_id', user.id)
.limit(1)
.single()
if (bucketError || !userBucket) {
throw new Error('User bucket not found')
}
// Download and store the video
const timestamp = Date.now()
const randomId = Math.random().toString(36).substring(2, 15)
const fileName = `ai-video-${formatName}-${timestamp}-${randomId}.mp4`
const storagePath = `ai-generated-videos/${fileName}`
console.log('Downloading video from Google AI...')
// Download the video file
const videoFile = await ai.files.download({
file: generatedVideo.video
})
// Debug logging to understand videoFile object structure
console.log('=== VIDEO FILE DEBUG INFO ===')
console.log('Raw videoFile value:', videoFile)
console.log('Downloaded video file type:', typeof videoFile)
console.log('Is Uint8Array:', videoFile instanceof Uint8Array)
console.log('Is Blob:', videoFile instanceof Blob)
console.log('Size:', videoFile?.byteLength || videoFile?.size)
//console.log('videoFile type:', typeof videoFile)
//console.log('videoFile constructor:', videoFile?.constructor?.name)
//console.log('videoFile instanceof Uint8Array:', videoFile instanceof Uint8Array)
//console.log('videoFile instanceof ArrayBuffer:', videoFile instanceof ArrayBuffer)
//console.log('videoFile instanceof Blob:', videoFile instanceof Blob)
//console.log('videoFile properties:', Object.getOwnPropertyNames(videoFile))
console.log('videoFile size property:', videoFile?.size)
console.log('videoFile byteLength property:', videoFile?.byteLength)
console.log('videoFile length property:', videoFile?.length)
if (videoFile instanceof ArrayBuffer) {
console.log('ArrayBuffer byteLength:', videoFile.byteLength)
}
if (videoFile instanceof Blob) {
console.log('Blob size:', videoFile.size)
console.log('Blob type:', videoFile.type)
}
console.log('=== END VIDEO FILE DEBUG INFO ===')
console.log('Video downloaded, uploading to Supabase storage...')
// Upload to Supabase storage
const { data: uploadData, error: uploadError } = await supabaseAdmin.storage
.from(userBucket.bucket_name)
.upload(storagePath, videoFile, {
contentType: 'video/mp4',
upsert: false
})
if (uploadError) {
throw new Error(`Failed to upload video: ${uploadError.message}`)
}
console.log('Video uploaded successfully:', uploadData.path)
// Generate signed URL
const { data: signedUrlData } = await supabaseAdmin.storage
.from(userBucket.bucket_name)
.createSignedUrl(storagePath, 3600) // 1 hour expiry
// Update database record with completion
const { error: updateError } = await supabaseAdmin
.from('ai_generated_videos')
.update({
status: 'completed',
video_url: signedUrlData?.signedUrl,
storage_path: storagePath,
file_size: videoFile?.size || videoFile?.byteLength || 0,
updated_at: new Date().toISOString()
})
.eq('id', videoRecord.id)
if (updateError) {
console.error('Error updating video record:', updateError)
}
generatedVideos.push({
id: videoRecord.id,
format: formatName,
video_url: signedUrlData?.signedUrl,
storage_path: storagePath,
dimensions: formatData.dimensions,
aspect_ratio: formatData.aspectRatio
})
console.log(`Successfully generated ${formatName} video ${i + 1}/${formatData.count}`)
} catch (error) {
console.error(`Error generating ${formatName} video ${i + 1}:`, error)
// Update database record with error
const { data: failedRecord } = await supabaseAdmin
.from('ai_generated_videos')
.select('id')
.eq('user_id', user.id)
.eq('video_format', formatName)
.eq('status', 'processing')
.order('created_at', { ascending: false })
.limit(1)
.single()
if (failedRecord) {
await supabaseAdmin
.from('ai_generated_videos')
.update({
status: 'failed',
error_message: error.message || 'Unknown error',
updated_at: new Date().toISOString()
})
.eq('id', failedRecord.id)
}
errors.push(`${formatName} video ${i + 1}: ${error.message}`)
}
}
}
console.log('=== VIDEO GENERATION SUMMARY ===')
console.log(`Successfully generated: ${generatedVideos.length} videos`)
console.log(`Errors: ${errors.length}`)
console.log('Generated videos:', generatedVideos)
console.log('Errors:', errors)
console.log('=== END VIDEO GENERATION SUMMARY ===')
return new Response(
JSON.stringify({
success: generatedVideos.length > 0,
generated_videos: generatedVideos,
total_generated: generatedVideos.length,
errors: errors,
message: `Successfully generated ${generatedVideos.length} videos${errors.length > 0 ? ` with ${errors.length} errors` : ''}`
}),
{
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 200,
}
)
} catch (error) {
console.error(‘Error in generate-ai-videos function:’, error)
return new Response(
JSON.stringify({
success: false,
error: error.message || 'Internal server error'
}),
{
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 400,
}
)
}
})