Friday, June 24, 2016

Javascript Event Listener quits (?) from listening… Youtube API - No console error

Leave a Comment

I spent 6 hours on this already.
(plus 1 editing this question!)

I'm kind of debugging the last 4 lines of it.

Problem is that I get NO console error as a hint.



Concept:
I want to link numerous Youtube videos to text links.
The wanted effect for the user is to be able to click on a citation link while reading a text... In order to be able to confirm the citation.
The video has NOT to play entirely.

The video shall start at a specific timecode AND end at a specific timecode.
Bonus complexity: All this is wanted to be shown in a modal view style.

My code WORKED quite fast for ONE video. See here.

I based my code on this tutorial and succedded real fast.

Then, having this working...
I had the need to build arrays to handle multiple videos.
For links ids, players ids, timecodes for start/end... AND LISTENERS!
Fun begins!

Like said, I spent most of my day on this. I always encountered bugs WITH console errors as clear (LOLLL) guides.

I am satisfied of my work... This is going in the right direction I think.
This is almost working...


But this time, no errors! See here. (check the console!)

WHATT!!! NO ERROR?!?
My arms are hacked off now.
In fact, the 1st shows but video doesn't start... And the second looks to be completely lost in the haze.

In console log messages, I see the 1st occurance of the onStateChange listener, wich is -1 (Unstarted). But then ??? It dies!
Arrgg!

I have to overstep my pride... And bring it as a question on StackOveflow.
;)



My full code (for multiple links):
This is a page called via ajax... So all external ressources like jQuery are already loaded.

<style> .ytplayer{     position:fixed;     z-index:2;     width:60%;     height:40%;     top:30%;     left:20%;     display:none; } #ytplayerModal{     display:none;     background-color:#000;     opacity:0;     position:fixed;     z-index:1;     top:0;     left:0;     width:100%;     height:100%; } .ytTriggerPlay{     text-decoration:underline;     color:dodgerblue;     cursor:pointer; } </style>    <h1>Youtube modal trigger link test</h1> <br> <br> <div id="text">     Lorem ipsum dolor sit amet, consectetur <a id="0" class="ytTriggerPlay">adipiscing elit</a>. Quisque feugiat lectus ut est vestibulum ornare. Vivamus felis nulla, facilisis id cursus non, pharetra non diam. Sed pellentesque turpis vel sem tincidunt consectetur. Aenean ut lorem erat. Donec ut tellus sed leo ultrices cursus. <a id="1" class="ytTriggerPlay">Cras varius libero</a> ut purus suscipit ultrices. Vivamus eget efficitur turpis. Aenean suscipit, dui nec luctus fringilla, neque tellus fringilla risus, et porta enim justo et turpis. Sed risus orci, vehicula sed eleifend eget, tincidunt ut turpis. Vestibulum in sapien non lacus tristique mattis id eget tortor.<br>     <br>     Proin est purus, maximus id nunc vel, consectetur tristique urna. Mauris cursus ipsum a varius luctus. Nunc interdum condimentum massa vitae rutrum. Morbi volutpat nec lorem eleifend malesuada. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis fringilla metus vel nunc elementum efficitur. Duis sed dolor diam. In eu ultrices libero, eget lobortis mi. Sed pretium orci non augue vehicula, eget placerat leo lacinia. Sed sed gravida dui. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In bibendum, erat eget venenatis elementum, nulla enim posuere lacus, quis efficitur dolor ex quis ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Phasellus volutpat finibus odio id venenatis. Fusce at leo libero. Cras eget velit sed justo egestas vehicula efficitur sit amet ex.<br> </div>  <!--iframe id="ytplayer" type="text/html" width="720" height="405" src="https://www.youtube.com/embed/5V_wKuw2mvI?end=60&start=20" frameborder="0" allowfullscreen-->  <div id="ytplayerModal"></div> <div id="player1" class="ytplayer"></div> <div id="player2" class="ytplayer"></div>  <script> // https://developers.google.com/youtube/iframe_api_reference // https://css-tricks.com/play-button-youtube-and-vimeo-api/  // Global variable for the player var player = []; var statePlaying=false;  playerArr = [{             linkID:"0",             divID:"player1",             ytID:"5V_wKuw2mvI", // Heavy metal playlist             start:20,             end:40,             },             {             linkID:"1",             divID:"player2",             ytID:"39b5v3-d6ZA", // Maiden             start:30,             end:60,             }];  // This function gets called when API is ready to use  function onYouTubePlayerAPIReady() {     for(i=0;i<playerArr.length;i++){         // Create the global player from the specific iframe (#video)         thisPlayer = new YT.Player(playerArr[i].divID, {             height: '352',             width: '640',             videoId: '5V_wKuw2mvI',             startSeconds:20,             endSeconds:40,             events: {                 // Call this function when player is ready to use                 // 'onReady': onPlayerReady         // Commented out willingly.             }         });         player[i] = thisPlayer;      }     onPlayerReady(); }  function onPlayerReady(event) {      // Binding events loop     console.log("playerArr.length: "+playerArr.length);     for(i=0;i<playerArr.length;i++){         console.log("");         console.log("onPlayerReady for loop ->i: "+i);          var playButton = document.getElementById(playerArr[i].linkID);         console.log("playButton.id: "+playButton.id);          var thisArr = playerArr[i];         console.log("playerArr[i] object (below): ");         console.log(thisArr);          var thissPlayer = player[i];          playButton.addEventListener("click", function() {             thisLinkID = parseInt($(this).attr("id"));             console.log("thisLinkID: "+thisLinkID);              var ytID = playerArr[thisLinkID].ytID;             var start = playerArr[thisLinkID].start;             var end = playerArr[thisLinkID].end;              console.log("ytID: "+ytID);             console.log("start: "+start);             console.log("end: "+end);             console.log("thissPlayer object (below): ");             console.log(thissPlayer);              $("#ytplayerModal").css({"display":"block"});             $("#ytplayerModal").animate({"opacity":"0.7"},1000,function(thissPlayer,ytID,start,end){                 $(".ytplayer").show();                  player[thisLinkID].loadVideoById({videoId:ytID, startSeconds:start, endSeconds:end});                 setTimeout(function(){                     player[thisLinkID].playVideo();                 },500);             });         });            thissPlayer.addEventListener("onStateChange", function(stateObj){             console.log("Player State: "+stateObj.data);             console.log("Again, thissPlayer object in the onStateChange listener (below).");             console.log(thissPlayer);             // State sequence : -1, 3, 1, 2, 0, which is: Unstarted, Buffering, Playing, Paused, Ended.              if(stateObj.data==1){                 statePlaying=true;             }             console.log("Player State bolean memory: "+statePlaying);              // Closes the modal             if((statePlaying) && (stateObj.data==0)){                 setTimeout(function(){                     console.log("Closing Modal");                     $(".ytplayer").css({"display":"none"});                     $("#ytplayerModal").animate({"opacity":"0"},1000,function(){                         $("#ytplayerModal").css({"display":"none"});                     });                     statePlaying=false;                 },500);             }         });     } }  // Inject YouTube API script var tag = document.createElement('script'); tag.src = "//www.youtube.com/player_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); </script> 





