I think that I found a bug on JavaScript Audio API.
My code is working perfectly like that :
var audio=new Audio();
var lastPause;
var myPlayer=document.querySelector('#player>.button.hand');
var playerStatus=("textContent" in document)?"textContent":"innerText";
myPlayer.addEventListener
(
'click',
function()
{
if(audio.paused)
{
audio.src="Nadav Guedj - Golden Boy (Eurovision 2015 - Israel - Karaoke).mp3";
myPlayer.innerHTML="►";
if(lastPause)
{
alert(lastPause); //If I remove this line it's not working when lastPause is defined!
audio.currentTime=lastPause;
}
audio.play();
}
else
{
myPlayer.innerHTML="||";
lastPause=audio.currentTime;
audio.pause();
}
}
);
BUT
If I remove or remark the alert line alert(lastPause); the audio stuck when lastPause is defined or iow... The second time the user press play (after pause).
I am assuming this question is related to this question my answer there is applicable for this also,
repeated setting audio.src is your problem. you have two ways of fixing this:
get rid of audio.src line, bunch of other redundant code could also be removed with it.
the reason it works when you use alert is, when alert is triggered, the next few lines are not executed till you dismiss it, in these precious few seconds/ milliseconds, the audio element whose source has been changed has enough time to load the new metadata, only then can the current time can be set. so you can modify the code to trigger setting of audio on metadata loading:
...
myPlayer.innerHTML="►";
audio.play();
audio.onloadedmetadata = function(){
if(lastPause){
audio.currentTime = lastPause;
}
};
fiddle demo
Related
I have a Chrome extension in which I'm trying to jump forward or backward (based on a user command) to a specific time in the video by setting the currentTime property of the video object. Before trying to set currentTime, a variety of operations work just fine. For example:
document.getElementsByTagName("video")[1].play(); // works fine
document.getElementsByTagName("video")[1].pause(); // works fine
document.getElementsByTagName("video")[1].muted = true; // works fine
document.getElementsByTagName("video")[1].muted = false; // works fine
BUT as soon as I try to jump to a specific point in the video by doing something like this:
document.getElementsByTagName("video")[1].currentTime = 500; // doesn't work
No errors are thrown, the video pauses, and any attempted actions after this point do nothing. So the items shown above (play/pause/mute/unmute) no longer work after attempting to set currentTime. If I read the value of currentTime after setting it, it correctly displays the new time that I just set it to. Yet nothing I do will make it play, and in fact even trying to make the video play by clicking the built-in toolbar no longer works. So, apparently setting currentTime wreaks all kinds of havoc in the video player. Yet if I reload the video, all works as before as long as I don't try to set currentTime.
I can easily jump to various times (backward or forward) by sliding the slider on the toolbar, so there must be some way internally to do that. Is there some way I can discover what code does a successful time jump? Because it's a Chrome extension I can inject custom js into the executing Hulu js, but I don't know what command I would send.
Any ideas?
Okay I fiddled around with it for a little while to see how I could reproduce the click event on the player and came up with the following solution:
handleViewer = function(){
var thumbnailMarker = $('.thumbnail-marker'),
progressBarTotal = thumbnailMarker.parent(),
controlsBar = $('.controls-bar'),
videoPlayer = $('#content-video-player');
var init = function(){
thumbnailMarker = $('.thumbnail-marker');
progressBarTotal = thumbnailMarker.parent();
controlsBar = $('.controls-bar');
videoPlayer = $('#content-video-player');
},
check = function(){
if(!thumbnailMarker || !thumbnailMarker.length){
init();
}
},
show = function(){
thumbnailMarker.show();
progressBarTotal.show();
controlsBar.show();
},
hide = function(){
controlsBar.hide();
},
getProgressBarWidth = function(){
return progressBarTotal[0].offsetWidth;
};
return {
goToTime: function(time){
var seekPercentage,
duration;
check();
duration = videoPlayer[0].duration;
if(time > 0 && time < duration){
seekPercentage = time/duration;
this.jumpToPercentage(seekPercentage);
}
},
jumpToPercentage: function(percentage){
check();
if(percentage >= 1 && percentage <= 100){
percentage = percentage/100;
}
if(percentage >= 0 && percentage < 1){
show();
thumbnailMarker[0].style.left = (getProgressBarWidth()*percentage)+"px";
thumbnailMarker[0].click();
hide();
}
}
}
}();
Once that code is initialized you can do the following:
handleViewer.goToTime(500);
Alternatively
handleViewer.jumpToPercentage(50);
I've tested this in chrome on a MacBook pro. Let me know if you run into any issues.
Rather than try to find the javascript responsible for changing the time, why not try to simulate the user events that cause the time to change?
Figure out the exact sequence of mouse events that trigger the time change.
This is probably some combination of mouseover, mousedown, mouseup, and click.
Then recreate those events synthetically and dispatch them to the appropriate elements.
This is the approach taken by extensions like Stream Keys and Vimium.
The video should be ready to play before setting the currentTime.
Try adding this line before setting currentTime?
document.getElementsByTagName("video")[1].play();
document.getElementsByTagName("video")[1].currentTime = 500;
Looks like it works if you first pause, then set currentTime, then play again.
document.getElementsByTagName("video")[1].pause()
document.getElementsByTagName("video")[1].currentTime = 800.000000
document.getElementsByTagName("video")[1].play()
Probably would need to hook into some event like onseeked to put in the play command to make it more robust.
I get a headache trying to understand why my script is not playing the sounds like i want to.
I have a quite simple HTML/Javascript animation with several sounds into it, executed on user's interaction (swipe, click, validate...).
Here is the url if you want to test and see the complete source :
http://artcode.fr/apps/bioprojet/simulation/index.html
The issues are at step 4 & 5 :
Step 4, clicking on the red button makes it disappear, execute an "injection sound", plays "validate sound" and then goes to step 5 where there should be a clock sound executed each second during the countdown to 10.
This regular behavior is ok in desktop browser (Safari/Chrome), but when launched in Safari mobile ou Chrome mobile, the sounds on step 4 are not always played (sometimes even inverted), and the clock sound on step 5 is never played.
I tried a lot of things to make it work, with no success, as :
"currenTime" and "pause()"
"onended" event, but doesn't seem to work on mobile
different sounds, to see if the issue came from this (answer is no)
inverted places
Here is the part of the code where the mess is :
if(e==4){
$("#voir_etape_3").addClass("voir_etape_on");
$("#bouchon_rouge").click(function(){
clic.play();
$("#anim_clic").animate({ top: "110px" }, { duration:50, complete: function(){
var pauseclic = setInterval(function(){
clearInterval(pauseclic);
valide.play();
$(".etape").hide();
$("#etape_5").show();
actions(5);
},2000);
}});
});
}
if(e==5){
$("#voir_etape_4").addClass("voir_etape_on");
var t=0;
var decompte = setInterval(function(){
t++;
if (t<=10){
tic.play();
$("#decompte").text(t);
if (t==10){ $("#decompte").addClass("decompte_on"); }
} else{
$("#decompte").addClass("decompte_on");
if (t==11){
clearInterval(decompte);
tictactic.pause();
//tictactic.currentTime = 0;
valide.play();
$(".etape").hide();
$("#etape_6").show();
actions(6);
}
}
}, 1000);
}
Sounds are defined at the beginning of my javascript script :
var valide = new Audio('./graph/valide.mp3');
var tic = new Audio('./graph/tictac.mp3');
var tictactic = new Audio('./graph/tictactic.mp3');
var clic = new Audio('./graph/clic.mp3');
I'm getting the audio/video duration of a file without appending it to the screen. "Using the same code", when I try to get the video duration on both sides it works as expected. But when using audio files it says that the duration is 0 on Android, but it works on a desktop computer.
// Only working on Desktop
var audio = new Audio(url);
// Hide audio player
// player.appendChild(audio);
audio.addEventListener('loadedmetadata', function() {
alert(audio.duration);
});
And the below code is working:
// Working on Desktop and Android
var video = document.createElement('video');
video.src = url;
// Hide video
// player.appendChild(video);
video.addEventListener('loadedmetadata', function() {
alert(video.duration);
});
There is a different approach you can try but, if duration doesn't work with your device (which IMO is a bug) then it's likely this doesn't either; worth a shot though:
audio.seekable.end(audio.seekable.length-1);
or even
audio.buffered.end(audio.buffered.length-1);
though the latter is dependent on content being loaded which in this case probably then won't help.
EDIT: Using the durationchange event is much easier. First the 0 is being output, but as soon as the file is loaded (that's where loadedmetadata fails I guess) the updated and real duration will be output.
audio.addEventListener('durationchange', function(e) {
console.log(e.target.duration); //FIRST 0, THEN REAL DURATION
});
OLD WAY (ABOVE IS MUCH FASTER)
Looks like this "bug" (if this is actually a real bug) is still around. Chrome (40) for Android still outputs 0 as the audio files duration. Researching the web didn't get me a solution but I found out the bug also occurs on iOS. I figured I should post my fix here for you guys.
While audio.duration outputs 0, logging audio outputs the object and you can see that the duration is displayed just right there. All this is happening in the loadedmetadata event.
audio.addEventListener('loadedmetadata', function(e) {
console.log(e.target.duration); //0
});
If you log audio.duration in the timeupdate event though, the real duration is being output. To only output it once you could do something like:
var fix = true;
audio.addEventListener('timeupdate', function(e) {
if(fix === true) {
console.log(e.target.duration); //REAL DURATION
fix = false;
}
console.log(e.target.currentTime); //UPDATED TIME POSITION
});
I'm not sure why all this is happening. But let's be happy it's nothing serious.
sounds["foo"]= new Audio("foo.ogg");
function playSound(id){
var snd = this.sounds[id];
if(snd.currentTime>snd.duration*0.99999){
snd.pause();
snd.currentTime=0;
//snd.seekable.start(); //doesnt work
//snd.load(); does the trick but with the cost re-downloading the clip every single time
snd.play();
}
if(snd.currentTime==0)
snd.play();
}
For some reason playSound('foo'); works on the first time, but fails after that on Chrome (works just fine on Firefox). Adding snd.load() seemed to fix this, but now it downloads the clip from the server every time the clip is played, which is a lot in my use case.
EDIT: Oh, and snd.currentTime seems to get stuck at the end, so snd.currentTime=0 does nothing.
The following script works just fine for me on OSX, Chrome Version 32.0.1700.102 and Firefox 27.0.
If you do not want to loop the file immediately, your script definitely is the right approach.
I couldn't reproduce your issues. I associated the method with an onclick action on a link and used a 5s .ogg file for testing. Clicking the link and re-playing the audio file worked for me all the time.
The only possible issue I noticed is your second if clause. If the first if is matched, the time is set to 0 and the file will be played. Even if it is very unlikely in my opinion, it could be that the second if is also matched and snd.play() is invoked a second time. I therefore would use else if.
Can you give it a try? Even though I suppose it does not solve your problem :/
<script type="text/javascript">
var sounds = [];
sounds["foo"]= new Audio("foo.ogg");
/* First solution, creating a loop */
function playSoundLoop(id){
var snd = this.sounds[id];
snd.play();
snd.addEventListener('ended', function(){
this.currentTime = 0;
this.play();
}, false);
}
/* Use else if instead of if */
function playSound(id){
var snd = this.sounds[id];
if(snd.currentTime>snd.duration*0.99999){
snd.pause();
snd.currentTime=0;
snd.play();
} else if(snd.currentTime==0) {
snd.play();
}
}
</script>
I'm trying to control html5 video with javascript. What I want is that when the user clicks on a button, the video will jump to another frame and keep playing from there. With my current code the playback always stops after the seek.
function jumpToTime(){
var video = $("video")[0];
video.currentTime = 160;
document.getElementbyId("videoclip").play(); //not working
};
//the following code is probably not really related to the question:
var endtimeoffset = 2000;
video.addEventListener("timeupdate", function() {
if (video.currentTime >= endtimeoffset) {
video.pause();
}
}, false);
I ran into a similar problem, and found a solution by pausing the video, then setting the currentTime, then playing the video. To update your code:
function jumpToTime(){
var video = $("video")[0];
video.pause();
video.currentTime = 160;
video.play();
};
Some things I would try:
in the jumpToTime() function, you have two different references to supposedly the same video (one obtained through jQuery and the other by getElementById()). Are you sure these reference the same video? To be safe, I would just call play() on the 'video' reference that you set the currentTime on.
This is probably a copy and paste issue since the console would complain if this was in the actual code, but you did mispell getElementById() (Need to capitalize the B).
For debugging purposes, I would comment out the 'timeupdate' event code, to make sure this isn't the issue and that this code isn't pausing the video after you update the timehead or call play. It probably isn't, since you are setting the current time to be much less than the offset you are comparing it with. It would, however, be an easy test to eliminate this as a possible cause of the issue.
function jumpToTime(){
document.getElementById("videoclip").currentTime = 160;
document.getElementById("videoclip").play(); //not working
};
getElementbyId --> getElementById -- b --> B
get direct object by id...