how to use mutationobserver with youtube spa technology in javascript - javascript

I want the video in a youtube playlist to go to the next video when it reaches the second I want. But I can't check the location.href all the time, I want the code to run when the same url is opened again because I loop the playlist.
const counterEle = document.querySelector("video");
function generateCount(limit) {
mutation(160.510023);
if (counterEle.currentTime > limit) {
console.log("current > limit");
} else {
setTimeout(generateCount, 3000, limit);
setTimeout(mutation, 3000, limit);
}
}
let previousUrl = 'https://www.youtube.com/watch?v=93qE6Z8qheA&list=PL9dIg-VwAsxZdZvuHjzTOP8-49CWpis17&index=5';
const observer = new MutationObserver(mutation);
function mutation(limit) {
generateCount(160.510023);
if (location.href == previousUrl) {
console.log(`URL changed to ${location.href}`);
if (counterEle.currentTime > limit) {
document
.querySelector(
"#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-left-controls > a.ytp-next-button.ytp-button"
)
.click();
} else {
setTimeout(generateCount, 3000);
}
} else {
console.log("!=");
}
}
I tried many methods, what am I doing wrong?
(There is no problem with the code working, just checking the domain name is a problem)

You can use ontimeupdate on the video html media element. This way, you get notified when the currentTime is changing.
Also you can check if an ad is playing using document.querySelector('.ad-interrupting')
The following code will play the next element in the playlist after 5 seconds:
const specificTime = 5; //play next after 5 seconds
const vid = document.querySelector("video");
vid.ontimeupdate = function onTimeUpdate(){
var ad = document.querySelector('.ad-interrupting');
if (!ad && vid.currentTime >= specificTime){
console.log("playing next");
document.querySelector(
"#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-left-controls > a.ytp-next-button.ytp-button"
).click();
}
}
Tampermonkey script example:
// ==UserScript==
// #name autoplay next video in playslist
// #version 0.1
// #description play next video in playlist after X seconds
// #include /https:\/\/www\.youtube\.com\/watch?.*list=PL9dIg-VwAsxZdZvuHjzTOP8-49CWpis17.*/
// ==/UserScript==
(function() {
'use strict';
const specificTime = 5; //play next after 5 seconds
const vid = document.querySelector("video");
vid.ontimeupdate = function onTimeUpdate(){
var ad = document.querySelector('.ad-interrupting');
if (!ad && vid.currentTime >= specificTime){
console.log("playing next");
document.querySelector(
"#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-left-controls > a.ytp-next-button.ytp-button"
).click();
}
}
})();
which match this playlist when activated

Related

Pure JavaScript: How to refresh page after countdown if the browser tab is active

I want to achieve the following scenario:
Set a timer of 60 seconds and start the countdown displaying the
timer. After the countdown is finished, refresh (not reload) the page
and restart the countdown timer and continue the process.
If the browser's tab is changed, after finishing the timer, refresh the
page and pause the timer, and only when the tab is active again refresh
the page and start the countdown timer again.
I have managed to achieve the first requirement like:
Result will Refresh in <span id="countdown"></span>
<script>
if (!document.hidden) {
function timedRefresh(e) {
var n = setInterval((function () {
e > 0 ? (e -= 1, document.getElementById("countdown").innerHTML = e) : (clearInterval(n), window.location.reload())
}), 1e3)
}
timedRefresh(59)
}
</script>
And is unable to achieve the second requirement. I have tried to implement the solution mentioned here: Is there a way to detect if a browser window is not currently active?
But it didn't work.
How do I achieve the requirements in pure JavaScript code?
The following code has solved this scenario. It's a little messy, but I think is understandable. We use both window.document.hasFocus() and the focus event to create this solution. I have changed the window.location.reload() methods to just consoles simulating a function that refresh the data inside the application, since is asked to us refresh (not reload).
function timedRefresh(intervalTime) {
let interval = null;
let currentTime = intervalTime;
const updateTimer = () => {
const countdownSpan = document.getElementById("countdown");
if (currentTime <= 0) {
console.log(`print refresh at ${new Date()}`); // refresh page
if (window.document.hasFocus()) {
currentTime = intervalTime;
} else {
clearInterval(interval);
interval = null;
currentTime = intervalTime;
}
}
if (!interval) {
countdownSpan.innerHTML = "#";
} else {
countdownSpan.innerHTML = currentTime;
currentTime -= 1;
}
};
interval = setInterval(updateTimer, 1000);
window.addEventListener("focus", () => {
if (!interval) {
interval = setInterval(updateTimer, 1000);
}
});
}
const TIMER_SECONDS = 5;
timedRefresh(TIMER_SECONDS);
Edit:
The answer above does not work with reloads using window.location.realod(). A code solving the issue may look like this:
const TIMER_SECONDS = 60;
/*
* Update span with current time
* `currentTime` is an integer bigger than zero or a string '#' meaning 'timer stopped'
*/
function updateDisplayTimer(currentTime){
const countdownSpan = document.getElementById("countdown");
countdownSpan.innerHTML = currentTime;
}
/*
* A timer with `loopDuration` seconds. After finished, the timer is cleared.
* `loopDuration` is an integer bigger than zero, representing the time duration in seconds.
*/
function timedRefresh(loopDuration) {
let currentTime = loopDuration;
let interval = setInterval(() => {
if (currentTime > 0) {
updateDisplayTimer(currentTime);
currentTime -= 1;
} else {
window.location.reload(); // refresh page
}
}, 1000);
window.addEventListener('load', () => {
if(window.document.hasFocus()){
timedRefresh(TIMER_SECONDS);
} else {
updateDisplayTimer("#");
clearInterval(interval);
interval = null;
}
});
window.addEventListener('focus', () => {
if(interval == null){
window.location.reload(); // refresh page
}
});
}
// exeute main fucntion
timedRefresh(TIMER_SECONDS);
Use window.document.hasFocus() to determine if the user is focused on your webpage or not.

