HTML audio can't set currentTime - javascript

I am using Chrome. In my dev tools console, I tried the following:
Everything works as expected except last line. Why can't I set currentTime on it?
Also in general, I am finding this whole HTML5 Audio thing to not be very reliable. Is there a robust javascript wrapper that fallsback to flash ?

You need to do something like this (if you use jQuery)
$('#elem_audio').bind('canplay', function() {
this.currentTime = 10;
});
or in Javascript
var aud = document.getElementById("elem_audio");
aud.oncanplay = function() {
aud.currentTime = 10;
};
The reason behind for this setup is you need to make sure the audio is ready to play.

Check your HTTP server configuration, in my testing environment ( Chrome 69 on Mac OS) setting currentTime property of audio element works only when the audio source is served by a HTTP server support persistent connection.
If the HTTP server you used support persistent connection, you will found (in Chrome DevTool) the Connection field of response headers of your audio source be keep-alive. By contrast if the audio source is served by a persistent connection incompatible server, there will be no Connection field in response headers.
The status code of your audio source HTTP request will be a reference too, 206 Partial Content for persistent connection supported server, 200 OK for an inferior one.

I had the same problem, and the reason was missing headers on the mp3 file, like:
Content-Length, Content-Range, Content-Type

Why cant I set currentTime on it?
Could not reproduce currentTime being set to 0 after setting to 10. Is 18.mp3 duration less than 10?
var request = new XMLHttpRequest();
request.open("GET", "/assets/audio/18.mp3", true);
request.responseType = "blob";
request.onload = function() {
if (this.status == 200) {
var audio = new Audio(URL.createObjectURL(this.response));
audio.load();
audio.currentTime = 10;
audio.play();
}
}
request.send();
jsfiddle http://jsfiddle.net/Lg5L4qso/3/

I ran into this problem after I'd started to use the PHP Server extension to VS Code to serve my HTML/PHP local testing. The problem was resolved for me by going back to my old Abysswebserver setup.
So, it's not simply that "you can't manipulate .currentTime on locally served files" but rather that "you need to pick a server that gives you the right headers". The Status entry for my file from AbyssWebserver, for example, read "Status Code: 206 Partial Content" while the one from PHP Server read "Status Code: 200 OK".
There are other differences though so there may be more to this than just the Status entry. See https://github.com/brapifra/vscode-phpserver/issues/85, for a full header comparison.

If you want to return to the beginning of the audio after it has been played, use load().
// x.currentTime = 0;
x.load();
( https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/load )

this worked for me:
var callaudio = audio; // the audio variable
function fetchAudioFile() {
var requestObj = new Request(callaudio.src, {
method: 'GET',
headers: {
'Accept-Ranges': '1000000000'
},
referrerPolicy: 'no-referrer'
});
fetch(requestObj).then(function (response) {
returnresponse;
}).then(async function (outcome) {
const blob = await outcome.blob();
const url = window.URL.createObjectURL(blob);
callaudio.src = url;
});
};
fetchAudioFile();

The only solution for setting currentTime I got to work reliably was using the onprogress event.
audio.onprogress = function() {
if (audio.currentTime == 0) {
audio.currentTime = 10;
}
}

The solution which worked for me was not setting "src" on directly, but use with type attribute, maybe type attribute is helping browser some way.

My guess is that '10' is longer that you mp3's length.
But that logs the length of mp3 instead of '0'.

Related

Detect 404 on video blob:https source

