Search videos through the YouTube Data API from C#
While doing some new .NET exception landing pages to link to from the elmah.io app, I had some fun challenges integrating with various services. One of them is YouTube to show relevant YouTube search results on the page. In this post, I'll share with you how to do YouTube searches from C#.
So, do you want to search YouTube and display search results on a website or similar? In this post, I'll create a simple ASP.NET Core application rendering a YouTube search result. The code will use Google's Data API, which is free to a certain limit which we will discuss later in the post. You can integrate with the API from multiple languages including JavaScript. If all you need is a search result on a website, JavaScript would be a valid way to go. For this example, I'm integrating from C# which will render everything on the server. There are both advantages and disadvantages to this approach. Advantages could be:
- Make use of caching
- Render search results in night jobs or similar
- Keep integration logic on the server
- Hide your Google Data API credentials
I won't be the judge about which way you choose to integrate and I'm not saying that server-side beats client-side here. With that out of the way, let's start building.
To communicate with the YouTube API you will need an API key from Google. If you haven't got one already, head over to the developer console at https://console.cloud.google.com/apis/dashboard and click the Enable APIs and Services link. Here, you search for 'YouTube Data API v3' and enable it.
For this demo, I'll create a simple Razor Pages app:
dotnet new razor
Next, I'll install the Google.Apis.YouTube.v3
NuGet package. TBH, my first integration was with a HttpClient
and using it to make requests to the YouTube API. I have had poor experiences with some of Google's NuGet packages in the past, so my initial thought was to create the integration manually. After looking at this NuGet package, I found it to work very well. Install the package:
dotnet add package Google.Apis.YouTube.v3
Create and register the YouTubeService
class in the Program.cs
file:
using Google.Apis.Services;
using Google.Apis.YouTube.v3;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSingleton(new YouTubeService(new BaseClientService.Initializer()
{
ApiKey = "API_KEY",
ApplicationName = "APPLICATION_NAME"
}));
I'm registering the service as a singleton and providing it with the API key generated in a previous step and an application name. Replace API_KEY
and APPLICATION_NAME
with real values in your code.
Ready to search YouTube 🙌 In the Index.cshtml.cs
file, inject the YouTubeService
we registered:
private readonly YouTubeService youTubeService;
public IndexModel(YouTubeService youTubeService)
{
this.youTubeService = youTubeService;
}
Create a new model class to hold YouTube video results for our view:
public class YouTubeVideo
{
public string Thumbnail { get; internal set; }
public string Title { get; internal set; }
public string VideoId { get; internal set; }
}
Add a public property for the view to access:
public List<YouTubeVideo> Videos { get; private set; } = new List<YouTubeVideo>();
And finally, in the OnGet
method do the search:
public async Task OnGet()
{
var searchListRequest = youTubeService.Search.List("snippet");
searchListRequest.Q = "elmah.io";
searchListRequest.Type = "video";
searchListRequest.Order = SearchResource.ListRequest.OrderEnum.Relevance;
var searchListResponse = await searchListRequest.ExecuteAsync();
Videos.AddRange(searchListResponse.Items.Select(video => new YouTubeVideo
{
Thumbnail = video.Snippet.Thumbnails.High.Url,
Title = video.Snippet.Title,
VideoId = video.Id.VideoId,
}));
}
I'll go over the code line by line to explain what is going on. In the first line, I'm using the Search.List
method to create a new search request object. The snippet
value provided for this method tells the YouTube API to return metadata like thumbnails and video titles.
Next, I set three properties. The Q
property should contain the text to search for. In this example, I'm using elmah.io
but this could be any value, including a query string provided by the user. By setting the Type
to video
, I tell the API only to search for videos and not users and channels. The Order
property will sort the results by relevance. By default, up to 5 results are returned which we will use for this example.
In the following lines, I call the ExecuteAsync
method and map the returned Items
(that are the videos) to YouTubeVideo
objects.
The only thing missing now is the view. With my limited design skills, I'll go for a basic Bootstrap look and feel by providing the following code in the Index.cshtml
file:
<h1 class="display-4">YouTube Videos</h1>
<div class="row">
@foreach (var video in Model.Videos)
{
<div class="col">
<figure class="figure">
<a href="https://www.youtube.com/watch?v=@video.VideoId" title="@video.Title">
<img src="@video.Thumbnail" class="figure-img img-fluid rounded" alt="@video.Title">
</a>
<figcaption class="figure-caption">@video.Title</figcaption>
</figure>
</div>
}
</div>
The code generates a row with up to five columns with YouTube video thumbnails and links. If you haven't already, enable Razor runtime compilation to make sure any changes made inside the HTML don't require you to rebuild the application. Here's the beauty in action:
Here's the full code sample for the Index.cshtml.cs
file:
public class IndexModel : PageModel
{
public List<YouTubeVideo> Videos { get; private set; } = new List<YouTubeVideo>();
private readonly YouTubeService youTubeService;
public IndexModel(YouTubeService youTubeService)
{
this.youTubeService = youTubeService;
}
public async Task OnGet()
{
var searchListRequest = youTubeService.Search.List("snippet");
searchListRequest.Q = "elmah.io";
searchListRequest.Type = "video";
searchListRequest.Order = SearchResource.ListRequest.OrderEnum.Relevance;
var searchListResponse = await searchListRequest.ExecuteAsync();
Videos.AddRange(searchListResponse.Items.Select(video => new YouTubeVideo
{
Thumbnail = video.Snippet.Thumbnails.High.Url,
Title = video.Snippet.Title,
VideoId = video.Id.VideoId,
}));
}
}
public class YouTubeVideo
{
public string Thumbnail { get; internal set; }
public string Title { get; internal set; }
public string VideoId { get; internal set; }
}
To see this integration in action, check out the exception landing pages (like this one: System.InvalidCastException).