Changing variable based on video time - javascript

I need to display a "caption" from a video but I cannot use built in captions provided by a vtt file for my situation. I created an array that stores the key,value pair (time, caption). I traverse that array every time the video time is updated and find which caption fits the current time. This solution works and I haven't had any issues (performance or otherwise) but it just feels like brute force to me, I'm hoping someone can help me either refine what I have or guide me toward a more elegant solution. Any comments or criticism is appreciated.
//this function is called whenever video time is updated
this.onUpdateTime = function(currentTime, totalTime) {
this.currentTime = currentTime;
this.totalTime = totalTime;
/*the for statement traverses the array chapters which contains
[{"time": X,"caption": "XYZ"},...]*/
for (var i = 0; i < $scope.chapters.length; i++) {
/* the first if statement checks if (i is not the same as
chapters.length (this was done to prevent an off by one error) and the time of
the video is greater than or equal to time at chapters[i] and the time of the
video is still less than time at the next chapter marker chapter[i+1]*/
if (i != $scope.chapters.length - 1 && currentTime >= $scope.chapters[i].time && currentTime < $scope.chapters[i + 1].time) {
/*set the caption to the value from chapters[i].caption*/
$rootScope.caption = $scope.chapters[i].caption;
/*I have never used $scope.$apply but it seemed to be needed to get the caption to display in my view*/
$scope.$apply(); {
/*break the for loop so that it does not needlessly loop through the rest of the array after finding a match*/
break;
}
/*this if statement if for when i is equal to chapters.length -1, it is the final value in the array so it does
not check against the "next value" like the previous if statement does because there is not next value*/
} else if (currentTime >= $scope.chapters[i].time && i == $scope.chapters.length - 1) {
/*set the caption to the value from chapters[i].caption*/
$rootScope.caption = $scope.chapters[i].caption;
/*I have never used $scope.$apply but it seemed to be needed to get the caption to display in my view*/
$scope.$apply(); {
/*break the for loop so that it does not needlessly loop through the rest of the array after finding a match*/
break;
}
/*if there is not chapter marker at the current time*/
} else {
/*set the caption to a blank value because no match was found therefore there is no caption*/
$rootScope.caption = "";
$scope.$apply();
}
}
// console.log("onUpdateTime function, currentTime is: " + currentTime);
// console.log("onUpdateTime function, totalTime is: " + totalTime);
};

Related

Creating a loop script

I have a script that notifies users every minute. I set an array of users for each minute.
I would like to create a script that limits the number of users for one minute and passes a user along to the next minute when the array is full.
I can create it stepwise like this (just a sample scheme of my script):
let min = 1
if (min.users.length > 10) {
let nextmin = min + 1
if (nextmin.users.length > 10) {
let nextnextmin = nextmin + 1
if (nextnextmin.users.length > 10) {
.....
} else {
// setting user for the next next min
}
} else {
// setting user for the next min
}
} else {
// setting user for the min
}
What I need is the script that checks on the next minute in a loop until it finds an array with a number of users under the set limit.
I hope I'm understanding your question correctly. Try the code below.
//usersArray = array that holds arrays of users per minute, maxUsers = limit of users per minute
function numUsersPerMin(usersArray,maxUsers){
for(let i = 0; i<userArray[i].length; i++) {
if(userArray[i].length < maxUsers){
return i; //I'm not sure what you want returned so I'm just returning the
//index of where the first array that is below maxUsers is
}
}
}
//i acts as the "minute"

How do I splice ranges in JavaScript?

