From db91b461c623e42cd1aba8f4018e7e0af20982c6 Mon Sep 17 00:00:00 2001 From: Guiners Date: Thu, 27 Nov 2025 13:12:08 +0100 Subject: [PATCH 1/5] adding videogen samples and tests --- genai/test/videogen-with-img.test.js | 59 ++++++++++++++++ genai/test/videogen-with-txt.test.js | 59 ++++++++++++++++ genai/video-generation/videogen-with-img.js | 75 +++++++++++++++++++++ genai/video-generation/videogen-with-txt.js | 70 +++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 genai/test/videogen-with-img.test.js create mode 100644 genai/test/videogen-with-txt.test.js create mode 100644 genai/video-generation/videogen-with-img.js create mode 100644 genai/video-generation/videogen-with-txt.js diff --git a/genai/test/videogen-with-img.test.js b/genai/test/videogen-with-img.test.js new file mode 100644 index 0000000000..9c8a65da3d --- /dev/null +++ b/genai/test/videogen-with-img.test.js @@ -0,0 +1,59 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const {assert} = require('chai'); +const {describe, it} = require('mocha'); +const {Storage} = require('@google-cloud/storage'); + +const location = process.env.GOOGLE_CLOUD_LOCATION || 'global'; +const projectId = process.env.CAIP_PROJECT_ID; +const sample = require('../video-generation/videogen-with-img.js'); +const {delay} = require('./util'); + +const storage = new Storage(); + +const GCS_OUTPUT_BUCKET = 'nodejs-docs-samples-tests'; + +async function gcs_output_uri() { + const dt = new Date(); + const prefix = `text_output/${dt.toISOString()}`; + const fullUri = `gs://${GCS_OUTPUT_BUCKET}/${prefix}`; + + return { + uri: fullUri, + async cleanup() { + const [files] = await storage.bucket(GCS_OUTPUT_BUCKET).getFiles({ + prefix, + }); + for (const file of files) { + await file.delete(); + } + }, + }; +} + +describe('videogen-with-img', async () => { + it('should generate video content from an image', async function () { + this.timeout(180000); + this.retries(4); + await delay(this.test); + const gscOutput = gcs_output_uri(); + const gscUri = (await gscOutput).uri; + const output = await sample.generateVideo(gscUri, projectId, location); + console.log('output', output); + assert(output); + }); +}); diff --git a/genai/test/videogen-with-txt.test.js b/genai/test/videogen-with-txt.test.js new file mode 100644 index 0000000000..adb3995fc9 --- /dev/null +++ b/genai/test/videogen-with-txt.test.js @@ -0,0 +1,59 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const {assert} = require('chai'); +const {describe, it} = require('mocha'); +const {Storage} = require('@google-cloud/storage'); + +const location = process.env.GOOGLE_CLOUD_LOCATION || 'global'; +const projectId = process.env.CAIP_PROJECT_ID; +const sample = require('../video-generation/videogen-with-txt.js'); +const {delay} = require('./util'); + +const storage = new Storage(); + +const GCS_OUTPUT_BUCKET = 'nodejs-docs-samples-tests'; + +async function gcs_output_uri() { + const dt = new Date(); + const prefix = `text_output/${dt.toISOString()}`; + const fullUri = `gs://${GCS_OUTPUT_BUCKET}/${prefix}`; + + return { + uri: fullUri, + async cleanup() { + const [files] = await storage.bucket(GCS_OUTPUT_BUCKET).getFiles({ + prefix, + }); + for (const file of files) { + await file.delete(); + } + }, + }; +} + +describe('videogen-with-txt', async () => { + it('should generate video content from a text prompt', async function () { + this.timeout(180000); + this.retries(4); + await delay(this.test); + const gscOutput = gcs_output_uri(); + const gscUri = (await gscOutput).uri; + const output = await sample.generateVideo(gscUri, projectId, location); + console.log('output', output); + assert(output); + }); +}); diff --git a/genai/video-generation/videogen-with-img.js b/genai/video-generation/videogen-with-img.js new file mode 100644 index 0000000000..d43c8ddd89 --- /dev/null +++ b/genai/video-generation/videogen-with-img.js @@ -0,0 +1,75 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START googlegenaisdk_videogen_with_img] +const {GoogleGenAI} = require('@google/genai'); + +const GOOGLE_CLOUD_PROJECT = process.env.GOOGLE_CLOUD_PROJECT; +const GOOGLE_CLOUD_LOCATION = process.env.GOOGLE_CLOUD_LOCATION || 'global'; + +async function generateVideo( + outputGcsUri, + projectId = GOOGLE_CLOUD_PROJECT, + location = GOOGLE_CLOUD_LOCATION +) { + const client = new GoogleGenAI({ + vertexai: true, + project: projectId, + location: location, + }); + + let operation = await client.models.generateVideos({ + model: 'veo-3.0-generate-preview', + prompt: + 'Extreme close-up of a cluster of vibrant wildflowers swaying gently in a sun-drenched meadow', + image: { + gcsUri: 'gs://cloud-samples-data/generative-ai/image/flowers.png', + mimeType: 'image/png', + }, + config: { + aspectRatio: '16:9', + outputGcsUri: outputGcsUri, + }, + }); + + while (!operation.done) { + await new Promise(resolve => setTimeout(resolve, 15000)); + operation = await client.operations.get({operation: operation}); + console.log(operation); + } + + if (operation.response) { + console.log(operation.response.generatedVideos[0].video.uri); + } + return operation; +} + +//todo +// output GenerateVideosOperation { +// name: 'projects/cloud-ai-devrel-softserve/locations/us-central1/publishers/google/models/veo-3.0-generate-preview/operations/adf19783-9f21-49c4-a4af-3265c2df4326', +// metadata: undefined, +// done: true, +// error: { +// code: 7, +// message: "service-448220130128@gcp-sa-aiplatform.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object. Permission 'storage.objects.create' denied on resource (or it may not exist). service-448220130128@gcp-sa-aiplatform.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object. Permission 'storage.objects.create' denied on resource (or it may not exist)." +// } +// } + +// [END googlegenaisdk_videogen_with_img] + +module.exports = { + generateVideo, +}; diff --git a/genai/video-generation/videogen-with-txt.js b/genai/video-generation/videogen-with-txt.js new file mode 100644 index 0000000000..0ac77d9b05 --- /dev/null +++ b/genai/video-generation/videogen-with-txt.js @@ -0,0 +1,70 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START googlegenaisdk_videogen_with_txt] +const {GoogleGenAI} = require('@google/genai'); + +const GOOGLE_CLOUD_PROJECT = process.env.GOOGLE_CLOUD_PROJECT; +const GOOGLE_CLOUD_LOCATION = process.env.GOOGLE_CLOUD_LOCATION || 'global'; + +async function generateVideo( + outputGcsUri, + projectId = GOOGLE_CLOUD_PROJECT, + location = GOOGLE_CLOUD_LOCATION +) { + const client = new GoogleGenAI({ + vertexai: true, + project: projectId, + location: location, + }); + + let operation = await client.models.generateVideos({ + model: 'veo-3.0-generate-001', + prompt: 'a cat reading a book', + config: { + aspectRatio: '16:9', + outputGcsUri: outputGcsUri, + }, + }); + + while (!operation.done) { + await new Promise(resolve => setTimeout(resolve, 15000)); + operation = await client.operations.get({operation: operation}); + console.log(operation); + } + + if (operation.response) { + console.log(operation.response.generatedVideos[0].video.uri); + } + return operation; +} + +//todo +// output GenerateVideosOperation { +// name: 'projects/cloud-ai-devrel-softserve/locations/us-central1/publishers/google/models/veo-3.0-generate-001/operations/33bcfb22-c402-4bae-80d8-e2f5b53e3be6', +// metadata: undefined, +// done: true, +// error: { +// code: 7, +// message: "service-448220130128@gcp-sa-aiplatform.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object. Permission 'storage.objects.create' denied on resource (or it may not exist). service-448220130128@gcp-sa-aiplatform.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object. Permission 'storage.objects.create' denied on resource (or it may not exist)." +// } +// } + +// [END googlegenaisdk_videogen_with_txt] + +module.exports = { + generateVideo, +}; From 4ae01fa99db1779530ac136669f65b7d96e3241c Mon Sep 17 00:00:00 2001 From: Robert Kozak <50328216+Guiners@users.noreply.github.com> Date: Thu, 27 Nov 2025 13:16:39 +0100 Subject: [PATCH 2/5] Update genai/test/videogen-with-img.test.js Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- genai/test/videogen-with-img.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genai/test/videogen-with-img.test.js b/genai/test/videogen-with-img.test.js index 9c8a65da3d..5999464380 100644 --- a/genai/test/videogen-with-img.test.js +++ b/genai/test/videogen-with-img.test.js @@ -29,7 +29,7 @@ const GCS_OUTPUT_BUCKET = 'nodejs-docs-samples-tests'; async function gcs_output_uri() { const dt = new Date(); - const prefix = `text_output/${dt.toISOString()}`; + const prefix = `video_output/${dt.toISOString()}`; const fullUri = `gs://${GCS_OUTPUT_BUCKET}/${prefix}`; return { From 8d9d683edf86a908509eeb5de0d380dff688f14c Mon Sep 17 00:00:00 2001 From: Guiners Date: Thu, 27 Nov 2025 13:18:45 +0100 Subject: [PATCH 3/5] adding videogen samples and tests --- genai/video-generation/videogen-with-img.js | 10 ---------- genai/video-generation/videogen-with-txt.js | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/genai/video-generation/videogen-with-img.js b/genai/video-generation/videogen-with-img.js index d43c8ddd89..b717f96536 100644 --- a/genai/video-generation/videogen-with-img.js +++ b/genai/video-generation/videogen-with-img.js @@ -57,16 +57,6 @@ async function generateVideo( return operation; } -//todo -// output GenerateVideosOperation { -// name: 'projects/cloud-ai-devrel-softserve/locations/us-central1/publishers/google/models/veo-3.0-generate-preview/operations/adf19783-9f21-49c4-a4af-3265c2df4326', -// metadata: undefined, -// done: true, -// error: { -// code: 7, -// message: "service-448220130128@gcp-sa-aiplatform.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object. Permission 'storage.objects.create' denied on resource (or it may not exist). service-448220130128@gcp-sa-aiplatform.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object. Permission 'storage.objects.create' denied on resource (or it may not exist)." -// } -// } // [END googlegenaisdk_videogen_with_img] diff --git a/genai/video-generation/videogen-with-txt.js b/genai/video-generation/videogen-with-txt.js index 0ac77d9b05..58206e7cb9 100644 --- a/genai/video-generation/videogen-with-txt.js +++ b/genai/video-generation/videogen-with-txt.js @@ -52,16 +52,6 @@ async function generateVideo( return operation; } -//todo -// output GenerateVideosOperation { -// name: 'projects/cloud-ai-devrel-softserve/locations/us-central1/publishers/google/models/veo-3.0-generate-001/operations/33bcfb22-c402-4bae-80d8-e2f5b53e3be6', -// metadata: undefined, -// done: true, -// error: { -// code: 7, -// message: "service-448220130128@gcp-sa-aiplatform.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object. Permission 'storage.objects.create' denied on resource (or it may not exist). service-448220130128@gcp-sa-aiplatform.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object. Permission 'storage.objects.create' denied on resource (or it may not exist)." -// } -// } // [END googlegenaisdk_videogen_with_txt] From 62e689a541a1be33e3329ff8cc674e9951988e28 Mon Sep 17 00:00:00 2001 From: Guiners Date: Thu, 27 Nov 2025 13:39:20 +0100 Subject: [PATCH 4/5] code review fix --- genai/video-generation/videogen-with-img.js | 2 +- genai/video-generation/videogen-with-txt.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/genai/video-generation/videogen-with-img.js b/genai/video-generation/videogen-with-img.js index b717f96536..88a7e5050b 100644 --- a/genai/video-generation/videogen-with-img.js +++ b/genai/video-generation/videogen-with-img.js @@ -54,7 +54,7 @@ async function generateVideo( if (operation.response) { console.log(operation.response.generatedVideos[0].video.uri); } - return operation; + return operation.response.generatedVideos[0].video.uri; } diff --git a/genai/video-generation/videogen-with-txt.js b/genai/video-generation/videogen-with-txt.js index 58206e7cb9..15cb209364 100644 --- a/genai/video-generation/videogen-with-txt.js +++ b/genai/video-generation/videogen-with-txt.js @@ -49,7 +49,7 @@ async function generateVideo( if (operation.response) { console.log(operation.response.generatedVideos[0].video.uri); } - return operation; + return operation.response.generatedVideos[0].video.uri; } From adaa1e2bafb91a810e86a1869287bd3bb64edb99 Mon Sep 17 00:00:00 2001 From: Guiners Date: Mon, 1 Dec 2025 15:01:20 +0100 Subject: [PATCH 5/5] fixing failed tests --- genai/package.json | 3 ++- genai/video-generation/videogen-with-img.js | 1 - genai/video-generation/videogen-with-txt.js | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/genai/package.json b/genai/package.json index 2c370bd447..6a8760a274 100644 --- a/genai/package.json +++ b/genai/package.json @@ -13,13 +13,14 @@ "test": "c8 mocha -p -j 2 --timeout 2400000 test/*.test.js test/**/*.test.js" }, "dependencies": { + "@google-cloud/storage": "^7.17.3", "@google/genai": "1.20.0", "axios": "^1.6.2", "google-auth-library": "^10.3.0", "luxon": "^3.7.1", - "proxyquire": "^2.1.3", "node-fetch": "^3.3.2", "openai": "^5.19.1", + "proxyquire": "^2.1.3", "supertest": "^7.0.0" }, "devDependencies": { diff --git a/genai/video-generation/videogen-with-img.js b/genai/video-generation/videogen-with-img.js index 88a7e5050b..fa8ae023fe 100644 --- a/genai/video-generation/videogen-with-img.js +++ b/genai/video-generation/videogen-with-img.js @@ -57,7 +57,6 @@ async function generateVideo( return operation.response.generatedVideos[0].video.uri; } - // [END googlegenaisdk_videogen_with_img] module.exports = { diff --git a/genai/video-generation/videogen-with-txt.js b/genai/video-generation/videogen-with-txt.js index 15cb209364..2fa7db0400 100644 --- a/genai/video-generation/videogen-with-txt.js +++ b/genai/video-generation/videogen-with-txt.js @@ -52,7 +52,6 @@ async function generateVideo( return operation.response.generatedVideos[0].video.uri; } - // [END googlegenaisdk_videogen_with_txt] module.exports = {