Skip to content

Commit d19f7bc

Browse files
matthew29tangcopybara-github
authored andcommitted
feat: Add DownloadToFileAsync overloaded methods for Video and GeneratedVideo
FUTURE_COPYBARA_INTEGRATE_REVIEW=#111 from googleapis:release-please--branches--main--components--Google.GenAI d22c938 PiperOrigin-RevId: 840452057
1 parent 2f59ba0 commit d19f7bc

12 files changed

+537
-217
lines changed

Google.GenAI.E2E.Tests/Files/FilesDownloadTest.cs

Lines changed: 117 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
using System;
1818
using System.Collections.Generic;
19+
using System.IO;
1920
using System.Linq;
2021
using System.Threading.Tasks;
2122

@@ -32,6 +33,7 @@ public class FilesDownloadTest
3233
private static TestServerProcess? _server;
3334
private Client vertexClient;
3435
private Client geminiClient;
36+
private string fileUri;
3537
public TestContext TestContext { get; set; }
3638

3739
[ClassInitialize]
@@ -76,52 +78,145 @@ public void TestInit()
7678
geminiClient =
7779
new Client(apiKey: apiKey, vertexAI: false, httpOptions: geminiClientHttpOptions);
7880

81+
// Specific setup for this test class
82+
fileUri = "https://generativelanguage.googleapis.com/v1beta/files/p1crkz5nfru4:download?alt=media";
83+
7984
}
8085

8186
[TestMethod]
8287
public async Task DownloadFileNameVertexTest()
8388
{
84-
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
85-
{
86-
await vertexClient.Files.DownloadAsync(fileName: "fileName");
87-
});
89+
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
90+
{
91+
await vertexClient.Files.DownloadAsync(fileName: "fileName");
92+
});
93+
94+
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
95+
}
8896

89-
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
97+
[TestMethod]
98+
public async Task DownloadFileNameGeminiTest()
99+
{
100+
var stream = await geminiClient.Files.DownloadAsync(
101+
fileName: fileUri
102+
);
103+
using (MemoryStream memoryStream = new MemoryStream()) {
104+
await stream.CopyToAsync(memoryStream);
105+
memoryStream.Position = 0;
106+
byte[] bytes = memoryStream.ToArray();
107+
Assert.IsTrue(bytes.Length > 0);
108+
}
90109
}
91110

92111
[TestMethod]
93112
public async Task DownloadToFileFileNameVertexTest()
94113
{
95-
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
96-
{
97-
await vertexClient.Files.DownloadToFileAsync(fileName: "fileName", outputPath: "outputPath");
98-
});
114+
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
115+
{
116+
await vertexClient.Files.DownloadToFileAsync(fileName: "fileName", outputPath: "outputPath");
117+
});
99118

100-
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
119+
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
120+
}
121+
122+
[TestMethod]
123+
public async Task DownloadToFileFileNameGeminiTest()
124+
{
125+
await geminiClient.Files.DownloadToFileAsync(
126+
fileName: fileUri,
127+
outputPath: "output.mp4"
128+
);
101129
}
102130

103131
[TestMethod]
104132
public async Task DownloadFileVertexTest()
105133
{
106-
var file = new Google.GenAI.Types.File { Name = "Name"};
107-
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
108-
{
109-
await vertexClient.Files.DownloadAsync(file: file);
110-
});
134+
var file = new Google.GenAI.Types.File { Name = "Name"};
135+
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
136+
{
137+
await vertexClient.Files.DownloadAsync(file: file);
138+
});
111139

112-
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
140+
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
141+
}
142+
143+
[TestMethod]
144+
public async Task DownloadFileGeminiTest()
145+
{
146+
var file = new Google.GenAI.Types.File {
147+
Name = fileUri
148+
};
149+
await geminiClient.Files.DownloadAsync(file: file);
113150
}
114151