how to make a custom audioplayer play songs hosted online

I want to use a custom audio player to play sound tracks hosted online.
For example I want to be able to feed the audio player a link like
example.com/path/to/track.mp3
and it should parse it and play it. I can play sound tracks hosted locally
Here is the code where I have the problem. I have an audio object that points to a local folder where the sound tracks are. I am not sure how to deal with this when it is not hosted locally.
When I replace the path with the URL the audio player just freezes.
$(function () {
var count = 0;
var audio;
//Hide Pause
$('#pause').hide();
initAudio($('#playlist li:first-child'));
function initAudio(element) {
var song = element.attr('song');
var cover = element.attr('cover');
var artist = element.attr('artist');
var title = element.text();
var temp = parseFloat($('#volume-slider').val() / 100);
//Create audio object
audio = new Audio('../media/books/' + song);
audio.volume = temp;
audio.addEventListener("loadedmetadata", function () {
minutes = parseInt(audio.duration / 60);
seconds = parseInt(audio.duration % 60);
if (seconds < 10) {
seconds = '0' + seconds;
};
if (minutes < 10) {
minutes = '0' + minutes;
};
$('#duration').html(minutes + ':' + seconds);
});
$("#auto-play").click(function () {
if (count === 1) {
$("#auto-play").css("background-color", "black");
count = 0;
} else {
$("#auto-play").css("background-color", "rgb(0,0,255)");
count = 1;
}
});
audio.addEventListener('ended', function () {
if (count > 0) {
var next = $('#playlist li.active').next();
if (next.length == 0) {
next = $('#playlist li:first-child');
}
initAudio($(next));
audio.play();
showCurrentTime();
}
});
//Inserts track info
$('.artist').text(artist);
$('.title').text(title);
$('#duration').text();
//Insert song cover. NOTE: the path below is relative to the
//html "img.cover" element not this js file.
$('img.cover').attr('src', '../media/books/covers/' + cover);
$('#playlist li').removeClass('active');
element.addClass('active');
};
});
You can avoid CORS errors with something like this.
For your local development it would be easy.
If you would like to go in production, it would be bit harder. The Server with the soundtracks has to accept your App.

Loading/Buffering text while loading audio in html5 custom player with JavaScript