I'm working on a schedule management system which loads a person's work schedule into an array, and then the user is allowed to "inject" a new shift within that day. There are a couple of rules the code must follow, of course, and they are:
There must be no gaps between existing shifts and the added item. This means the added item's end time must be equal to or greater than the first item's start time, and the added item's start time must be equal to or less than the last item's end time.
The added shift must overwrite any existing shifts which occur during it's time frame. Essentially, it needs to splice itself into the array, but I'm unsure of how to do so using the .splice() method.
Any affected time blocks must be truncated/compressed to accommodate the new item. For example, if there is an existing time block which is an hour in duration, and we inject a 10 minute block into that beginning 20 minutes in, it would result in the first 20 minute portion of the original block, followed by the 10 minute added item, followed by the remainder of the original block. Added items must also be able to overlap other time blocks as well. So if we add a three hour block that covers 4 other time blocks (or starts/stops within them!) it must overwrite each of those.
This seems like a simple enough task, because basically it's just splicing ranges, but I don't know how to splice ranges in JavaScript.
Here's the code I've written to implement these rules so far:
var obj = {
start: '',
end: '',
};
var objArray = [];
var tmpArray = [];
var finalArray = [];
objArray.push({start: "12:00", end: "12:45"});
objArray.push({start: "12:45", end: "1:00"});
objArray.push({start: "1:00", end: "1:30"});
objArray.push({start: "1:30", end: "2:30"});
// added object
obj.start = "12:00";
obj.end = "12:10";
for (var i = 0; i < objArray.length; i++) { tmpArray.push(objArray[i]); }
//tmpArray = objArray; // preserve the original array
objArray.push(obj);
console.clear();
console.log("%o", objArray);
objArray.sort(function(a, b) { var x = a.start; var y = b.start; return ((x < y) ? -1 : ((x > y) ? 1 : 0)); });
// sanity check
if (obj.start >= obj.end){
console.log('Time logic is invalid.');
return false;
}
if (obj.end < tmpArray[0].start) {
console.log('There is a gap before the first item.');
return false;
}
if (obj.start > tmpArray[tmpArray.length - 1].end){
console.log('There is a gap after the last item.');
return false;
}
// Now for the fun stuff...
for (var i = 0; i < objArray.length; i++){
var tmpobj = objArray[i];
if (tmpobj.start == obj.start && tmpobj.end == obj.end){ // find our inserted object
index = i;
console.log('<<< obj injected: %s - %s [%d] >>>', obj.start, obj.end, index);
if (index == 0){ // is first item, start time was less than first item's start time
finalArray.push(obj);
if (obj.end == tmpArray[0].start){
finalArray.push(tmpArray[0]); // item insertion was a complete prepend...
} else if (obj.end >= tmpArray[tmpArray.length - 1].end){
console.log('entire array is consumed'); // item covers entire shift...
} else {
// This code is reached when obj start time is before or equal to first item
console.log('obj <= tmpArray[0].start, end > tmpArray[0].start');
}
} else {
if (obj.start == tmpArray[tmpArray.length - 1].end){
console.log('Item added at end of shift');
finalArray.push(tmpArray[i - 1]);
finalArray.push(obj);
}
}
} else {
console.log('obj tested: %s - %s [%d]', tmpobj.start, tmpobj.end, i);
if (obj.start > tmpobj.end) {
finalArray.push(tmpobj);
}
if (obj.end < tmpobj.start) {
finalArray.push(tmpobj);
}
}
// now iterate through array and combine like terms (activity type)
}
for (var i = 0; i < finalArray.length; i++){
console.log('%d) s: %s, e: %s', i, finalArray[i].start, finalArray[i].end);
}
How do I splice ranges in JavaScript?
You could consider your time blocks in term of start and end separately. I conceptually think of it as a linked list, which you can implement as an Array. With a special requirement that each node in the list must follow the order of START_TIME -> END_TIME and never have 2 START_TIME or 2 END_TIME nodes adjacent to each other. And any in between (free time blocks) is also defined by a START_TIME and END_TIME node.
Sis START_TIME
E is END_TIME
The array of valid time blocks may look like this [S1, E1, S2, E2, S3 ,E3]
So then when coding the logic to insert new blocks of time, you find your desired block by going to the appropriate START_TIME node for your new timeblock, then "splice" by creating an END_TIME E* node and a new START_TIME S-NEW node marking the beginning of your new block.
[S1, E1 ,S2 ,E* ,S-NEW, E2, S3, E3]
Then you may define the ending time by creating END_TIME node if necessary (remove old END_TIME E2 and creating a "free" block),
[S1, E1, S2, E*, S-NEW, E-NEW, S-FREE, E-FREE, S3, E3]
or reuse the old END_TIME E2 node that you "spliced" earlier if they end at the same time
[S1, E1, S2, E*, S-NEW, E2, S3, E3]
The reason you're hurting your brain at the end is because your code is doing too much all at once. I suggest breaking it up into functions, here are some suggestions:
canStart(time)
canEnd(time)
insertBlock(startBlock, endBlock)
removeBlock(startBlock, endBlock)

Jump around in a video to times from an array