I am using blob:https as source for my video-tags, like this:
function mk_bloburl(source_id, url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob'; //important
xhr.onload = function(e) {
if (this.status == 200) {
var blob = this.response;
var source = document.getElementById(source_id);
var video = source;
if (video.tagName=="SOURCE") {
video = source.parentNode;
}
video.oncanplaythrough = function() {
URL.revokeObjectURL(source.src);
};
source.src = URL.createObjectURL(blob);
video.load();
}
};
xhr.send();
}
mk_bloburl('review-video-source', my_video_url );
Having HTML like this:
<video id="review-video" controls="" width="100%">
<source id="review-video-source" />
</video>
Now if I reload the page and start playing, it works. If I play and let it play through and then replay with no wait-time it works.
If I however reload the page and then wait for a while (like 1-2 minutes) and then press play, it fails.
Message I get in the Chrome browser looks like this:
GET blob:https://example.com/debeecfe-49b0-4c98-87d6-8ead325b2d75 404 (Not Found)
So, it's like the blob is auto-removed from the browser memory after a while. I want to catch the event when it is erased or when I get the 404 by starting the playback, so I can refresh the blob.
I have tried:
var source = document.querySelector("#review-video-source");
source.addEventListener("error", function(event) {
console.debug("An error accoured");
});
But this does not seem to catch the error.
What can I do?
Your main problem is caused by the fact you revoked the blobURI in the canplaythrough event.
The canplaythrough event is just a notice sent by the browser to let us know it thinks it will be able to load and play the whole media without interruption ; it doesn't mean that it has loaded everything yet.
In the case of BlobURI, the connection speed is so fast (it comes from memory) that the browser could think it is able to fetch the StarWars saga in 4k in a blink.
So you get this canplaythrough event really early, but the browser didn't actually uncompressed all the data yet. Still, you revoke the BlobURI, and when the browser tries again to fetch data so it can uncompress and read it, there is nothing anymore at the end of the BlobURI's pointer.
So for your problem, you've got two solutions :
In case you need to play the media only once :
call URL.revokeObjectURL(blobURI) in the ended event. This will fire, the first time the currentTime of the video will reach the end.
If you need to play the video multiple times :
revoke the blobURI in the beforeunload event of the page. This way, your BlobURI pointer is always active for as long as the page is alive, but will not block the whole Blob in memory for longer than the page life (which would happen if you don't revoke the BlobURI at all).
And about how to Detect 404 on video blob:https source, I don't really know a good way, except listening for an unexpected jump to the end, but this should not be needed for blobURIs anyway.
One way to get around it is using the fetch API like so :
function mk_bloburl(source_id, url){
fetch(url)//usual get
.then(response=>{
if(response.status == 404)
//Should set here what is an error (ex: x>299)
Promise.reject("Error 404!");
return response.blob();
})//get blob
.then(blobObj=>{//use blob
var blob = blobObj;
var source = focument.getElementById(source_id);
var video = source;
if(video.tagName === "SOURCE")
video = source.parentNode;
video.oncanplaythrough = ()=>{
URL.revokeObjectUrl(source.src);
};
source.src = URL.revokeObjectURL(blob);
video.load();
})
.catch(error=>{
//There has been an error,
//do something about it
//here
});
}

How to check if the user is online using javascript or any library?

I need some help on how I could check the internet connection using Javascript or jQuery or any library if available. cause i'm developing an offline application and I want to show a version if the user is offline and another version if the user is online.
For the moment i'm using this code :
if (navigator.onLine) {
alert('online');
} else {
alert('offline');
}
But this is working very slow to detect. sometimes it's just connected to a network without internet, it takes 5 to 10 seconds to alert false (No internet).
I took a look at Offline.js library, but I'm not sure if this library is useful in my case. and I don't know how to use it
I just got this bit of code functionality from a Mozilla Site:
window.addEventListener('load', function(e) {
if (navigator.onLine) {
console.log('We\'re online!');
} else {
console.log('We\'re offline...');
}
}, false);
window.addEventListener('online', function(e) {
console.log('And we\'re back :).');
}, false);
window.addEventListener('offline', function(e) {
console.log('Connection is down.');
}, false);
They even have a link to see it working. I tried it in IE, Firefox and Chrome. Chrome appeared the slowest but it was only about half a second.
i think you should try OFFLINE.js.. it looks pretty easy to use, just give it a try.
it even provides the option checkOnLoad which checks the connection immediately on page load.
Offline.check(): Check the current status of the connection.
Offline.state: The current state of the connection 'up' or 'down'
haven't tried it, would be nice to know if it works as intended.
EDIT took a little peak into the code, it uses the method with FAILED XHR REQUEST suggested in THIS SO Question
Take a look at Detect that the Internet connection is offline? Basically, make an ajax request to something you know is likely to be up (say google.com) and if it fails, there is no internet connection.
navigator.onLine is a property that maintains a true/false value (true for online, false for offline). This property is updated whenever the user switches into "Offline Mode".
window.addEventListener('load', function() {
function updateOnlineStatus(event) {
document.body.setAttribute("data-online", navigator.onLine);
}
updateOnlineStatus();
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
});
// check if online/offline
// http://www.kirupa.com/html5/check_if_internet_connection_exists_in_javascript.htm
function doesConnectionExist() {
var xhr = new XMLHttpRequest();
var file = "http://www.yoursite.com/somefile.png";
var randomNum = Math.round(Math.random() * 10000);
xhr.open('HEAD', file + "?rand=" + randomNum, false);
try {
xhr.send();
if (xhr.status >= 200 && xhr.status < 304) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
My solution is to grab a very small image (1x1), not cached and always onLine.
<head>
<script src="jquery.min.js"></script>
</head>
<body>
<script>
$( document ).ready(function() {
function onLine() {
alert("onLine")
}
function offLine() {
alert("offLine")
}
var i = new Image();
i.onload = onLine;
i.onerror = offLine;
i.src = 'http://www.google-analytics.com/__utm.gif';
});
</script>
<body>
Notes:
Use a local copy of jQuery otherwise it won't work offLine.
I've tested the code onLine/offLine and it works without delay.
Works with all browsers, Desktop or Mobile.
In case you wonder, there's no tracking made from Google Analytics as we don't use any arguments.
Feel free to change the image, just make sure it doesn't get cached and it's small in size.
Try utilizing WebRTC , see diafygi/webrtc-ips; in part
Additionally, these STUN requests are made outside of the normal
XMLHttpRequest procedure, so they are not visible in the developer
console or able to be blocked by plugins such as AdBlockPlus or
Ghostery. This makes these types of requests available for online
tracking if an advertiser sets up a STUN server with a wildcard
domain.
modified minimally to log "online" or "offline" at console
// https://github.com/diafygi/webrtc-ips
function online(callback){
//compatibility for firefox and chrome
var RTCPeerConnection = window.RTCPeerConnection
|| window.mozRTCPeerConnection
|| window.webkitRTCPeerConnection;
var useWebKit = !!window.webkitRTCPeerConnection;
//bypass naive webrtc blocking using an iframe
if(!RTCPeerConnection) {
//NOTE: you need to have an iframe in the page
// right above the script tag
//
//<iframe id="iframe" sandbox="allow-same-origin" style="display: none"></iframe>
//<script>...getIPs called in here...
//
var win = iframe.contentWindow;
RTCPeerConnection = win.RTCPeerConnection
|| win.mozRTCPeerConnection
|| win.webkitRTCPeerConnection;
useWebKit = !!win.webkitRTCPeerConnection;
}
//minimal requirements for data connection
var mediaConstraints = {
optional: [{RtpDataChannels: true}]
};
//firefox already has a default stun server in about:config
// media.peerconnection.default_iceservers =
// [{"url": "stun:stun.services.mozilla.com"}]
var servers = undefined;
//add same stun server for chrome
if(useWebKit)
servers = {iceServers: [{urls: "stun:stun.services.mozilla.com"}]};
//construct a new RTCPeerConnection
var pc = new RTCPeerConnection(servers, mediaConstraints);
//create a bogus data channel
pc.createDataChannel("");
var fn = function() {};
//create an offer sdp
pc.createOffer(function(result){
//trigger the stun server request
pc.setLocalDescription(result, fn, fn);
}, fn);
//wait for a while to let everything done
setTimeout(function(){
//read candidate info from local description
var lines = pc.localDescription.sdp.split("\n");
// return `true`:"online" , or `false`:"offline"
var res = lines.some(function(line) {
return line.indexOf("a=candidate") === 0
});
callback(res);
}, 500);
}
//Test: Print "online" or "offline" into the console
online(function(connection) {
if (connection) {
console.log("online")
} else {
console.log("offline")
}
});
You can use SignalR, if you're developing using MS web technologies. SignalR will establish either long polling or web sockets depending on your server/client browser technology, transparent to you the developer. You don't need to use it for anything else than determining if you have an active connection to the site or not.
If SignalR disconnects for any reason, then you have lost connection to the site, as long as your SignalR server instance is actually installed on the site. Thus, you can use $.connection.hub.disconnected() event/method on the client to set a global var which holds your connection status.
Read up about SignalR and how to use it for determining connection states here...
http://www.asp.net/signalr/overview/guide-to-the-api/handling-connection-lifetime-events#clientdisconnect
See How do I check connection type (WiFi/LAN/WWAN) using HTML5/JavaScript? answers:
Rob W suggests navigator.connection;
Bergi suggests Windows.Networking.Connectivity API through this tutorial;
Gerard Sexton suggests Gmail approach.
You can use the new Fetch API which will trigger an error almost immediately if no network is present.
The problem with this is that the Fetch API has infant support at the moment (currently Chrome has the most stable implementation, Firefox and Opera is getting there, IE does not support it). There exists a polyfill to support the fetch principle but not necessarily the rapid return as with a pure implementation. On the other hand, an offline app would require a modern browser...
An example which will try to load a plain text file over HTTPS to avoid CORS requirements (link is picked at random, you should set up a server with a tiny text file to test against - test in Chrome, for now):
fetch("https://link.to/some/testfile")
.then(function(response) {
if (response.status !== 200) { // add more checks here, ie. 30x etc.
alert("Not available"); // could be server errors
}
else
alert("OK");
})
.catch(function(err) {
alert("No network"); // likely network errors (incl. no connection)
});
Another option is to set up a Service worker and use fetch from there. This way you could serve an optional/custom offline page or a cached page when the requested page is not available. Also this is a very fresh API.
best one liner
console.log(navigator.onLine ? 'online' : 'offline');

Server doesn't respond to an xmlHTTP request using the get method

I'm doing a project with arduino in which I send different requests to the server (the arduino board) with the method XMLHttprequest and Get from a webpage. Except one of the request the others are used only for sending orders to the server, so I don't expect for an XML response. The other one is a request sent in intervals of 5 seconds for getting different values from the server.
The problem arrives with this last one. Actually the webpage sends the request (because I see it on the browser console and the arduino serial monitor) every 5 seconds, but it doesn't get anything, just the headers of the answer confirming the response but nothing about the XML file. Surprisingly, when I write a normal request using the get method in the browser I get instantly the XML file with the values, and It happens all the time I do that.
I'm going to write the javascript code I'm using on the webpage
setInterval(function tiempo()
{
var request = new XMLHttpRequest();
request.onreadystatechange = function()
{
if (this.readyState == 4) {
if (this.status == 200) {
if (this.responseXML != null) {
// extract XML data from XML file (containing switch states and analog value)
document.getElementById("input1").innerHTML = this.responseXML.getElementsByTagName('dato')[0].childNodes[0].nodeValue;
document.getElementById("input2").innerHTML = this.responseXML.getElementsByTagName('dato')[1].childNodes[0].nodeValue;
document.getElementById("input3").innerHTML = this.responseXML.getElementsByTagName('dato')[2].childNodes[0].nodeValue;
document.getElementById("input4").innerHTML = this.responseXML.getElementsByTagName('dato')[3].childNodes[0].nodeValue;
document.getElementById("input5").innerHTML = this.responseXML.getElementsByTagName('dato')[4].childNodes[0].nodeValue;
document.getElementById("input6").innerHTML = this.responseXML.getElementsByTagName('dato')[5].childNodes[0].nodeValue;
document.getElementById("input7").innerHTML = this.responseXML.getElementsByTagName('dato')[6].childNodes[0].nodeValue;
}
}
}
}
request.open("GET", "URL" + Math.random(), true);
request.send(null);
}
, 5000);
On the other hand, if I only write in the browser URL, I get the XML without any problem.
One las thing I have to say is that right now I'm using a webpage stored in my computer but before I was using a webpage stored in the arduino (on an SD card) and loaded also through the internet from arduino. The same code in that case worked perfectly. The reason because I changed It is because arduino ethernet is not too fast and It took so much time. With the webpage stored in my computer It goes faster because It only needs to send the orders.
Thanks!!
Finally, I figured out the problem. It is the browser. For any reason only Internet Explorer works correctly with the webpage. Neither firefox nor other web browsers got the xml file. I don't know the reason but I would like to find it.
If someone knows something about I would be glad of trying to resolve the problem.
Thanks!!

XMLHttpRequest in NodeWebkit, status = 0

This works in Chrome when doing it on my local server. However, when I transfer over to NodeWebkit, it fails with status === 0.
function ReadText(filename) {
var txtFile = new XMLHttpRequest();
txtFile.open("GET", filename, true);
txtFile.onreadystatechange = function () {
if (txtFile.readyState === 4) // Makes sure the document is ready to parse.
{
if (txtFile.status === 200) // Makes sure it's found the file.
{
g_FileLoadContents = txtFile.responseText;
ReadFile();
}
}
}
txtFile.send(null);
};
g_FileLoadContents is a global and ReadFile is a function that does some work on g_FileLoadContents... but it does not get that far in the NodeWebkit (Again, I'll stress all is ok in Chrome when on my local server).
In NodeWebkit I watch txtFile.readyState change up to 4, but then txtFile.status is 0.
Why is the status 0? When I use the nodeWebkit, should I just let the status be 0 in my code above?
I hope someone can explain, as I am very confused.
HTTP status codes are returned by webservers. Presumedly your local server returns 200 when you're doing this in Chrome, but node-webkit just returns 0 (Unknown?).
Normally reading local files is restricted though. Does the code above actually produce the file contents? Even so, if you're trying to read files in node-webkit I would suggest using the node fs module to access the filesystem directly.

How to get progress from XMLHttpRequest

Is it possible to get the progress of an XMLHttpRequest (bytes uploaded, bytes downloaded)?
This would be useful to show a progress bar when the user is uploading a large file. The standard API doesn't seem to support it, but maybe there's some non-standard extension in any of the browsers out there? It seems like a pretty obvious feature to have after all, since the client knows how many bytes were uploaded/downloaded.
note: I'm aware of the "poll the server for progress" alternative (it's what I'm doing right now). the main problem with this (other than the complicated server-side code) is that typically, while uploading a big file, the user's connection is completely hosed, because most ISPs offer poor upstream. So making extra requests is not as responsive as I'd hoped. I was hoping there'd be a way (maybe non-standard) to get this information, which the browser has at all times.
For the bytes uploaded it is quite easy. Just monitor the xhr.upload.onprogress event. The browser knows the size of the files it has to upload and the size of the uploaded data, so it can provide the progress info.
For the bytes downloaded (when getting the info with xhr.responseText), it is a little bit more difficult, because the browser doesn't know how many bytes will be sent in the server request. The only thing that the browser knows in this case is the size of the bytes it is receiving.
There is a solution for this, it's sufficient to set a Content-Length header on the server script, in order to get the total size of the bytes the browser is going to receive.
For more go to https://developer.mozilla.org/en/Using_XMLHttpRequest .
Example:
My server script reads a zip file (it takes 5 seconds):
$filesize=filesize('test.zip');
header("Content-Length: " . $filesize); // set header length
// if the headers is not set then the evt.loaded will be 0
readfile('test.zip');
exit 0;
Now I can monitor the download process of the server script, because I know it's total length:
function updateProgress(evt)
{
if (evt.lengthComputable)
{ // evt.loaded the bytes the browser received
// evt.total the total bytes set by the header
// jQuery UI progress bar to show the progress on screen
var percentComplete = (evt.loaded / evt.total) * 100;
$('#progressbar').progressbar( "option", "value", percentComplete );
}
}
function sendreq(evt)
{
var req = new XMLHttpRequest();
$('#progressbar').progressbar();
req.onprogress = updateProgress;
req.open('GET', 'test.php', true);
req.onreadystatechange = function (aEvt) {
if (req.readyState == 4)
{
//run any callback here
}
};
req.send();
}
Firefox supports XHR download progress events.
EDIT 2021-07-08 10:30 PDT
The above link is dead. Doing a search on the Mozilla WebDev site turned up the following link:
https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent
It describes how to use the progress event with XMLHttpRequest and provides an example. I've included the example below:
var progressBar = document.getElementById("p"),
client = new XMLHttpRequest()
client.open("GET", "magical-unicorns")
client.onprogress = function(pe) {
if(pe.lengthComputable) {
progressBar.max = pe.total
progressBar.value = pe.loaded
}
}
client.onloadend = function(pe) {
progressBar.value = pe.loaded
}
client.send()
I also found this link as well which is what I think the original link pointed to.
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/progress_event
One of the most promising approaches seems to be opening a second communication channel back to the server to ask it how much of the transfer has been completed.
For the total uploaded there doesn't seem to be a way to handle that, but there's something similar to what you want for download. Once readyState is 3, you can periodically query responseText to get all the content downloaded so far as a String (this doesn't work in IE), up until all of it is available at which point it will transition to readyState 4. The total bytes downloaded at any given time will be equal to the total bytes in the string stored in responseText.
For a all or nothing approach to the upload question, since you have to pass a string for upload (and it's possible to determine the total bytes of that) the total bytes sent for readyState 0 and 1 will be 0, and the total for readyState 2 will be the total bytes in the string you passed in. The total bytes both sent and received in readyState 3 and 4 will be the sum of the bytes in the original string plus the total bytes in responseText.
<!DOCTYPE html>
<html>
<body>
<p id="demo">result</p>
<button type="button" onclick="get_post_ajax();">Change Content</button>
<script type="text/javascript">
function update_progress(e)
{
if (e.lengthComputable)
{
var percentage = Math.round((e.loaded/e.total)*100);
console.log("percent " + percentage + '%' );
}
else
{
console.log("Unable to compute progress information since the total size is unknown");
}
}
function transfer_complete(e){console.log("The transfer is complete.");}
function transfer_failed(e){console.log("An error occurred while transferring the file.");}
function transfer_canceled(e){console.log("The transfer has been canceled by the user.");}
function get_post_ajax()
{
var xhttp;
if (window.XMLHttpRequest){xhttp = new XMLHttpRequest();}//code for modern browsers}
else{xhttp = new ActiveXObject("Microsoft.XMLHTTP");}// code for IE6, IE5
xhttp.onprogress = update_progress;
xhttp.addEventListener("load", transfer_complete, false);
xhttp.addEventListener("error", transfer_failed, false);
xhttp.addEventListener("abort", transfer_canceled, false);
xhttp.onreadystatechange = function()
{
if (xhttp.readyState == 4 && xhttp.status == 200)
{
document.getElementById("demo").innerHTML = xhttp.responseText;
}
};
xhttp.open("GET", "http://it-tu.com/ajax_test.php", true);
xhttp.send();
}
</script>
</body>
</html>
If you have access to your apache install and trust third-party code, you can use the apache upload progress module (if you use apache; there's also a nginx upload progress module).
Otherwise, you'd have to write a script that you can hit out of band to request the status of the file (checking the filesize of the tmp file for instance).
There's some work going on in firefox 3 I believe to add upload progress support to the browser, but that's not going to get into all the browsers and be widely adopted for a while (more's the pity).
The only way to do that with pure javascript is to implement some kind of polling mechanism.
You will need to send ajax requests at fixed intervals (each 5 seconds for example) to get the number of bytes received by the server.
A more efficient way would be to use flash. The flex component FileReference dispatchs periodically a 'progress' event holding the number of bytes already uploaded.
If you need to stick with javascript, bridges are available between actionscript and javascript.
The good news is that this work has been already done for you :)
swfupload
This library allows to register a javascript handler on the flash progress event.
This solution has the hudge advantage of not requiring aditionnal resources on the server side.

Categories

Resources