I found a custom html5 audio player and successfully redesigned it, now I want to add a "loading/buffering" text while player is loading audio (otherwise users may freak out because nothing happening after they hit play).
Here is the code to explain:
function calculateTotalValue(length) {
var minutes = Math.floor(length / 60),
seconds_int = length - minutes * 60,
seconds_str = seconds_int.toString(),
seconds = seconds_str.substr(0, 2),
time = minutes + ':' + seconds
return time;
}
function calculateCurrentValue(currentTime) {
var current_hour = parseInt(currentTime / 3600) % 24,
current_minute = parseInt(currentTime / 60) % 60,
current_seconds_long = currentTime % 60,
current_seconds = current_seconds_long.toFixed(),
current_time = (current_minute < 10 ? "0" + current_minute : current_minute) + ":" + (current_seconds < 10 ? "0" + current_seconds : current_seconds);
return current_time;
}
function initProgressBar() {
var player = document.getElementById('player');
var length = player.duration
var current_time = player.currentTime;
// calculate total length of value
var totalLength = calculateTotalValue(length)
jQuery(".end-time").html(totalLength);
// calculate current value time
var currentTime = calculateCurrentValue(current_time);
jQuery(".start-time").html(currentTime);
var progressbar = document.getElementById('seekObj');
progressbar.value = (player.currentTime / player.duration);
progressbar.addEventListener("click", seek);
if (player.currentTime == player.duration) {
$('#play-btn').removeClass('pause');
}
function seek(evt) {
var percent = evt.offsetX / this.offsetWidth;
player.currentTime = percent * player.duration;
progressbar.value = percent / 100;
}
};
function initPlayers(num) {
// pass num in if there are multiple audio players e.g 'player' + i
for (var i = 0; i < num; i++) {
(function() {
// Variables
// ----------------------------------------------------------
// audio embed object
var playerContainer = document.getElementById('player-container'),
player = document.getElementById('player'),
isPlaying = false,
playBtn = document.getElementById('play-btn');
// Controls Listeners
// ----------------------------------------------------------
if (playBtn != null) {
playBtn.addEventListener('click', function() {
togglePlay()
});
}
// Controls & Sounds Methods
// ----------------------------------------------------------
function togglePlay() {
if (player.paused === false) {
player.pause();
isPlaying = false;
$('#play-btn').removeClass('pause');
} else {
player.play();
$('#play-btn').addClass('pause');
isPlaying = true;
}
}
}());
}
}
initPlayers(jQuery('#player-container').length);
Player code (source) on CodePen
I want some text will be shown in the same "span" that shows
"start time" (please see CodePen) while media is loading;
The text " 'loading.' 'loading..' 'loading...' " must changing on
loop while media is loading;
When it is loaded the "loading" text must be changing on "start
time" as it is now.
So basically I wat to put some text in start time while it is not shows anything but zeroes
I'm new to JS
Thats why I need some help or point to right direction
You can use the "readyState" event of the audio player to show hide loading.
There is already a "SetInterval" even which is getting fired so in that we can add this code to show/hide the "Loading"
1st add the loading element(You can put it where ever you want"
<h3 id="loading" style="display:none;">Loading</h3>
Now let's add the code to check "readystate" inside "SetInterval"
if(player.readyState>0&&player.readyState<4){
$("#loading").show();
}
else{
$("#loading").hide();
}
You can read more about "readystate" here
/As per the request I have changed the code to use the start time as loading, to make it work we don't have to add anything inside html but need to do some changes in JS
First, add this inside "togglePlay" functions pay condition in the "else" block.
$(".start-time").html("Loading...");
After this inside "initProgressBar()" function replace the "jQuery(".start-time").html(currentTime);" with the below code
if (player.readyState === 4) {
jQuery(".start-time").html(currentTime);
}
so how it will work, When you click play button the start time text will show as "Loading" but once the file is loaded and the player is ready to play the text will be changed to "start time", Hope it works. Also updated the CodePen for better understanding
You can find the full code in the CodePen
You could use setInterval to cycle through the different 'loading' text and clearInterval when the player's play promise is done.
Here's a basic example:
var dots = 1;
var loading = setInterval(function(){
dots = (dots % 3) + 1;
$(".start-time").text("Loading" + Array(dots + 1).join("."));
console.log($(".start-time").text());
}, 250);
player.play().then(function() {
clearInterval(loading);
}).catch((error) => {
$(".start-time").text("Error loading");
});

Starting timer when clicking first card of memory game

Ok I am trying to wrap up a project and the only thing holding me back is it that they call for the timer to start on clicking a match game. The timer starts when the HTML file loads which is not what the project wants and I have tried some methods but ends up freezing the game. I want the timer to be able to start when clicking a card.
var open = [];
var matched = 0;
var moveCounter = 0;
var numStars = 3;
var timer = {
seconds: 0,
minutes: 0,
clearTime: -1
};
//Start timer
var startTimer = function () {
if (timer.seconds === 59) {
timer.minutes++;
timer.seconds = 0;
} else {
timer.seconds++;
};
// Ensure that single digit seconds are preceded with a 0
var formattedSec = "0";
if (timer.seconds < 10) {
formattedSec += timer.seconds
} else {
formattedSec = String(timer.seconds);
}
var time = String(timer.minutes) + ":" + formattedSec;
$(".timer").text(time);
};
This is the code for clicking on a card. I have tried to include a startTimer code into this but doesn't work.
var onClick = function() {
if (isValid( $(this) )) {
if (open.length === 0) {
openCard( $(this) );
} else if (open.length === 1) {
openCard( $(this) );
moveCounter++;
updateMoveCounter();
if (checkMatch()) {
setTimeout(setMatch, 300);
} else {
setTimeout(resetOpen, 700);
}
}
}
};
And this class code I use for my HTML file
<span class="timer">0:00</span>
Try this: https://codepen.io/anon/pen/boWQbe
All you needed to do was remove resetTimer() call from the function that happens on page load and then just do a check in the onClick (of card) to see if the timer has started yet. timer.seconds == 0 && timer.minutes == 0.

How do I pause an HTML video and resume automatically over a loop?

I have a 4 second long video embedded in my HTML page using video tag. I need to pause the video for 2 secs first at 2.99s, 3.44s and then at 4.00s. For that to happen the video needs to resume from previous instance.
But after long hours of effort I haven't been able to find a solution. Thus far I have just been able to get the video paused for >=2.99s but it does not resume even after I had used setInterval function.
Here is the code:
var vTag = document.getElementById('video');
setInterval(function() { pVideo();}, 2000);
function pVideo() {
vTag.ontimeupdate = function(e) {
cTime = vTag.currentTime;
(cTime >= 2.9 || cTime >= 3.44 || cTime >= 4.00) ? vTag.pause(): vTag.play();
};
};
PS: The video is being used as a background video for header section.
Try this.. this is a fully working code
HTML
<video id="video" autoplay muted loop>
<source src="http://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4">
<source src="http://www.w3schools.com/html/mov_bbb.ogg" type="video/ogg">
Your browser does not support HTML5 video.
</video>
JS
var vTag = document.getElementById('video');
vTag.play();
var wasPausedBefore1 = 0;
var wasPausedBefore2 = 0;
var wasPausedBefore3 = 0;
vTag.ontimeupdate = function(e) {
cTime = vTag.currentTime;
if (cTime < 1) {
wasPausedBefore1 = 0;
wasPausedBefore2 = 0;
wasPausedBefore3 = 0;
} else if (cTime > 2.8 && cTime < 3.1 && wasPausedBefore1 == 0) {
vTag.pause();
wasPausedBefore1 = 1;
setTimeout(function() {
vTag.play();
}, 4000);
} else if(cTime > 3.3 && cTime < 3.6 && wasPausedBefore2 == 0){
vTag.pause();
wasPausedBefore2 = 1;
setTimeout(function() {
vTag.play();
}, 4000);
} else if(cTime > 3.8 && cTime < 4.0 && wasPausedBefore3 == 0){
vTag.pause();
wasPausedBefore3 = 1;
setTimeout(function() {
vTag.play();
}, 4000);
}
};
And here is a working fiddle : https://jsfiddle.net/o9mknsx0/1/
You'll need to update your if statement to track the pause values somehow, otherwise your video will not play because after 2.9 seconds, the if will always return true for all those conditions.
var vTag = document.getElementById('video');
setInterval(pVideo, 2000); //function reference
var nextPause = [2.9, 3.44, 4.00];
var pauseIndex = 0;
function pVideo() {
vTag.ontimeupdate = function(e) {
cTime = vTag.currentTime;
var pauseTime = nextPause[pauseIndex];
if (cTime >= pauseTime) {
vTag.pause();
setTimeout(vTag.play, 2000); //unpause after 2 seconds
if (++index <= nextPause.length) index++
else index = 0;
}
}
};
This is untested and my logic may be flawed, but I feel like the concept is sound.
Put all your pause times into an array, and track the last used index (resetting as well, if need be (looping video)). Then, if the if statement matches, pause the video and run a timeout for 2 seconds to play the video again.
Use setTimeout instead of setInterval.
I'm not sure if you'd be able to catch the times you need using timeupdate since it fires either every 200ms or 250ms but I'd do it like so:
var vTag = document.getElementById('video');
vTag.addEventListener('timeupdate', function() {
var cTime = vTag.currentTime;
if(cTime === 2.99 || cTime === 3.44 || cTime === 4)) {
vTag.pause();
setTimeout(function() {
vTag.play();
}, 2000);
}
});
For it to catch your times like 2.99 and 3.44 then you need to poll for it every 10ms using setInterval.
setInterval(function() {
var cTime = vTag.currentTime;
if(cTime === 2.99 || cTime === 3.44 || cTime === 4) {
vTag.pause();
setTimeout(function() {
vTag.play();
}, 2000);
}
}, 10);

Categories