Following along the lines of Control start position and duration of play in HTML5 video, I am trying to make a video jump from one segment to the next automatically when each has finished playing. Each segment will have the same duration, and the start times for each segment will be in an array.
I can't seem to figure out how to loop through the array after addEventListener.
var video = document.getElementById('player1');
function settheVariables() {
var videoStartTime= ["4","15","26","39"];
for (var i = 0; i < videoStartTime.length; i++) {
if (video.currentTime < videoStartTime[0] ){
video.currentTime = videoStartTime[i];
}
durationTime = 5;
}
//This part works when I plug in numbers for videoStartTime.
video.addEventListener('timeupdate', function() {
if(this.currentTime > (// videoStartTime [current index] + durationTime)){
this.currentTime = (// videoStartTime with next index);
video.play(); }
});
}
you need to change the values in your array to integers, not strings - you're not comparing apples to apples.
the updated and somewhat simplified sample below plays (initially from the start of the video) until the timestamp hits the current marker plus five seconds then jumps to the next marker (and loops back around).
it doesn't cater for the user scrubbing the video themselves (though it will trap as soon as they go >5s past the start of the current section, but going back will confuse things a little) - if you want to control within those 5s boundaries you'll want to do some smarter examination of the time stamp vs the array to make sure you're where you're supposed to be
anyway ... the code:
<script>
var video = document.getElementById('player1');
var videoStartTime= [4,15,26,39];
durationTime = 5;
currentIndex=0;
video.addEventListener('timeupdate', function() {
// console.log(this.currentTime);
if (this.currentTime > (videoStartTime[currentIndex] + durationTime))
{
currentIndex = (currentIndex + 1) % videoStartTime.length // this just loops us back around
this.currentTime = videoStartTime[currentIndex];
// video.play(); // don't need this if the video is already playing
}
});
</script>

How to make a real Javascript timer

I'm looking for a way to manipulate animation without using libraries
and as usual I make a setTimeout in another setTimout in order to smooth the UI
but I want to make a more accurate function to do it, so if I want to make a 50ms-per-piece
animation, and I type:
............
sum=0,
copy=(new Date()).getMilliseconds()
function change(){
var curTime=(new Date()).getMilliseconds(),
diff=(1000+(curTime-copy))%1000 //caculate the time between each setTimeout
console.log("diff time spam: ",diff)
sum+=diff
copy=curTime
var cur=parseInt(p.style.width)
if (sum<47){//ignore small error
//if time sum is less than 47,since we want a 50ms-per animation
// we wait to count the sum to more than the number
console.log("still wating: ",sum)
}
else{
//here the sum is bigger what we want,so make the UI change
console.log("------------runing: ",sum)
sum=0 //reset the sum to caculate the next diff
if(cur < 100)
{
p.style.width=++cur+"px"
}
else{
clearInterval(temp)
}
}
}
var temp=setInterval(change,10)
I don't know the core thought of my code is right,anyone get some ideas about how to make a more accurate timer in most browser?
Set the JsFiddle url:
http://jsfiddle.net/lanston/Vzdau/1/
Looks too complicated to me, use setInterval and one start date, like:
var start = +new Date();
var frame = -1;
var timer = setInterval(checkIfNewFrame, 20);
function checkIfNewFrame () {
var diff = +new Date() - start;
var f = Math.floor(diff / 50);
if (f > frame) {
// use one of these, depending on whether skip or animate lost frames
++frame; // in case you do not skip
frame = f; // in case you do skip
moveAnimation();
}
}
function moveAnimation () {
... do whatever you want, there is new frame, clear timer past last one
}

Replace Picture Source (Error Handling)

Hi I have a script wich changes the source of an image ie webpics/1.jpg to webpics/2.jpg on click of an image. One problem i am having though is that the script will still work for one more image after there is images, so if i have 11 images i can press next 11 times and will get an empty image, what i would like is for the script to run a error check and stay on the current image if the next on doesn't exist. Here is the script:
$("#prev, #next").click(function() {
var currentNumber = parseInt($("#image1").attr("src").split('gallery/')[1]); // get the
number
var newNumber = ($(this).attr("id")=="next")?currentNumber+1:currentNumber-1;
var testImage = new Image();
testImage.onload=function() {
var img = $("#image1");
img.attr("src",this.src);
img.css("visibility","visible");
}
testImage.onerror=function() {
$("#image1").css("visibility","hidden");
}
testImage.src="http://www.yogahealth.net.au/gallery/"+newNumber+".jpg";
return false;
});
Do you know how many images exist? If so, just test if the new number would exceed the total count (if newNumber ...) and do nothing (i.e. skip the part where you change the testImage.src.
For example, you need to have the total count in the variable totalImages, then you could do:
$("#prev, #next").click(function() {
var currentNumber = parseInt($("#image1").attr("src").split('gallery/')[1]); // get the number
var newNumber = ($(this).attr("id")=="next")?currentNumber+1:currentNumber-1;
var totalImages = // get the number of total images here
if ((newNumber > totalImages) || (newNumber <= 0)) {
return false; // just do nothing
}
// here comes the rest of your code
});
Note that I also added the possibility that your number becomes less than 0 or 0, depending on whether you have an image named 0. If yes, you can change the <= to <, otherwise it won't show the 0 image.
In order to get this number into your Javascript code, you could either render the code using PHP and insert it into your script with <?php echo $total; ?> or you extract it from another element from the HTML page, as you did with the currentNumber.

Categories