Skip to content

Commit db0a01a

Browse files
committed
Youtube Data V3
1 parent 373c430 commit db0a01a

File tree

1 file changed

+283
-0
lines changed

1 file changed

+283
-0
lines changed

content/blog/general/youtube.md

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
---
2+
title: "Dynamically Update Youtube Video Title and Description using YouTube Data API in .NET Applications"
3+
author: "PrashantUnity"
4+
weight: 106
5+
date: 2025-12-23
6+
lastmod: 2025-12-23
7+
dateString: December 2025
8+
description: "Learn how to dynamically update YouTube video titles and descriptions using the YouTube Data API in .NET applications. This guide provides step-by-step instructions and code examples to help you manage your YouTube content programmatically."
9+
#canonicalURL: "https://canonical.url/to/page"
10+
cover:
11+
image: "cover.jpg" # image path/url
12+
alt: "Download Logo" # alt text
13+
#caption: "Optical Character Recognition" display caption under cover
14+
15+
tags: [ "NET", "codefrydev", "C sharp", "CFD", "YouTube API", "YouTube Data API", "Video Management", "API Integration", "Programming", "Software Development" ]
16+
keywords: [ "NET", "codefrydev", "C sharp", "CFD", "YouTube API", "YouTube Data API", "Video Management", "API Integration", "Programming", "Software Development" ]
17+
---
18+
19+
## YouTube Data API Sample in .NET Interactive Notebooks
20+
21+
This sample demonstrates how to use the YouTube Data API to update video metadata (title, description, tags, etc.) in bulk using .NET Interactive Notebooks. It covers OAuth2 authentication, fetching uploaded videos, generating new metadata using an AI model, and updating the videos via the API.
22+
23+
[Get Gemini API Key](https://aistudio.google.com/app/api-keys)
24+
[Google Cloud Console for creating secret credentials](https://console.cloud.google.com/)
25+
26+
27+
```csharp
28+
#r "nuget: Google.Apis.YouTube.v3"
29+
#r "nuget: Google.Apis.Auth"
30+
```
31+
32+
This cell loads the NuGet package references required to use the YouTube Data API and Google authentication in a polyglot/.NET Interactive notebook.
33+
34+
```csharp
35+
using Google.Apis.Auth.OAuth2;
36+
using Google.Apis.Services;
37+
using Google.Apis.Util.Store;
38+
using Google.Apis.YouTube.v3;
39+
using Google.Apis.YouTube.v3.Data;
40+
```
41+
42+
These `using` statements import the Google APIs namespaces used for OAuth2, the YouTube service, and data models.
43+
44+
```csharp
45+
using System.IO;
46+
using System.Threading;
47+
using System.Threading.Tasks;
48+
using System.Net.Http;
49+
using System.Text;
50+
using System.Text.Json;
51+
```
52+
53+
Standard .NET imports for file I/O, async/threading, HTTP requests, text handling, and JSON serialization used by the sample.
54+
55+
```csharp
56+
// 1️⃣ OAuth
57+
UserCredential credential;
58+
using (var stream = new FileStream("client_secret.json", FileMode.Open))
59+
{
60+
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
61+
GoogleClientSecrets.FromStream(stream).Secrets,
62+
new[] { YouTubeService.Scope.Youtube },
63+
"user",
64+
CancellationToken.None,
65+
new FileDataStore("YouTubeAuth")
66+
);
67+
}
68+
```
69+
70+
This snippet performs OAuth2 authorization using the `client_secret.json` file and stores the resulting user credential locally.
71+
72+
```csharp
73+
var youtube = new YouTubeService(new BaseClientService.Initializer
74+
{
75+
HttpClientInitializer = credential,
76+
ApplicationName = "Channel Bulk Metadata Updater"
77+
});
78+
```
79+
80+
Create a `YouTubeService` client instance initialized with the authorized credentials to call the YouTube Data API.
81+
82+
```csharp
83+
// 2️⃣ Get channel uploads playlist
84+
var channelRequest = youtube.Channels.List("contentDetails");
85+
channelRequest.Mine = true;
86+
var channelResponse = await channelRequest.ExecuteAsync();
87+
88+
var uploadsPlaylistId =
89+
channelResponse.Items[0].ContentDetails.RelatedPlaylists.Uploads;
90+
91+
Console.WriteLine($"Uploads playlist: {uploadsPlaylistId}");
92+
93+
```
94+
95+
Request the authenticated user's channel content details and extract the uploads playlist ID (where all uploaded videos are listed).
96+
97+
```csharp
98+
// 3️⃣ Get ALL video IDs
99+
var videoIds = new List<string>();
100+
string nextPageToken = null;
101+
102+
do
103+
{
104+
var playlistRequest = youtube.PlaylistItems.List("contentDetails");
105+
playlistRequest.PlaylistId = uploadsPlaylistId;
106+
playlistRequest.MaxResults = 50;
107+
playlistRequest.PageToken = nextPageToken;
108+
109+
var playlistResponse = await playlistRequest.ExecuteAsync();
110+
111+
videoIds.AddRange(
112+
playlistResponse.Items.Select(i => i.ContentDetails.VideoId)
113+
);
114+
115+
nextPageToken = playlistResponse.NextPageToken;
116+
117+
} while (nextPageToken != null);
118+
119+
Console.WriteLine($"Found {videoIds.Count} videos");
120+
// save video Ids in file for later use
121+
File.WriteAllLines("video_ids.txt", videoIds);
122+
```
123+
124+
Iterate the uploads playlist pages to gather all video IDs and save them to `video_ids.txt` for later processing.
125+
126+
```csharp
127+
128+
class AiMetadata
129+
{
130+
public string Title { get; set; }
131+
public string Description { get; set; }
132+
public List<string> Tags { get; set; }
133+
public string CategoryId { get; set; }
134+
public string Language { get; set; }
135+
}
136+
static void ApplyAiMetadata(Video video, AiMetadata ai)
137+
{
138+
video.Snippet.Title = ai.Title;
139+
video.Snippet.Description = ai.Description;
140+
video.Snippet.Tags = ai.Tags;
141+
video.Snippet.CategoryId = ai.CategoryId;
142+
video.Snippet.DefaultLanguage = ai.Language;
143+
}
144+
// Generate AI Metadata using Gemeini API if dosent work Plese take a look at https://aistudio.google.com for more details how their curl works and upage below code snippet accordingly
145+
static async Task<AiMetadata?> GenerateMetadataFromTitle(string videoTitle)
146+
{
147+
var apiKey = "you api key here";
148+
149+
var prompt = $$$"""
150+
You are an assistant that generates YouTube metadata.
151+
152+
Given this video title:
153+
"{{{videoTitle}}}"
154+
155+
Generate optimized YouTube metadata.
156+
157+
Rules:
158+
- Return ONLY valid JSON
159+
- No markdown
160+
- No explanations
161+
- English language only
162+
163+
JSON format:
164+
{{
165+
"title": "",
166+
"description": "",
167+
"tags": [],
168+
"categoryId": "",
169+
"language": ""
170+
}}
171+
""";
172+
173+
var requestBody = new
174+
{
175+
contents = new[]
176+
{
177+
new
178+
{
179+
parts = new[]
180+
{
181+
new { text = prompt }
182+
}
183+
}
184+
},
185+
generationConfig = new
186+
{
187+
temperature = 0.6,
188+
maxOutputTokens = 512
189+
}
190+
};
191+
192+
using var client = new HttpClient();
193+
194+
var request = new HttpRequestMessage(
195+
HttpMethod.Post,
196+
"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"
197+
);
198+
199+
request.Headers.Add("X-goog-api-key", apiKey);
200+
request.Content = new StringContent(
201+
JsonSerializer.Serialize(requestBody),
202+
Encoding.UTF8,
203+
"application/json"
204+
);
205+
206+
var response = await client.SendAsync(request);
207+
if (!response.IsSuccessStatusCode)
208+
{
209+
Console.WriteLine($"Gemini error: {response.StatusCode}");
210+
return null;
211+
}
212+
213+
var json = await response.Content.ReadAsStringAsync();
214+
215+
using var doc = JsonDocument.Parse(json);
216+
217+
if (!doc.RootElement.TryGetProperty("candidates", out var candidates))
218+
return null;
219+
220+
var text = candidates[0]
221+
.GetProperty("content")
222+
.GetProperty("parts")[0]
223+
.GetProperty("text")
224+
.GetString();
225+
226+
if (string.IsNullOrWhiteSpace(text))
227+
return null;
228+
229+
// Parse AI JSON safely
230+
return JsonSerializer.Deserialize<AiMetadata>(
231+
text,
232+
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
233+
);
234+
}
235+
```
236+
237+
238+
Defines an `AiMetadata` model, a helper to apply metadata to a `Video` object, and a function that calls a Generative API to produce metadata JSON from a video title.
239+
240+
241+
```csharp
242+
// 4️⃣ Process videos in batches
243+
244+
var videoIdsList = File.ReadAllLines("video_ids.txt");
245+
foreach (var batch in videoIdsList.Chunk(50))
246+
{
247+
var listRequest = youtube.Videos.List("snippet");
248+
listRequest.Id = string.Join(",", batch);
249+
250+
var listResponse = await listRequest.ExecuteAsync();
251+
252+
foreach (var video in listResponse.Items)
253+
{
254+
try
255+
{
256+
// 5️⃣ UPDATE ONLY REQUIRED FIELDS
257+
var aiMetadata = await GenerateMetadataFromTitle(video.Snippet.Title);
258+
259+
// Optional safety checks
260+
if (string.IsNullOrWhiteSpace(aiMetadata.Title))
261+
throw new Exception("AI returned empty title");
262+
263+
ApplyAiMetadata(video, aiMetadata);
264+
265+
var updateRequest = youtube.Videos.Update(video, "snippet");
266+
await updateRequest.ExecuteAsync();
267+
268+
269+
Console.WriteLine($"✔ Updated {video.Id}");
270+
271+
// 7️⃣ Throttle
272+
await Task.Delay(1200);
273+
}
274+
catch (Exception ex)
275+
{
276+
Console.WriteLine($"❌ {video.Id} failed: {ex.Message}");
277+
}
278+
}
279+
}
280+
Console.WriteLine("Done.");
281+
```
282+
283+
Process videos in batches: fetch each video's snippet, generate AI metadata, apply updates via the YouTube API, and throttle requests to respect quotas.

0 commit comments

Comments
 (0)