LucidVideo Automated Previews

The The LucidVideo Automated Previews Plugin allows you to quickly replace all static media thumbnails on your page or app with animated previews, increasing CTR and improving the overall user experience. Simply upload your media and then use our Javascript plugin or our SDKs to activate the Previews in your properties.

If you already have an account, you can now start uploading your library of contents to the LucidVideo platform.

You can upload contents in three different ways:

Automated Previews JS

Integrate our JS library to automatically replace static thumbnails for animated previews.

Automated Previews SDK

We’ll create a private folder in the cloud in which you may drop your assets.

Automated Previews JS

The LucidVideo Preview Player looks for certain patterns on your html, and tries to match the urls to the external-id or guid of your contents. If it finds an asset that matches one in your contents library, then it replaces the thumbnail by its corresponding preview, copying the same styles and mouse events. The gallery takes care of everything, so the only thing you need to do to enable previews on your website, is to add the JS plugin and pass your publicKey.

<script src="//static.lucidvideo.com/gallery/vpg.5.11.1.min.js"
        data-publickey="yourpublickey"
        data-redirect="true"></script>
HTML

To get your publickey, you can simply log into the Dashboard, click on Settings, and copy the text under PublicKey.

With this configuration you will get the default Gallery user experience:

Desktop Experience

Previews play when the user hovers over the video thumbnail with a redirect countdown to your video content.

Mobile Experience

LucidVideo creates a private folder in the cloud in which you can drop your assets in.

The Automated Previews Plugin allows you to fully customize the experience according to your needs.

Supported DOM Structures

For more information how the gallery detects the image teasers to be replaced.

Advanced Gallery Configuration

To further configure the Gallery (Redirect, Autoplay of previews or Audio support).

Interaction With Browser Policies

To further understand the behavior of certain browsers regarding the autoplaying of previews or videos and the different options the gallery supports.

Cookies and User Data

About handling cookies and user data.

Third Party Integration

If you are planning to perform an AB test, LucidVideo offers built-in support for Optimizely and other AB testing platforms).

Supported DOM Structures

LucidVideo looks for certain DOM patterns containing a valid video ID that corresponds to a video in your account. The basic pattern consists of a DOM element with an image inside – we will refer to the outer DOM Element as preview container and the inner image will be called as preview thumbnail:

  • The preview container must contain your video ID so it can be linked to the correct preview. This can be set as the href, if is inside an <a> TAG, or data-lucidvideo-id. (If your DOM elements already have an attribute which contains the video ID and with a different name you can use it with idattribute parameter)
  • The preview thumbnail can be an HTML Image Element with a valid src, an HTML Picture with a valid srcsetor any other DOM element with a background-image set by the style attribute.

Gallery plugin is compatible with most DOM structures. Some examples of supported DOM structures are shown below:

Video URL on an HREF inside an <a> TAG

<a> TAG with a Video URL and HTML Image as preview thumbnail

<a href="your_video_URL">
    <img src="img/bg01.jpg">
</a>
HTML

<a> TAG with a video URL and a DIV with background-image as preview thumbnail

<a href="your_video_URL">
    <div style="background-image: url('img/bg01.jpg')">
</a>
HTML
LucidVideo takes the HREF value of your <a> TAG and compares it with the video IDs in your account. If this value matches with any of your IDs, your image will be replaced by the preview.

Video ID on a data-lucidvideo-id attribute

<div> TAG with a video ID and HTML Image as preview thumbnail

<div data-lucidvideo-id="your_video_ID">
    <img src="img/bg01.jpg">
</div>
HTML

<span> TAG with an video ID and a DIV with background-image as preview thumbnail

<span data-lucidvideo-id="your_video_ID">
    <div style="background-image: url('img/bg01.jpg')">
</span>
HTML
LucidVideo will look for every DOM Element with a data-lucidvideo-id attribute to compare its value with your video IDs stored in your account.

Video ID and background image

<div data-lucidvideo-id="your_video_ID" style="background-image: url('img/bg01.jpg')"></div>
HTML
If background-image is set on your preview container this element will also be evaluated as a preview thumbnailcandidate, and will append the preview behind it.

Integrate The Automated Previews JS

Customize the user experience

LucidVideo gives you the opportunity to customize your user experience.

Desktop Experience

The default behavior is that a preview plays when the user hovers over the video thumbnail and will keep looping until the cursor leaves the thumbnail. This behavior can be modified in many ways according your needs.

  • Redirect Countdown
    One of the most valued features of the Gallery library is the ability to redirect (with a 3, 2, 1 countdown) to your video content when a user has seen one loop of the preview.
    To add this behavior on your website just add a data-redirect attribute to the Gallery script tag or add a single item configuration parameter.
    To add this behavior on your website just add a data-redirect attribute to the Gallery script tag or add a single item configuration parameter.
  • Autoplaying Previews
    Another possible experience is to have the previews play automatically when they are in the viewport.
    To add this behavior on your website you only need to add a data-autoplay attribute to the Gallery script tag or add a single item configuration parameter.
    If you want more customization on the autoplay behavior check Autoplay options section.
  • Previews With Audio
    If you want your previews to play with audio, add a data-audio parameter to the Gallery script tag or add a single item configuration parameter.

If you want more customization on the audio behavior check Audio options section.

Webkit browsers don't allow media content to play with audio if there isn't a user interaction with the site first. The plugin will play the previews with audio in this case, check Audio policies section for more information.

Mobile Experience

On mobile devices, LucidVideo’s default behavior is one autoplay preview at a time that starts to play when the element is in the viewport. But this can be modified.

  • Previews playing on tap
    In addition to autoplaying when the preview enters the viewport, previews can be played with a swipe action. To get this behavior add data-taptoplay on your Gallery script tag.
  • CTA message at preview’s end
    Similar to redirect for Desktop browsers, on mobile you can add a CTA (call-to-action) message at the end of the preview adding data-mobilecta on your Gallery script tag or add a single item configuration parameter.
If you want more customization on the mobile CTA behavior check Mobile options section.

Multiple Images

LucidVideo uses the preview thumbnail to place the preview, in the same position and with the same dimensions. If multiple preview thumbnail candidates are found inside the preview container, it will use the first it finds. If this first image is not the one needed you can use imgignore parameter to ignore it.

For example, imagine that you have inside your preview container two HTML Images, one is your preview thumbnail and the other is a play icon with play_icon class:

<div data-lucidvideo-id="your_video_ID">
    <img src="img/icon.jpg" class="play_icon"/>
    <img src="img/bg01.jpg"/>
</div>
HTML

With that structure the Gallery plugin could get confused and consider your first image as the preview thumbnail. If the imgignore parameter is added on the Gallery script tag with .play_icon value the Gallery will ignore all the image candidates that matches this Query Selector. Your script tag would look like:

<script type="text/javascript" src="//static.lucidvideo.com/gallery/vpg.5.11.1.min.js"
        data-publickey="your_public_key"
        data-imgignore=".play_icon"></script>
HTML

Background images support

LucidVideo supports any element with background image as an image candidate. By default every <div> and <button> tag is inspected to look for a background image.

In this example the <div> tag will be identified as an image candidate:

<div data-lucidvideo-id="your_video_external_id">
    <div style="background-image: url('img/bg01.jpg')"/>
</div>
HTML

If you have any other tag on your website that needs to be used as an image candidate for the library, you have to add it on bgimageselector as a Query Selector.

In this example, LucidVideo will look for <div> and <span> tags as image candidates:

<script type="text/javascript" src="//static.lucidvideo.com/gallery/vpg.5.11.1.min.js"
        publickey="your_public_key"
        bgimageselector="div, span"></script>
HTML
<div data-lucidvideo-id="your_video_external_id">
    <div style="background-image: url('img/bg01.jpg')"/>
</div>

<div data-lucidvideo-id="your_video_external_id">
    <span style="background-image: url('img/bg02.jpg')"/>
</div>
HTML

Lazy loading

Image lazy loading

Plugin supports lazy loading with a placeholder image without any extra configuration.

Otherwise, if the <img> tag doesn’t exists when the DOM is loaded, the element with the LucidVideo identifier needs to have lucidvideo_enabled class to be observed by the library for an image addition.

For example:

<div class="lucidvideo_enabled" data-lucidvideo-id="your_video_external_id">
</div>
HTML
<a href="your_video_URL" class="lucidvideo_enabled">
</a>
HTML

These elements will be observed until a valid image candidate is appended on it. When the image appears the preview will be placed behind it.

Video ID lazy loading

Apart from image lazy loading, the library has also support for data-lucidvideo-id attribute lazy loading.

LucidVideo will observe elements with lucidvideo_enabled that don’t have a data-lucidvideo-idattribute. When a valid identifier is added, the library will add a preview on that element.

Here the <div> tag does not have an external identifier:

<div class="lucidvideo_enabled">
    <img src="img/bg01.jpg">
<div>
HTML
Since the element has the lucidvideo_enabled class, the plugin knows it could be a valid element in the future and will observe it until a data-lucidvideo-id is added, then add the preview.