115152
[TestMethod]
116153
public async Task DownloadToFileFileVertexTest()
117154
{
118-
var file = new Google.GenAI.Types.File { Name = "Name"};
119-
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
120-
{
121-
await vertexClient.Files.DownloadToFileAsync(file: file, outputPath: "outputPath");
122-
});
155+
var file = new Google.GenAI.Types.File { Name = "Name"};
156+
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
157+
{
158+
await vertexClient.Files.DownloadToFileAsync(file: file, outputPath: "outputPath");
159+
});
160+
161+
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
162+
}
163+
164+
[TestMethod]
165+
public async Task DownloadToFileFileGeminiTest()
166+
{
167+
var file = new Google.GenAI.Types.File {
168+
Name = fileUri
169+
};
170+
await geminiClient.Files.DownloadToFileAsync(file: file, outputPath: "output.mp4");
171+
}
123172

124-
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
173+
[TestMethod]
174+
public async Task DownloadToFileVideoVertexTest()
175+
{
176+
var video = new Google.GenAI.Types.Video {
177+
Uri = fileUri
178+
};
179+
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
180+
{
181+
await vertexClient.Files.DownloadToFileAsync(video: video, outputPath: "outputPath");
182+
});
183+
184+
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
185+
}
186+
187+
[TestMethod]
188+
public async Task DownloadToFileVideoGeminiTest()
189+
{
190+
var video = new Google.GenAI.Types.Video {
191+
Uri = fileUri
192+
};
193+
await geminiClient.Files.DownloadToFileAsync(video: video, outputPath: "output.mp4");
194+
}
195+
196+
197+
[TestMethod]
198+
public async Task DownloadToFileGeneratedVideoVertexTest()
199+
{
200+
var video = new Google.GenAI.Types.Video {
201+
Uri = fileUri
202+
};
203+
var generatedVideo = new Google.GenAI.Types.GeneratedVideo { Video = video };
204+
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
205+
{
206+
await vertexClient.Files.DownloadToFileAsync(generatedVideo: generatedVideo, outputPath: "outputPath");
207+
});
208+
209+
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
210+
}
211+
212+
[TestMethod]
213+
public async Task DownloadToFileGeneratedVideoGeminiTest()
214+
{
215+
var video = new Google.GenAI.Types.Video {
216+
Uri = fileUri
217+
};
218+
var generatedVideo = new Google.GenAI.Types.GeneratedVideo { Video = video };
219+
await geminiClient.Files.DownloadToFileAsync(generatedVideo: generatedVideo, outputPath: "output.mp4");
125220
}
126221

127222
}

