Intuitive Knowledge Articles in Salesforce with YouTube integration
YouTube doesn't need an introduction. We all know what it is and how big it is. Before we jump into the details of its integration and use cases with Salesforce let us first take a look at some of the amazing facts and figures:
- The monthly active users on YouTube is around 2 Billion as of 2019
- YouTube is the second most popular social media platform where 79% of total population hold an account
- YouTube is responsible for 37% of all mobile traffic
- 90% of people say they discover new brands/products on YouTube
- 62% of Businesses (inteygrate.com too) uses YouTube as a channel to post video or Content
These stats are not just interesting to read but its also amusing to think about the potential viewership and outreach businesses can have by engaging to its customers on YouTube. Be it a small tutorial about your product or about the core values of your company or about Life at your company; it has far reaching impact when you compare it with having content limited to your website/intranet.
How will Salesforce integration with YouTube be helpful?
Here are few of the many possible scenarios where YouTube Salesforce integration can help business in a significant way
Emergency news flash and latest updates for Reps, without the need for the reps to leave Salesforce and search it somewhere else. Additionally, the news flash could be checked for authenticity by admins before flashing it across the organization which avoids the chances of employees falling prey to fake news etc. | |
Intuitive Knowledge Articles on Cases (Demo in next section) | |
Quick entertaining Videos for the reps; all while working on their Leads right within Salesforce (additionally add some intelligence by asking Einstein to identify the mood of the rep based on the activity trail and accordingly serve the videos pertaining his/her mood) | |
Are you a vlogger posting videos regularly over YouTube and want to gain some insights over the performance of each video? Then this integration might help you because you can not just play the videos in Salesforce but also pull the stats of videos into Salesforce for reporting, analytics etc. Salesforce YouTube integration can prove to be a great Video CMS for you! | |
You want to start an online tutorial portal where you create videos for your users. But you want the videos to be available only to the paid subscribers and also don't want to bear the cost of hosting the videos and maintaining them on a new platform. With Salesforce YouTube integration and Salesforce LMS (Learning Management Systems) you can upload the videos to YouTube in private mode, and whenever a user pays and subscribes to your portal (e.g. using Salesforce Stripe integration - coming up in next article) you can open up access to the paid user for the videos relevant to the course that the user has subscribed. | |
Similarly, you can also manage the Marketing videos, Product Demo Videos and other videos related to your organization from within Salesforce. |
Sneak peek
In this article we demonstrate the use case of intuitive knowledge articles. Unlike the text based knowledge article, we display YouTube videos searched based on the Subject of a sample Case. The demo also brings in data such as likes, dislikes and views of a particular video.
The Lightning Component is configurable such that it can be dragged and dropped on any objects (standard/custom) record page and it will fetch videos based on the text from the field whose API name is configured in a custom metadata created for this purpose. Below is a snapshot of some sample metadata records for few objects. Take a look at fieldName for Case object, configured with Subject field - hence the videos on Case record page will fetch videos based on the text in the Subject field.
Quick Demo
How to integrate YouTube with Salesforce
Firstly, few things to setup in Google Cloud Platform
We need a Google Account to access the Google API Console, request an API key, and register our application.
- Create a project in the Google Developers Console and obtain authorization credentials so your application can submit API requests.
After creating your project, make sure the YouTube Data API is one of the services that your application is registered to use:
- Go to the API Console and select the project that you just registered.
- Visit the Enabled APIs page and follow below steps to obtain the key
The API Request & Response structure and Code Snippets
Part I : Fetch videos based on the Subject field of Case record
Below is a sample callout code using the YouTube Data API v3 and following is the response from the API.
HttpRequest request = new HttpRequest();
HttpResponse response = new HttpResponse();
Http http = new Http();
request.setEndpoint(''+searchResults.replaceAll(' ', '+')+'&type=video&key={YOUR_API_KEY}');
request.setMethod('GET');
try{
response = http.send(request);
System.debug(response.getBody());
}catch(System.CalloutException e) {
System.debug('Callout error: '+ e);
System.debug(response.toString());
}
Below is the JSON response for video search results based on the Subject of a sample case record.
{
"kind": "youtube#searchListResponse",
"etag": "\"xwzn9fn_LczrfK9QS3iZcGzqRGs/2ptDzqXXFWnBe5A1b1ypvIdH8NM\"",
"nextPageToken": "CAUQAA",
"regionCode": "US",
"pageInfo": {
"totalResults": 1000000,
"resultsPerPage": 5
},
"items": [
{
"kind": "youtube#searchResult",
"etag": "\"xwzn9fn_LczrfK9QS3iZcGzqRGs/XAQjwNlxoLq_epHnE2XFJB2G2sk\"",
"id": {
"kind": "youtube#video",
"videoId": "AHX6tHdQGiQ"
},
"snippet": {
"publishedAt": "2018-03-15T15:27:07.000Z",
"channelId": "UCbxQcz9k0NRRuy0ukgQTDQQ",
"title": "Ink Cartridges Are A Scam",
"description": "Printer companies are ripping us off, and it's high time we did something about it. Join me in starting the revolution.",
"thumbnails": {
"default": {
"url": "",
"width": 120,
"height": 90
},
"medium": {
"url": "",
"width": 320,
"height": 180
},
"high": {
"url": "",
"width": 480,
"height": 360
}
},
"channelTitle": "AustinMcConnell",
"liveBroadcastContent": "none"
}
},
{
"kind": "youtube#searchResult",
"etag": "\"xwzn9fn_LczrfK9QS3iZcGzqRGs/kZJUmeb5Su2YvYD_s8U0NOD7exs\"",
"id": {
"kind": "youtube#video",
"videoId": "wW6cw4ydn-o"
},
"snippet": {
"publishedAt": "2008-09-28T17:25:10.000Z",
"channelId": "UCQlGBspQdj17WOPBQMT1k9A",
"title": "Computer Upgrades & Repairs : How to Change Cartridges on an Inkjet Printer",
"description": "Change cartridges on an Inkjet printer by opening the printer door and lifting up on the flap to unlock the cartridge. Remove and replace an Inkjet printer ...",
"thumbnails": {
"default": {
"url": "",
"width": 120,
"height": 90
},
"medium": {
"url": "",
"width": 320,
"height": 180
},
"high": {
"url": "",
"width": 480,
"height": 360
}
},
"channelTitle": "expertvillage",
"liveBroadcastContent": "none"
}
},
{
"kind": "youtube#searchResult",
"etag": "\"xwzn9fn_LczrfK9QS3iZcGzqRGs/NmUS9NDUZ8TYGWot_MqUes4sCSw\"",
"id": {
"kind": "youtube#video",
"videoId": "_S2lYXf-uu0"
},
"snippet": {
"publishedAt": "2019-08-10T15:00:04.000Z",
"channelId": "UCcyq283he07B7_KUX07mmtA",
"title": "Why Printer Ink Is So Expensive | So Expensive",
"description": "Printer ink can sometimes cost more than the printer itself. We talk with a former editor of the Recycler to find out why printer ink is so expensive. MORE SO ...",
"thumbnails": {
"default": {
"url": "",
"width": 120,
"height": 90
},
"medium": {
"url": "",
"width": 320,
"height": 180
},
"high": {
"url": "",
"width": 480,
"height": 360
}
},
"channelTitle": "Business Insider",
"liveBroadcastContent": "none"
}
},
{
"kind": "youtube#searchResult",
"etag": "\"xwzn9fn_LczrfK9QS3iZcGzqRGs/ykbIv6fovakqi13OenCoJ0fO6ko\"",
"id": {
"kind": "youtube#video",
"videoId": "kaHHvDuuGV0"
},
"snippet": {
"publishedAt": "2008-09-28T17:15:59.000Z",
"channelId": "UCQlGBspQdj17WOPBQMT1k9A",
"title": "Computer Upgrades & Repairs : How to Change a Laser Printer Cartridge",
"description": "Change a laser printer cartridge by opening the printer door and detaching the cartridge from the system. Remove and replace a laser printer cartridge with tips ...",
"thumbnails": {
"default": {
"url": "",
"width": 120,
"height": 90
},
"medium": {
"url": "",
"width": 320,
"height": 180
},
"high": {
"url": "",
"width": 480,
"height": 360
}
},
"channelTitle": "expertvillage",
"liveBroadcastContent": "none"
}
},
{
"kind": "youtube#searchResult",
"etag": "\"xwzn9fn_LczrfK9QS3iZcGzqRGs/0uDr-nCvxGVm4yx-KYXrJ0ioCaE\"",
"id": {
"kind": "youtube#video",
"videoId": "wqmA8jpJae4"
},
"snippet": {
"publishedAt": "2015-08-04T19:33:12.000Z",
"channelId": "UCgFb3rcjC_J7o97bM10cPqg",
"title": "How to get more out of your ink cartridge",
"description": "In this video, we show you how you can extend the life of an ink cartridge by resetting the memory of the cartridge. Follow the steps to reset the ink cartridge ...",
"thumbnails": {
"default": {
"url": "",
"width": 120,
"height": 90
},
"medium": {
"url": "",
"width": 320,
"height": 180
},
"high": {
"url": "",
"width": 480,
"height": 360
}
},
"channelTitle": "Inkjet Star, Inc.",
"liveBroadcastContent": "none"
}
}
]
}
Part II : Fetching likes, dislikes and views of the videos fetched in Part I
We can get statistics based on video-Id
. Below is a sample callout code and following is the response structure. To fetch stats for multiple videos we need to pass comma separated string of video-Ids
HttpRequest request = new HttpRequest();
HttpResponse response = new HttpResponse();
Http http = new Http();
request.setEndpoint(''+searchkeyword+'&key={YOUR_API_KEY}');
request.setMethod('GET');
try{
response = http.send(request);
System.debug(response.getBody());
}catch(System.CalloutException e) {
System.debug('Callout error: '+ e);
System.debug(response.toString());
}
Below is JSON response for the statistics of videos that were fetched for a sample case record in the previous section.
{
"kind": "youtube#videoListResponse",
"etag": "\"xwzn9fnLczrfK9QS3iZcGzqRGs/4F-j0jHUNnpEF28bWSk1U5QR6yc\"",
"pageInfo": {
"totalResults": 5,
"resultsPerPage": 5
},
"items": [
{
"kind": "youtube#video",
"etag": "\"xwzn9fnLczrfK9QS3iZcGzqRGs/QI9T-hGUzWJ3O4obR6JVd89ub44\"",
"id": "wW6cw4ydn-o",
"statistics": {
"viewCount": "2253",
"likeCount": "3",
"dislikeCount": "1",
"favoriteCount": "0",
"commentCount": "1"
}
},
{
"kind": "youtube#video",
"etag": "\"xwzn9fnLczrfK9QS3iZcGzqRGs/d9JBVFPfaMPeRR5lnpStZUfr5J0\"",
"id": "AHX6tHdQGiQ",
"statistics": {
"viewCount": "6783848",
"likeCount": "409704",
"dislikeCount": "8360",
"favoriteCount": "0",
"commentCount": "33588"
}
},
{
"kind": "youtube#video",
"etag": "\"xwzn9fnLczrfK9QS3iZcGzqRGs/fpZ1NnnKDFBKSB44bRAXwp-ORRQ\"",
"id": "kaHHvDuuGV0",
"statistics": {
"viewCount": "3789",
"likeCount": "2",
"dislikeCount": "4",
"favoriteCount": "0",
"commentCount": "0"
}
},
{
"kind": "youtube#video",
"etag": "\"xwzn9fnLczrfK9QS3iZcGzqRGs/QgSbp0b85uXwldHjwTcDRwvbTY\"",
"id": "S2lYXf-uu0",
"statistics": {
"viewCount": "1544592",
"likeCount": "30921",
"dislikeCount": "634",
"favoriteCount": "0",
"commentCount": "2507"
}
},
{
"kind": "youtube#video",
"etag": "\"xwzn9fnLczrfK9QS3iZcGzqRGs/b5l8HC-8KjG-rCu5CNMzSzQBBM8\"",
"id": "q3n7YYkRhVU",
"statistics": {
"viewCount": "24293",
"likeCount": "202",
"dislikeCount": "18",
"favoriteCount": "0",
"commentCount": "59"
}
}
]
}
The callout code snippets goes into the controller associated to the component - Youtube_API_V3 here
The component code
<aura:component controller="Youtube_API_V3" implements="force:hasSObjectName,force:hasRecordId,force:lightningQuickAction,force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,forceCommunity:availableForAllPageTypes" access="global">
<aura:attribute name="recordId" type="String" />
<aura:attribute name="sObjectName" type="String" />
<aura:attribute name="videoIds" type="Map"/>
<aura:attribute name="videoIds1" type="Map"/>
<aura:attribute name="searchKeyword" type="String"/>
<aura:attribute name="Message" type="boolean" default="false"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<lightning:spinner variant="brand" size="large" aura:id="Id_spinner" class="slds-hide" />
<article class="slds-post" style="padding-top: 20px;border-radius: 5px;border: 1px solid #ccc;">
<div class="slds-card__body">
<aura:iteration items="{!v.videoIds}" var="video">
<div class="slds-feed">
<ul class="slds-feed__list">
<li class="slds-feed__item">
<article class="slds-card">
<div class="slds-card__body slds-card__body_inner">
<div style="margin-bottom:10px">
<h3 style="color: rgb(0, 109, 204);font-weight: bold;font-size: 14px;"> <aura:unescapedHtml value="{!video.value}"/></h3>
</div>
<div class="slds-post__payload">
<a href="javascript:void(0);" class="slds-media slds-box slds-grow slds-text-link_reset">
<div class="slds-media__figure slds-medium-show">
<div class="slds-file slds-size_small">
<iframe frameborder="10" src="{!''+video.key}" width="150" height="100" style="border:0px solid #ccc;border-radius:5px;"></iframe>
</div>
</div>
<div class="slds-media__body">
<!--<h3 class="slds-text-heading_small">{!video.value}</h3>-->
<!--<p>{!video.value}</p>-->
<span class="slds-text-body_small" style="width: 183px;margin-left: -90px;display: block;">{!video.description}</span>
</div>
</a>
</div>
</div>
<footer class="slds-post__footer">
<ul class="slds-post__footer-actions-list slds-list_horizontal">
</ul>
<ul style="padding:0 20px 10px 0px" class="slds-post__footer-meta-list slds-list_horizontal slds-has-dividers_right slds-text-title">
<li class="slds-item" id="{!video.key+'-shares'}">{!video.viewCount} views</li>
<li class="slds-item">{!video.likeCount} likes</li>
<li class="slds-item">{!video.dislikeCount} dislikes</li>
</ul>
</footer>
</article>
</li>
</ul>
</div>
</aura:iteration>
</div>
</article>
</aura:component>
The component controller code
({
doInit:function(component, event, helper) {
var action1 = component.get('c.getResponse');
action1.setParams({
"RecordId": component.get("v.recordId"),
"sObjectName" : component.get("v.sObjectName")
});
action1.setCallback(this, function(response){
var state = response.getState();
if((state === 'SUCCESS')){
var custs = [];
var conts = response.getReturnValue();
for ( var key in conts ) {
var title = conts[key].split('!@#$')[0];
var description = conts[key].split('!@#$')[1];
var viewCount = conts[key].split('!@#$')[2];
var likeCount = conts[key].split('!@#$')[3];
var dislikeCount = conts[key].split('!@#$')[4];
custs.push({value:title,key:key,description:description,viewCount:viewCount,likeCount:likeCount,dislikeCount:dislikeCount});
}
component.set("v.videoIds", custs);
} else if( state === 'INCOMPLETE'){
console.log("User is offline, device doesn't support.");
} else if( state === 'ERROR'){
console.log('Problem , error: ' +
JSON.stringify(response.getError()));
} else{
console.log('Unknown problem, state: ' + state +
', error: ' + JSON.stringify(response.getError()));
}
});
$A.enqueueAction(action1);
},
})
YouTube statistics: https://www.oberlo.com/blog/youtube-statistics