Advanced Gallery Configuration

Configuration Parameters

Gallery library has a set of parameters that allow you to customize the user experience.
This configuration can be set with Global configuration, which will affect all the previews on your website, or Single Item configuration that will affect only those elements where the configuration attributes are added.
Global configuration is done via attributes on the Gallery script tag and affects all the previews placed by the Gallery on your website.
For example, for a redirect experience with custom redirect text, your script file would look like this:
<script src="//static.lucidvideo.com/gallery/vpg.5.11.1.min.js"
        data-publickey="youruserhash"
        data-redirect="true"
        data-redirecttext="Personalized redirect text"></script>
HTML
Single item configuration is done using configuration classes by adding data-lucidvideo-attributename (or data-attributename if you are using a version previous to 5.0) to the node. This is useful for customizing your experience on specific elements or sections of your site. Note that these options will override Global Configuration if set. For example:
<a href="external_video_id" data-lucidvideo-redirect="true" data-lucidvideo-redirectafter="3000">
   <img src="img/bg01.jpg">
</a>
HTML
<a href="external_video_id" data-lucidvideo-redirect="true" data-lucidvideo-redirectafter="3000">
   <img src="img/bg01.jpg">
</a>
HTML

Options

The available attributes are in the following tables. The column Item config shows if the attribute is available for single item configuration or no.

One of the most valued features of the Gallery library is the ability to redirect (with a 3, 2, 1 countdown) to your video content after one loop of the preview. Redirect is only available for Desktop browsers; if you want a similar experience for mobile devices take a look at Mobile options. Example:


<script type="text/javascript" src="//static.lucidvideo.com/gallery/vpg.5.11.1.min.js"
        data-publickey="your_public_key"
        data-redirect="true"></script>
HTML
To enable this, add data-redirect attribute as global configuration. There are a set of parameters to customize the redirect experience:
NameTypeDefaultItem configDescription
redirectBooleanfalseYesShows a countdown while user is hovering over the video. Once the countdown is finished, the user gets redirected to the parent link.
redirecttextStringVideo Will Begin In...YesMessage shown along with the redirect countdown.
redirectwithclickBooleantrueYesWhen redirect countdown finishes, does a .click() on the element with external id. Needed if the parent is not an <a> tag. (example: inline video player)
redirectwithclicktargetString Query SelectorNoDoes .click() action on the element recieved at applying the Query Selector on the element with external id.
redirectafterNumberVideo durationYesTime in ms to wait before starting the countdown.
redirectautoplayBooleanfalseYesOnly with autoplay. If a user hovers over the autoplaying preview, redirect text is shown after one video loop. (This time can be modified with redirectafter attribute).
audiopolicyctaBooleanfalseYesShows CTA (call-to-action) text instead of the redirect countdown when user has audio policy active for this browser. Read Audio policies section if you need more information.

Gallery provides the option to play the preview automatically when it appears in the screen. Example:

<script type="text/javascript" src="//static.lucidvideo.com/gallery/vpg.5.11.1.min.js"
        data-publickey="your_public_key"
        data-autoplay="true"></script>
HTML
To do this, add the data-autoplay attribute as a global configuration. There are a set of parameters to customize the autoplay experience:
NameTypeDefaultItem configDescription
autoplayBooleanfalseYesShows the preview when it enters the viewport.
redirectautoplayBooleanfalseYesOnly with redirect. If a user hovers over the autoplaying preview, redirect text is shown after one video loop. (This time can be modified with redirectafter).
redirectafterNumberVideo durationYesOnly with redirect. Time in ms to wait before starting the countdown.
autoplayonceBooleanfalseYesPreview will autoplay and loop once when it enters the viewport.
fadeinadvanceNumber700YesCombined with autoplayonce, time in ms that the image will be faded in before video first loop ends. Default value is added to hide the video own fading.
pauseonhoverBooleanfalseYesPauses the autoplaying preview when the user hovers over it.
audioonce*BooleanfalseYesFirst loop of the preview is played with audio.
audiohover*BooleanfalseYesPreview is played with audio when the user hovers over on it.
stopnoimageBooleanfalseYesWhen a video is stopped by adding Item configuration lucidvideo_stop, paused video frame is showed instead of original image.
previewdelayNumber0YesTime in seconds to wait before preview starts playing once it enters the viewport. Note that this configuration option will only work, in desktop browsers, for autoplay elements.

* Webkit Browsers have strict policies while trying to play media content with audio. Check the Audio Polices section to know what Gallery library does when a preview with audio is played on a user which has audio policy active.

The default user experience on mobile is one preview playing at a time, which starts when the preview appears in user’s screen. Example:

<script type="text/javascript" src="//static.lucidvideo.com/gallery/vpg.5.11.1.min.js"
        data-publickey="your_public_key"></script>
HTML
There are a set of parameters to customize this mobile experience:
NameTypeDefaultItem configDescription
taptoplayBooleanfalseYesPreviews will be activated with a tap and swipe action on mobile devices, instead of autoplaying when entering the viewport.
mobilepreloadBooleantrueNoPreviews are preloaded before entering the viewport for a quicker start.
maxpreloadedvideosBoolean5NoOnly with mobilepreload. Maximum number of previews that can be preloaded at the same time.
mobilectaBooleanfalseYesShows a CTA (call-to-action) message after the first preview loop.
ctatextStringSee The Full ClipYesOnly with mobilecta. Text displayed when CTA message appears.
mobilectaloopcountNumber1YesOnly with mobilecta. Loop after which the CTA message appears.
hidectaiconBooleanfalseYesOnly with mobilecta. Hides play icon when CTA message appears.
followscrollBooleanfalseNoPreviews follow user scrolling movement to play next preview.
topviewportoffsetNumber0NoOffset (in pixels) from the top of device's screen to consider as the top of viewport. Useful when the page has a header bar on the top.
bottomviewportoffsetNumber0NoOffset (in pixels) from the bottom of device's screen to consider as the bottom of viewport. Useful when the page has a footer bar on the bottom.
previewdelayNumber0YesTime in seconds to wait before preview starts playing once it enters the viewport.
horizontalThresholdNumber80No % of element's width in viewport needed to play the preview.

If you want your previews to play with sound data-audio is needed on the script tag. Example:

The default user experience on mobile is one preview playing at a time, which starts when the preview appears in user’s screen. Example:

<script type="text/javascript" src="//static.lucidvideo.com/gallery/vpg.5.11.1.min.js"
        data-publickey="your_public_key"
        data-audio="true"></script>
HTML

Note that some browsers are moving forward on preventing media playback with sound if the user has not previously interacted with the website. You can find more information in the Audio policies section. If the preview can’t be played with sound due to audio policy it will be played without sound.

Apart from specific UX configurations, there are some general integration parameters.

NameTypeDefaultItem configDescription
publickeyStringNoUnique id to identify your LucidVideo account.
idattributeStringNoA custom attribute of yours that will contain the id to link video teases to previews in your account. Will be used in the same way as data-LucidVideo-id attriute.
disableallBooleanfalseNoDisables all elements for previews except those with the LucidVideo_enabled class specified.
nomobileBooleanfalseNoDeactivates the Gallery on mobile devices.
notabletBooleanfalseNoDeactivates the Gallery on tablet devices.
bgimageselectorString Query Selectordiv, buttonNoQuery selector used to find elements with background inside the element with external id. You can find more information in Background images section.
directurlBooleanfalseNoOnly if using guids as external ID. Gets previews with a redirection instead of using get_hashes API call. Contact us if you want more information.
imgignoreString Query SelectorNoIgnores a specific image (via Query Selector) from being selected as the preview main image (the preview size and positioning is based on the main image). You can find more information in Working with multiple images section
divignoreString Query SelectorNoElements to ignore so as not to disrupt preview playback on hover. Very usefull when, for example, a play icon is on top of the preview and is capturing the mousover event, making the preview stop when the mouse passes through this icon.
divignorelevelsNumber0NoNumber of parent nodes of the element with the external id to apply divignore Query Selector. For example, if the element to ignore is on the same div as the preview element, a value of 1 will be needed.
noimageBooleanfalseYesRemoves the original thumbnail (displays the preview right away).
ignorehrefBooleanfalseNoIgnores href attributes for <a> tags, looking only for data-LucidVideo-id attribute.
proactiveloadBooleanfalseNoLooks for new videos in viewport every 300 ms instead of on scroll. Necessary when working with carousels.
urlregexpString Regular ExpressionNoRegular expression to remove parts of the url before being compared to the external ids stored in your LucidVideo account. Useful to remove query parameters on href values for your preview candidates.
ignorecurrenturlBooleanfalseNoIgnores current path to be a preview candidate.
disablestorageBooleanfalseNoIf set to true, prevents the Gallery from storing any cookies on your site. Check the Cookie storage policies section for more information.
reduceresolutionBooleanfalseNoIf enabled, the preview resolution will be lowered one level.
scrollrenderdelayNumber0NoDelay the previews' rendering until user has stopped socrolling for the number of ms set in this parameter. The minimum value is 100ms to gently reduce the rendering actions on a usual scrolling behavior for all browsers.
personalizedpreviewsBooleanfalseNoIf enabled, different users will be shown different previews based on their engagement behavior. Contact us if you want to enable this feature in your account.
There are some parameters that are specific for single items:
ClassDescription
lucidvideo_disabledBefore the gallery loads prevents an element from having a preview.
After the gallery loads disables an element that already has a preview.
lucidvideo_enabledIf disableall parameter is active, allows this element to be tracked by the Gallery libray. If the element doesn't have an image or a data-lucidvideo-id attribute, makes the library observe it until having one.
lucidvideo_stopIf an element's preview is playing this class will pause/unpause the video by adding or removing it.

