I have a list of films (buttons) which - when a user clicks - performs an AJAX request to update the video tag's source attribute. Before loading the new video (.load()), the video element is cloned, removed, then re-inserted into the DOM (this is to fix a Safari bug). See code below:
//load in new paths
var contentVideos = $("#projectsMedia video source");
contentVideos.each(function () {
if ($(this).attr("src").lastIndexOf("mp4") !== -1) {
$(this).attr("src", videoPath + ".mp4");
} else if ($(this).attr("src").lastIndexOf("ogv") !== -1) {
$(this).attr("src", videoPath + ".ogv");
} else if ($(this).attr("src").lastIndexOf("webm") !== -1) {
$(this).attr("src", videoPath + ".webm");
}
});
//clone vid, delete, reload for safari bug
var clonedVid = $("#projectsMedia video").clone();
$("#projectsMedia video").remove();
clonedVid.insertAfter($("#projectsMedia h1"));
$("#projectsMedia video")[0].load();
This works fine for all browsers, but Chrome seems to be throwing a spanner into the works. When the new path is put into the src attribute and the video is loaded, Chrome takes anywhere between 2 second to infinity to load the video.
Opening the dev console, I found that the mp4 file is being downloaded multiple times (an apparent feature of Chrome), and the requests are stuck in pending for an indefinite amount of time and the video rarely loads within 10 seconds. See screenshot.
Another curios behaviour in Chrome is that on a page refresh (or button-click to perform a new AJAX request), if the dev console is not open, then opening it will force the mp4 to load, and it works fine.
Does anyone know of a solution to this?
Related
I have registered for a course that has roughly 150 videos.
What I have done Uptil NOW:
There is no download button available right now.
In order to get the URL of each video file, I have created the script which I run through Console as below:
The site where I am watching these videos is different than the xxxxx marked site.
e.g. I am watching on linkedin learning and video is on lynda,etc.
console.log(("<h2>"+ document.title)+"</h2>"
+
" click here ");
document.getElementsByClassName("video-next-button")[0].click();
an example of output from above code is:
<h2>Overview of QGIS features: Learning QGIS (2015)</h2>
<a href="https://files3.xxxxx.com/secure/courses/383524/VBR_MP4h264_main_SD/383524_01_01_XR15_Overview.mp4?V0lIWk4afWPs3ejN5lxsCi1SIkGKYcNR_F7ijKuQhDmS1sYUK7Ps5TYBcV-MHzdVTujT5p03HP10F_kqzhwhqi38fhOAPnNJz-dMyvA2-YIpBOI-wGtuOjItlVbRUDn6QUWpwe1sRoAl__IA1zmJn3gPvC7Fu926GViqVdLa3oLB0mxRGa7i> click here </a>
I have replaced domain name with xxxxx
This way I can get cover all videos without clicking next (I would like to know if I can automate this process by using some timeout techniques as well)
each of this link, when clicked, chrome window looks like below:
this way after clicking 3dots -> Download, I can save video individually.
What I want:
Method to save all videos without the need to open individually.
Challenge
To begin with, fetching and saving large binary files is possible when:
The host server's CORS support is enabled.
Accessing the host's network from the same site-origin.
Server-to-Server.
Okay, this would reason why your anchor attempt did not work, in fact, accessing the host's network from your localhost will deny you from accessing the resource's content unless the host server's CORS support is enabled which is unlikely.
Workaround
Alternatively, this will leave us with the other two options, accessing from the same site-origin in particular due to its simplicity, the strategy lies in executing the fetching/saving script from the browser itself, thus, the host server will be gentle with the requests, since they are very similar to the ones coming from the same site.
Steps
Go to the site you wish to download the files from (I used https://www.sample-videos.com).
Right-click the web page and select 'Inspect' (Ctrl + Shift + I).
Finally, switch to the 'Console' tab to start coding.
Code
const downloadVideos = (videos, marker) => {
// it's important to throttle between requests to dodge performance or network issues
const throttleTime = 10000; // in milliseconds; adjust it to suit your hardware/network capabilities
const domain = 'https://www.sample-videos.com'; // site's domain
if (marker < videos.length) {
console.log(`Download initiated for video ${videos[marker].name} # marker:${marker}`);
const anchorElement = document.createElement('a');
anchorElement.setAttribute('href', `${domain}${videos[marker].src}`);
anchorElement.setAttribute('download', videos[marker].name);
document.body.appendChild(anchorElement);
// trigger download manually
anchorElement.click();
anchorElement.remove();
marker += 1;
setTimeout(downloadVideos, throttleTime, videos, marker);
}
};
// assuming all videos are stored in an array, each video must have 'src' and 'name' attributes
const videos = [
{ src: '/video123/mp4/480/big_buck_bunny_480p_30mb.mp4', name: 'video_480p.mp4' },
{ src: '/video123/mp4/720/big_buck_bunny_720p_1mb.mp4', name: 'video_720p.mp4' }
];
// fireup
downloadVideos(videos, 0);
... ahem!
This is a weird problem with IE 11 but I have no option. Here's the thing;
I have an HTML <video> tag to show an MP4 video and it's tested, it works but the server is configured to serve cached versions with Redis (i can't change that, is on the server of the client). When the cache expires (eg: you reload the browser or just let time pass for 3 minutes) you can't reproduce the video and need a refresh; for this purpose, I make a script:
Note: this is Vue:
async videoErrorHandler() {
const video = document.querySelector('video');
const source = document.querySelector('source');
await this.$store.dispatch('getDocFromNet');
this.retries++;
if (this.retries > 5) {
source.removeEventListener('error', this.videoErrorHandler);
video.removeEventListener('play', this.videoErrorHandler);
return false;
}
video.load();
video.removeEventListener('play', this.videoErrorHandler);
video.play();
return true;
}
And the attachments:
updated() {
const video = document.querySelector('video');
const source = document.querySelector('source');
if (this.retries === 0) {
source.addEventListener('error', this.videoErrorHandler);
video.addEventListener('play', this.videoErrorHandler);
}
The problem here is pretty simple. When the cache returns invalid, the video script enters in a reload loop and ask for a new valid URL and save it to the store.
Well, in IE the error event is not fired. Chrome, Firefox, even Edge is working, checking the DOM element events attached on the <source> tag, the error event is bound but no matter what, in IE is never fired.
Note: in the first load of the page, the video is accessible and reproducible, but after 3 minutes reload, it just stops working and shows me an "invalid source" in the video box.
I have a JavaScript audio player with skip forward/back 10 second buttons. I do this by setting the currentTime of my audio element:
function Player(skipTime)
{
this.skipTime = skipTime;
this.waitLoad = false;
// initialise main narration audio
this.narration = new Audio(getFileName(dynamicNarration));
this.narration.preload = "auto";
this.narration.addEventListener('canplaythrough', () => { this.loaded(); });
this.narration.addEventListener('timeupdate', () => { this.seek(); });
this.narration.addEventListener('ended', () => { this.ended(); });
this.narration.addEventListener('waiting', () => { this.audioWaiting(); });
this.narration.addEventListener('playing', () => { this.loaded(); });
}
Player.prototype = {
rew: function rew()
{
if (!this.waitLoad) {
this.skip(-this.skipTime);
}
},
ffw: function ffw()
{
if (!this.waitLoad) {
this.skip(this.skipTime);
}
},
skip: function skip(amount)
{
const curTime = this.narration.currentTime;
const newTime = curTime + amount;
console.log(`Changing currentTime (${curTime}) to ${newTime}`);
this.narration.currentTime = newTime;
console.log(`Result: currentTime = ${this.narration.currentTime}`);
},
loaded: function loaded()
{
if (this.waitLoad) {
this.waitLoad = false;
playButton.removeClass('loading');
}
},
audioWaiting: function audioWaiting()
{
if (!this.waitLoad) {
this.waitLoad = true;
playButton.addClass('loading');
}
},
}
(I'm including here some of the event listeners I'm attaching because previously I'd debugged a similar problem as being down to conflicts in event listeners. Having thoroughly debugged event listeners this time though, I don't think that's the root of the problem.)
Though this all works fine on my local copy, when I test an online version I get the following results:
Chrome: resets play position to 0. Final console line reads Result: currentTime = 0.
Safari: doesn't change play position at all. Final console.log line gives a value for currentTime equal to newTime (even though the play position actually doesn't change).
Firefox: skipping forward works; skipping backwards interrupts the audio for a few seconds, then it starts playing again from a couple of seconds before where the playhead had been. In both cases, final console.log line gives a value for currentTime equal to newTime
The issue must have something to do with the way audio is loaded. I have tried adding another console log line to show the start and end values for buffered.
In Chrome it goes up to 2 seconds after current play position. In Safari it goes up to ~170 seconds, and in Firefox it seems to buffer the full audio length.
However, in each case the start of the buffered object is 0.
Does anyone have any idea what might be going wrong?
There are some requirements to properly load an audio file and use the properties.
Your response while serving the file needs to have the following headers.
accept-ranges: bytes
Content-Length: BYTE_LENGTH_OF_YOUR_FILE
Content-Range: bytes 0-BYTE_LENGTH_OF_YOUR_FILE/BYTE_LENGTH_OF_YOUR_FILE
content-type: audio/mp3
My colleagues and I have been struggling over this for a few days and finally this worked
Image of Response header for an audio file
If your browser did not load the audio then the audio can not be played. The browser did not know your audio file and becaue of this it tries to play your audio from the start. May be your audio could be only 1 second long or even shorter.
Solution
You have to wait for loadedmetadata event and after it you can play your audion from any time position. After this event your browser knows all relevant information about your audio file.
Please change your code like follows:
function Player(skipTime)
{
this.skipTime = skipTime;
// initialise main narration audio
this.narration = new Audio(getFileName(dynamicNarration));
this.narration.preload = "auto";
this.narration.addEventListener('canplaythrough', () => { this.loaded(); });
this.narration.addEventListener('timeupdate', () => { this.seek(); });
this.narration.addEventListener('ended', () => { this.ended(); });
this.narration.addEventListener('waiting', () => { this.audioWaiting(); });
this.narration.addEventListener('playing', () => { this.loaded(); });
this.narration.addEventListener('loadedmetadata', () => {playButton.removeClass('loading');});
playButton.addClass('loading');
}
Player.prototype =
{
rew: function()
{
this.skip(-this.skipTime);
},
ffw: function()
{
this.skip(this.skipTime);
},
skip: function(amount)
{
var curTime = this.narration.currentTime;
var newTime = curTime + amount;
console.log(`Changing currentTime (${curTime}) to ${newTime}`);
this.narration.currentTime = newTime;
console.log(`Result: currentTime = ${this.narration.currentTime}`);
}
};
But if you do not want long to wait for audio loading then you have only one option more: to convert all your audiofiles to dataURL format which looks like follows:
var data = "data:audio/mp3;base64,...
But in this case you have to wait for your page load even more than for one audio file load. And by audio file load it is only the metadata and it is faster.
This solved my issue...
private refreshSrc() {
const src = this.media.src;
this.media.src = '';
this.media.src = src;
}
I found a solution to my problem, if not exactly an explanation.
My hosting provider uses a CDN, for which it must replace resource's URLs with those of a different domain. The URLs of my audio resources are dynamically constructed by JS, because there's a random element to them; as such, the deployment process that replaces URLs wasn't catching those for my audio files. To get around this, I manually excluded the audio files from the CDN, meaning I could refer to them using relative file paths.
This was how things stood when I was having this issue.
Then, due to a separate issue, I took a different approach: I got the audio files back on the CDN and wrote a function to extract the domain name I needed to use to retrieve the files. When I did that, suddenly I found that all my problems to do with setting currentTime had disappeared. Somehow, not having the files on the CDN was severely interfering with the browser's ability to load them in an orderly manner.
If anyone can volunteer an explanation for why this might have been, I'd be very curious to hear it...
Edit
I've been working on another project which involves streaming audio, this time also with PWA support, so I had to implement a caching mechanism in my service worker for audio files. Through this guide I learned all about the pitfalls of range requests, and understand now that failing to serve correct responses to requests with range headers will break seeking on some browsers.
It seems that in the above case, when I excluded my files from the CDN they were served from somewhere that didn't support range headers. When I moved them back on the CDN this was fixed, as it must have been built with explicit support for streaming media.
Here is a good explanation of correct responses to range requests. But for anyone having this issue while using a third party hosting service, it suffices to know that probably they do not support range headers for streaming media. If you want to verify this is the case, you can query the audio object's duration. At least in Safari's case, the duration is set to infinity when it can't successfully make a range request, and at that point seeking will be disabled.
We have a set of HTML blocks -- say around 50 of them -- which are iteratively parsed and have Audio objects dynamically added:
var SomeAudioWrapper = function(name) {
this.internal_player = new Audio();
this.internal_player.src = this.determineSrcFromName(name);
// ultimately an MP3
this.play = function() {
if (someOtherConditionsAreMet()) {
this.internal_player.play();
}
}
}
Suppose we generate about 40 to 80 of these on page load, but always the same set for a particular configuration. In all browsers tested, this basic strategy appears to work. The audio load and play successfully.
In IE's 9 and 10, a transient bug surfaces. On occasion, calling .play() on the inner Audio object fails. Upon inspection, the inner Audio object has a .error.code of 4 (MEDIA_ERR_SRC_NOT_SUPPORTED). The file's .duration shows NaN.
However, this only happens occasionally, and to some random subset of the audio files. E.g., usually file_abc.mp3 plays, but sometimes it generates the error. The network monitor shows a successful download in either case. And attempting to reload the file via the console also fails -- and no requests appears in IE's network monitor:
var a = new Audio();
a.src = "the_broken_file.mp3";
a.play(); // fails
a.error.code; // 4
Even appending a query value fails to refetch the audio or trigger any network requests:
var a = new Audio();
a.src = "the_broken_file.mp3?v=12345";
a.play(); // fails
a.error.code; // 4
However, attempting the load the broken audio file in a new tab using the same code works: the "unsupported src" plays perfectly.
Are there any resource limits we could be hitting? (Maybe the "unsupported" audio finishes downloading late?) Are there any known bugs? Workarounds?
I think we can pretty easily detect when a file fails. For other compatibility reasons we run a loop to check audio progress and completion stats to prevent progression through the app (an assessment) until the audio is complete. We could easily look for .error values -- but if we find one, what do we do about it!?
Addendum: I just found a related question (IE 9/10/11 sound file limit) that suggests there's an undocumented limit of 41 -- not sure whether that's a limit of "41 requests for audio files", "41 in-memory audio objects", or what. I have yet to find any M$ documentation on the matter -- or known solutions.
Have you seen these pages on the audio file limits within IE? These are specific to Sound.js, but the information may be applicable to your issue:
https://github.com/CreateJS/SoundJS/issues/40 ...
Possible solution as mentioned in the last comment: "control the maximum number of audio tags depending on the platform and reuse these instead of recreating them"
Additional Info: http://community.createjs.com/kb/faq/soundjs-faq (see the section entitled “I load a lot of sounds, why am running into errors in Internet Explorer?”)
I have not experienced this problem in Edge or IE11. But, I wrote a javascript file to run some tests by looping through 200 audio files and seeing what happens. What I found is that the problem for IE9 and IE10 is consistent between ALL tabs. So, you are not even guaranteed to be able to load 41 files if other tabs have audio opened.
The app that I am working on has a custom sound manager. Our solution is to disable preloading audio for IE9 and IE10 (just load on demand) and then when the onended or onpause callback gets triggered, to run:
this.src = '';
This will free up the number of audio that are contained in IE. Although I should warn that it may make a request to the current page the user is on. When the play method in the sound manager is called again, set the src and play it.
I haven't tested this code, but I wrote something similar that works. What I think you could do for your implementation, is resolve the issue by using a solution like this:
var isIE = window.navigator.userAgent.match(/MSIE (9|10)/);
var SomeAudioWrapper = function(name) {
var src = this.determineSrcFromName(name);
this.internal_player = new Audio();
// If the browser is IE9 or IE10, remove the src when the
// audio is paused or done playing. Otherwise, set the src
// at the start.
if (isIE) {
this.internal_player.onended = function() {
this.src = '';
};
this.internal_player.onpause = this.internal_player.onended;
} else {
this.internal_player.src = src;
}
this.play = function() {
if (someOtherConditionsAreMet()) {
// If the browser is IE, set the src before playing.
if (isIE) {
this.internal_player.src = src;
}
this.internal_player.play();
}
}
}
I am using Roy Tanck's WP-Cumulus Tag Cloud viewer. It's an SWF file that is used with SWFObject.js to show a spinning sphere of tags. It works fine in Chrome and FireFox, but in Internet Explorer 10 the canvas is blank. If I right-click on the canvas I do see the attribution link to Roy Tanck's web site indicating the movie was loaded, and there are no errors showing in the Debugger's Console tab, but no sphere of tags, just a blank white canvas. I am using SWFObject v2.2 and my Flash player version is 11.8.800.175. I traced my main function (shown below) for creating the tag XML for the SWF and everything checks out fine. Unfortunately, I get nothing but a blank display for the movie and no errors in the console. I did look at the values for the flashvars and they are identical to what they are when I trace the method in Chrome and FireFox. Can anyone give me some things to try that might help fix this problem?:
// Create the tag cloud using the given associative array where the Key is the
// value to display in the tag cloud and the Value is the HREF for the link
// to be associated with the display value.
function createTagCloud(aryDisplayStringsWithLinks, style)
{
if (typeof aryDisplayStringsWithLinks == 'undefined')
return;
// Build tags XML partial for use by the Cumulus Tag Cloud.
var tagCloudXML = "<tags>";
var iCount = 0;
for (var Key in aryDisplayStringsWithLinks)
{
tagCloudXML += createOneTagCloudXMLElement(Key, aryDisplayStringsWithLinks[Key], style);
iCount++;
} // for()
tagCloudXML += "</tags>";
// Must have at least two elements or the tag cloud won't make any sense.
if (iCount < 2)
return;
var params =
{
wmode: "transparent"
};
var flashvars =
{
// TAGS HYPERLINKS ***MUST** HAVE THE STYLE ATTRIBUTE OR YOU WON'T SEE ANYTHING IN THE VIEWPORT! (and you
// will think it is broken when it is not).
tagcloud: tagCloudXML,
// tagcloud: '<tags><a href="http://google.com/" style="font-size:9pt;" >One</a><a href="http://microsoft.com/" style="font-size:9pt;" >Two</a></tags>',
mode: "tags",
distr: "true",
tcolor: "0x3366CC",
hicolor: "0x0000bb"
};
swfobject.embedSWF(
"/Content/flash/tagcloud.swf",
"flashcontent",
"470",
"380",
"9.0.0",
"",
flashvars
);
} // function createTagCloud(aryDisplayStringsWithLins) {
Are you facing the problem on
OS: windows 8
Browser: Internet Explorer 10
open the debugger (F12)
Cache (menu bar) -> select ["Always refresh from server"]
It will work now.
The problem exist when all request are not 200
Debugging further to find the fix without the above tweak
Found this check if it fixes the issue for you
Flash not working on Windows 8 / Internet explorer 10