/ Development  

Use VideoJS to view video content

Hi there AppWorks fans,

Welcome to a new installment of AppWorks tips.

In this post we’re going to explore and implement something really nice! It’s all about viewing video content from our beloved AppWorks platform. You should also be able to use Brava! for this (in the future the new ‘Intelligent Viewer’), but we as developers like to explore around on the internet where I got inspired by this post where an interesting usage of the ‘VideoJS‘ library shows video content from a D2 (a Documentum client) perspective. Well, if it can be done for D2, we can for sure make this library show video content for our AppWorks runtime…Nice, as when we can make this one work for us the world off open-source content viewers is on our fingertips!…So…


Let get right into it…

Let’s first scope our requirements on what we would like to build for this post! Well, to make it all simple we just need to take a look at ‘Netflix’ on how they bring the UI to the world. We’re not rebuilding it all, but when you open the Netflix app, the first thing you see are those profiles to choose from and that’s our first entity. Behind every profile are video items (our second entity) to be explored. To bring it back to tabled content we would start with something like this:

Entity ‘Video Profile’

Properties

Label Name Type
Name vp_name Text
Description vp_description Long Text
Avatar vp_avatar Image
Is kids vp_is_kids Boolean
Language vp_language Enumerated Text

Other building block related thoughts:

  • Generate the default building blocks and update all the default stuff, so it all looks nice
  • Extend the ‘DefaultLayout’ with:
    • a ‘Results panel’ to view all the video’s related to the profile (with a filter something like Properties.ItemId={item.Properties.parent.ItemId})!?
    • a ‘Preview’ panel which shows the selected ‘video’.

Entity ‘Video’

Properties

Label Name Type
Title vid_title Text
Subtitle vid_subtitle Long Text
Poster vid_poster Image
Format vid_format Enumerated Text
Genre vid_genre Enumerated Text
Rating vid_rating Integer

Other building block related thoughts:

  • Generate the default building blocks and update all the default stuff…

  • The ‘all videos’ list can have a nice ‘Card view’!

  • DefaultLayout ONLY for viewing in full screen

  • Create an additional ‘PreviewLayout’ ONLY for viewing in ‘Preview’ panel

    • Use a ‘Web Content’ panel to which point to something like this: /home/appworks_tips/html/video.htm?itemId={item.Identity.ItemId}&format={item.Properties.vid_format}&posterURL={item.Properties.vid_poster}
  • Add the ‘File’ BB (for a video upload functionality)

  • Create a relation ‘One to many’ to the ‘video_profile’ and place it on the ‘Create’ form (as multiselector…You see it later on!)

That’s our overview…Time for some craftsmanship…

Start your VM and grab a coffee! ☕


Crafting the ‘Video Profile’ entity

Let’s jump directly into our ‘entities’ folder on our ‘generic’ project and create a new entity ‘video_profile’ with all the basic generated building blocks:

videojs_001

This is the list of items I also update:

  • Entity properties itself: add a display name ‘Video profile’

  • Property ‘vp_is_kids’: Remove the ‘None’ and add labels ‘Yes/No’ with some nice icons (uploaded in the ‘Assets’ folder…See previous posts!)

  • Property ‘vp_language’: Remove the ‘None’ and add labels ‘Dutch’, ‘English’, etc. with values like ‘nl_NL’, ‘en_US’, etc…With also some flag-icons!?

  • Label ‘DefaultList’ as ‘All profiles’ and update the ‘Column presentation’ for the above properties to show nice icons!

    videojs_002

  • The ‘Create’ form:

    videojs_003

That’s it…Time for a first publish to runtime and see if our profiles can be created! Avatar images can be generated here

We update the ‘DefaultLayout’ later on as we don’t have that ‘video’ entity yet!

videojs_004

With an overview like this:

videojs_005

Nice…next step…


Crafting the ‘Video’ entity

Same steps for this section! Create a new entity ‘video’ with all the basic generated building blocks. No need for a screenshot…I guess you can figure out this one yourself!?

This is the list of items I also update:

  • Entity properties itself: add a display name ‘Video’

  • Add the ‘File’ building block

  • Property ‘vid_format’: Remove the ‘None’ and add labels ‘MP4’, ‘AVI’ and ‘WEBM’ with these values ‘mp4’, ‘avi’ and ‘webm’…These types are supported when we start to call the ‘VideoJS’ API!

  • Property ‘vid_genre’: Remove the ‘None’ and add some nice video genres (like ‘Adventure’, ‘Action’, ‘Drama’, ‘Horror’, etc.)

  • Property ‘vid_rating’: Make it a minimal value of 1 and maximum value of 5.

  • Label ‘DefaultList’ as ‘All videos’ and update the ‘Column presentation’ where needed (The ‘vid_rating’ can be a ‘progress bar’ presentation!)

    videojs_006

  • The ‘Create’ form:

    videojs_007

That’s it as a first step…Time for publication to runtime and see if our videos can be created! Movie posters can be found here, but I don’t use them in my demo as I’m not sure if they are free to use!? For this post we will use example video’s downloaded from this site, and I just grab a snapshot of the video itself to use it as poster image.

videojs_008

With an overview like this where we can upload our video files:

videojs_009

Ok…We are on the correct track…next steps for this entity!

The ‘DefaultLayout’…let’s mark it only for using it in full screen mode (that’s when you ‘Open’ the entity for viewing)

videojs_010

Next to this layout we create an additional ‘PreviewLayout’ where we mark the other option (from the above screenshot). This layout will have a ‘Web Content’ panel which will show a custom page (HTML) with the ‘VideoJS’ implementation…Check the next section for this part.

The ‘Web Content’ panel will point to something like this: /home/appworks_tips/html/video.htm?itemId={item.Identity.ItemId}&format={item.Properties.vid_format}&posterURL={item.Properties.vid_poster}

videojs_011

Next step is to create a relation ‘To many’ to the ‘video_profile’:

videojs_012

With this relation we can make sure a video instance can be connected to several profiles. For this to happen we will update the ‘Create’ form of the ‘video’ entity and add the relation on the ‘Create’ form (as multiselect type!)

videojs_013

Let’s do a publication and see the end result runtime which will look like this:

videojs_014

Relations can be created…Our custom code can be called…Time for some ‘glue’ on the ‘video_profile’ entity. Let’s update the ‘DefaultLayout’ for our ‘Video profile’ entity like this:

videojs_015

For now, we just show all the video’s (unfiltered) on that ‘Result’ panel. After another publish in runtime we see this result happening:

videojs_016

See that magic happening here…Indeed, a 404, but that’s because our HTML is not (yet!) in the correct place. Important here is the call to our custom file where we can play around with scripts like ‘VideoJS’…In the next section…hold on!

Why does it show this content as we dragged & dropped a ‘Preview’ panel in the layout? Why is it not just showing the video? Great questions with a straight answer: Double-check the ‘PreviewLayout’ on our ‘video’ entity where we placed that mark called “To view items in the preview panel”…remember?

Also, double-check the Chrome developer tools on this preview panel where you can see it’s loaded in an iframe element with a URL which looks like this:

/home/appworks_tips/html/video.htm?itemId=080027ab36d6a1eba02fbbcf22773d21.2&format=mp4&posterURL=/home/appworks_tips/app/entityRestService/Items(080027ab36d6a1eba02fbbcf22773d21.2)/Image?iv=1615286688954&name=vid_poster

Remember? /home/appworks_tips/html/video.htm?itemId={item.Identity.ItemId}&format={item.Properties.vid_format}&posterURL={item.Properties.vid_poster}

Ahaaaa…Here we see the correct parameter values passing by as input for our custom page….NICE!! Also, nice to see there is an ‘entityRestService’ being called on this ‘posterURL’ parameter. Another thing I found is when you download the uploaded content and watch the network traffic in the Chrome developer console we see this URL passing by: http://192.168.56.107:8080/home/appworks_tips/app/entityRestService/Items(080027ab36d6a1eba02fbbcf22773d21.1)/ContentStream?forceDownload=true

One to remember for later on!

One final thing to do before we can build that custom part!

We now always see all the videos when we open a profile, but we only want to see the video’s related to that open profile…

For this we go back to the ‘DefaultLayout’ of the ‘video_profile’ where we added the ‘Results’ panel with the ‘All videos’ list…Behind that dropdown list you will find a small ‘filter’ icon:

videojs_017

Hit it like a pro and paste in content which looks like this:

to_many_video_profile.Properties.vp_name={item.Properties.vp_name}

