YouTubeContentProvider
Content provider to incorporate YouTube content into EPiServer CMS templates.
Install / Use
/learn @episerver/YouTubeContentProviderREADME
YouTube Content Provider
This document describes how to create a read only content provider using the YouTube Data API (v3) to incorporate YouTube functionality such as browsing playlists and subscriptions, play movies and easily use this content in EPiServer CMS templates.
The integration also demonstrates how to use an external API using OAuth 2.0 authentication, renewal of the access token and how to save these settings in the EPiServer user interface. The integration is built as an add-on for easy installation in EPiServer as well as show how an Add-on project is designed.
Requirements and notifications
The following apply when deploying the YouTube content provider to an EPiServer website.
- Requires EPiServer CMS version 7.14.1.0 or higher
- Authorization credentials to be able to use the YouTube Data API, https://developers.google.com/youtube/registering_an_application
Integration
The integration consists of three main parts. The first part is the content provider who is responsible for requesting the YouTube Data API and translate YouTube resources to instances that inherit from ContentFolders or MediaData types. There is a UI component that registers a new tab in the media gadget where editors can navigate YouTube content. The last part is an initialization module responsible for creating the entry point as well as register the provider, the module also registers routes for the content.
The integration also includes a content search provider that allows searching of YouTube videos directly in the media gadget, as well as an implementation of dynamic data store to save settings related to the integration.
Creating the content provider
A custom content provider must inherit from the ContentProvider class that resides in the EPiServer.dll assembly. When creating a custom content provider it is only one the abstract method LoadContent that must be implemented, but for this example the method LoadChildrenReferenceSandTypes must be implemented as well.
<h4>LoadContent</h4>LoadContent is the method to use to pull out one specific YouTube resource from the API and return it - an instance of an object that is implementing the IContent interface (for example YouTubeVideo).
protected override IContent LoadContent(ContentReference contentLink, ILanguageSelector languageSelector)
{
var mappedItem = _identityMappingService.Get(contentLink);
if (mappedItem == null) return null;
switch (GetYouTubeResourceType(mappedItem.ExternalIdentifier))
{
case YouTubeResourceType.Playlist:
var playlist = _youTubeRepository.GetPlaylist(mappedItem.ExternalIdentifier.Segments[3]);
if (playlist != null)
return CreateYouTubeData(mappedItem, typeof(YouTubePlaylist), playlist);
break;
case YouTubeResourceType.PlaylistItem:
var playlistItem = _youTubeRepository.GetPlaylistItem(mappedItem.ExternalIdentifier.Segments[4]);
if (playlistItem != null)
return CreatePlaylistItem(mappedItem, playlistItem);
break;
case YouTubeResourceType.Subscription:
var subscription = _youTubeRepository.GetSubscription(
RemoveEndingSlash(mappedItem.ExternalIdentifier.Segments[3]));
if (subscription != null)
return CreateYouTubeData(mappedItem, typeof(YouTubeSubscription), subscription);
break;
case YouTubeResourceType.Video:
var video = _youTubeRepository.GetVideo(
RemoveEndingSlash(mappedItem.ExternalIdentifier.Segments[3]));
if (video != null)
return CreateVideo(mappedItem, video);
break;
}
return CreateAndAssignIdentity(
mappedItem, typeof(YouTubeFolder), RemoveStartingSlash(mappedItem.ExternalIdentifier.LocalPath));
}
This method takes a ContentReference and ILanguageSelector, in this integration, we are not dependent on language, and therefore ignores it is value.
ContentLink contains information about which item we'll get from YouTube Data API, EPiServer uses internally ContentReference consisting of integers and a GUID to identify content while YouTube makes use of unique strings. To keep track of the mapping the services IdentityMappingService is used to handles the mapping between YouTube identity and EPiServers content references.
Based on the content reference, we pick out the mapped identity that identifies the YouTube resource using the property ExternalIdentifier which is of type Uri, the structure of the URI contains information to identify the external resource, and in this integration the structure of a playlist item is as follow: playlist/808/FLpNTbg-UP1Fpk5xBbAyMqA/FLftzDmUNtkJZF4_GvUGUsS6BYV_LQLqjN
- The first segment identifies the resource as a playlist.
- Second segment indicates the ID of the parent node
- Third segment identifies the YouTube's playlist id
- The last and final segment identifies the playlist item id.
if (EntryPoint.CompareToIgnoreWorkID(contentLink))
{
childrenList.Add(new GetChildrenReferenceResult { ContentLink = _identityMappingService.Get(
MappedIdentity.ConstructExternalIdentifier(ProviderKey, "Playlists"), true).ContentLink,
IsLeafNode = false, ModelType = typeof(YouTubeFolder) });
childrenList.Add(new GetChildrenReferenceResult { ContentLink = _identityMappingService.Get(
MappedIdentity.ConstructExternalIdentifier(ProviderKey, "Subscriptions"), true).ContentLink,
IsLeafNode = false, ModelType = typeof(YouTubeFolder) });
SearchResultNode = _identityMappingService.Get(
MappedIdentity.ConstructExternalIdentifier(ProviderKey, "Search"), true).ContentLink;
childrenList.Add(new GetChildrenReferenceResult { ContentLink = SearchResultNode,
IsLeafNode = false, ModelType = typeof(YouTubeFolder) });
return childrenList;
}
Under the node Search will search results for videos appear as it is not possible to find out which playlist a specific video belongs when requesting the YouTube Data API as well as the search is done on all videos, even those not belonging to the logged in YouTube account.
<h4>Request a list of playlists</h4> In the same way as in method LoadContent we pick out the mapped identity that identifies the YouTube resource for a content reference. Depending on what type of YouTube resource it is, we request the different methods in the YouTube Data API to retrieve the associated resources. Below is a code snippet to retrieve playlist items for a specific playlist.var childrenList = new List<GetChildrenReferenceResult>();
var mappedItem = _identityMappingService.Get(contentLink);
dynamic items;
switch (GetYouTubeResourceType(mappedItem.ExternalIdentifier))
{
case YouTubeResourceType.PlaylistRoot:
// Implementation to request playlists....
break;
case YouTubeResourceType.Playlist:
// Request playlist items for a playlist
items = _youTubeRepository.ListPlaylistItems(mappedItem.ExternalIdentifier.Segments[3]);
if (items != null)
{
foreach (var item in items)
{
var uri = MappedIdentity.ConstructExternalIdentifier(ProviderKey,
string.Format("{0}/{1}/{2}/{3}",
YouTubeResourceType.Playlist.ToString().ToLower(),
contentLink.ID,
mappedItem.ExternalIdentifier.Segments[3],
item.id));
var mappedChild = _identityMappingService.Get(uri, true);
childrenList.Add(new GetChildrenReferenceResult
{
ContentLink = mappedChild.ContentLink,
IsLeafNode = true,
ModelType = typeof(YouTubePlaylistItem)
});
// We have all the data about the YouTube resource and creates the content instance and adds it to the cache.
AddContentToCache(CreatePlaylistItem(mappedChild, item));
}
}
break;
Continue....
}
An external identifier is constructed based on the custom provider and a provider unique path.
var uri = MappedIdentity.ConstructExternalIdentifier(ProviderKey,
string.Format("{0}/{1}/{2}/{3}",
YouTubeResourceType.Playlist.ToString().ToLower(),
ontentLink.ID,
mappedItem.ExternalIdentifier.Segments[3],
item.id));
The mapped identity gets loaded for the external identifier, passing true to create the mapping if it not exists.
var mappedChild = _identityMappingService.Get(uri, true);
An instance of GetChildrenReferenceResult is added to the children collection where it defines the ContentLink to the mapped identity, the children should be considered as a leaf node (the node will have no children) and the content type of the child.
childrenList.Add(new GetChildrenReferenceResult
{
ContentLink = mappedChild.ContentLink,
IsLeafNode = true,
ModelType = typeof(YouTubePlaylistItem)
});
Usually, a list of the children and their types is created in the method LoadChildrenReferencesAndTypes, but in this integration we receive all the data about the resource when the YouTube API is requested and we can thus create up the content instance and add it to the EPiServer cache, this increases the performance because the resource does not need to be loaded through the LoadContent method.
// We have all the data about the YouTube resource and creates the content
// instance and adds it to the cache.
AddContentToCache(CreatePlaylistItem(mappedChild, item));
<h4>Creating YouTube content</h4>