Progress slider not working properly (Music Player) - javascript

I am trying to make a progress bar for my music player written in HTML, CSS and JS using TaiwindCSS. But the slider bar's length is more than the song's, i.e., the song ends till the bar reaches the half of it.
Here is my code:-
Slider div:
<div>
<input type="range" class="w-full" name="slider" id="slider" onchange="seekTo()" min="0" max="500">
</div>
JS Logic for playing song and updating slider:
const songPlayer = new Audio();
slider.addEventListener("oninput", () => {
songPlayer.currentTime = slider.value
})
function seekTo() {
let seekto = songPlayer.duration * (slider.value / 100);
songPlayer.currentTime = seekto;
}
const setPlayer = (name, image, link, artists, copyright) => {
# Setting the UI items
blurred_results.classList.add("hidden");
player.classList.remove("hidden")
player.scrollIntoView(true);
songName.innerHTML = name
thumbnail.src = image
artists_marquee.innerHTML = artists
copyrights_label.innerHTML = copyright
songPlayer.src = link
songPlayer.load();
songPlayer.addEventListener("loadeddata", () => {
let duration = songPlayer.duration;
// convert float to int
slider.max = Math.floor(duration).toString()
console.log("hio" + slider.max)
let duration_mins = (Math.floor(duration / 60)).toString() + ":" + (Math.floor(duration % 60)).toString()
console.log(duration_mins)
endTime.innerHTML = duration_mins
setInterval(() => {
let currentTime = songPlayer.currentTime;
seekPosition = currentTime * (100 / songPlayer.duration);
console.log(seekPosition)
slider.value = seekPosition;
}, 1000)
songPlayer.currentTime = 0;
songPlayer.play();
})
}
Here are some screenshots:
In this case, the song has ended yet the slider has reached till the middle of it.
I have also noticed that if the song's duration is short (here 1:43), the slider almost reaches the end.
I am not sure whether there is some issue with my logic or there is something wrong with the styling portion.
Please help me find out what's wrong...

Related

Custom progress bar for javascript audio player

I have two tracks that get loaded on a single <audio> when you hit play on either of these tracks. That seems to work perfectly fine. Each of these tracks has a separate <progress> field but no matter what track you play you only see the progress bar moving on the first <progress> field.
Is there a way to fix this?
There's also an error when you pause a song and resume it. The total and current time text fields display a "Na" message for a split second before showing the correct total/current time back again.
How can I stop that from happening?
HTML
<audio id="globalAudio"></audio>
<div class="mp3Player" data-src="https://www.freesound.org/data/previews/338/338825_1648170-lq.mp3" data-pos="0">
<button class="btnPlayPause button">Play</button>
<span class="infoLabel">
<span id="seekObjContainer">
<progress id="seekbar" value="0" max="1"></progress>
</span>
<span class="start-time"></span>
<span class="end-time"></span>
</span>
</div>
<div class="mp3Player" data-src="http://www.lukeduncan.me/oslo.mp3" data-pos="0">
<button class="btnPlayPause button">Play</button>
<span class="infoLabel">
<span id="seekObjContainer">
<progress class="seekbar1" value="0" max="1"></progress>
</span>
<span class="start-time"></span>
<span class="end-time"></span>
</span>
</div>
JS
var globalAudio = document.getElementById("globalAudio");
var current = null;
var playingString = "<span>Pause</span>";
var pausedString = "<span>Play</span>";
$(document.body).on('click', '.btnPlayPause', function(e) {
var target = this;
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 length = globalAudio.duration;
var current_time = globalAudio.currentTime;
var totalLength = calculateTotalValue(length);
jQuery(".end-time").html(totalLength);
var currentTime = calculateCurrentValue(current_time);
jQuery(".start-time").html(currentTime);
var progressbar = document.getElementById('seekbar');
progressbar.value = globalAudio.currentTime / globalAudio.duration;
console.log(target);
}
if (current == target) {
target.innerHTML = pausedString;
target.parentNode.setAttribute('data-pos', globalAudio.currentTime); //start from paused
globalAudio.pause();
current = null;
} else {
if (current != null) {
current.innerHTML = pausedString;
current.parentNode.setAttribute('data-pos', '0'); //reset position
target.parentNode.setAttribute('data-pos', globalAudio.currentTime); //start from paused
}
current = target;
target.innerHTML = playingString;
globalAudio.src = target.parentNode.getAttribute('data-src');
globalAudio.play();
globalAudio.onloadeddata = function() {
globalAudio.currentTime = parseFloat(target.parentNode.getAttribute('data-pos'));
globalAudio.addEventListener("timeupdate",function(){ initProgressBar(); });
};
}
});
Here's is a working example in this fiddle
Thank you
That's because only the first <progress> tag have an id:
<progress id="seekbar" value="0" max="1"></progress>
<progress class="seekbar1" value="0" max="1"></progress>
Then:
var progressbar = document.getElementById('seekbar'); // <-- always getting the first progress tag
progressbar.value = globalAudio.currentTime / globalAudio.duration;
For the NaN issue appearing for a split second, that's because when loading a source in an <audio> tag the duration is unknown until the browser has retrieved the file's metadata. While this duration is unknown its value is NaN (Not A Number).
You may add a check:
if (Number.isNaN(element.duration)) {
// display 00:00 or whatever you want
}

