how to make a custom audioplayer play songs hosted online - javascript

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.

Related

how to use mutationobserver with youtube spa technology in 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

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

Multiple audio players on the same page

I'm attempting to create two audio players on the same page. I've seen a similar question, which suggested, "filtering tasks for each audio element and audio value," but my player is so different than the one in that question that I'm not sure how to apply it to mine.
Whenever I try to play the first player, the second starts instead and breaks if I start clicking around. The scripts for the two players are in different files, I've already changed the IDs so there aren't duplicates, and I also tried changing the variable and function names thinking that maybe they were having some impact. Nothing has worked.
I'm using the same code for each player, but with a different prefix on each ID:
/* global $ */
var audio;
//Hide Pause
$('#pause').hide();
initAudio($('#playlist').find('li:first-child'));
$('#playlist').on('click', function(event) {
initAudio($(event.target));
});
// Initiate play sequence
function initAudio(element){
var recording = element.attr('data-id');
var title = element.text();
var artist = element.attr('data-artist');
//Create audio object
audio = new Audio('public/audio_files/'+ recording);
//Insert audio info
$('.artist').text(artist);
$('.title').text(title);
$('#playlist li').removeClass('active');
element.addClass('active');
// Set timer to 0
$('#duration').html('0:00');
// Maintain volume on song change
audio.volume = $('#volume').val() / 100;
}
//Play button
$('#play').click(function() {
audio.play();
showPause();
showDuration();
});
//Pause button
$('#pause').click(function() {
audio.pause();
showPlay();
});
//Next button
$('#next').click(function() {
audio.pause();
var next = $('#playlist li.active').next();
if(next.length == 0){
next = $('#playlist li:first-child');
}
showPause();
initAudio(next);
audio.play();
showDuration();
});
//Prev button
$('#prev').click(function() {
audio.pause();
var prev = $('#playlist li.active').prev();
if(prev.length == 0){
prev = $('#playlist li:last-child');
}
showPause();
initAudio(prev);
audio.play();
showDuration();
});
//Playlist song click
$('#playlist li').click(function() {
audio.pause();
initAudio($(this));
showPause();
audio.play();
showDuration();
});
//Volume control
$('#volume').change(function() {
audio.volume = parseFloat(this.value / 100);
});
// Show play button, hide pause
function showPlay() {
$('#play').show();
$('#pause').hide();
}
// Show pause button, hide play
function showPause() {
$('#play').hide();
$('#pause').show();
}
// Automatically play next song
function autoPlayNext() {
if(audio.currentTime == audio.duration) {
var next = $('#playlist li.active').next();
if(next.length == 0){
next = $('#playlist li:first-child');
}
showPause();
initAudio(next);
audio.play();
showDuration();
}
}
// Time/Duration
function showDuration() {
$(audio).bind('timeupdate', function() {
//Get hours and minutes
var s = parseInt(audio.currentTime % 60, 10);
var m = parseInt(audio.currentTime / 60, 10) % 60;
if(s < 10) {
s = '0' + s;
}
$('#duration').html(m + ':'+ s);
var value = 0;
if(audio.currentTime > 0){
value = Math.floor((100 / audio.duration) * audio.currentTime);
}
$('#progress').css('width', value + '%');
autoPlayNext();
});
}
// Skip time via progress bar click
$('#progress-bar').bind('click', function (e) {
var $div = $(e.target);
var offset = $div.offset();
var x = e.clientX - offset.left;
audio.currentTime = ((x / 2.3)/(100 / audio.duration)); /* length of progress-bar is 230px hence (x/2.3) */
});
Any insight?

HTML5 Audio Tag - Start and end at position