EDIT (Work in progress)

Based on the answer below, I modified my code.
It give some new results... But I'm still in the woods.

Here is my «work in progress» live link.

I now get player states like this:
State 5 (video cued) for 1 second (100 x 10 millisec).
State undefined 1 time.
State 3 (buffering) for 120 millisec.
State -1 (unstarted) endlessly...

Here is the new code, placed in the click listener:
The onStateChange listener is commented out.

// Bugfix - Set Interval instead of listener var IntervalCounter=0; listenerInterval = setInterval( function() {      var state = player[thisLinkID].getPlayerState();     var stateMsg;      switch (state){         case -1: stateMsg="unstarted"; break;         case  0: stateMsg="ended"; break;         case  1: stateMsg="playing"; break;         case  2: stateMsg="paused"; break;         case  3: stateMsg="buffering"; break;         case  5: stateMsg="video cued"; break;         default: stateMsg="Undefined player state...";     }     console.log(state+" : "+stateMsg);      if(state==1){         statePlaying=true;     }      // Closes the modal     if((statePlaying) && (state==0)){     //if((statePlaying) && (stateObj.data==0)){         setTimeout(function(){             console.log("Closing Modal");             $(".ytplayer").css({"display":"none"});             $("#ytplayerModal").animate({"opacity":"0"},1000,function(){                 $("#ytplayerModal").css({"display":"none"});             });             statePlaying=false;         },500);     }      // Stop the interval at 1000... Endless instead!     IntervalCounter++;     if(IntervalCounter>999){         clearInterval(listenerInterval);         console.log("Interval loop volontarely stopped. Endless otherwise.")     } }, 10); 

Here is a snapshot of my console:
enter image description here

1 Answers

Answers 1

There was a temporary issue with the iFrame Player API that you can read about here: https://code.google.com/p/gdata-issues/issues/detail?id=4706

As a temporary fix, you just need to add the event listener within the onReady event:

function onReady() { player.addEventListener('onStateChange', function(e) { console.log('State is:', e.data); }); } 

Also, as someone mentioned on the Google Code Issue Thread, you set an interval and poll the player for its current state instead of listening for the onStateChange event. Here is an example code snippet for doing that:

setInterval( function() { var state = player.getPlayerState(); if ( playerState !== state ) { onPlayerStateChange( { data: state }); } }, 10); 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment