VideoJS, YouTube & Local MP4s, and Playlist

I’m trying to implement a video player using VideoJS that can play both YouTube videos and locally-uploaded MP4s within a playlist object / array. I’m trying to do all this within Drupal 7.x and I’ve come pretty close to having it working but at the moment, I can’t seem to make the YouTube videos work with the playlist code.

Here’s where I am right now…

1.) I’m currently loading the following JavaScript library / code in the following order:

  • jQuery 1.4.x
  • VideoJS 4.3.x
  • media.youtube.js (YouTube “wrapper” API, version unspecified.)
  • videojs-playlists.min.js (The VideoJS playlist library, verion 0.1.)
  • The player initialization code itself that is responsible for loading the player with the videos / playlists / options, etc.

The jQuery is loaded in the head but everything else is down at the bottom of the markup.

2.) I’m using the following markup for the player itself:
<video id=“video” class=“video-js vjs-default-skin video_url” controls data-setup=“” width=“640” height=“360”></video>';
<button type=“button” data-action=“prev”>Previous</button>
<button type=“button” data-action=“next”>Next</button>

3.) I’m trying to make the player use two objects / arrays:

  • options.
  • videos (playlist).

The “options” array / object contains the player types and their respective order of use (i.e. - YouTube, HTML5, or Flash). The “videos” is just the playlist array / object that contains video specifics, such as the src of the video, the poster, and title.

Here’s the code:

(function ($) {
                                    var playlistIndex = 0;
                                    var videos = [
                                    {
                                      src : [
                                        'https://www.youtube.com/watch?v=GaMcsKtBDwE',
                                      ],
                                      poster : 'http://flowplayer.org/media/img/demos/minimalist.jpg',
                                      title : 'Video 1'
                                    },
                                    {
                                      src : [
                                        'http://stream.flowplayer.org/bauhaus/624x260.webm',
                                        'http://stream.flowplayer.org/bauhaus/624x260.mp4',
                                        'http://stream.flowplayer.org/bauhaus/624x260.ogv'
                                      ],
                                      poster : 'http://flowplayer.org/media/img/demos/minimalist.jpg',
                                      title : 'Video 1'
                                    },
                                    {
                                      src : [
                                        'http://stream.flowplayer.org/night3/640x360.webm',
                                        'http://stream.flowplayer.org/night3/640x360.mp4',
                                        'http://stream.flowplayer.org/night3/640x360.ogv'
                                      ],
                                      poster : 'http://flowplayer.org/media/img/demos/playlist/railway_station.jpg',
                                      title : 'Video 2'
                                    },
                                    {
                                      src : [
                                        'http://stream.flowplayer.org/functional/624x260.webm',
                                        'http://stream.flowplayer.org/functional/624x260.mp4',
                                        'http://stream.flowplayer.org/functional/624x260.ogv'
                                      ],
                                      poster : 'http://flowplayer.org/media/img/demos/functional.jpg',
                                      title : 'Video 3'
                                    }
                                  ];
                                    
                                    var options = { "techOrder": ["youtube", "html5", "flash"] };                                        //Works w/ playlist (but fails w/ YouTube).
                                    //var options = { "techOrder": ["youtube"] , "src" : "https://www.youtube.com/watch?v=GaMcsKtBDwE"};                                                        //Fails w/ playlist (but works w/ YouTube).
                                    var player = videojs('video', options);
                                    player.playList(videos, {
                                        getVideoSource: function(vid, cb) {
                                            cb(vid.src, vid.poster);
                                        }
                                    });
                                  
                                    $('[data-action=prev]').bind('click', function(e) {
                                        player.prev();
                                    });
                                  
                                    $('[data-action=next]').bind('click', function(e) {
                                        player.next();
                                    });
                                    })(jQuery)

Right now, I’m able to get videos to play, but the “Previous” and “Next” buttons seem to have problems when I include a YouTube video inside the “videos” playlist array / object. What happens is that the YouTube video basically gets loaded but a brief VideoJS error message in the uppermost area of the video player screen indicates that no compatible player was found–this eventually disappears with the YouTube video getting loaded up anyway, which makes the video playable. Obviously this isn’t ideal.

Beyond this, the YouTube video does play and if I click on the “Next” button, that same error message appears for locally-served MP4 videos on the web server, but I’m still able to play them if I click on the video screen. Once the video is serving local MP4s from the playlist, the “Previous” and “Next” buttons seem to work, but if I keep pressing “Previous” to get back to the YouTube video from the playlist, it won’t work: it will go back to the first locally-served video in the playlist but won’t go past that to the first video, which is a YouTube video.

Any insight into this is appreciated. It’s pretty complex, but surely I’m not the first who’s worked on something like this. I’ve tried to search for complete information about implementing a video player with VideoJS that can handle both MP4s (local files, I mean) and YouTube videos within playlist data structures, but I haven’t been able to find anything that “ties it all together.”

hey did u solve the problem? i also having the same problem …i unable to switch video from vimeo to local mp4 video using video.js…

Yes, I solved it but it required a lot of work… Depending on your setup, you’ll need to find a plugin / extension that ties into VideoJS that can handle Vimeo URLs. Once you’ve found one, figure out what form the payload needs to be in for your players per-source (i.e. - if MP4, then put the dimensions here, if Vimeo, then put the dimensions there, etc.). Finally, once you’ve isolated the players like this and understand what pieces of data go where, all you’ll need to do it write the logic responsible for generating each player payload received. It can be a pain figuring out where the dependencies are between the payloads’ elements but once you do this, you’ve essentially parsed what the players consume. It’s time consuming and requires a lot of testing but it’s not impossible, either.

