Video events not triggering on every refresh - javascript

I am currently trying to get the width and height of a video by using the following:
var vid = document.getElementById('myVideo');
vid.onloadedmetadata = function () {
var vidHeight = this.videoHeight;
var vidWidth = this.videoWidth;
console.log(vidHeight, vidWidth);
};
I noticed when I tried to use the variables later, it sometimes worked and sometimes didn't. I put a console log in (as above) and refreshed my browser. Sometimes the values show and sometimes they don't, like the onloadedmetadata is not triggering at all. Does anyone know why this is and how to get around it?
I also tried it with another event, onloadstart. This had the same behaviour.

I found the answer (this example is in jQuery, easy enough in plain JS too though):
this.options.$video.on('canplay canplaythrough', cb);
if (this.options.$video[0].readyState > 3) {
cb();
}
function cb() {
var vidWidth = me.options.$video[0].videoWidth;
var vidHeight = me.options.$video[0].videoHeight;
console.log(vidWidth, vidHeight);
}

Related

Set videoElement.currentTime in Hulu video player doesn't work, and breaks the player

I have a Chrome extension in which I'm trying to jump forward or backward (based on a user command) to a specific time in the video by setting the currentTime property of the video object. Before trying to set currentTime, a variety of operations work just fine. For example:
document.getElementsByTagName("video")[1].play(); // works fine
document.getElementsByTagName("video")[1].pause(); // works fine
document.getElementsByTagName("video")[1].muted = true; // works fine
document.getElementsByTagName("video")[1].muted = false; // works fine
BUT as soon as I try to jump to a specific point in the video by doing something like this:
document.getElementsByTagName("video")[1].currentTime = 500; // doesn't work
No errors are thrown, the video pauses, and any attempted actions after this point do nothing. So the items shown above (play/pause/mute/unmute) no longer work after attempting to set currentTime. If I read the value of currentTime after setting it, it correctly displays the new time that I just set it to. Yet nothing I do will make it play, and in fact even trying to make the video play by clicking the built-in toolbar no longer works. So, apparently setting currentTime wreaks all kinds of havoc in the video player. Yet if I reload the video, all works as before as long as I don't try to set currentTime.
I can easily jump to various times (backward or forward) by sliding the slider on the toolbar, so there must be some way internally to do that. Is there some way I can discover what code does a successful time jump? Because it's a Chrome extension I can inject custom js into the executing Hulu js, but I don't know what command I would send.
Any ideas?
Okay I fiddled around with it for a little while to see how I could reproduce the click event on the player and came up with the following solution:
handleViewer = function(){
var thumbnailMarker = $('.thumbnail-marker'),
progressBarTotal = thumbnailMarker.parent(),
controlsBar = $('.controls-bar'),
videoPlayer = $('#content-video-player');
var init = function(){
thumbnailMarker = $('.thumbnail-marker');
progressBarTotal = thumbnailMarker.parent();
controlsBar = $('.controls-bar');
videoPlayer = $('#content-video-player');
},
check = function(){
if(!thumbnailMarker || !thumbnailMarker.length){
init();
}
},
show = function(){
thumbnailMarker.show();
progressBarTotal.show();
controlsBar.show();
},
hide = function(){
controlsBar.hide();
},
getProgressBarWidth = function(){
return progressBarTotal[0].offsetWidth;
};
return {
goToTime: function(time){
var seekPercentage,
duration;
check();
duration = videoPlayer[0].duration;
if(time > 0 && time < duration){
seekPercentage = time/duration;
this.jumpToPercentage(seekPercentage);
}
},
jumpToPercentage: function(percentage){
check();
if(percentage >= 1 && percentage <= 100){
percentage = percentage/100;
}
if(percentage >= 0 && percentage < 1){
show();
thumbnailMarker[0].style.left = (getProgressBarWidth()*percentage)+"px";
thumbnailMarker[0].click();
hide();
}
}
}
}();
Once that code is initialized you can do the following:
handleViewer.goToTime(500);
Alternatively
handleViewer.jumpToPercentage(50);
I've tested this in chrome on a MacBook pro. Let me know if you run into any issues.
Rather than try to find the javascript responsible for changing the time, why not try to simulate the user events that cause the time to change?
Figure out the exact sequence of mouse events that trigger the time change.
This is probably some combination of mouseover, mousedown, mouseup, and click.
Then recreate those events synthetically and dispatch them to the appropriate elements.
This is the approach taken by extensions like Stream Keys and Vimium.
The video should be ready to play before setting the currentTime.
Try adding this line before setting currentTime?
document.getElementsByTagName("video")[1].play();
document.getElementsByTagName("video")[1].currentTime = 500;
Looks like it works if you first pause, then set currentTime, then play again.
document.getElementsByTagName("video")[1].pause()
document.getElementsByTagName("video")[1].currentTime = 800.000000
document.getElementsByTagName("video")[1].play()
Probably would need to hook into some event like onseeked to put in the play command to make it more robust.