There are also single item attributes:

AttributeDescription
playpriority(Mobile only) This attribute is used on mobile gallery to specify which preview should be played when more than one is visible on the screen. The gallery will select the one with the highest priority. The gallery also listens to dynamic changes you made on the attribute once the page is loaded. By default all the elements have a priority of 0.

Gallery library allows third-party integration for analytics or A/B testing. The library will register and track events with Optimizely or your own tools. You can find more information on third-party integration section.

Here are the configuration parameters to integrate with third parties:

NameTypeDefaultDescription
ongalleryloadBooleanfalseFunction name to execute when the gallery loads. Check the Other analytics tools integration section
optimizelyBooleanfalseSends optimizely events to optimizely (Note: will use the account registered in your domain). Check the Optimizely integration section.
optimizelyoffBooleanfalseDisables LucidVideo previews but keeps tracking clicks for Optimizely. Check the Optimizely integration section.
previewplaytimeoutNumber500Time in ms since the preview starts playing and the Sumary Play event is tracked..

Interaction with Browser Policies

Audio Policies

Modern browsers are starting to prevent media playback with audio if the user doesn’t interact with the website first. Currently, webkit browsers (Google Chrome and Safari), have done applied this policy. You can find more information on Autoplay Policy Changes post written by Chrome developers.
Gallery library will manage the browser policy, so you don’t need to worry about this if your Gallery script configuration has previews with audio. Those previews will continue playing, but will play muted if the user has their browser audio policies active and with sound if the user interacts with the website at any point.
If you have inline video players on your site, and you are using a configuration with redirect and redirectwithclick parameters, audio policies will prevent a .click() by the plugin from being accepted as a user gesture. Your inline video player, if it has audio, will be paused on the first frame waiting for a real user click.
As a workaround, the Gallery library gives the option to show a CTA (call-to-action) message (this message can be modified with mobilectatext attribute) instead of the redirect countdown. This will ensure a real click action instead of firing a false .click()on the element.
To enable this, use lucidvideo_audiopolicycta class configuration on your embedded player elements.
<div class="lucidvideo_redirect lucidvideo_audiopolicycta" data-LucidVideo-id="external_id">
     <img src="img/bg01.jpg">
 <div>
HTML

Notice that the parameters are added as single item configuration but could also be added as global configuration.