VideoJS is about the best resource for doing this at the moment from what I can tell (at least, it was for the requirements I had where I needed a single player for both local MP4s and YouTube URLs). After doing all this though, I managed to get it to work… Works well, too! :slight_smile:

@Wolf_22 , any chance you could post your solution to how you solved this? I have the exact same issue!

Thanks

rabort, that’s completely understandable and I feel for ya because it was a pain getting mine to work. I can’t post anything right now because I’m a bit tied-up at work but I’ll try to get something on here tomorrow.

1 Like

1.) Verify that you have VideoJS (4.3) with the YouTube Media Controller (I can’t remember the version I have but I think this is the one I used). The YouTube library helps VideoJS play YouTube URLs. You’ll this in your markup stack for pages that you need to play YouTube videos (so it should go without saying that you’ll include VideoJS BEFORE the YouTube JS file). Also, make sure you have a video content type that leverages 2 fields: one for the YouTube URLs and another for your own manual uploads.

2.) For my own needs, I put everything in template.php. I used HOOK_preprocess_page() to add my overall video logic for 2 basic conditions: if the page is the front page and / or if the page is a video page. I had to do this because I wanted to serve select videos on the front page but also have players on my video page content type nodes which could also have playlists (i.e. - video pages that had a series of videos).

3.) I then added the CSS references in their respective template.php locations for basic styling. I then referenced the JS files (those I used are as follows):

    drupal_add_js(libraries_get_path('videojs')  . '/lib/video.js');
    drupal_add_js(libraries_get_path('videojs')  . '/src/media.youtube.js');
    drupal_add_js('videojs.options.flash.swf = "'.libraries_get_path('videojs').'/lib/video-js.swf";', array('type' => 'inline'));

(The first file is the VideoJS library. The second is the YouTube helper library. The third is a Flash fallback. I can’t say much about that last one because I’m not 100% positive mine is even working because I haven’t had the time to test it yet.)

4.) Still within template.php, you’ll need to add the VideoJS-specific PHP code responsible for creating the elements in the theme layer that VideoJS relies on. Remember, we’re dealing with both YouTube URLs and manual uploads here, so for me, I had 2 sets of logic to handle this in template.php (this is where that legwork comes in because you’ll have to parse through all the field stuff to get to specific values necessary to determine which videos to play wherever you want them to be played):

//Youtube...
if(isset($video->field_video_url['und'][0])){
   if(isset($video->field_video_url['und'][0]['title'])){
      $title = $video->field_video_url['und'][0]['title'];
   }else{
      $title = $video->field_video_url['und'][0]['value'];
   }
   $variables['video_payload']['videos'][] = "<video id=\"vid".$ittr."\" class=\"video-js vjs-default-skin video_url transparent ".$class."\" controls preload=\"none\" width=\"425\" height=\"250\" poster=\"".$poster."\" data-setup='{ \"techOrder\": [\"youtube\"], \"src\": \"".$video->field_video_url['und'][0]['value']."\", \"ytcontrols\": \"true\", \"quality\": \"720p\" }'></video>";
}

//Uploads...
if(isset($video->field_video_upload['und'][0])){
   if(isset($video->field_video_title['und'][0])){
      $title = $video->field_video_title['und'][0]['safe_value'];
   }else{
      $title = $video->field_video_upload['und'][0]['filename'];
   }
$variables['video_payload']['videos'][] = "<video id=\"vid".$ittr."\" class=\"video-js vjs-default-skin transparent ".$class."\" controls preload=\"none\" width=\"425\" height=\"250\" poster=\"".$poster."\" data-setup='{ \"techOrder\": [\"html5\" , \"flash\"], \"type\": \"video/mp4\", \"src\": \"".file_create_url($video->field_video_upload['und'][0]['uri'])."\" }'></video>";
$uploads[$ittr] = file_create_url($video->field_video_upload['und'][0]['uri']);
}

(I know that I’m probably referencing the fields inefficiently, but again, this was where I left off and at the time, I had just been doing all of this to “get something running.” So with that being said, the first section (everything under “//Youtube”) is for the YouTube player. The other (everything under “//Uploads”) is for the manual videos uploads. For my purposes, I wanted playlists, so I created some SQL to extract all the NIDs of the pages that have videos on them whereby I stored this and iterated though each to node_reference the fields corresponding to each player. This is what $video is.

5.) In your theme layer file, you’ll want something like this:

    <?php if ($page['content']) : ?>
       <div class="content-middle">
          <div id="video">
             <?php
                if(isset($video_payload)){
                   foreach($video_payload['videos'] as $video){
                      print $video;
                   }
                   print $video_payload['video_controls'];
                }
                print render ($page['content']);
             ?>
          </div>
       </div>
    <?php endif; ?>

The $video_payload variable is created in template.php (step 4), which means that theme layer files have access to it through the $variables variable…

6.) Showing every video player all at once would make no sense, so you’ll have to use something to hide the videos that aren’t active. (For me, this was jQuery: I just looked for every anchor that corresponded to the VideoJS video and hid those that were inactive–remember that VideoJS uses anchor tags for the video presentation, hence referencing anchor tags).

I know all this is a lot to soak in and some of it might even be incorrect or outside the confines of some sort of standards, but it worked for me and works rather well. :smile:

Long story short…
1.) Get a demo working of the player that can play both YouTube and manual uploads.
2.) Add everything in your template file (i.e. - CSS, JS files, and general PHP logic to parse everything and stuff the theme layer variables you’ll need to print the player).
3.) Print everything in your theme layer.

Again, let me know if you have any questions. I’ll do my best to answer them. Hope this helps!

3 Likes