I'm struggling to fetch an HTML5 video using xhr2 and blob responseType with Chrome on Android 4.2. The code works perfectly on Chrome and Firefox desktop and on Firefox Android 4.2 (with FF desktop, I use a webm video instead of the mp4).
// Taking care of prefix
window.URL = window.URL || window.webkitURL;
// This function download the video
var loadVideo = function() {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', addVideoFile, false);
xhr.open('GET', "videos/myvideo.mp4" , true);
xhr.responseType = 'blob';
xhr.send();
};
// this function sets the video source
var addVideoFile = function() {
if(4 == this.readyState && 200 == this.status) {
var video = document.getElementById('vid'),
blob = this.response;
video.src = window.URL.createObjectURL(blob);
console.log('video ready');
}
};
loadVideo();
Can anyone explain me why this does not work with Chrome on Android? If I plug my phone to use the remote debugging, the console will display 'video ready', suggesting that the video was downloaded but it's impossible to play it, video is just a black screen.
Also, this code works if I use it to fetch images instead of video. Is there a limitation I'm not aware of, preventing to download Blob above a certain size? (My video is 1.5 MB).
Thanks you very much for your help!
This is most certainly a bug. If you get something that works on Desktop Chrome but not Android then 99.5% of the time it is an issue we need to fix.
I have replicated your issue http://jsbin.com/uyehun/1 and I have filed the bug too https://code.google.com/p/chromium/issues/detail?id=253465
Per http://caniuse.com/bloburls, for Android 4.0-4.3 you need to use window.webkitURL.createObjectUrl() instead of window.URL.createObjectUrl().
This will let you generate a blob url, though I haven't actually been able to get a video element to play such an url.
Related
Im playing a video that weights 31.6MB on a website. The video plays alright on the latest versions of chrome in desktop, but on mobile the frames seems to lag behind the audio, even after looping the video a couple of times, so it should have been loaded completely already.
My best guess is that it's because of the weight of the video. Having played a much smaller file (3MB) and checking that the images was in sync with the audio kind of confirms it. But the fact that I preload the video, and that even if I make it play through more than once the problems persists (after the first play through there shouldn't be anything else to load), makes me believe is something else.
Im leaving the code that I use to preload the file just in case its needed.
HTML
<video id="videoId" src="public/videos/0620_ShakeHandam_VP9.webm" playsinline muted loop="true"></video>
JS
document.addEventListener("DOMContentLoaded", function(event) {
const video = document.getElementById("videoId")
const url = video.dataset.src;
const xhr = new XMLHttpRequest();
const loadingStartedDate = Date.now();
document.body.classList.add("loading");
xhr.open("GET", url, true);
xhr.responseType = "arraybuffer";
xhr.onload = function(oEvent) {
const blob = new Blob([oEvent.target.response], {type: "video/yourvideosmimmetype"});
const loadingTime = Date.now() - loadingStartedDate;
video.src = URL.createObjectURL(blob);
alert(`VIDEO LOADED: ${loadingTime}ms`);
console.log(`Video loaded after ${loadingTime}ms`);
document.body.classList.remove("loading");
document.body.classList.add("loaded");
//video.play() if you want it to play on load
};
xhr.onprogress = function(oEvent) {
if (oEvent.lengthComputable) {
const percentComplete = oEvent.loaded/oEvent.total * 100;
console.log("PROGRESS", percentComplete);
document.getElementById("load-percentage").textContent = `${percentComplete.toFixed(2)}%`;
// do something with this
}
}
xhr.send();
});
EDIT #1
The video in question has a transparent background. After further testing, I believe that it may be the cause of the problem. It doesn't seems to be happening with videos without a transparent background (mp4 or webm)
I'm using file-saver in my angular application to download a PDF generated in my backend. The library generally works fine on desktop and android. But I don't seem to be able to download a file on IOS. file-saver doesn't, as stated in on the GitHub page, open the blob in a new Page either. it jus opens on the same page (not wanted). Funnily enough it works fine in safari (it opens a dialog that asks me to download and then downloads it without opening it). In any other browser (opera, firefox and chrome) it doesn't seem to work.
I've tried file-saver, downloadJ, creating an anchor tag myself together with the download attribute, using the application/octet-stream mime-type and several other solutions posted on the internet. All of these methods in most browsers just doe nothing or open the PDF blob in the same page instead of downloading it or opening it in a new tab (as file-saver states it would do on IOS).
I'm generating the PDF in a Google Cloud Function. Is there maybe a way to skip the whole client side of things and make the browser download the file directly from there?
Does anyone have another idea on how to download PDF's on mobile IOS (e.g. with a service worker or something)?
Thanks in advance
Best solution as per new chrome specification https://developers.google.com/web/updates/2018/02/chrome-65-deprecations
Vanilla JavaScript
public static downloadFile(url: string): void {
const xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = () => {
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
const blobUrl = window.URL.createObjectURL(xmlHttp.response);
const e = document.createElement('a');
e.href = blobUrl;
e.download = blobUrl.substr(blobUrl.lastIndexOf('/') + 1);
document.body.appendChild(e);
e.click();
document.body.removeChild(e);
}
};
xmlHttp.responseType = 'blob';
xmlHttp.open('GET', url, true);
xmlHttp.send(null);
}
If you're using angular try this.
async downloadBrochure(url: string) {
try {
const res = await this.httpClient.get(url, { responseType: 'blob' }).toPromise();
this.downloadFile(res);
} catch (e) {
console.log(e.body.message);
}
}
downloadFile(data) {
const url = window.URL.createObjectURL(data);
const e = document.createElement('a');
e.href = url;
e.download = url.substr(url.lastIndexOf('/') + 1);
document.body.appendChild(e);
e.click();
document.body.removeChild(e);
}
I have a URL, for example this:
https://r6---sn-vgqsrn76.googlevideo.com/videoplayback?expire=1566535969&ei=wRxfXezPAoORV-3ogpgK&ip=185.27.134.50&id=o-ALFdSvuvmX_bqDsm4oRW7q9c4igbKlBmECWdISuA4Jxe&itag=22&source=youtube&requiressl=yes&mime=video%2Fmp4&ratebypass=yes&dur=624.175&lmt=1529213992430932&fvip=6&c=WEB&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cmime%2Cratebypass%2Cdur%2Clmt&sig=ALgxI2wwRAIgZzTTsBPpVznwCvzArBFuSF7Bm3yhcO0rwQdfOjBibnsCIBqf8iHuAwahqi0T6qZ3MNbj8BfLgGo2Y3fPOi96RgEV&redirect_counter=1&cm2rm=sn-aigeey7d&req_id=8f890b1c72fda3ee&cms_redirect=yes&mip=2607:fea8:4d9f:fa68:40a2:35d0:8863:2d17&mm=34&mn=sn-vgqsrn76&ms=ltu&mt=1566514280&mv=m&mvi=5&pl=41&lsparams=mip,mm,mn,ms,mv,mvi,pl&lsig=AHylml4wRQIgSCcxaGd_IpVykCuglJtHwewUuZZIyKKr1FBbNP5MvqsCIQCYQEUoM9SpfpySHA_13lB6SvevIuMvhyFDEcrsX0y0ig==
How can I download the video in this URL programmatically through JavaScript? I cannot use PHP, Apache, JQuery etc, only Pure JavaScript and HTML.
I have tried using download.js, but I do not think that is the right approach to download videos. I have also looked/tried at various other websites and Stack Overflow answers, but none of them fixed this issue.
EDIT: The other SO answer that someone suggested will not work since the video is on a different baseurl than my own, which means that
<a href="file" download="filename">
will not work on Chrome. Doing this just opens the video.
function downloadImage() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://via.placeholder.com/150', true);
xhr.responseType = 'blob';
xhr.onload = function () {
var urlCreator = window.URL || window.webkitURL;
var imageUrl = urlCreator.createObjectURL(this.response);
var tag = document.createElement('a');
tag.href = imageUrl;
tag.target = '_blank';
tag.download = 'sample.png';
document.body.appendChild(tag);
tag.click();
document.body.removeChild(tag);
};
xhr.onerror = err => {
alert('Failed to download picture');
};
xhr.send();
}
I found the solution to the problem. The link was a YouTube source video link that I was trying to download, and on all videos (except the ones with music or the music genre) all you needed to do was to add
&title=[NAME OF FILE HERE]
which would download the video.
Edit: Downloading these videos worked with download.js. You need to make a XMLHTTP request to the video to get the data, and then use the function
download(data, name, mime)
For more documentation, look on the download.js GitHub page.
Hello JavaScript gurus,
I need a file download functionality using XMLHttpRequest (with responseType="blob") that works in Safari 9+.
At the moment I'm using FileSaver.js like this:
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// using FileSaver.js to save blob
saveAs(xhr.response, filename);
// notify download finished, resolve promise
defer.resolve(true);
}
};
xhr.send();
which works fine in all main browsers but not in current version (9.x) of Safari.
I'll get a "Failed to load resource: Frame load interrupted". Usually a download is a zip file but I also tried to set "application/octet-stream".
I have one requirement: I need to know when then download has finished on client-side so using an iframe is no option (I guess).
I'm thankful for any hint how to download a file in Safari using XHR (no Flash).
Thanks,
Chris
Simple answer:
There is no solution!
See also: https://forums.developer.apple.com/message/119222
Thanks Safari ... my new almost IE6
I'm using the WAAPISim Polyfill for cross-browser support in a visualization from an audio file, using the Web Audio API. The polyfill attempts to use the following methods in this order: "WebAudioAPI => AudioDataAPI => Flash". I am loading the audio file like this in JS:
// load the specified sound
function loadSound(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
// When loaded decode the data
request.onload = function() {
// decode the data
context.decodeAudioData(request.response, function(buffer) {
// when the audio is decoded play the sound
playSound(buffer);
}, onError);
}
request.send();
}
loadSound("audio/bird.wav");
As noted in the polyfill's documentation, this polyfill only supports wav format for this method. "createBuffer from ArrayBuffer and decodeAudioData supports only wav format."
Right now, it is only loading a .wav, but I'd like to load a .mp3 (smaller file) instead for browsers that will support it. How can I detect whether the implementation will work with a .mp3 and load the right file accordingly?
Full demo example
If:
(new Audio()).canPlayType("audio/mp3")
returns "maybe" or "probably", then the browser supports mp3 in decodeAudioData.
I got this response from the polyfill developer:
if (typeof window.waapisimContexts != 'undefined'){
loadSound("audio/bird.wav");
} else {
loadSound("audio/bird.mp3");
}
The polyfill creates waapisimContexts only if the browser requires it to play a .wav, so this approach looks for the waapisimContexts and loads the .wav if it is defined. Otherwise, it loads the .mp3.