I have a number of <audio> tags in my code and I would like to know if there is a way that I can set each one to have a custom start and end position as they all load the same file.
This should happen without user interaction. effectively I need to deploy and <audio> tag and have something like data-start="04.22" data-end="09.45"
There is a timerange parameter available in MediaElement's src attribute which does exactly this.
://url/to/media.ext#t=[starttime][,endtime]
Note that if you enable the controls on these elements, then the user will be able to seek to different positions.
Example :
var url = 'https://upload.wikimedia.org/wikipedia/commons/4/4b/011229beowulf_grendel.ogg';
var slice_length = 12;
var audio, start, end;
for (var i = 0; i < 10; i++) {
start = slice_length * i; // set the start
end = slice_length * (i + 1); // set the end
// simply append our timerange param
audio = new Audio(url + '#t=' + start + ',' + end);
audio.controls = true; // beware this allows the user to change the range
document.body.appendChild(document.createTextNode(start + 's ~ ' + end + 's'));
document.body.appendChild(document.createElement('br'));
document.body.appendChild(audio);
document.body.appendChild(document.createElement('br'));
}
The following is what your looking for. I have four options from which you can choose from each with a bit less difficulty than the one before. I recommend the last one based on what you needed.
The start time is in seconds and in this example it is 12 seconds. Instead of a end time you have a play time and this is in milliseconds.
myAudio=document.getElementById('audio2');
myAudio.addEventListener('canplaythrough', function() {
if(this.currentTime < 72){this.currentTime = 72;}
this.play();
setTimeout(function(){
document.getElementById('audio2').pause();
}, 3000);
});
<audio id="audio2"
preload="auto"
src="https://upload.wikimedia.org/wikipedia/commons/f/f0/Drum.ogg" >
<p>Your browser does not support the audio element</p>
</audio>
If you really want to have an end time you can write a function that will take your input and subtract it from start time and convert it into millisecond.
An example of that is seen below:
var startTime = 72;
var endTime = 75;
var delaySec = endTime - startTime;
var delayMillis = delaySec * 1000;
myAudio=document.getElementById('audio2');
myAudio.addEventListener('canplaythrough', function() {
if(this.currentTime < startTime){this.currentTime = startTime;}
this.play();
setTimeout(function(){
document.getElementById('audio2').pause();
}, delayMillis);
});
<audio id="audio2"
preload="auto"
src="https://upload.wikimedia.org/wikipedia/commons/f/f0/Drum.ogg" >
<p>Your browser does not support the audio element</p>
</audio>
In this one you can set both the start time and end time in seconds.
Or you could do this with the start time in minutes and seconds.
var startMinute = 1;
var startSecond = 12;
var endMinute = 1;
var endSecond = 15;
var startinsec = startMinute * 60;
var startTime = startinsec + startSecond;
var endinsec = endMinute * 60;
var endTime = endinsec + endSecond;;
var delaySec = endTime - startTime;
var delayMillis = delaySec * 1000;
myAudio=document.getElementById('audio2');
myAudio.addEventListener('canplaythrough', function() {
if(this.currentTime < startTime){this.currentTime = startTime;}
this.play();
setTimeout(function(){
document.getElementById('audio2').pause();
}, delayMillis);
});
<audio id="audio2"
preload="auto"
src="https://upload.wikimedia.org/wikipedia/commons/f/f0/Drum.ogg" >
<p>Your browser does not support the audio element</p>
</audio>
Here is another one which should actually be easier for you to do since you have multiple files:
audioControl('audio2', 1, 12, 1, 15);
function audioControl(elemID,sM, sS, eM, eS) {
var startinsec = sM * 60;
var startTime = startinsec + sS;
var endinsec = eM * 60;
var endTime = endinsec + eS;;
var delaySec = endTime - startTime;
var delayMillis = delaySec * 1000;
myAudio=document.getElementById(elemID);
myAudio.addEventListener('canplaythrough', function() {
if(this.currentTime < startTime){this.currentTime = startTime;}
this.play();
setTimeout(function(){
document.getElementById(elemID).pause();
}, delayMillis);
});
}
<audio id="audio2"
preload="auto"
src="https://upload.wikimedia.org/wikipedia/commons/f/f0/Drum.ogg" >
<p>Your browser does not support the audio element</p>
</audio>
Anytime you want to do it you just run the following:
audioControl(ElementID, StartMinute, StartSecond, EndMinute, EndSecond);
See this answer for how to play an audio file at a certain time:
HTML 5 <audio> - Play file at certain time point

Countdown timer working stop on chrome extensions.

I think that I will build chrome extensions to count down.
In case of the present code, if popup.html is closed, processing will be completed compulsorily.
In this case, is it better to write setInterval to background.js?
Moreover, I want you to teach whether what I should do concrete.
background.js
var num = window.localStorage.setItem("minite_num", 20);
default_count = num*60;
function count_start() {
count = default_count;
chrome.browserAction.setIcon({
path: {
"19": "img/icon_count19.png",
"38": "img/icon_count38.png"
}
});
}
function count_down() {
count--;
if (count <= 0) {
page_change();
count_stop();
}
}
popup.js
var bg = chrome.extension.getBackgroundPage();
var num = bg.num;
var start_btn = document.count_timer.start_btn;
var count_time = document.getElementById("counter");
document.addEventListener('DOMContentLoaded', function () {
load();
start_btn.addEventListener('click', start_display);
});
function timer_reset() {
timerID = 0;
}
function count_format(num) {
var tm,ts;
tm = Math.floor(num / 60); //分
ts = num % 60; //秒
if (ts < 10) ts = "0" + ts;
return tm + ":" + ts;
}
function load(){
display_num = bg.count_format(bg.default_count);
bg.timer_reset();
start_btn.disabled = false;
count_time.innerHTML = display_num;
}
function start_display() {
start_btn.disabled = true;
bg.count_start();
timerID = setInterval(function() {bg.count_down();count_time.innerHTML = bg.count;}, 1000);
}
popup.html
<form name="count_timer" class="count_timer">
<div id="counter" class="counter"></div>
<input type="button" name="start_btn" value="START">
</form>
Thanks.
use chrome storage / localStorage to remember your target time.
Use an interval from the popup to update your calculations. First time, the popup needs to read from storage and determine where the countdown is at.
Nothing needs to be done from background, unless you want to separate logic (as in MVC), then maybe do all storage stuff from background, and ask for it with messaging.
I have a published open-source chrome extension (plus for trello) with a count(up) timer feature that does something similar.

Categories