Skip to content

Commit 3a3e02c

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 3a3e02c

12 files changed

+541
-217
lines changed

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

Lines changed: 121 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,149 @@ public void TestInit()
7678
geminiClient =
7779
new Client(apiKey: apiKey, vertexAI: false, httpOptions: geminiClientHttpOptions);
7880

81+
// Specific setup for this test class
82+
// Note that replay mode does not return any bytes when dowloading a file.
83+
fileUri = "https://generativelanguage.googleapis.com/v1beta/files/p1crkz5nfru4:download?alt=media";
84+
7985
}
8086

8187
[TestMethod]
8288
public async Task DownloadFileNameVertexTest()
8389
{
84-
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
85-
{
86-
await vertexClient.Files.DownloadAsync(fileName: "fileName");
87-
});
90+
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
91+
{
92+
await vertexClient.Files.DownloadAsync(fileName: "fileName");
93+
});
8894

89-
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
95+
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
96+
}
97+
98+
[TestMethod]
99+
public async Task DownloadFileNameGeminiTest()
100+
{
101+
var stream = await geminiClient.Files.DownloadAsync(
102+
fileName: fileUri
103+
);
104+
using (MemoryStream memoryStream = new MemoryStream()) {
105+
await stream.CopyToAsync(memoryStream);
106+
memoryStream.Position = 0;
107+
byte[] bytes = memoryStream.ToArray();
108+
// Replay mode does not return any bytes when dowloading a file.
109+
if (!TestServer.IsReplayMode) {
110+
Assert.IsTrue(bytes.Length > 0);
111+
}
112+
}
90113
}
91114

92115
[TestMethod]
93116
public async Task DownloadToFileFileNameVertexTest()
94117
{
95-
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
96-
{
97-
await vertexClient.Files.DownloadToFileAsync(fileName: "fileName", outputPath: "outputPath");
98-
});
118+
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
119+
{
120+
await vertexClient.Files.DownloadToFileAsync(fileName: "fileName", outputPath: "outputPath");
121+
});
122+
123+
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
124+
}
99125

100-
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
126+
[TestMethod]
127+
public async Task DownloadToFileFileNameGeminiTest()
128+
{
129+
await geminiClient.Files.DownloadToFileAsync(
130+
fileName: fileUri,
131+
outputPath: "output.mp4"
132+
);
101133
}
102134

103135
[TestMethod]
104136
public async Task DownloadFileVertexTest()
105137
{
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-
});
138+
var file = new Google.GenAI.Types.File { Name = "Name"};
139+
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
140+
{
141+
await vertexClient.Files.DownloadAsync(file: file);
142+
});
143+
144+
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
145+
}
111146

112-
StringAssert.Contains(ex.Message, "This method is only supported in the Gemini Developer API client");
147+
[TestMethod]
148+
public async Task DownloadFileGeminiTest()
149+
{
150+
var file = new Google.GenAI.Types.File {
151+
Name = fileUri
152+
};
153+
await geminiClient.Files.DownloadAsync(file: file);
113154
}
114155

115156
[TestMethod]
116157
public async Task DownloadToFileFileVertexTest()
117158
{
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-
});
159+
var file = new Google.GenAI.Types.File { Name = "Name"};
160+
var ex = await Assert.ThrowsExceptionAsync<NotSupportedException>(async () =>
161+
{
162+
await vertexClient.Files.DownloadToFileAsync(file: file, outputPath: "outputPath");
163+
});
123164

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

127226
}

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: "generatedVideo.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+
}

0 commit comments

Comments
 (0)