On the left side of the equal-sign we have the name of our ‘parent’ selected video profile for each row in the result and on the right side we see the current entity (our video profile) from where we want to check the value of ‘vp_name’! Read this indeed again as it took me a while before the logic falls into place, but I don’t think I can explain it more clearly. Maybe this will also help: (related)video.to_many_video_profile.Properties.vp_name=(current)video_profile.Properties.vp_name

Also, the low-code guide has a section about this type of filter although it’s explained in not such a logic location at ‘Form’ level in the section called ‘Defining a filter’. Here you will also find these examples passing by (with also a nice table of other ‘operators’ to use…interesting stuff!):

  • Properties.Campus=eq({item.Properties.Campus})
    • This is the same:Properties.Campus={item.Properties.Campus}
  • ResearchArea.Properties.Name=eq({item.ResearchArea.Properties.Name})

Where did I gain the knowledge of the ‘to_many_video’? Well, the best thing to do is creating a quick ‘Rule’ BB where you are able to open the advanced expression editor where you just click correct properties for a few like this (which will help in the correct direction!)

videojs_017_1

Interesting filter options…and will only work when the ‘Name’ of the video profile is also available as filter on the ‘All videos’ list…So, a quick jump to the ‘video’ entity where we update our list with this option:

videojs_018

OK…Just one last tweak before we continue…The ‘Card view’ for video instances!

Back on the ‘DefaultList’ on that video entity where we will find a ‘Card view’ tab…Select a nice template…Fill in the required fields (and make it a default view if you like) and publish again!

videojs_019

In runtime? We can easily switch it with the ‘View settings’ options…

videojs_020

That’s it…next step!


Crafting the ‘Assets’ (HTML and JavaScript)

As you already saw from previous posts I have an ‘Assets’ folder available with folders like ‘html’, ‘js’, ‘css’, ‘images’, ‘icons’, etc. With a ‘Web Library Definition’ I make sure this uploaded content is available on our server in runtime (have a search for more examples on this site)

So, I assume you have it in place…Time to create our first HTML file with a separate JS file!

video.htm

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
<script src="/cordys/thirdparty/jquery/jquery.debug.js" type="text/javascript"></script>
<script src="/cordys/html5/cordys.html5sdk.debug.js" type="text/javascript"></script>
<script src="/cordys/html5/cordys.html5sdk.util.debug.js" type="text/javascript"></script>
<script src="../js/video.js" type="text/javascript"></script>
</head>
<body>
<H1>Hello VideoJS</H1>
</body>
</html>

You already see 3 basic scripts passing by for jQuery and HTML5SDK…This last one is optional, but it’s a habit of mine to include great stuff which can help you on your journey, so you know it’s available from your fingertips!

video.js

1
2
3
$(document).ready(function() {
console.log("Ready!");
});

After publication into runtime you should have a URL available which looks like this:

http://192.168.56.107:8080/home/appworks_tips/html/video.htm

videojs_021

As we already point to this URL from our video ‘PreviewLayout’ (with some parameters) we should see the same for our runtime UI…

videojs_022

You also see the power of that ‘Preview’ panel with a tabbed structure when you mark 2 video instances…How nice!

Next step is to extend both sources with some ‘VideoJS’ elements!

video.htm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
<head>
<link href="https://vjs.zencdn.net/7.10.2/video-js.css" rel="stylesheet" />
<script src="/cordys/thirdparty/jquery/jquery.debug.js" type="text/javascript"></script>
<script src="/cordys/html5/cordys.html5sdk.debug.js" type="text/javascript"></script>
<script src="/cordys/html5/cordys.html5sdk.util.debug.js" type="text/javascript"></script>
<script src="https://vjs.zencdn.net/7.10.2/video.min.js"></script>
<script src="../js/video.js" type="text/javascript"></script>
</head>
<body>
<video id="aw_video" class="video-js" controls preload="auto" width="640" height="360">
<source src="" type="" />
<p class="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading
to a web browser that <a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
</p>
</video>
</body>
</html>

You see a new included CSS file, and a JavaScript file in the header…The body gets that ‘video’ tag. The documentation about these lines can be found here where we did a split into 2 separate files.