Google.GenAI.E2E.Tests/GenerateVideos/GenerateVideosTest.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,14 @@ public async Task GenerateVideosSourceGeneratedVideoExtensionGeminiTest() {
336336
Assert.IsTrue(operation1.Response.GeneratedVideos.Count > 0);
337337
Assert.IsNotNull(operation1.Response.GeneratedVideos.First().Video.Uri);
338338

339+
// Download generated video
340+
await geminiClient.Files.DownloadToFileAsync(
341+
generatedVideo: operation1.Response.GeneratedVideos.First(),
342+
outputPath: "video.mp4"
343+
);
344+
// Downloaded video is a file. Bytes do not get written to the Video object.
345+
Assert.IsNull(operation1.Response.GeneratedVideos.First().Video.VideoBytes);
346+
339347

340348
var source2 = new GenerateVideosSource {
341349
Prompt = "Driving through a tunnel.",
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"recordID": "FilesDownloadTest.DownloadFileGeminiTest",
3+
"interactions": [
4+
{
5+
"request": {
6+
"method": "GET",
7+
"url": "/v1beta/files/p1crkz5nfru4:download?alt=media",
8+
"request": "GET /v1beta/files/p1crkz5nfru4:download?alt=media HTTP/1.1",
9+
"headers": {
10+
"Test-Name": "FilesDownloadTest.DownloadFileGeminiTest"
11+
},
12+
"bodySegments": [
13+
null
14+
],
15+
"previousRequest": "b4d6e60a9b97e7b98c63df9308728c5c88c0b40c398046772c63447b94608b4d",
16+
"serverAddress": "generativelanguage.googleapis.com",
17+
"port": 443,
18+
"protocol": "https"
19+
},
20+
"shaSum": "08bb6900db8c1081bd4fba2165729edcb5ef19d480cc9720f44afb2051b2fa64",
21+
"response": {
22+
"statusCode": 200,
23+
"headers": {
24+
"Cache-Control": "private, max-age=0",
25+
"Content-Disposition": "attachment",
26+
"Content-Length": "9864924",
27+
"Content-Type": "video/mp4",
28+
"Date": "Fri, 05 Dec 2025 00:28:41 GMT",
29+
"Expires": "Fri, 05 Dec 2025 00:28:41 GMT",
30+
"Server": "UploadServer",
31+
"Vary": "Origin, X-Origin, Referer",
32+
"X-Goog-Hash": "crc32c=gJcFCw==",
33+
"X-Guploader-Uploadid": "AHVrFxNByvn8lmmbTbI9uaG057t8JlUFnsfGOucrJjeoKE8nKxTTw4WU4B4duLoXA55ENK5L"
34+
}
35+
}
36+
}
37+
]
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"recordID": "FilesDownloadTest.DownloadFileNameGeminiTest",
3+
"interactions": [
4+
{
5+
"request": {
6+
"method": "GET",
7+
"url": "/v1beta/files/p1crkz5nfru4:download?alt=media",
8+
"request": "GET /v1beta/files/p1crkz5nfru4:download?alt=media HTTP/1.1",
9+
"headers": {
10+
"Test-Name": "FilesDownloadTest.DownloadFileNameGeminiTest"
11+
},
12+
"bodySegments": [
13+
null
14+
],
15+
"previousRequest": "b4d6e60a9b97e7b98c63df9308728c5c88c0b40c398046772c63447b94608b4d",
16+
"serverAddress": "generativelanguage.googleapis.com",
17+
"port": 443,
18+
"protocol": "https"
19+
},
20+
"shaSum": "18bbcea5cd8767590ff9faa8ccacafea676641cb903f9f5c4387961aff0178b7",
21+
"response": {
22+
"statusCode": 200,
23+
"headers": {
24+
"Cache-Control": "private, max-age=0",
25+
"Content-Disposition": "attachment",
26+
"Content-Length": "9864924",
27+
"Content-Type": "video/mp4",
28+
"Date": "Fri, 05 Dec 2025 00:28:41 GMT",
29+
"Expires": "Fri, 05 Dec 2025 00:28:41 GMT",
30+
"Server": "UploadServer",
31+
"Vary": "Origin, X-Origin, Referer",
32+
"X-Goog-Hash": "crc32c=gJcFCw==",
33+
"X-Guploader-Uploadid": "AOCedOGMI8lsVHl-DTBMudTwo8x7jESnNE_MCTq-ftZcNtTncchOwSn48BO8J9rA8mnuWnKe"
34+
}
35+
}
36+
}
37+
]
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"recordID": "FilesDownloadTest.DownloadToFileFileGeminiTest",
3+
"interactions": [
4+
{
5+
"request": {
6+
"method": "GET",
7+
"url": "/v1beta/files/p1crkz5nfru4:download?alt=media",
8+
"request": "GET /v1beta/files/p1crkz5nfru4:download?alt=media HTTP/1.1",
9+
"headers": {
10+
"Test-Name": "FilesDownloadTest.DownloadToFileFileGeminiTest"
11+
},
12+
"bodySegments": [
13+
null
14+
],
15+
"previousRequest": "b4d6e60a9b97e7b98c63df9308728c5c88c0b40c398046772c63447b94608b4d",
16+
"serverAddress": "generativelanguage.googleapis.com",
17+
"port": 443,
18+
"protocol": "https"
19+
},
20+
"shaSum": "ccaef8722bcdb8adda81f07cfa81d11207a9733097e78cc3a7d8fb77c15a9784",
21+
"response": {
22+
"statusCode": 200,
23+
"headers": {
24+
"Cache-Control": "private, max-age=0",
25+
"Content-Disposition": "attachment",
26+
"Content-Length": "9864924",
27+
"Content-Type": "video/mp4",
28+
"Date": "Fri, 05 Dec 2025 00:28:41 GMT",
29+
"Expires": "Fri, 05 Dec 2025 00:28:41 GMT",
30+
"Server": "UploadServer",
31+
"Vary": "Origin, X-Origin, Referer",
32+
"X-Goog-Hash": "crc32c=gJcFCw==",
33+
"X-Guploader-Uploadid": "AOCedOGKSBhUtiM7zN7ZwzzSik4a6nr_jXIjsnLdZPc6Og91PRIUwK9yLdz6owLI2A01c_JT"
34+
}
35+
}
36+
}
37+
]
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"recordID": "FilesDownloadTest.DownloadToFileFileNameGeminiTest",
3+
"interactions": [
4+
{
5+
"request": {
6+
"method": "GET",
7+
"url": "/v1beta/files/p1crkz5nfru4:download?alt=media",
8+
"request": "GET /v1beta/files/p1crkz5nfru4:download?alt=media HTTP/1.1",
9+
"headers": {
10+
"Test-Name": "FilesDownloadTest.DownloadToFileFileNameGeminiTest"
11+
},
12+
"bodySegments": [
13+
null
14+
],
15+
"previousRequest": "b4d6e60a9b97e7b98c63df9308728c5c88c0b40c398046772c63447b94608b4d",
16+
"serverAddress": "generativelanguage.googleapis.com",
17+
"port": 443,
18+
"protocol": "https"
19+
},
20+
"shaSum": "d2db4a49d811b14b0e90fd20d54a74dc94ab86bda393ab232032246bd9d643f2",
21+
"response": {
22+
"statusCode": 200,
23+
"headers": {
24+
"Cache-Control": "private, max-age=0",
25+
"Content-Disposition": "attachment",
26+
"Content-Length": "9864924",
27+
"Content-Type": "video/mp4",
28+
"Date": "Fri, 05 Dec 2025 00:28:41 GMT",
29+
"Expires": "Fri, 05 Dec 2025 00:28:41 GMT",
30+
"Server": "UploadServer",
31+
"Vary": "Origin, X-Origin, Referer",
32+
"X-Goog-Hash": "crc32c=gJcFCw==",
33+
"X-Guploader-Uploadid": "AOCedOFaLQvh80wJ-fvOmETYMnJe4RQA-koiijz7DaTUX7EI-v-sxtnjam9Zq7RlzhNKjfJbsroMF1A"
34+
}
35+
}
36+
}
37+
]
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"recordID": "FilesDownloadTest.DownloadToFileGeneratedVideoGeminiTest",
3+
"interactions": [
4+
{
5+
"request": {
6+
"method": "GET",
7+
"url": "/v1beta/files/p1crkz5nfru4:download?alt=media",
8+
"request": "GET /v1beta/files/p1crkz5nfru4:download?alt=media HTTP/1.1",
9+
"headers": {
10+
"Test-Name": "FilesDownloadTest.DownloadToFileGeneratedVideoGeminiTest"
11+
},
12+
"bodySegments": [
13+
null
14+
],
15+
"previousRequest": "b4d6e60a9b97e7b98c63df9308728c5c88c0b40c398046772c63447b94608b4d",
16+
"serverAddress": "generativelanguage.googleapis.com",
17+
"port": 443,
18+
"protocol": "https"
19+
},
20+
"shaSum": "90d3378561db7a42c770bb29b2c7506c8e738787da0f1f4185bb1c7b28d7ed9a",
21+
"response": {
22+
"statusCode": 200,
23+
"headers": {
24+
"Cache-Control": "private, max-age=0",
25+
"Content-Disposition": "attachment",
26+
"Content-Length": "9864924",
27+
"Content-Type": "video/mp4",
28+
"Date": "Fri, 05 Dec 2025 00:28:42 GMT",
29+
"Expires": "Fri, 05 Dec 2025 00:28:42 GMT",
30+
"Server": "UploadServer",
31+
"Vary": "Origin, X-Origin, Referer",
32+
"X-Goog-Hash": "crc32c=gJcFCw==",
33+
"X-Guploader-Uploadid": "AHVrFxP90fo6JeUHSuf8bwD9l1dO9BTGiQzNnhF5RwD_wsowUlm1sCX8aVErd0IR9oXGJtSu"
34+
}
35+
}
36+
}
37+
]
38+
}

0 commit comments

Comments
 (0)