Play a random video on button click

I should start by saying I'm not good with JS and I'm still trying to learn it.
I have 10 mp4s and I need to play one at random on a button click. I'm having trouble making it happen and I've searched around for a solution but to no avail.
Here's what I have so far:
HTML
<div class="videoHolder">
<video id="video" autoplay muted>
Your browser does not support the video tag.
</video>
</div>
<button type="button" name="button">Push to Play</button>
JS
var videos = [
"mp4s/video1.mp4",
"mp4s/video2.mp4",
"mp4s/video3.mp4",
"mp4s/video4.mp4",
"mp4s/video5.mp4",
"mp4s/video6.mp4",
"mp4s/video7.mp4",
"mp4s/video8.mp4",
"mp4s/video9.mp4",
"mp4s/video10.mp4",
];
var videoId = 0;
var elemVideo = document.getElementById('video');
var elemSource = document.createElement('source');
elemVideo.appendChild(elemSource);
elemVideo.addEventListener('ended', play_next, false);
function play_next() {
if (videoId == 10) {
elemVideo.stop();
} else {
video.pause();
elemSource.setAttribute('src', videos[videoId]);
videoId++;
elemVideo.load();
elemVideo.play();
}
}
play_next();
</script>
This unfortunately doesn't do what I need. This code will play all 10 videos one after the other on page load, then stop. I need it to only play one of the random 10 videos when someone pushes the "Push to Play" button. And I need it to keep going after 10.
Like I said I'm not good with JS at all and I was able to find this code from browsing around and modifying it a bit.
This should work for you.
<div class="videoHolder">
<video id="video" autoplay muted src="">
</video>
</div>
<button id='playRandom' type="button" name="button">Push to Play</button>
This is a really basic implementation of what you want
var videos = [
"mp4s/video1.mp4",
"mp4s/video2.mp4",
"mp4s/video3.mp4",
"mp4s/video4.mp4",
"mp4s/video5.mp4",
"mp4s/video6.mp4",
"mp4s/video7.mp4",
"mp4s/video8.mp4",
"mp4s/video9.mp4",
"mp4s/video10.mp4"
];
var videoId = getRandomInt(0, 9);
var elemVideo = document.getElementById('video');
elemVideo.setAttribute('src', '/' + videos[videoId]);
var btn = document.getElementById('playRandom');
btn.addEventListener('click', function (e) {
var nextId = getRandomInt(0, 9);
if (nextId != videoId) {
playNext(nextId);
} else {
while (nextId == videoId) {
nextId = getRandomInt(0, 9);
}
playNext(nextId);
}
});
/* gets random whole number between 0 and 9 */
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function playNext(id) {
elemVideo.setAttribute('src', '/' + videos[id]);
elemVideo.play();
}
This is a more robust implementation that includes a history of previously played videos so you don't play the same videos over again.
var videos = [
"mp4s/video1.mp4",
"mp4s/video2.mp4",
"mp4s/video3.mp4",
"mp4s/video4.mp4",
"mp4s/video5.mp4",
"mp4s/video6.mp4",
"mp4s/video7.mp4",
"mp4s/video8.mp4",
"mp4s/video9.mp4",
"mp4s/video10.mp4"
];
var playedVideos = [];
var videoId = getRandomInt(0, 9);
var elemVideo = document.getElementById('video');
elemVideo.setAttribute('src', '/' + videos[videoId]);
var btn = document.getElementById('playRandom');
btn.addEventListener('click', playRandom);
function playRandom(e) {
var nextId = getRandomInt(0, 9);
if (nextId != videoId) {
if (!playNext(nextId)) {
playRandom(e);
}
} else {
while (nextId == videoId) {
nextId = getRandomInt(0, 9);
}
if (!playNext(nextId)) {
playRandom(e);
}
}
}
/* gets random whole number between 0 and 9 */
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function playNext(id) {
// checks if the video has already been played
for (var src in playedVideos) {
if (src == videos[id]) {
return false;
}
}
elemVideo.setAttribute('src', '/' + videos[id]);
elemVideo.play();
// adds src to arr of played videos
playedVideos.push(videos[id]);
/*
* Everything from here on is optional depending on whether you want to
* - iterate over the arr of videos and play each videos at least once before starting over or
* - you want to stop playing videos after playing each at least once
*/
// empties the played videos arr and allows you to start playing a new set of random videos
if (playedVideos.length() == 10) {
playedVideos = [];
// if you'd like to stop playing videos altogether at this, point delete the previous statement and display a prompt saying the playlist is over
// or if you'd like to reset the playlist, then remove the event listener ('playRandom') and display a reset button that starts everything over from the beginning.
}
return true;
}

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");
});