video.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$(document).ready(function() {
console.log("Ready!");

//Setup the input data for the player (format and URL)
var data = {
format: 'mp4',
url: '//vjs.zencdn.net/v/oceans.mp4'
};

//Build the player object
var myPlayer = videojs("aw_video");
myPlayer.src({
type: 'video/' + data.format,
src: data.url
});
});

Ready?…Let’s publish…Refresh the browser (and maybe clear some cash). For these type of changes I also use an ‘Incognito’ tab in Chrome, so you have fewer problems with script caching! Even you should eventually see something like this:

videojs_023

Now we’re getting somewhere! 💪

We have a loaded video, so the only thing which requires a further update is our JavaScript file…Let’s add some options to the player first:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$(document).ready(function() {
console.log("Ready!");

//Setup the input data for the player (format and URL)
var data = {
format: 'mp4',
url: '//vjs.zencdn.net/v/oceans.mp4'
};

//Build the player object
var myPlayer = videojs("aw_video");
myPlayer.src({
type: 'video/' + data.format,
src: data.url
});

//When ready set some defaults...
myPlayer.ready(function() {
console.log(myPlayer.muted(), myPlayer.volume());
//myPlayer.play(); //to play it directly on load.
myPlayer.volume(0.01);
});
});

More options can be found here

Time to get back to our URL which is passed into the iframe of the preview panel…The one where we passed all the parameters with values of the current selected video entity…

Something like this: /home/appworks_tips/html/video.htm?itemId=080027ab36d6a1eba02fbbcf22773d21.1&format=mp4&posterURL=/home/appworks_tips/app/entityRestService/Items(080027ab36d6a1eba02fbbcf22773d21.1)/Image?iv=1615286486147&name=vid_poster

Let’s first write some lines of code which grabs all the values from our URL and place it into a ‘params’ variable object (in our $(document).ready(function(){...})

1
2
3
4
5
6
7
8
9
const queryString = window.location.search;
console.log(queryString);
const urlParams = new URLSearchParams(queryString);
const format = urlParams.get('format');
const itemId = urlParams.get('itemId');
const posterURL = urlParams.get('posterURL');
const name = urlParams.get('name');
const params = {format, itemId, posterURL, name};
console.log(params);

The output in our developer console in Chrome will give some JSON data like this:

1
2
3
4
5
6
{
"format": "mp4",
"itemId": "080027ab36d6a1eba02fbbcf22773d21.1",
"posterURL": "/home/appworks_tips/app/entityRestService/Items(080027ab36d6a1eba02fbbcf22773d21.1)/Image?iv=1615286486147",
"name": "vid_poster"
}

Nice…As this is all the required information we need to show our own uploaded video…How? Check this change in our code:

1
2
3
4
5
//Setup the input data for the player (format and URL)
var data = {
format: params.format,
url: '/home/appworks_tips/app/entityRestService/Items(' + params.itemId + ')/ContentStream'
};

The end result in runtime…

videojs_024

Cool!!…next…

Next what? it’s working right?

Yes, but we add one small tweak where we load the poster image before we start the video!

1
2
3
4
5
6
//When ready set some defaults...
myPlayer.ready(function() {
console.log(myPlayer.muted(), myPlayer.volume());
myPlayer.poster(params.posterURL + '&name=' + params.name);
myPlayer.volume(0.01);
});

The final result

videojs_025

Sources for you to consume and learn from:


Other thoughts

After a search on Google on how to call YouTube video’s I passed this interesting GitHub repo!

With some small tweaks in our code we can call something like this:

1
2
format: 'youtube',
url: 'https://www.youtube.com/watch?v=xjS6SftYQaQ'

In the HTM file we add this script <script src="../js/Youtube.min.js"></script> which is uploaded in our ‘Assets’ folder…

For you to try out! 😁

Anything else?

Well, let’s end with a very interesting list of ways to view content on our beloved platform:

All for you to play around with…


I guess that’s it…I also guess we can give this post a well-earned “DONE”. We learned a lot again about the platform and its endless possibilities. This post was all about calling the ‘VideoJS’ library to view video content in our runtime UI…And boy did it view video content. Also, interesting was the part where we filtered our video list based on the current profile. Nice stuff, and I for sure remember these kinds of things for future implementation on our beloved AppWorks platform! Have a great week-end, and I see you in the next post. Cheers…🍺

Don’t forget to subscribe to get updates on the activities happening on this site. Have you noticed the quiz where you find out if you are also “The AppWorks guy”?