why does javascript `for` loop rapidly mute/unmute video - javascript

I have an in-house electron app for playing videos with filters. Source code here. On most computers, the code works flawlessly, but on the booth computer where the video is shown to a class of students, when a mute filter is applied, the player.mute() and player.unmute() rapidly alternate many times per second. This only happens for some of the mute filters on some movies on some computers. I need help to track down the source of the problem.
The relevant part for this question (I think) is in script.js, lines 224-258.
for (var i = 0; i < numAnnotations; i++) {
var vMuted = player.video_obj.muted
var vBlanked = player.video_obj.classList.contains('blanked')
var vBlurred = player.video_obj.classList.contains('blurred')
var a = player.annotations[i]
var aStart = a['start']
var aEnd = a['end']
var aType = a['type']
var aDetails = a['details']
switch (a['type']) {
case 'skip':
if (time >= aStart && time < aEnd) {
console.log('skipped to '+Number(aEnd).toFixed(3))
player.skip_to(aEnd)
}
break
case 'mute':
case 'mutePlugin':
if (currently.muting === -1 || currently.muting === i) { //if no annotation is currently muting or *this* current annotaiton is muting
if (time >= aStart && time < aEnd) { //if within annotation time
if (!vMuted) {
console.log('mute on')
currently.muting = i
player.mute()
}
} else {
if (vMuted) {
console.log('mute off')
currently.muting = -1
player.unmute()
}
}
}
This code usually works flawlessly, but it sometimes fails at very inconvenient times. What is the source of the problem, or how can I debug it myself?

Turns out the problem was only with videos that were not the first one loaded after launching the player. We were able to track down that we were getting multiple eventListeners that were fighting with each other. We just had add to add a tracker to only add event listeners when they didn't already exist.

Related

Javascript - if-statement with multiple numeric conditions doesn't work

I'm trying to make a simple program in javascript+html. If exp exceeds within a certain range/exceeds a certain number, the level displayed goes up by 1. I've tried to make it show onload, but the level doesn't change no matter what happens to the exp staying at the highest one I've written code for so far.
Javascript:
var exp6 = localStorage.exp6;
var pexp6 = parseInt(exp6);
function char6() {
res.innerHTML = res6;
var lsps = pexp6;
localStorage.setItem("lsp", lsps);
return PrUpdate();
}
var lsp = localStorage.lps;
function PrUpdate() {
if (lsp <= 999) {
lvl.innerHTML = 1;
}
else if (lsp >= 1000 && lsp <= 1999) {
lvl.innerHTML = 2;
}
else if (lsp >= 2000 && lsp <= 2999) {
lvl.innerHTML = 3;
}
else if (lsp >= 3000 && lsp <= 3999) {
lvl.innerHTML = 4;
}
else if (lsp >= 4000 && lsp <= 4999) {
lvl.innerHTML = 5;
}
}
I've also included the setChar() function in the window.onload of the page. I've tried including the function itself in it as well, but whether I add it at the end of the setChar() or in the window.onload the problem stays the same. All other functions work fine, it's just this part that doesn't. I'm also trying to make it generic to fit other 'accounts' I'm trying to make to save myself time. But I just can't make it work.
Edit:
I've figured out why it didn't work, and it was cause I had my localStorage.lsp in the wrong place.
I'm trying to figure out now how to make it so I don't have to refresh the page to get it to appear.
[ Solved my issue :), if unclear by my edit above]
The way you are trying to access values from localstorage is incorrect. This is not valid: localstorage.exp6.
Instead try this: localstorage.getItem('exp6')
See the documentation of Web Storage API for more information

'CSS display: none' will work just fine, but 'document.body.removeChild(child);' will break my code

people that love programming.
Before I explain my issue, I need you to understand what I am doing here. It is a very simple game.
Do you remember that console video game "Guitar Hero"? Or if you are a little younger, "Piano Tiles"?
If you don't know what I'm talking about, click on this link to get an idea of what it is = https://www.youtube.com/watch?v=nWLKlEg0VMs&ab_channel=MattSterner
Well, that is exactly what I am doing with this website (on a more basic level of course).
In other words, there are musical notes falling from the top and the player has to press a button when that note hits the bottom of the screen.
Finally, what is my issue?
Well, when the note gets to the button, I am able to recognize it, which was actually hard and took me quite a while. And when I recognize it (inside the for loop below), and when I press the key E or keycode 69, the note disappears, which is exactly what I want.
So what's wrong?
Well, as you can see, I am setting the div's display property to none but the div is not actually being eliminated, this results in every div/musical note in the game to accumulate at the bottom with a display property of none, making the game slow.
So what do I want?
I want the div/musical note to be eliminated from the website entirely, gone, finito... not just setting its display property to none.
So what have I tried myself?
I tried document.body.removeChild(child) but this actually broke the game, notes stopped coming from the top, so it was not a fix (I have no clue of why this happened by the way).
You can see the part of the code I am having trouble with here:
document.body.addEventListener("keydown", event => {
if (event.keyCode === 69 /* E */) {
for (let i = 0; i < leftSquares.length; i++){
if (leftSquares[i].style.top > "740.5px" && leftSquares[i].style.top < "790.5px"){
console.log('correctin');
leftSquares[i].style.display = "none";
// document.body.removeChild(leftSquares[i]);
}
}
}
})
Or you can play around with the full game here: https://codepen.io/xavi-font/pen/MWeRdwr
Sticks and stones won't break my code, but document.body.removeChild(child) does (for a reason I don't know).
When you are doing document.body.removeChild(leftSquares[i]);
You remove the element from the page (DOM), but you do not remove the id from idArray = [];
So on the next loop of your code you get to:
for (let i = 0; i < idArray.length; i++) {
let element = document.getElementById(i.toString());
animateFunction(element);
}
and now the element does not exist, so you can't animate it, and you get an error that breaks the game.
A simple solution (not necessarily the best) could be:
for (let i = 0; i < idArray.length; i++) {
let element = document.getElementById(i.toString());
if (element) {
animateFunction(element);
}
}
Another option would be to keep specific id's in the array idArray, then when you delete an element, remove it's ID from the array, and where you previously have for (let i = 0; i < idArray.length; i++) { you can now use
idArray.forEach((id) => {
let element = document.getElementById(id.toString());
...
});
Your game also slows down, and if you console.log the element.style.top from within animateFunction you will see that if you don't press E at the correct time, then the element just keeps animating down the page forever.
So I would also suggest a garbage collect function within the animateFunction
maybe something like this?
function animateFunction(element) {
if (element.style.top == "-100px") {
const interval = setInterval(function() {
element.style.top = eval(parseInt(element.style.top) + 1).toString() + "px";
let currentNotePosition = element.getBoundingClientRect();
// Garbage Collect
if (currentNotePosition.top > window.innerHeight) {
document.body.removeChild(element);
}
}, speed);
}
}

Why is my array appearing empty after appearing to have an element in it?

I'm trying to make a discord bot that randomly shuffles an array of planets and then picks one to hide a bounty on. Then, if a player has that planet role, they can search for the bounty, The problem is whenever I search for the bounty on the planet, it says that the array is empty, but when I shuffle the planets and show the result, the array doesn't appear to be empty.
This is the shuffling code.
let coreworlds = [verzan, albregao, corellia, rishi, coruscant, vurdon, wobani]
let coreworldsresult = []
case 'start':
function shuffle(coreworlds) {
for (let i = coreworlds.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[coreworlds[i], coreworlds[j]] = [coreworlds[j], coreworlds[i]];
}
}for (let i = 0; i < 1000000; i++) {
shuffle(coreworlds);}
coreworldsresult.push(coreworlds[1]);
message.channel.send('A small bounty has been set somewhere in the core worlds. You will have 6 hours to find the bounty.');
message.channel.send(coreworldsresult);// This always shows the random world.
setTimeout(() => {
if(coreworldsresult.lenght != 0){
coreworldsresult.length = 0
message.channel.send('Nobody claimed the bounty in time. It`ll take me another 12 hours before I can find another small bounty.')
}else{
coreworldsresult.length = 0
message.channel.send('I`m gonna be getting another small bounty in 12 hours.')
}
}, 21600000);
setInterval(() => {
function shuffle(coreworlds) {
for (let i = coreworlds.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[coreworlds[i], coreworlds[j]] = [coreworlds[j], coreworlds[i]];
}
}for (let i = 0; i < 1000000; i++) {
shuffle(coreworlds);}
coreworldsresult.push(coreworlds[1])
message.channel.send('A small bounty has been set somewhere in the core worlds. You will have 6 hours to find the bounty.')
setTimeout(() => {
if(coreworldsresult.lenght != 0){
coreworldsresult.length = 0
message.channel.send('Nobody claimed the bounty in time. It`ll take me another 12 hours before I can find another small bounty.')
}else{
coreworldsresult.length = 0
message.channel.send('I`m gonna be getting another small bounty in 12 hours.')
}
}, 21600000);
}, 64800000);
This is the searching code.
case 'search':
message.channel.send('You look around for the bounty.')
message.channel.send(coreworldsresult.length);// This always comes back as "0"
if(member.roles.cache.some(r => r.name === coreworldsresult || midrimresult || outerrimresult)){
message.reply('you found a bounty! Now shoot him with `!attack <large, medium, or small>-
bounty <weapon>`!')
}else{
message.reply('you did not find anything. Try a different planet.')
}
break;
I figured out that the reason was because my arrays were declared under the
client.on('message', message =>{
so the changes to them only applied to the code in the same case area. I fixed this by putting the arrays at the top.
Being a new to programming, one thing I would suggest when getting stuck with unexpected outcomes is to work from the top down and create cases where you KNOW what the outcome should be and eventually you'll get down to the part of your code that has either a syntax error or a logical error.
I usually add console logs to all my conditionals when debugging problems.
Like #EvanMorrison said you have typo in the word length
setTimeout(() => {
if(coreworldsresult.lenght != 0){
also please use the DRY principal (Dont't repeat yourself), by wrapping your code in functions and then use them as many time as you need

Logic: Prevent infinite loop when using portals

In a game that I am currently working on, there are portals, which are linked in pairs of two. Whenever the player enters a portal, it should be teleported to the other portal that is linked to the portal that they just entered. The problem is, that once it is teleported over, the other portal see's that the player is collding with it, and will teleport it back to the first, which will send it back and so on. The code for this porject is large and over many files, but here is the most important:
(Javascript, ignore the frame and pause vaibles, those are for animation)
export class Portal extends gameObject{
constructor(x, y, size, id){
super(x, y, size);
this.currentFrame = 0;//updated when (below) > (2 below)
this.countingFrame =0;//update every time;
this.pause = 3;//frames between frames
this.id = id;
this.justVisited = false;
}
update(player, portals){
if(this.countingFrame >= this.pause){
this.currentFrame = (this.currentFrame + 1) % 4;
this.countingFrame = 0;
}else{
this.countingFrame ++;
}
if(super.checkPlayerCollision(player)){
this.justVisited = true;
for(let i in portals){
if(this.id === portals[i].id && portals[i] !== this && !portals[i].justVisited){
player.x = portals[i].x;
player.y = portals[i].y;
}
}
}else{
for(let i in portals){
if(this.id === portals[i].id && portals[i] !== this && portals[i].justVisited){
this.justVisited = false;
}
}
}
}
render(canvas, spritesheet){
super.render(canvas, spritesheet, this.currentFrame , 26);
}
}
This is one attempt at patching it up, but it still failed. How can I make it so that the player goes throught the first one as soon as it collides, comes out the second, and won't retrun to the first untill the player moves off and then back on?
EDIT:After a little bit of seaching, the answer that was most often given to this question was "Move the player outside the reach of the second portal when it teleports over there". This will not work in my case, because when the player updates, his position changes with respect to the amount of time seince the last frame. In a perfet world, that would be ok, but because it is not perfect, the player cannot reliably be on a single point. Also, it would look weird if I moved it out of the hitbox entirely, so that doesn't work either.

Handling events with new data before previous event has completed

This code takes two inputs: div, the div (actually a textbox) and target (a number). It'll then try and in/decrement the number in a pseudo-animated way. The problem is that I'm using jQuery sliders as one form of input, which can result in multiple calls before the first call finished. This isn't a problem unless the slider is quickly increased, and then decreased before the increase rollUp finishes, resulting in an eternal decrementing div. I can't figure out what's causing it. Thoughts?
function rollNum(div, target) {
var contentString = $(div).val();
content = parseInt(contentString.substring(1));
if(content === target)
return;
else if(div !== "#costMinusMSP" && div !== "#savingsWithMSP") {
var total = rollNumTotalCost(div, target);
rollNum("#costMinusMSP", total);
rollNum("#savingsWithMSP", total /*- somehow find the cost here*/)
}
if(isNaN(content))
content = 0;
var remainingChange = target - content;
if(remainingChange > 0)
loopUp();
else
loopDown();
function loopUp() {
var length = remainingChange.toString().length;
var incrementBy = 1;
//Find how far away we are from target
for(var i=0;i<length-1;i++)
incrementBy *= 10;
content += incrementBy;
remainingChange -= incrementBy;
$(div).val("$" + (content))
if(content === target)
return;
else if(content > target) {
$(div).val("$" + (target));
return;
}
setTimeout(loopUp, 60);
}
function loopDown() {
remainingChange = Math.abs(remainingChange);
var length = remainingChange.toString().length;
var decrementBy = 1;
//Find how far away we are from target
for(var i=0;i<length-1;i++)
decrementBy *= 10;
content -= decrementBy;
remainingChange -= decrementBy;
if(content < target) {
$(div).val("$" + (target));
return;
}
//This ensures we won't promise our clients negative values.
if(content <= 0) {
$(div).val("$0");
return;
}
$(div).val("$" + (content))
if(content === target)
return;
setTimeout(loopDown, 60);
}
}
Strangely enough, adjusting another slider (that modifies an unrelated div) fixes the eternal decrement.
Things I have tried:
-Creating a boolean "running" that the function sets to true, then false before it returns. If running was true, then the function would wait until it was false to continue executing. This killed the browser or achieved maximum stack.
SomeKittens of years ago: You've learned a lot since you asked this, particularly about managing state & multiple events (not to mention how to properly ask a StackOverflow question). A simple answer would be something like this:
var rolling = false;
function rollNum(div, target) {
if (rolling) { return; }
rolling = true;
// Set rolling to false when done
}
That's all well and good but it ignores any events that are fired while we're rolling. The above won't adjust to changes on the slider made after the first adjustment, but before the numbers have finished rolling. Now, I (we?) would use Angular ($scope.$watch would come in handy here) but that didn't exist when you were working on this. Instead of passing a target number, why don't we check against the live value on the slider? (Note the use of vanilla JS, it's much faster).
var rollNum = function(textarea) {
var content = parseInt(textarea.value.substring(1), 10)
, target = parseInt(document.getElementById('sliderId').value, 10);
if (content === target) {
return;
}
// Roll up/down logic
setTimeout(function() { rollNum(textarea); }, 60);
};
A few other misc changes:
Use brackets after if statements. Waaaay easier to debug
Don't forget the radix param in parseInt
Unfortunately, you didn't think to include a JSFiddle, so I can't provide a live demonstration.

Categories