javascript - window.print() issues on Ipad/Iphone

I'm currently having an issue trying to print a report. We are using an Angularjs solution. We display the report for the user, and when they hit print we have the template switch. Then we call window.print() (which normally stops javascript code execution). Then after printing we swap the template back to the display.
This currently works in chrome and safari on a mac. However when we use an Ipad/Iphone the templates swaps, swaps back and then print appears and the print preview is incorrect.
Searching for answers I found:
people talking about using var mql = window.matchMedia('print') and then using add.listener(function) and doing the swap in the function. The below code I was able to get Ipad/Iphone to swap to the correct template and print however it does not swap back.
VcrReportController.prototype.printReport = function () {
var vm = this;
var mql = window.matchMedia('print');
var printHandler = function(mql){
if (mql.matches){
console.log('print');
vm.template = 'displayTemplate'
}
else{
console.log('not print');
}
}
function changeTemplate() {
var deferred = vm.$q.defer();
vm.template = 'printTemplate';
vm.$timeout(function () {
deferred.resolve(true);
},250);
return deferred.promise;
}
changeTemplate()
.then(function (response) {
mql.addListener(printHandler);
vm.$window.print();
});
Not 100% sure about the listener stuff. Any help or other solutions people might have used to deal with this kind of situation would be great.
Thanks,

How to fix frozen div when using compressor.reduction.value to monitor compression reduction

My problem is the following.
I am attempting to connect the compressor.reduction.value of the compressor node to a div's height so I can monitor the compression reduction effect dynamically. This works fine. The problem is when the audio signal stops the div freezes at its current position. I would like the div to not freeze and have it's height go to zero. The way I fixed this is by using a setInterval that checks for the height of the div and if it remains at exactly the same height for more than a few seconds then the display is set to none effectively hiding the div. Now my question is two fold. First, if there is a better way to do this please share, but irrespective there is one little thing that is irking me that I can't figure out. When I write the code as such it works. However, it looks a bit ugly since the compressor node is outside the play function..........
var compressor = audioContext.createDynamicsCompressor();
soundObj.play = function() {
$(".compression-meter").css("display", "block");
var playSound = audioContext.createBufferSource();
compressor.threshold.value = -40;
compressor.ratio.value = 20;
playSound.buffer = soundObj.soundToPlay;
playSound.connect(compressor);
compressor.connect(audioContext.destination)
playSound.start(audioContext.currentTime);
compReductionMeter()
}
/*______________ Compressor metering __________*/
var cachedMeterValue = null
function compReductionMeter() {
cachedMeterValue = $(".compression-meter").height()
var reduction = compressor.reduction.value;
var bar = $(".compression-meter");
bar.height((-1 * reduction) + '%');
requestAnimationFrame(compReductionMeter);
};
window.setInterval(function() {
if ($(".compression-meter").height() == cachedMeterValue) {
console.log("checking compression meter height when matched with cachedMeterValue.It is " + $(".compression-meter").height())
$(".compression-meter").css("display", "none")
}
}, 2000);
When I write the code like this the div doesn't even appear and I am not sure why. From my view it "should" work and I really want to know why it doesn't and what I'm missing.
soundObj.play = function() {
$(".compression-meter").css("display", "block");
var playSound = audioContext.createBufferSource();
var compressor = audioContext.createDynamicsCompressor(); // modified placement
compressor.threshold.value = -40;
compressor.ratio.value = 20;
playSound.buffer = soundObj.soundToPlay;
playSound.connect(compressor);
compressor.connect(audioContext.destination)
playSound.start(audioContext.currentTime);
compReductionMeter(compressor.reduction.value) // modified - added argument
}
/*______________ Compressor metering __________*/
var cachedMeterValue = null
function compReductionMeter(compVal) { // modified - added parameter
cachedMeterValue = $(".compression-meter").height()
var reduction = compVal; // modified - is now param value
var bar = $(".compression-meter");
bar.height((-1 * reduction) + '%');
requestAnimationFrame(compReductionMeter);
};
window.setInterval(function() {
if ($(".compression-meter").height() == cachedMeterValue) {
console.log("checking compression meter height when matched with cachedMeterValue.It is " + $(".compression-meter").height())
$(".compression-meter").css("display", "none")
}
}, 2000);
Thank you.
This annoyance in DynamicsComporessorNode will be fixed at Chrome version M40.
https://codereview.chromium.org/645853010/
Unfortunately, the current design of DynamicCompressorNode keeps the gain reduction value from being updated when the stream from source node stops. That is, the GR value is only being updated when the active audio stream is running. AnalyserNode has the very same issue.
If your audio graph is simply using a single source node, you can use .onended event from the source node to zero the height of DIV. However, if you rather have a complex audio graph, then it is going to be a bit more involved.
http://www.w3.org/TR/webaudio/#dfn-onended_AudioBufferSourceNode
Here is a possible hack to get zeroes to the compressor and analyzer. Create a new buffer of all zeroes. Assign that to a new AudioBufferSourceNode. Connect this node to the compressor and/or analyser and schedule the source to start when your source ends (or slightly before). This should keep the compressor/analyser node processing so the GR value and analyser node to drop to zero.
I didn't actually try this.

Js Animation using setInterval getting slow over time

I have a web site which displays a portfolio using this scrollbar http://manos.malihu.gr/jquery-custom-content-scroller/ .
I want to have an automatic scroll when the page is
fully loaded. When a user pass over the portfolio it stops the scroll and whe, he leaves it starts moving again.
It currently works well but sometimes it's getting slow or sluggish randomly.
Is there something wrong with my js function?
Here's the page having problems : http://www.lecarreau.net/new/spip.php?rubrique5 (I need the change the hosting provider, I know the site is slow).
This is the function:
(function($){
var timerId = 0;
$(window).load(function(){
$("#carousel").show().mCustomScrollbar( {
mouseWheel:false,
mouseWheelPixels:50,
horizontalScroll:true,
setHeight:370,
scrollButtons:{
enable: true,
scrollSpeed:50
},
advanced:{
updateOnContentResize:true
}
});
timerId = setInterval(function(){myTimer()},50);
$('#carousel').mouseenter(function () {
clearInterval(timerId);
});
$('#carousel').mouseleave(function () {
timerId = setInterval(function(){myTimer()},50);
});
});
})(jQuery);
function myTimer()
{
var width = $(".mCSB_container").width();
var left = $(".mCSB_container").position().left;
if (width-left>0) {
var scrollTo = -left+4;
$("#carousel").mCustomScrollbar("scrollTo", scrollTo);
}
}
Is there something obvious I'm missing?
Timing in the browser is never guaranteed. Since all functionality contents for time on a single event loop, continuous repeated tasks sometimes get a little janky.
One thing you could do that might decrease the jank is to cache the jQuery objects you're creating every 50ms:
var mCSBContainerEl = $(".mCSB_container");
var carouselEl = $("#carousel")
function myTimer()
{
var width = mCSBContainerEl.width();
var left = mCSBContainerEl.position().left;
if (width-left>0) {
var scrollTo = -left+4;
carouselEl.mCustomScrollbar("scrollTo", scrollTo);
}
}
Selecting these to elements only needs to happen once. From there they can just reference a variable instead of having to find the element on the page and wrap it in jQuery again and again.
Caching the variable in this case depends on them being in the DOM when the code runs, which isn't guaranteed here. You may have to move the variable assignment and function declaration into the function passed to load.
Also, as John Smith suggested, consider checking out requestAnimationFrame. Here's a shim I use by Paul Irish: http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/

Javascript/jQuery can't seem to get ClearInterval to work

I am reading a jQuery book and trying to do an example. In the example lightning flashes on the screen, and that part works fine. The problem is that when you switch tabs, and then switch back, the lightning starts flashing in rapid succession. The problem is supposed to be fixed using the window.onblur and window.onfocus events, but it's not working. Can anybody see what I am doing wrong?
There are three hidden lightning pictures with different id's, and three different functions that make each one flash. goLightning() sets the interval for each function to go, and stopLightning() should clear that interval. That part is what seems not to be working for all I know. Here is the code:
$(document).ready(function(){
goLightning();
window.onblur = stopLightning;
window.onfocus = goLightning;
var int1; var int2; var int3;
// this function sets the lightning interval
function goLightning() {
int1 = setInterval(function() { lightning_one(); },4000);
int2 = setInterval(function() { lightning_two(); },5000);
int3 = setInterval(function() { lightning_three(); },7000);
}
// this function clears the lightning when the window isn't focused
function stopLightning() {
window.clearInterval(int1);
window.clearInterval(int2);
window.clearInterval(int3);
}
// these three functions make the lightning flash and seem to be working fine
function lightning_one() {
$("#container #lightning1").fadeIn(250).fadeOut(250);
}
function lightning_two() {
$("#container #lightning2").fadeIn(250).fadeOut(250);
}
function lightning_three() {
$("#container #lightning3").fadeIn(250).fadeOut(250);
}
});
I have no idea why this isn't working. It seems like stopLightning() isn't clearing the interval, or window.onblur isn't working. Anyway, any feedback would be helpful. Thanks!
Add stopLightning() to the beginning to goLightning(), before setting the new intervals.
Re-arrange the following lines:
goLightning();
window.onblur = stopLightning;
window.onfocus = goLightning;
var int1; var int2; var int3;
in to this instead:
var int1, int2, int3, w = window;
w.onblur = stopLightning;
w.onfocus = goLightning;
goLightning();
Next, use the variable w instead of window in the stopLightning() function to ensure you use a reference to the proper window. Also, as #Kolink mentioned, it wouldn't hurt to put a call to stopLightning() at the beginning of your goLightning() function to ensure you don't have multiple instances running in the event the focus event gets fired more than once.
EDIT I moved the call to goLightning() after having set up the events - untested but I think it would be better that way?

Categories