change css of element depending on current slide

I'm currently working on a slider for a website.
This slider has three components.
Game list
Team overview
Awards
I have all games displayed at the same time, but only want the one corresponding to the current team be highlighted. The amount of games displayed is equal to the amount of teams and game[0] belongs to team[0] and so on.
EDIT I've changed the slider a bit :)
var slides = document.getElementsByClassName('slider');
[].forEach.call(slides, function (s) {
var next = s.getElementsByClassName('next')[0],
prev = s.getElementsByClassName('prev')[0],
inner = s.getElementsByClassName('inner')[0],
slides = inner.getElementsByClassName('slide'),
currentTeamIndex = 0,
width = 1344;
var $gameList = $('#gameList'),
$gameContainer = $gameList.find('.slides'),
$game = $gameContainer.find('.slide');
function switchSlide() {
inner.style.left = -width * currentTeamIndex + 'px';
}
next.addEventListener('click', function () {
currentTeamIndex++;
if (currentTeamIndex >= slides.length) {
currentTeamIndex = 0;
}
switchSlide();
});
prev.addEventListener('click', function () {
currentTeamIndex--;
if (currentTeamIndex < 0) {
currentTeamIndex = slides.length - 1;
}
switchSlide();
});
});
How can I do that? I'm fairly new to javascript and jQuery.

Custom JavaScript/HTML5 player issue

I've been using this custom audio player I put together with a few tutorials and solves around the Internet and have been using it for the past 2 months.
It worked 100% in Chrome, but after an update the seek bar happened to stop working.
Is this an issue on my end? or does it work for anyone else?
HTML:
<div id="musicplayer">
<table>
<tr>
<audio id="audio" src="../audio/oby/weightless/Got To Be Free.mp3"></audio>
<td>
<button id="play" type="button" onclick="playPause()" ></button>
</td>
<td>
<div id="boxed" >
Select a song from the playlist
<p class="right" id="timeInfo">00:00</p>
<input type="range" step="any" id="seekbar"></input>
</div>
</td>
</tr>
</table>
</div>
JavaScript:
<script>
var audio = document.getElementsByTagName('audio')[0],
div = document.getElementById('timeInfo');
function formatTime(s, m) {
s = Math.floor( s );
m = Math.floor( s / 60 );
m = m >= 10 ? m : '0' + m;
s = Math.floor( s % 60 );
s = s >= 10 ? s : '0' + s;
return m + ':' + s;
}
setInterval(function()
{
div.textContent = formatTime(audio.currentTime);
}, 100);
seekbar.value = 0;
var audio = document.getElementById("audio");
var seekbar = document.getElementById('seekbar');
function setupSeekbar() {
seekbar.min = audio.startTime;
seekbar.max = audio.startTime + audio.duration;
}
audio.ondurationchange = setupSeekbar;
function seekAudio() {
audio.currentTime = seekbar.value;
}
function updateUI() {
var lastBuffered = audio.buffered.end(audio.buffered.length-1);
seekbar.min = audio.startTime;
seekbar.max = lastBuffered;
seekbar.value = audio.currentTime;
}
seekbar.onchange = seekAudio;
audio.ontimeupdate = updateUI;
audio.addEventListener('durationchange', setupSeekbar);
audio.addEventListener('timeupdate', updateUI);
</script>
Duplicate code:
audio.ondurationchange = setupSeekbar;
and
audio.addEventListener('durationchange', setupSeekbar);
try removing any of these lines.
and if setting seekbar value in javascript will triger onchange... you will have to remove seekbar.onchange = seekAudio;
Is your seekbar doesn't seek when user click any time? Or is there a problem on updating time. (time is updating but seekbar doesn't)?
You do not need these codes, might also cause problem one... (the seekbar update problem)
var lastBuffered = audio.buffered.end(audio.buffered.length-1);
seekbar.min = audio.startTime;
seekbar.max = lastBuffered;
EDIT (offtopic): For better performance remove the code
setInterval(function()
{
div.textContent = formatTime(audio.currentTime);
}, 100);
and append this to updateUI
div.textContent = formatTime(audio.currentTime);

Categories