You can determine the current state of the audio policy by calling checkAudioPolicyfrom lucidvideo.gallery. The first callback will fire if the audio policy is enabled – meaning, the page doest not have permission to play video with sound. The second callback will fire if the audio policy is disabled and video can play with sound. Example:
lucidvideo.gallery.checkAudioPolicy(
    function () {
        console.log("Audio policy is active. Playback with audio not allowed.");
    },
    function () {
        console.log("Audio policy is inactive. Playback with audio allowed.");
JavaScript

Cookie storage policies

The new GDPR Policy prohibits publishers from storing any identifying information about users without their explicit consent. We’re happy to report that none of the LucidVideo products collect personal information about users and are therefore fully compliant with GDPR. That being said, we do use cookies to store LucidVideo-specific user IDs.
If you want to allow your users to block this cookie for a specific page, use the disablestorage param in the script.
For example:
(function () {

 	var fileRef = document.createElement('script');

 	fileRef.setAttribute("type", "text/javascript");
 	fileRef.setAttribute("src", "//static.lucidvideo.com/gallery/vpg.5.11.1.min.js");
 	fileRef.setAttribute("data-publickey", "YOUR_USER_ID");
 	fileRef.setAttribute("data-disablestorage", "true");

 	document.body.appendChild(fileRef);

 })();
JavaScript

Third Party Integration

Comscore

A tracking pixel is a little piece of code containing a 1×1 pixel image that you can use to track any important activity on your website. This is done by making a server call to a third party like Comscore in the form of a URL.
The endpoint URL requested by Comscore includes a querystring with a variety of parameters. Most common are the “c-parameters” which represent different pieces of information, for example:
<!-- Begin Comscore Tag -->
	<noscript>
		<div>
			<img src="https://sb.scorecardresearch.com/p?c1=2&c2={{Client ID}}&c4={{Page URL}}" height="1" width="1" alt="*">
		</div>
	</noscript>
<!-- End Comscore Tag -->
HTML
Some of these fields are required, so you should make sure to pass all the mandatory labels. Don’t forget to include your C2 Value provided by Comscore, also known as the client ID, as well as the timestamp for when the pixel tracking request is created as a Comscore parameter!
Example LucidVideo can help you send your data to Comscore using the hook systemdescribed in the Other analytics tools integration section.
After implementing the code below, the Comscore tracking pixels will be added to each preview and fired when the user starts watching any preview.
The same can be done for the three key events tracked by LucidVideo: on_preview_click, on_preview_play and on_enter_viewport.
window.vlx_comscore = {
	// trackingPixelOnLoad will be passed to lucidvideo Gallery script tag, and will be called once the gallery plugin is ready.
	trackingPixelOnLoad: function () {

		// The tracking pixel URL will be called when the user watches a preview for 500ms or more.
		window.lucidvideo.gallery.addOnTrackSummaryPlay(
		function () {
			var cs_data = {
				cs_hostname: 'https://sb.scorecardresearch.com/b',
				c1: '2',
				c2: 'CLIENT_ID',// Your own client ID from comScore as the c2 parameter.
				c3: 'CAMPAIGN_ID',
				c4: '*null', // Unused mandatory labels must still be passed with the literal string value *null.
				ns__t: Date.now() // Timestamp for when the pixel is created.
			};

			// Add the hostname and the custom or mandatory labels to the tracking pixel URL:
			var pixelUrl = cs_data.cs_hostname + '?c1=' + cs_data.c1 + '&c2=' + cs_data.c2 + '&c3=' + cs_data.c3 + '&c4=' + cs_data.c4 + '&ns__t=' + cs_data.ns__t;

			// comScore api call via tracking pixel.
			var noscript = document.body.querySelector('[vlx-id="vlx_cs"]'),
			trackingPixel;

			if (!noscript) {
				noscript = document.createElement('NOSCRIPT');
				noscript.setAttribute('vlx-id', 'vlx_cs');
				document.body.appendChild(noscript);

				// Create an <img> tag from the comScore domain.
				trackingPixel = document.createElement('IMG');
				noscript.appendChild(trackingPixel);
			} else if (noscript.firstChild) {
				trackingPixel = noscript.firstChild;
			}

			// On the img request, the pixelUrl is updated with new data every time the summary_play event is triggered.
			trackingPixel.setAttribute('src', pixelUrl);
		}
		);

	}
};

// The lucidvideo Gallery plugin can be inserted with JS or can be in the DOM when the document loads. But the trackingPixelOnLoad function must exist when the gallery is loaded.
var galleryScript = document.createElement("script");

// ongalleryload will be called when the gallery plugin is successfully loaded.
galleryScript.setAttribute("data-ongalleryload", "window.vlx_comscore.trackingPixelOnLoad");

// publickey is required to identify you as a lucidvideo user.
galleryScript.setAttribute("data-publickey", "YOUR_USER_ID");

galleryScript.setAttribute("src", "//static.lucidvideo.com/gallery/vpg.5.11.1.min.js");

document.body.appendChild(galleryScript);
JavaScript

Other Analytic Tools

You can use the hook system to integrate any other analytics tool that provides a JS API. To track events, follow the next two steps:
  1. Add the ongalleryload param: On your script configuration add the ongalleryload attribute to set the name of the custom function that you wish to execute before the script starts to scan your page.
Example
(function addScript() {

	var fileRef = document.createElement('script');

	fileRef.setAttribute("type", "text/javascript");
	fileRef.setAttribute("src", "//static.LucidVideo.com/gallery/vpg.5.11.1.min.js");
	fileRef.setAttribute("data-publickey", "YOUR_USER_ID");
	fileRef.setAttribute("data-ongalleryload", "window.galleryTracking.customFunctionOnLoad");

	document.body.appendChild(fileRef);

})();
JavaScript
The function that you passed as a parameter will be executed when Gallery loads. In the example above, it would be window.galleryTracking.customFunctionOnLoad.
  1. Tracking events: The LucidVideo Gallery has a hook system that allows you to execute a list of callbacks to track some important events:
  • thumbnail_shown: (Mobile only) Will be fired when the element enters on viewport for first time.
  • summay_play: Will be fired every time the LucidLiveo preview starts playing for at least 500ms.
  • preview_click: Will be fired every time a user does a click over the preview container.
  • preview_redirect: Will be fired every time the redirect countdown finishes and the redirect action is done This event will be tracked only if redirect configuration is set to true.
  • preview_loop: Will be fired every time the preview does an entire loop.
These callbacks are set inside window.galleryTracking.customFunctionOnLoad as follows:
window.myAnalytics = {
	// Your custom function must contain the code that will be executed when the event is triggered
	track: function (eventTrack) {
		// As a simple example: let's get some feedback
		console.info('Tracking event: ' + eventTrack);
	}
};

window.galleryTracking = {
	// customFunctionOnLoad will be passed to lucidvideo Gallery script tag, and will be called once the gallery plugin is ready
	customFunctionOnLoad: function () {
		// You need to pass your function (window.myAnalytics.track() in this example) as first argument for any event you want to track:

		// To track every click on a preview
		window.lucidvideo.gallery.addOnPreviewClick(function (previewContainer) {

            console.log('Preview clicked. Element's id -> ', previewContainer.dataset.lucidvideoId);

			window.myAnalytics.track('on_preview_click');
		});

		// To track previews played (for at least 500ms)
		window.lucidvideo.gallery.addOnTrackSummaryPlay(function (previewContainer) {

            console.log('Preview has been played for 500ms. Element's id -> ', previewContainer.dataset.lucidvideoId);

			window.myAnalytics.track('on_preview_play');
		});

		// (Mobile only) To track when a preview element enters the viewport
		window.lucidvideo.gallery.addOnTrackThumbnailShown(function (previewContainer) {

            console.log('Preview entered viewport. Element's id -> ', previewContainer.dataset.lucidvideoId);

			window.myAnalytics.track('on_enter_viewport');
		});
	}
};

// The lucidvideo Gallery plugin can be inserted with JS or can be in the DOM when the document loads. But the customFunctionOnLoad function must exist when the gallery is loaded
var galleryScript = document.createElement('script');

// ongalleryload will be called when the gallery plugin is successfully loaded.
galleryScript.setAttribute('data-ongalleryload', 'window.galleryTracking.customFunctionOnLoad');

// publickey is required to identify you as a lucidvideo user.
galleryScript.setAttribute('data-publickey', 'YOUR_USER_ID');

galleryScript.setAttribute('src', '//static.lucidvideo.com/gallery/vpg.5.11.1.min.js');

document.body.appendChild(galleryScript);
JavaScript
Example The following example should help you understand the complete integration of the two steps described above.
window.galleryTracking = {
	customFunctionOnLoad: function () {
        // On those examples we will use window.myAnalytics as an example of your analytics provider.
		// You need to pass your function as first argument for any event you want to track.

        /**
         * (Mobile only) Function fired the first time the "previewContainer" element
         * enters the viewport.
         *
         * @param {HTMLElement} previewContainer - Element where the preview is attached.
         */
        window.lucidvideo.gallery.addOnTrackThumbnailShown(function (previewContainer) {
            console.log('Preview entered viewport. Element's id -> ', previewContainer.dataset.lucidvideoId);

            window.myAnalytics.track('on_enter_viewport');
        });

        /**
         * Function fired when the Preview has been playing for 500ms.
         *
         * @param {HTMLElement} previewContainer - Element where the preview is attached.
         */
        window.lucidvideo.gallery.addOnTrackSummaryPlay(function (previewContainer) {

            console.log('Preview has been played for 500ms. Element's id -> ', previewContainer.dataset.lucidvideoId);

            window.myAnalytics.track('on_preview_play');
        });

        /**
         * Function fired every time a user does a click on the "previewContainer" element.
         *
         * @param {HTMLElement} previewContainer - Element where the preview is attached.
         */
        window.lucidvideo.gallery.addOnPreviewClick(function (previewContainer) {

            console.log('Preview clicked. Element's id -> ', previewContainer.dataset.lucidvideoId);

            window.myAnalytics.track('on_preview_click');
        });

        /**
         * Function fired when the redirect countdown finishes and the user is redirected to a new page.
         *
         * @param {HTMLElement} previewContainer - Element where the preview is attached.
         * @param {Boolean} soundBlocked - Boolean indicating if the sound is blocked by browser audio policy.
         */
        window.lucidvideo.gallery.addOnPreviewRedirect(function (previewContainer, soundBlocked) {

            console.log('Redirect countdown finished. Element's id -> ',
                previewContainer.dataset.lucidvideoId);

            window.myAnalytics.track('on_preview_redirect');
        });


		/**
         * Function fired every time the preview's loop finishes.
         *
         * @param {HTMLElement} previewContainer - Element where the preview is attached.
         * @param {number} countLoop - Number of the finished loop starting from 1.
         */
        window.lucidvideo.gallery.addOnPreviewLoop(function (previewContainer, countLoop) {

            console.log('Preview loop' + countLoop + ' finished. Element's id -> ', previewContainer.dataset.lucidvideoId);

            window.myAnalytics.track('on_preview_loop');
        });

	}
};
JavaScript

Optimizely

To add a LucidVideo variation to your Optimizely account, open your Optimizely Console, create a new variation and add the optimizely param to your script. Example:
(function () {

	var fileRef = document.createElement('script');

	fileRef.setAttribute("type", "text/javascript");
	fileRef.setAttribute("src", "//static.lucidvideo.com/gallery/vpg.5.11.1.min.js");
	fileRef.setAttribute("data-publickey", "YOUR_USER_ID");
	fileRef.setAttribute("data-optimizely", "true");

	document.body.appendChild(fileRef);

})();
JavaScript
In order to compare the same events on the control page (A) and the variation (B) it’s necessary to include the library in the control but set the optimizelyoff param in the setup. The previous example will look like this in the control page:
(function () {

	var fileRef = document.createElement('script');

	fileRef.setAttribute("type", "text/javascript");
	fileRef.setAttribute("src", "//static.lucidvideo.com/gallery/vpg.5.11.1.min.js");
	fileRef.setAttribute("data-publickey", "YOUR_USER_ID");
	fileRef.setAttribute("data-optimizely", "true");
	fileRef.setAttribute("data-optimizelyoff", "true");

	document.body.appendChild(fileRef);

})();
JavaScript
Once this is done, you only need to register the events that you want to track:
  • Summary Play: register a new goal as usual and under events, select Custom Event, with this event to track: ‘summary_play’.
  • Summary Click: register a new goal as usual and under events, select Custom Event, with this event to track: ‘summary_click’.
  • Clicks: register a new click event. Make sure that the click event is assigned to the <a> element, not the <img>. To verify this, please click on Advanced and make sure you’re tracking the ‘a’, or ‘a#yourlinkid’.

Automated Previews SDK

The Automated Previews SDKs allow you to quickly add Automated Previews to your apps, with minimum development time. We have built-in SDKs for Android, TVML, Swift and ROKU. Click on one of the options below to start integrating now.

Android

Compatible from API 15 and upwards.

Swift

Compatible with Swift 5.1 and upwards and iOS 10 and upwards.

TVML

Compatible with any tvOS version.

ROKU

Compatible with Roku 7 and upwards.

Download SDK

Contact your account manager to download the SDK and an example app.

Android SDK Guide V2.7.0

To include LucidVideo previews on your Android or Android TV app, add the following dependency to your build.gradle file:

implementation 'com.lucidvideo:gallery:2.6.0'
Gradle

Requirements

The SDK uses ExoPlayer for diplaying the previews, so the first step will be to add ExoPlayer to your project. In your build.gradle file of your app module add:

implementation 'com.google.android.exoplayer:exoplayer:2.9.6'
Gradle

The SDK also requires Java 8, if you have not added it yo your project, go to your gradle app module and add:

android {
...
compileOptions {
    targetCompatibility 1.8
    sourceCompatibility 1.8
}
...
}
Gradle

If you are using the automatic manage of previews by the SDK then you need to use androidx RecyclerView, since the SDK expects a androidx.recyclerview.widget.RecyclerView:

AndroidX is the new replacement to the old Android support library. You can learn more about it here and learn how to migrate to AndroidX here.

Automated Previews Integration

Initialize the SDK

To initialize the LucidVideo SDK create a new instance of LucidVideoManager:

manager = LucidVideoManager(this, "your_public_key_here", LogLevel.Debug)
Kotlin

If you are using analytics you need to call manager.initAnalytics() after initializing the LucidVideoManager.

The SDK supports 2 different log levels: LogLevel.Debug and LogLevel.None, if no parameter is provided for logLevel, LogLevel.None is assumed.

Requesting Previews Data

Once you have the LucidVideoManager instantiated, to request a set of videos and thumbnails you need to create an array of LucidVideoResponse items.


class ... {

// variable to hold the response objects
private var listLucidVideoResponse = java.util.ArrayList()
...
    private fun startLucidVideo() {

        val requests = java.util.ArrayList<LucidVideoRequest>()
        requests.add(LucidVideoRequest("vid-1", "Video 1", "Subtitle 1", "https://www.LucidVideo.com/video_sample.mp4"))
        requests.add(LucidVideoRequest("vid-2", "Video 2", "Subtitle 2", "https://www.LucidVideo.com/video_sample.mp4"))

        // If the frame is set, it will be used to automatically calculate the best video resolution for it
        manager.getLucidVideoResponses(
                requests = requests,
                frame = LucidVideoFrame(640.0, 320.0),
                callback = object: LucidVideoManager.LucidVideoListener {
            override fun callbackErrorResponse(error: String) {

            }

            override fun callbackPlayerUrlResponse(response: ArrayList<LucidVideoResponse>) {
                // Assign the response to our class variable
                listLucidVideoResponse = response

            }
        })
    }
}
Kotlin

You can also manually set the quality of the videos:

// The last 2 parameters of lucidvideoRequest are the video and image quality.
requests.add(lucidvideoRequest(
    "vid-8",
    "Video 8",
    "Subtitle 8",
    "https://www.lucidvideo.com/video_sample.mp4",
    lucidvideoVideoQuality.VIDEO_PRO69_GALLERY_QUALITY,
    lucidvideoImageQuality.IMAGE_PRO69_GALLERY_QUALITY)
)

// Pass a null frame to ignore the parameter
manager.getlucidvideoResponses(
    requests = requests,
    frame = null,
    callback = object: lucidvideoManager.lucidvideoListener {
        ...
    }
Kotlin

Layout Setup

In order to display previews you need to add a PlayerView and an ImageView to your layout:

<com.google.android.exoplayer2.ui.PlayerView
    android:id="@+id/lucidvideo_video"
    android:layout_width="0dp"
    android:layout_height="220dp"
    android:background="@android:color/background_light"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:resize_mode="zoom"
    app:surface_type="texture_view"
    app:use_controller="false" />

<ImageView
    android:id="@+id/lucidvideo_img"
    android:layout_width="match_parent"
    android:layout_height="220dp"
    android:layout_gravity="center_horizontal"
    android:background="@android:color/background_light"
    android:contentDescription="@string/app_name"
    android:scaleType="centerCrop"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
XML

Implement the LucidVideoView Interface

In the view that corresponds to your layout you will need to implement the LucidVideoView interface, which has the following fields:

interface lucidvideoView {
    var videoInfo: lucidvideoResponse?
    var playerView: PlayerView
    var thumbnailView: ImageView
}
Kotlin

This interface is used in order to make the SDK know which are the view it has to manage and were to add the previews.

videoInfo is an instance of LucidVideoResponse, that is the object that you get when making a request to the SDK.

Example integration in Java code:

class lucidvideoViewHolder extends RecyclerView.ViewHolder implements lucidvideoView {
...
ImageView imageCover;
PlayerView playerView;
lucidvideoResponse lucidvideoResponse;

lucidvideoViewHolder(@NonNull View itemView) {
    super(itemView);
    ....
    playerView = itemView.findViewById(R.id.lucidvideo_video);
    ...
}
...
@NotNull
@Override
public lucidvideoResponse getVideoInfo() {
    return lucidvideoResponse;
}

@Override
public void setVideoInfo(@NotNull lucidvideoResponse lucidvideoResponse) {

}

@NotNull
@Override
public PlayerView getPlayerView() {
    return playerView;
}

@Override
public void setPlayerView(@NotNull PlayerView playerView) {

}

@NotNull
@Override
public ImageView getThumbnailView() {
    return imageCover;
}

@Override
public void setThumbnailView(@NotNull ImageView imageView) {

}
...
Java

Example integration in Kotlin code:

class MainActivity : ..., lucidvideoView {
...
override var videoInfo: lucidvideoResponse? = null
override lateinit var playerView: PlayerView
override lateinit var thumbnailView: ImageView

public override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    ...
    playerView = findViewById(R.id.lucidvideo_video)
    thumbnailView = findViewById(R.id.image_view_main_activity)
    ...
}
Kotlin

When videoInfo is null the SDK will not insert any preview into the view, and when it is not it will use the provided videoInfo object to insert the preview related to it.

Automated Previews Resolution

The SDK comes with a set of predefined enums that define the resolution of video and image resources:

enum class LucidVideoVideoQuality(val quality: String) {
    VIDEO_PRO69_HIGH_QUALITY("pro69high.viwindow.mp4"),
    VIDEO_PRO69_MID_QUALITY("pro69.viwindow.mp4"),
    VIDEO_PRO69_GALLERY_QUALITY("gallery.pro69.viwindow.mp4"),
    VIDEO_SHORT_QUALITY("short.viwindow.mp4"),
    VIDEO_SHORT_GALLERY_QUALITY("gallery.short.viwindow.mp4")
}

enum class LucidVideoImageQuality(val quality: String) {
    IMAGE_PRO69_HIGH_QUALITY("pro69high.viwindow0.jpg"),
    IMAGE_PRO69_MID_QUALITY("pro69.viwindow0.jpg"),
    IMAGE_PRO69_GALLERY_QUALITY("gallery.pro69.viwindow0.jpg"),
    IMAGE_SHORT_QUALITY("short.viwindow0.jpg"),
    IMAGE_SHORT_GALLERY_QUALITY ("gallery.short.viwindow0.jpg")
}
Kotlin

The resolutions associated are:

  • PRO69_HIGH: 1280×720
  • PRO69_MID: 640×360
  • PRO69_GALLERY: 360×202

*PRO69 stands for 16:9 aspect ratio.

Managing Previews

Now that everything is configured the last step is to tell the SDK which views will it manage.

Automatically Manage Previews Inside a RecyclerView

If you are using a RecyclerView and their ViewHolders implement the LucidVideoViewinterface then you can tell the SDK to automatically manage the play/pause of the video, the hide/show of the thumbnail and sending analytics related to the play/pause events.

manager.configureRecyclerView(recyclerView!!)
Kotlin

A good place to call it would be after making the request to the SDK and configuring your RecyclerView.

When scrolling the recycler view the SDK will look for the most visible item at it and then add an ExoPlayer to it, start playing it and tracking the events. The SDK will also automatically manage cleaning old players and decoders.

This method is used in the example Android app of the SDK.

Manually Managing Previews

If you want to add a preview to any kind of view that it is not on a RecyclerView, or want to manually manage the RecyclerView you can use the built-in function buildExoPlayer to get an ExoPlayer with the preview built on it.

If you want to use analytics you will need to use the built-in function buildLinkedExoPlayer to get an ExoPlayer which has attached listeners for the play/pause events.

This method is used on the Android TV/FireTV demo of the SDK.

Releasing Resources

When displaying videos on Android devices, we must take special care of releasing the resources (players, media decoders…). The SDK comes with a built-in function that will clear all the resources associated to a view that implements the LucidVideoView interface:

class lucidvideoManager... {
...
fun releaselucidvideoView(lucidvideoView: lucidvideoView) {
    ...
}
Kotlin

Personalized Previews

If your account supports personalized previews, you can request them by passing RequestType.PersonalizedPreviews as the requestType parameter of getLucidVideoResponses:


manager.getlucidvideoResponses(
    requests = requests as java.util.ArrayList /< lucidvideoRequest />,
    frame = null,
    requestType = RequestType.PersonalizedPreviews,
    callback = object: lucidvideoManager.lucidvideoListener {
        ...
    }
Kotlin

The SDK will compute the best preview for the current user based on their unique profile. When a user clicks on a video, manager.registerClickForLucidVideoResponse(lucidVideoResponse: LucidVideoResponse, endTime: Int) must be called in order to register the content view for the user’s profile.

manager = lucidvideoManager(
    ...
    disableStorage = false,
    ...
)
Kotlin

When disableStorage is activated it will clear the current user profile, if it exists, and no data will be stored on the device.

Disabling User Profile

You can disable storing a user profile generated by personalized previews by passing true to the disableStorage parameter when initializing your LucidVideoManager instance:

Analytics

To initialize analytics on your project you just need to call initAnalytics() on a LucidVideoManager instance.

manager.initAnalytics()
Kotlin

Analytics: Managing The Click Event

Since views in Android can only have one ClickListener the click event must be manually tracked. To do so you need to call:

fun trackSummaryClick(lucidvideoResponse: lucidvideoResponse, endTime: Int)
Kotlin

Which is available in manager.analytics.

Cache Management

The Android SDK comes with built-in cache for video resources. The SDKs uses 512MB at max and relies on the Android cache management system to clean the old files.

The cache can be manually cleared by calling the clearCache() method of a LucidVideoManager instance.

Download SDK & Example

To get access to our SDKs, please contact us.

Swift SDK Guide

Including the Library

Manually Adding the SDK

To add the LucidVideoSDK to your Xcode project:

  • Add the file Gallery.xcframework to your Xcode project.
  • From the General tab of your project add Gallery.xcframework to Embedded Binaries and Linked Framework and Libraries.
  • Import Gallery from your source files.

Configuring The Library

Configure the SDK with your LucidVideo public key (available on the dashboard) and a log level from your AppDelegate:

func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
) -> Bool {
    lucidvideo.sharedInstance.configureWith(
        publicKey: "PUBLIC_KEY",
        logLevel: .debug/.none)

    return true
}
Swift

Automated Previews Integration

Making Requests With The SDK

To get the previews you need to create a LucidVideoRequest instance for each preview you want to display. The only required parameter is videoId (corresponding to the content’s guid or URL).

LucidVideoRequest has the following fields:

  • videoId: String!: guid or url of the video in LucidVideo.
  • title: String?: If set, the title is returned in LucidVideoResponse.
  • subtitle: String?: If set, the subtitle is returned in LucidVideoResponse.
  • videoUrl: String?: If set, the url is returned in LucidVideoResponse.
  • videoQuality LucidVideoVideoQuality?: Sets the video resolution for the response.
  • imageQuality: LucidVideoImageQuality?: Sets the image resolution for the response.

Once you have an array of LucidVideoRequest instances, you can call LucidVideo.sharedInstance.getLucidVideoResponse to obtain the preview data. This method has the following parameters:

  • requests: [LucidVideoRequest]: the array of LucidVideoRequest instances.
  • frame: CGRect?: a frame used to compute the resolution of the thumbnail and preview. It has a default value of nil and you can skip setting it if your LucidVideoRequestinstances have videoQuality and imageQuality configured.
  • completion: @escaping ([LucidVideoResponse]) -> (): callback used to return an array of LucidVideoResponse instances.
  • onError: @escaping(LucidVideoRequestError) -> (): error callback for the request.

LucidVideoResponse class contains the following fields:

  • videoUrl: String?: video url.
  • thumbnailUrl: String?: url of the thumbnail generated by LucidVideo.
  • videoPreviewUrl: String?: url of the preview generated by LucidVideo.
  • id: String!: guid or url of the video.
  • title: String?: the value of title if set in the corresponding LucidVideoRequest instance.
  • subtitle: String?: the value of subtitle if set in the corresponding LucidVideoRequest instance.
  • variation: ABTestVariation?: current user group for the active A/B test of your account, or nil if no A/B test is active.

Sample code:

...
fileprivate var lucidvideoResponse = [lucidvideoResponse]()
// The lucidvideoRequests array, you might build it dynamically in your app.
fileprivate var requests = [lucidvideoRequest(videoId: "vid-1"),
                        lucidvideoRequest(videoId: "vid-2")]
...

override func viewDidLoad() {
    super.viewDidLoad()


    // Call to the SDK to get the responses
    lucidvideo.sharedInstance
        .getlucidvideoResponses(
            requests: requests,
            frame: CGRect.init(x: 0,
                               y: 0,
                               width: UIScreen.main.bounds.width - 30,
                               height: 202)),
        { (resp) in

            // Starting the analytics module once we got a response
            lucidvideo.sharedInstance.initAnalytics()
            self.lucidvideoResponse = resp
    }, { (error) in {
        ...
    })
}
Swift

Integrating Previews on UITableView and UICollectionView

If you want to display your previews on a UITableView or UICollectionView the SDK comes with two classes that can help you do that:

  • LucidVideoCollectionViewManager: class used for managing UICollectionViews
  • LucidVideoTableViewManager: class used for managing UITableViews

The usage of the two classes is very similar. In order to use them, the first step is to create an instance:

self.collectionViewManager = lucidvideoCollectionViewManager(collectionView: self.collectionView,
                                                        placeholder: placeholder,
                                                        selectionType: .center)
Swift

The selectionType is used for indicating which visible cell has to display the preview. The possible values are:

  • first
  • second
  • center
  • penultimate
  • last

Placeholder is used to display a placeholder image on the thumbnails while it loads.

Once this is done, you need to add a UIView and a UIImageView to your cell view. The UIImageView should be at the top and will be used for the thumbnail. The UIView will be used for embedding an AVPlayerLayer with the preview.

You will also need to add the LucidVideoView protocol to your cell class:

class LucidVideoCollectionViewCell: UICollectionViewCell, LucidVideoVideoView {
var LucidVideoResponse: LucidVideoResponse?

@IBOutlet weak var videoView: UIView!
@IBOutlet weak var videoTitleLabel: UILabel!
@IBOutlet weak var thumbnailImageView: UIImageView!

func setData(LucidVideoResponse: LucidVideoResponse) {
    self.LucidVideoResponse = LucidVideoResponse

    videoTitleLabel.text = LucidVideoResponse.title
    self.backgroundColor = .white
}
Swift

The lucidVideoResponse variable from the protocol corresponds to the LucidVideoResponse instance of the cell.

Finally, you need to listen to scroll events, and play/pause the previews when they appear in the screen.

In order to allow the SDK to listen to scroll events, you will need to add the following code to the ViewController where your UICollectionView/UITableView is:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    collectionViewManager?.didScroll()
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "previewItem", for: indexPath) as! lucidvideoCollectionViewCell

    ...
    collectionViewManager?.configureCell(cell)
    ...

    return cell
}
Swift

In order to pause/play when the current view appears/disappears from the screen you will need to add:

override func viewDidDisappear(_ animated: Bool) {
    collectionViewManager?.didDisappear()
}

override func viewDidAppear(_ animated: Bool) {
    collectionViewManager?.didAppear()
} 
Swift

If you want to add analytics to UITableView or UICollectionView, please follow the steps here.

Integrating Previews on Other View Using The LucidVideo UIView Extension

The SDK comes with a UIView extension, which allows you to add a preview directly to any UIView subclass (UIButton, UIImage…).

/**
- Returns: an AVPlayerLayer named "lucidvideoVideo" if it exists in the view.
*/
public func getlucidvideoPlayerLayer() -> AVPlayerLayer?

/**
Looks at all view sublayers, find those named "lucidvideoVideo" and clears them.
*/
public func clearlucidvideoSublayers()

/**
Adds an AVPlayerLayer to the view
- parameter url: the url to load the video resource.
*/
public func addlucidvideoVideoLayer(url: String, paused: Bool = false)

/**
Adds an AVPlayerLayer to the view and tracks the events.
- parameter url: the url to load the video resource.
*/
public func addTrackedlucidvideoVideoLayer(lucidvideoResponse: lucidvideoResponse, paused: Bool = false)

/**
Adds an AVPlayerLayer to the view which loops on end.
- parameter url: the url to load the video resource.
*/
public func addLoopinglucidvideoVideoLayer(url: String)

/**
Adds an AVPlayerLayer to the view which loops on end and tracks the events.
- parameter url: the url to load the video resource.
*/
public func addTrackedLoopinglucidvideoVideoLayer(lucidvideoResponse: lucidvideoResponse, paused: Bool = false)
Swift

You can access to the AVPlayerLayer that got added to a view by calling .getLucidVideoPlayerLayer() of the extension. To access the associated player you can call .getLucidVideoPlayerLayer()?.player.

clearLucidVideoSublayers() will clear all the LucidVideo player layers that are on a view.

To completely release the resources for the looping and/or tracked players you need to call one of the memory managing methods that are explained on 3.3.1 Memory management.

Since most of the default view elements in iOS/tvOS inherit from UIView, the extension can be used on them. The EmbedViewController that comes with the tvOS example app uses this method.

button.addlucidvideoVideoLayer(url: urlVideo)
Swift

Memory Management

iOS and tvOS have a hard limit of 16 AVPlayer objects that can be instantiated at the same time. If you try to create more players after that they will not work. Because of that, you have to be careful with the players that you have at each time and release them when they are no longer needed.

If you are using UIView extensions you will also need to free up the resources used by the SDK. Every time you create a looping player with the SDK, the SDK will create listeners and internally manage the looping of the video. Those listeners keep a strong reference to the player and in will not be completely cleared as long as this reference is alive. To release the resources the SDK has the following methods:

  • clearLoopers(): Will release all the looping listeners.
  • clearLoopersExceptLast(): Will release all the looping listeners except the last one created. This is useful if you are making some kind of fade-in animation of the thumbnail image, since deleting all the looping listeners would stop the player from playing before the animation is completed.
  • releaseResources(): will clear all the loopers and pending download tasks, useful when you want to completely stop all the players. It is recommended to call this function on viewWillDisappear ViewController life-cycle method to release the resources.
  • clearLoopersAndListeners(keepLast: Bool): releases the loop listeners and event listeners (used for analytics). Use this method if you are using the analytics module.

lucidvideo.sharedInstance.getlucidvideoResponses( requests: requests,
    frame: ...,
    requestType: .personalizedPreviews,
    onCompletion: { (resp) in
        ...
    }, onError: {...}
})
Swift

Personalized Previews

If your account supports personalized previews, you can request them by passing .personalizedPreviews as the requestType parameter of getLucidVideoResponses:



lucidvideo.sharedInstance.getlucidvideoResponses( requests: requests,
    frame: ...,
    requestType: .personalizedPreviews,
    onCompletion: { (resp) in
        ...
    }, onError: {...}
})
Swift

The SDK will compute the best preview for the current user based on their unique profile. When a user clicks on a video, LucidVideo.sharedInstance.registerClickFor(lucidVideoResponse: LucidVideoResponse, endTime: Int) must be called in order to register the content view for the user’s profile.

Disabling User Profile

You can disable storing a user profile generated by personalized previews by passing true to the disableStorage parameter when configuring the library:

lucidvideo.sharedInstance.configureWith(
    ...
    disableStorage: true,
    ...
)
Swift

When disableStorage is activated it will clear the current user profile, if it exists, and no data will be stored on the device.

Analytics

To enable the analytics module you need to call LucidVideo.sharedInstance.initAnalytics() from your application. The SDK will start to track events only after you call it.

Analytics On UTTableView and UICollectionView

The UITableView and UICollectionView managers that come with the SDK will automatically track all the play and pause events of the previews, but you will need to take care of the click event.

Analytics Using the LucidVideo UIView Extension

To add analytics to the UIView extension, you only need to use any of the two addLucidVideoLayer methods that having tracking activated: (addTrackedLucidVideoLayerand addTrackedLoopingLucidVideoLayer) instead of the regular ones (addLucidVideoLayer and addLoopingLucidVideoLayer). These methods take care of tracking all the events for analytics but the click event.

/**
Adds an AVPlayerLayer to the view and tracks the events.
- parameter url: the url to load the video resource.
*/
public func addTrackedlucidvideoVideoLayer(lucidvideoResponse: lucidvideoResponse, paused: Bool = false)

/**
Adds an AVPlayerLayer to the view which loops on end and tracks the events.
- parameter url: the url to load the video resource.
*/
public func addTrackedLoopinglucidvideoVideoLayer(lucidvideoResponse: lucidvideoResponse, paused: Bool = false)
Swift

Handling The Click Event

To handle the click event you need to call trackSummaryClick after a successful click is performed: LucidVideo.sharedInstance.analytics.trackSummaryClick(lucidVideoResponse: LucidVideoResponse, endTime: Int)
/**
Starts downloading a resource and saves it to cache.
- Parameter url: url of the resource to preload
*/
public func preloadResource(url: String)
Swift

Preloading

If you want to start loading some video resources before the user requests them (to avoid waiting times), the SDK comes with a function for doing it:

Cache Management

The SDK comes with integrated cache for video resources. It relies on iOS cache management system to clean the old files.

Download SKD + Example

To get access to our SDKs, please contact us.

TVML SDK Guide

Configuring The Library

Adding LucidVideo previews on tvOs is very simple and doesn’t require any changes to your application’s core code.

You’ll need to include the LucidVideo TVJS script, enter your user key (which you can obtain from your User details in the LucidVideo Dashboard) and add the LucidVideo_media parameter to your Media Content. Please refer to the following instructions for information on how to integrate LucidVideo previews into your tvOS App.

1. Load The LucidVideo OTTScript

Load the following script on your App.onLaunch function:

An example of this can be seen here:

App.onLaunch = function () {
const lucidvideoOttScriptUrl = "https://static.lucidvideo.com/ott/tvOsVpg.1.0.0.min.js";
var jsFilesArray = [lucidvideoOttScriptUrl];

evaluateScripts(javascriptFiles, function (success) {
    if (success) {
        //All the script are injected
        lucidvideoGallery = new lucidvideoGallery();// Create a new lucidvideo Gallery instance
        lucidvideoGallery.key = 'yourpublickey'//Your user key
        var extension = "templates/main_template.xml"; //Get your TVML template
        getDocument(extension);

    } else {
        throw ("Playback Example: unable to evaluate scripts.");
    }
});
}
JavaScript

2. Modify Your TVML Layout

The second step is to add the lucidvideo_media parameter to the media contents in which you want to enable the previews.

Your mediaContent can be as you want, but it should contain at least the playbackMode=”onFocus”, the lucidvideo_media and include an image element.

To do so, please follow this format on your TVML layout:

<!--  Your TVML code -->
<lockup>
    <mediaContent playbackMode="onFocus" lucidvideo_media="videoGUID or videoUrl">
        <img src="example.com/newsImage.jpg" width="250" height="141">
        <title>This is a title</title>
    </mediaContent>
</lockup>
<!-- More TVML code -->
HTML

Remember that you will have to pass the video url or GUID that identifies that video so that the API can understand which teaser to display.

3. Executive LucidVideoGallery.AnimateMedia()

Finally, before inserting your TVML layout, you need to execute the following function:

CopylucidvideoGallery.animateMedia(document);
JavaScript
And that should be it. The LucidVideoGallery OTT plugin will take care of showing the teasers to animate your thumbnails.

Integration Examples

The following integration example should help you understand the three steps described above. If you need more help to integrate the OTT plugin send us an email to info@makeitlucid.com

On the next snippet, is a example of TVJS code for the integration:

var LucidVideoGallery;

App.onLaunch = function () {
const LucidVideoOttScriptUrl = "https://static.LucidVideo.com/ott/tvOsVpg.1.0.0.min.js";
var jsFilesArray = [LucidVideoOttScriptUrl];

evaluateScripts(javascriptFiles, function (success) {
    if (success) {
        //All the script are injected
        LucidVideoGallery = new LucidVideoGallery();// Create a new LucidVideo Gallery instance
        LucidVideoGallery.key = 'yourpublickey'//Your user key
        var extension = "templates/main_template.xml"; //Get your TVML template
        getDocument(extension);

    } else {
        throw ("Playback Example: unable to evaluate scripts.");
    }
});
}

/* fetch a document page from a remote server */
function getDocument(extension) {
var templateXHR = new XMLHttpRequest();
var url = baseURL + extension;
loadingTemplate();
templateXHR.responseType = "document";
templateXHR.addEventListener("load", function() {pushPage(templateXHR.responseXML);}, false);
templateXHR.open("GET", url, true);
templateXHR.send();
}

/* This function will inject a loaded xml doc into de main view */
function pushPage(document){
var currentDoc = getActiveDocument();

//Once the TVML is loaded and before injecting it into the page, we will let LucidVideo take control of all the media elements
LucidVideoGallery.animateMedia(document);

// Inject or replace
if (currentDoc.getElementsByTagName("loadingTemplate").item(0) == null) {
    console.log("no loading");
    navigationDocument.pushDocument(document);

} else {
    navigationDocument.replaceDocument(document, currentDoc);
    console.log("loading");
}
}
JavaScript

Roku SDK Guide

Configuring The Library

This library adds a video preview component called LucidVideoPreview.

The Roku platform doesn’t allow playing multiple videos at the same time, therefore it is required to manage every change in the player’s status.

In the home screen, we will call the main fullscreen video player object “VideoPlayer”.

There are two possible structures to this code:

  • The LucidVideoPreview and VideoPlayer are in the same XML.
  • LucidVideoPreview and the VideoPlayer are in different XMLs.

Both options follow the same logic, with the only difference that everything should be added in the same .xml file and handled by its .brs file in the first option.

Below we will review an example using two different XMLs (option 2).

1. INSTALL

  • Add the folder LucidVideo/ to the folder /components
  • Add the file LucidVideo.brs on the /source folder
  • Every item in the grid screen must have the following structure (there is an example on the main.brs file included in the demo):

item = {
"title": "Title example",
"description":"Description example",
"url":"here goes the fullscreen video url",
"lucidvideoUrlForHash":"here goes the lucidvideo URL needed to get the corresponding hash"
}
JSON

Before adding the items to the grid, it is necessary to call to init the LucidVideo SDK from your channel main file.

initLucidVideo("<LucidVideo owner token>").

Calling addLucidVideoItemsToRow(row, itemsArray) will return the row with LucidVideoItems inserted in the same order:

/**
* "row" is a object type "RoSGNode"
* "itemsArray" is the array of "items" with the indicated schema.
**/
lucidvideoRow = addlucidvideoItemsToRow(row, itemsArray)
BrightScript

This row can be appended to the grid.

An example usage can be found on the demo project:

/**
* "row" is a object type "RoSGNode"
* "itemsArray" is the array of "items" with the indicated schema.
**/
lucidvideoRow = addlucidvideoItemsToRow(row, itemsArray)
BrightScript
Function setGridContent(list As Object)
RowItems = createObject("RoSGNode","ContentNode")

'Set the lucidvideo Public Key
initlucidvideo("XXXXXXXXXX")

for each rowAA in list
    row = createObject("RoSGNode","ContentNode")
    row.Title = rowAA.Title


    row = addlucidvideoItemsToRow(row, rowAA.ContentList)

    RowItems.appendChild(row)
end for

return RowItems
End Function
BrightScript

2. Cache Management

LucidVideoPreview comes as a component that has a video player. When you request it to play a video, it will use the LucidVideoCacheTask (that comes with the SDK) to check if the requested video was previously cached. If so, the video will be played from local device storage, otherwise it will download it and then play it.

Roku OS only supports caching since version 8.0. The SDK internally checks if cache is available for the device the channel is running, if the device has cache then the SDK will use it. If the cache is not available, then it will download the video to the tmp/ directory. Contents here are not persisted when the channel exits, but improves the overall performance of the channel.

So now we have 2 scripts on our GridScreen.xml.

In the <children> tag, add the following structure. This will handle the full screen preview.

<lucidvideoPreview id="lucidvideoPreview" width="1280" height="720" color="0xAAAAAA" ShadeOpacity="0.1"/> <Timer id="waitToParentToFinishVideo" repeat="false" duration="0.5"/>

On the <RowList> element, set the itemComponentName property to LucidVideoGroup. This way, every new grid element created, will be handled by the SDK.

itemComponentName="LucidVideoGroup"

Also, on the GridScreen.xml we have to connect the focused and selected events.

<field id="itemFocused" type="intarray" alias="RowList.rowItemFocused" onChange="OnItemFocused"/>
<field id="itemSelected" type="intarray" alias="RowList.rowItemSelected" onChange="OnItemSelected"/>
XML

b. On the BRS the will contain LucidVideoPreview

Inside the init() function, call

initLucidVideoGridScreen()

We can get when the focus changes by the OnItemFocused observer function. When it changes, call startPreviewVideo(lucidVideoThumbnail, lucidVideoUrl), then the SDK will handle all the events from resolving the cached uri if it exists, to stop and play the new video:


Sub OnItemFocused()
itemFocused = m.top.itemFocused

'When an item gains the key focus, set to a 2-element array,
'where element 0 contains the index of the focused row,
'and element 1 contains the index of the focused item in that row.
if itemFocused.Count() = 2 then
...
    focusedContent = m.top.content.getChild(row).getChild(cellIndex)

    if focusedContent <> invalid then
        'set focused content to top interface
        m.top.focusedContent = focusedContent

        'set content to description node
        m.Description.content = focusedContent

        'set background wallpaper
        m.Background.uri = focusedContent.hdBackgroundImageUrl
        startVideoPreview(focusedContent.lucidvideoThumbnail, focusedContent.lucidvideoUrl)

    end if
...
end if
end Sub
BrightScript

When the main video must play, due Roku limitations, there is need to tell the SDK to stop playing/buffering the current preview. That must be done calling this function. Since we connected the selected event to videoPlayerGridWillStart(), we call it inside this function:


Sub OnItemSelected()
videoPlayerGridWillStart()
end Sub
BrightScript

c. Usage of the LucidVideoPreview component

XML with VideoPlayer:

Get the parent .XML file and connect it to the library:

Sub OnItemSelected()
videoPlayerGridWillStart()
end Sub
BrightScript

On the <children&gt> tag, add the following XML code:

<Timer
id="waitToFinishlucidvideoVideo"
repeat="false"
duration="0.5"/>
XML

BRS with VideoPlayer:

On the Init() function, add call the following function to init the component.

initLucidVideoHomeScreen()

To play the videoPlayer, call this function:

videoPlayerHomeWillStart()

Then the following callback must be implemented and the videoPlayer can be safely started within this function:

videoPlayerCanPlay()

The same should happen AFTER video player is stopped:

videoPlayerDidStop()

d. Troubleshooting

In case of OnVideoPlayerStateChange(), for videoPlayer.state = "finished";, it could be necessary to force the video to stop (i.e. videoPlayer.control = “stop”) before calling.

videoPlayerDidStop()

Beware that all the playing videos or showing components logic will be handled by the Library. If other components control the videoPlayer behavior, this can create conflicts and unexpected results.

Predownloading Videos

The SDK comes with a task named LucidVideoDownloadTask, that if the device has cache available you can use it to start downloading videos before the user has selected them. For example, when using a grid with rows, when an item is selected you can request the previous and next video.

An example of this usage comes with the demo channel on GridScreen.brs source file:

Sub launchDownloadTask(videoUrl as String, videoWidth as Integer)
m.lucidvideoDownloadTask = createObject("roSGNode", "lucidvideoDownloadTask")
m.lucidvideoDownloadTask.videoUrl = videoUrl
m.lucidvideoDownloadTask.videoWidth = videoWidth

m.lucidvideoDownloadTask.control = "RUN"
end sub

Sub OnItemFocused()
itemFocused = m.top.itemFocused
'? ">> GridScreen > OnItemFocused"; itemFocused

'When an item gains the key focus, set to a 2-element array,
'where element 0 contains the index of the focused row,
'and element 1 contains the index of the focused item in that row.
if itemFocused.Count() = 2 then
    'get content node by index from grid

    'Get the row
    row = itemFocused[0]
    'Get the cell index inside the row
    cellIndex = itemFocused[1]
    'Get the child count to know the limits of our objects array
    childCount = m.top.content.getChild(row).getChildCount()
    print "Row is: " + Str(row) + " cellIndex: " + Str(cellIndex) + " child count: " + Str(childCount)

    'If the previous item is greater than 0, just request the previous item
    if cellIndex - 1 >= 0
        previousPreview = m.top.content.getChild(row).getChild(cellIndex - 1)
        launchDownloadTask(previousPreview.lucidvideoUrl, m.lucidvideoPreview.boundingRect().width)
    else
        'If it is less than 0, we go to the end og the array (childCount - 1) and request that item
        previousPreview = m.top.content.getChild(row).getChild(childCount - 1)
        launchDownloadTask(previousPreview.lucidvideoUrl, m.lucidvideoPreview.boundingRect().width)
    end if

    ' If the next item is not out of bounds, requests that item
    if cellIndex + 1 < childCount
        nextPreview = m.top.content.getChild(row).getChild(cellIndex + 1)
        launchDownloadTask(nextPreview.lucidvideoUrl, m.lucidvideoPreview.boundingRect().width)
    else
        ' If it was out, go to the start of the array and request that item
        nextPreview = m.top.content.getChild(row).getChild(0)
        launchDownloadTask(nextPreview.lucidvideoUrl, m.lucidvideoPreview.boundingRect().width)
    end if
    ...
end Sub
BrightScript

launchDownloadTask(videoUrl as String, videoWidth as Integer) will create a LucidVideoDownloadTask Task node that will start downloading the video if needed. The first parameter of the function is just the video preview url, and the second one is the width of the current player. Based on the width the SDK will choose which resolution of the video should be requested.

The library will check if the video was already downloaded, and will not download it again if it was.

Adding Analytics To Your Channel

The first step to integrate Analytics in your channel is to include the <Analytics/>node in your scene. To do it add the script tag to Analytics.brs to your GridScreen.xml and then add the <Analytics/> node in your <children>:

<?xml version="1.0" encoding="utf-8" ?>

<component name="GridScreen" extends="Group" initialFocus="RowList">
...

<script type="text/brightscript" uri="pkg:/components/screens/GridScreen/GridScreen.brs" />
<script type="text/brightscript" uri="pkg:/components/lucidvideo/lucidvideoGridScreen.brs" />
<? Adding the Analytics script ?>
<script type="text/brightscript" uri="pkg:/components/lucidvideo/Analytics/Analytics.brs" />

<children>
    <? Adding the analytics node ?>
    <Analytics/>
    ...
</children>
</component>
XML

Once you have imported the Analytics script and added the node, you need to start the analytics module by calling startAnalyticsTimer():

Function Init()
...
startAnalyticsTimer()
End Function
BASIC

Tracking Events

The analytics module has the following functions for tracking events:

sub trackSummaryPlay(videoUrl as String, id as String)

sub trackSummaryEnd(videoUrl as String, id as String, currentTime as Integer)

sub trackSummaryClick(videoUrl as String, id as String, currentTime as Integer)
BASIC

The attributes of the functions are:

  • videoUrl: the url of the preview.
  • id: the hash of your video.
  • currentTime: time of the player when the event happens.

If you are using LucidVideoGridScreen.brs and LucidVideoPreview.brs for managing your previews playback, then the SDK will automatically manage the play(trackSummaryPlay)/pause(trackSummaryEnd) events of them.

For tracking the click event you will need to call trackSummaryClick() when a click happens. You can integrate it in you GridScreen.brs:

Sub OnItemSelected()
trackSummaryClick(m.videoUri, m.id, m.lucidvideoPreview.callFunc("getCurrentTime"))
end Sub
BASIC

Configuring The Event View Field

By default the SDK sends application as the value for the view on the events. The url field is used on the browser JavaScript plugin to track the origin url of the events. You can change this value in order to track the screen of you Roku channel where the events are genated by calling setEventsViewName(view as String) from the Analytics module:

setEventsViewName("grid_scene")
BASIC

Download SDK & Example

To get access to our SDKs, please contact us.