Javascript verify image without file type - javascript

The question may look simple, but I have found the answer to be very elusive. I have a shoutcast server broadcasting two images, and they can only be retrieved from certain urls, which don't contain the file's type. What I'm trying to do is get the album art that is being broadcast on a constant url. If that url doesn't have an image that can used, switch to the other url. The code I have written is on JS fiddle. And here's the trouble spot:
try {
console.log("testing...");
if (img2.height > 0) {
//I have to use something other than the image height, because
//that's already determined by CSS
//thus always setting clear to true,
//regardless of the image.
console.log("cleared [true]");
clear = true;
} else {
console.log("cleared [false]");
clear = false;
}
One solution I thought might work is the user's browser throws up an error in the console
"Resource interpreted as Image but transferred with MIME type text/plain:" but from what I gathered, javascript can't access that kind of information. I've also tried numerous comparisons in the if statement, but with no luck, they always give the same results, regardless of the image. So I've hit a roadblock and don't really know what I need, I hate leaving this so open-ended, but it's really the best I've got. I've got the server running two music tracks and their album art, I'll let you guys play around with it.

Use the onerror event, like:
img2.onerror = function(){
alert('see');
}
http://jsfiddle.net/364F8/1/
I'm not rewriting your code, but you should 'see' how it works with the example.

Related

How to call a fetch request and wait for it's answer inside a onBeforeRequest in a web extension

I'm trying to write a web extension that stops the requests from a url list provided locally, fetches the URL's response, analyzes it in a certain way and based on the analysis results, blocks or doesn't block the request.
Is that even possible?
The browser doesn't matter.
If it's possible, could you provide some examples?
I tried doing it with Chrome extensions, but it seems like it's not possible.
I heard it's possible on mozilla though
I think that this is only possible using the old webRequestBlocking API which Chrome is removing as a part of Manifest v3. Fortunately, Firefox is planning to continue supporting blocking web requests even as they transition to manifest v3 (read more here).
In terms of implementation, I would highly recommend referring to the MDN documentation for webRequest, in particular their section on modifying responses and their documentation for the filterResponseData method.
Mozilla have also provided a great example project that demonstrates how to achieve something very close to what I think you want to do.
Below I've modified their background.js code slightly so it is a little closer to what you want to do:
function listener(details) {
if (mySpecialUrls.indexOf(details.url) === -1) {
// Ignore this url, it's not on our list.
return {};
}
let filter = browser.webRequest.filterResponseData(details.requestId);
let decoder = new TextDecoder("utf-8");
let encoder = new TextEncoder();
filter.ondata = event => {
let str = decoder.decode(event.data, {stream: true});
// Just change any instance of Example in the HTTP response
// to WebExtension Example.
str = str.replace(/Example/g, 'WebExtension Example');
filter.write(encoder.encode(str));
filter.disconnect();
}
// This is a BlockingResponse object, you can set parameters here to e.g. cancel the request if you want to.
// See: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/BlockingResponse#type
return {};
}
browser.webRequest.onBeforeRequest.addListener(
listener,
// 'main_frame' means this will only affect requests for the main frame of the browser (e.g. the HTML for a page rather than the images, CSS, etc. that are loaded afterwards). You might want to look into whether you want to expand this.
{urls: ["*://*/*"], types: ["main_frame"]},
["blocking"]
);
Correction:
The above example only works properly if the response data fits in one chunk. If it is larger (and you still want to inspect the entirety of the response data), you would need to put all of the data into a buffer, and then work on it once all data has been received. See the document here for more information: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/StreamFilter/ondata#webextension_examples (the code section titled "This example combines all buffers into a single buffer" would be of most interest to you I think).
In terms of using this API to block responses, data is only returned from this URL if you call filter.write(), so if you don't like the response, you can simply not call it (just call filter.close()) and an empty response will be returned. You can also only return part of the full response body by filter.write()ing only the bits that you want to return.

How to find specific cache entries in firefox and turn them into a File or Blob object?

I have the following scenario:
A user can paste html content in a wysiwyg editor. When that pasted content contains images which are hosted on other domains, I want these to be uploaded to my server. Right now the only way of doing that is manually downloading via "save image as..." context menu, then uploading the image to the server via a form and updating the images in the editor.
I have to solve this client side.
I'm working on a firefox addon that can automate the process. Of course I could download these images, store them on the harddrive and then upload them with FormData or better the pupload , but this seems clumsy as since the content is displayed in the browser, it must be downloaded already and reside somewhere in memory. I would like to grab the image files from memory and tell firefox to upload them (being able to make a Blob of them would suffice it seems).
However, I'm getting hopelessly lost in the API documentation for several different Caching systems on MDN and fail to find any example code of how to use them. I checked code of other addons that access the cache, but most is uncommented and still quite cryptic.
Can you point me to some sample code of what the recommended way would be to achieve this? The best possible solution would be if I can request the particular url from firefox so I can use it in FormData, and if it isn't in the cache firefox downloads to memory, but if it's already there I just get it directly.
The master documentation for Mozilla's version 2 HTTP Cache is located here. Aside from the blurbs on this page, the only way I was able to make sense of this new scheme is by looking at the actual code for each object and back-referencing almost everything. Even though I wasn't able to get a 100% clear picture of what exactly was going on, I figured out enough to get it working. In my opinion, Mozilla should have taken the time to create some simple-terms documentation before they went ahead an pushed out the new API. But, we get what they give us I suppose.
On to your problem. We're assuming that the users who want to upload an image already have this image saved in their cache somewhere. In order to be able to pull it out of the user's cache for upload, you must first be able to determine the URI of the image before it can be pulled explicitly from the cache. For the sake of brevity, I'm going to assume that you already have this part figured out.
An important thing to note about the new HTTP Cache is that although it's all based off callbacks, there can still only ever be a single writing process. While in your example it may not be necessary to write to the descriptor, you should still request write access since that will prevent any other processes (i.e. the browser) from altering/deleting the data until you are done with it. Another side note and a source of a lot of pain for me was the fact that requesting a cache entry from the memory cache will ALWAYS created a new entry, overwriting any pre-existing entries. You shouldn't need this, but if it is necessary, you can access the memory cache from the disk (the disk cache is physical disk+memory cache -- Mozilla logic) cache without that side effect.
Once the URI is in hand, you can then make a request to pull it out of the cache. The new caching system is based completely on callbacks. There is one key object that we will need in order to be able to fetch the cache entry's data -- nsICacheEntryOpenCallback. This is a user-defined object that handles the response after a cache entry is requested. It must have two member functions: onCacheEntryCheck(entry, appcache) and onCacheEntryAvilable(descriptor, isnew, appcache, status).
Here is a cut-down example from my code of such an object:
var cacheWaiter = {
//This function essentially tells the cache service whether or not we want
//this cache descriptor. If ENTRY_WANTED is returned, the cache descriptor is
//passed to onCacheEntryAvailable()
onCacheEntryCheck: function( descriptor, appcache )
{
//First, we want to be sure the cache entry is not currently being written
//so that we can be sure that the file is complete when we go to open it.
//If predictedDataSize > dataSize, chances are it's still in the process of
//being cached and we won't be able to get an exclusive lock on it and it
//will be incomplete, so we don't want it right now.
try{
if( descriptor.dataSize < descriptor.predictedDataSize )
//This tells the nsICacheService to call this function again once the
//currently writing process is done writing the cache entry.
return Components.interfaces.nsICacheEntryOpenCallback.RECHECK_AFTER_WRITE_FINISHED;
}
catch(e){
//Also return the same value for any other error
return Components.interfaces.nsICacheEntryOpenCallback.RECHECK_AFTER_WRITE_FINISHED;
}
//If no exceptions occurred and predictedDataSize == dataSize, tell the
//nsICacheService to pass the descriptor to this.onCacheEntryAvailable()
return Components.interfaces.nsICacheEntryOpenCallback.ENTRY_WANTED;
}
//Once we are certain we want to use this descriptor (i.e. it is done
//downloading and we want to read it), it gets passed to this function
//where we can do what we wish with it.
//At this point we will have full control of the descriptor until this
//function exits (or, I believe that's how it works)
onCacheEntryAvailable: function( descriptor, isnew, appcache, status )
{
//In this function, you can do your cache descriptor reads and store
//it in a Blob() for upload. I haven't actually tested the code I put
//here, modifications may be needed.
var cacheentryinputstream = descriptor.openInputStream(0);
var blobarray = new Array(0);
var buffer = new Array(1024);
for( var i = descriptor.dataSize; i == 0; i -= 1024)
{
var chunksize = 1024;
if( i < 0 )
chunksize = 1024 + i;
try{
cacheentryinputstream.read( buffer, chunksize );
}
catch(e){
//Nasty NS_ERROR_WOULD_BLOCK exceptions seem to happen to me
//frequently. The Mozilla guys don't provide a way around this,
//since they want a responsive UI at all costs. So, just keep
//trying until it succeeds.
i += 1024;
continue;
}
for( var j = 0; j < chunksize; j++ )
{
blobarray.push(buffer.charAt(j));
}
if( i < 0 )
i = 0 //Set i == 0 to signal loop break
}
}
var theblob = new Blob(blobarray);
//Do an AJAX POST request here.
}
Now that the callback object is set up, we can actually do some requests for cache descriptors. Try something like this:
var theuri = "http://www.example.com/image.jpg";
//Load the cache service
var cacheservice = Components.classes["#mozilla.org/netwerk/cache-storage-service;1"].getService(Components.interfaces.nsICacheStorageService)
//Select the default disk cache.
var hdcache = cacheservice.diskCacheStorage(Services.loadContextInfo.default, true);
//Request a cache entry for the URI. OPEN_NORMALLY requests write access.
hdcache.asyncOpenURI(ioservice.newURI(theuri, null, null), "", hdcache.OPEN_NORMALLY, cacheWaiter);
As far as actually getting the URI, you could provide a window for a user to drag-and-drop an image into or perhaps just paste the URL of the image into. Then, you could do an AJAX request to fetch the image (in the case that the user hasn't actually visited the image for some reason, it would then be cached). You could then use that URL to then fetch the cache entry for upload. As an aesthetic touch, you could even show a preview of the image but that's a bit out of scope of the question.
If you need any more clarifications, please feel free to ask!

Get Element available under Dev Tools -> Resources -> Frames

I'm trying to do this by using a Tampermonkey Script. However I'm open to new approaches...
What I want to do is extract some data (data-video), from a specific <div>. However this data is not available under the HTML code of the page, but it's available under Dev Tools -> Resources and then on Frames.
Anyone knows if it's possible to get that information available under DevTools? And how can I do that?
Comparative between the two pages can be found here: "Original HTML PAGE" and "HTML PAGE under DevTools"
On the first hyperlink the id=video-canvas cannot be seen, however it's on the <object type="application/x-shockwave-flash(...)
As you state in your question the data you're looking for is available in DevTools under the "Resources" tab in the "Frames" folder. What you are looking at there is the Source HTML, similar to View Source.
The code you want, is what is getting replaced. It appears the site is using the JW Player Plugin, which is replacing the <div id="video-canvas"> with the appropriate HTML for the device / browser detected to play the video. With all of my browsers on my Mac, they are being forced to use the Flash, even when it's disabled. When using my iPhone, which can't play flash , and inspecting the page it uses JW's own custom video element. It appears that it must be storing the file location in memory since it is not in the generated markup.
I am able to run through the console in the dev tools and access their JS class. It appears i can call jwplayer._tracker , which has an object b . Object b has an object AlWv3iHmEeOzwBIxOUCPzg This object seems to be consistent each time i check between different browsers, you can use the for loop inmy first example to get the correct value but tirmming it down to .b Following that object is e and in e is the object http://i.n.jwpltx.com/v1.... really long string that appears to contain a url, so it will need to parsed.
So to get the HTML string i ran
for ( var loc in jwplayer._tracker.b.AlWv3iHmEeOzwBIxOUCPzg.e){
loc
}
so if we put that in a function to parse the string and return a value
function getSubURL(){
var initURL;
for ( var loc in jwplayer._tracker.b.AlWv3iHmEeOzwBIxOUCPzg.e){
initURL = loc;
}
//look for 'mp4:' this is in front of the file path
var start = initURL.indexOf("mp4%3A");
//look for the .mp4 for the end of the file name
var stop = initURL.indexOf(".mp4");
//grab the string between
//start+6 to remove characters used to find it
//and stop+4 to include characters used to find it
var subPath = (initURL.substring((start+6),(stop+4))).split("%2F").join("/");
return subPath;
}
//and run it
getSubURL();
it will return ciencia/astronomia/fimsol.mp4
you can run this from your console, but I am unaware of how you can use this in Tamper Monkey, but i think it gets ya a lot closer to what you wanted.
This is the approach I've used to solve my problem... I couldn't grab the code I want under Dev Tools, but I find a way to get the data from jwplayer with the function getPlaylistItem. And this is how I get the url filename of each video:
function getFilename(filename) {
var filename;
if(jwplayer().getPlaylistItem){
filename = jwplayer().getPlaylistItem()['file'];
}
else{
return filename;
}
filename = filename.substring(filename.indexOf("/mp4:") + 5);
return filename;
}

Cannot trigger "durationchange" of HTML5 audio

thanks for looking at my question. I am working on a HTML5 audio related project. And now I meet a question.
What I want to do is assigning an audio.src to another audio.src. Actually, it works well in my beginning demo. But it does not work in my current project. The original audio cannot be played. And I console out all it's loading procedure and figured out the problem happening in durationchange. But I have no idea what is wrong with it since my logic is very similar to my beginning demo.
Hopefully, someone here could help me find out what is wrong with my code. The following is my code:
// the original audio is Glogal.audio
var segs = $('.cutter-room .container').find('.seg-container');
var audio_self = "<audio id='player_0'>";
// add one more segment for the new cut part
$('.cutter-room').append(audio_self);
$('.cutter-room .container').append(cut.audio_seg); // audio_seg is the 'clothes' of audio tag, a GUI
// new_seg is the audio tag which I want to assign to
var new_seg = document.getElementById("player_0");
var temp = GLOBAL.audio.src;
new_seg.src = GLOBAL.audio.src; // if I comment this one, the original will be fine
And the following is my testing code, when the new <audio> inserted in DOM successfully, if I try to play the original audio, its durationchange will not be called:
/*for testing*/
GLOBAL.audio.addEventListener("loadstart", function(){
console.log('start loading');
});
GLOBAL.audio.addEventListener("durationchange", function(){
console.log("change duration");
});
/*end testing*/
By the way, I am sure that the music file is correct. Thanks again!
Update 2013/11/28
Here is the jsFilddle link. I am sorry that I don't know which music link should I put in the src so I just put my local path. The problem shown in jsFilddle is a little bit different from what I said above. In jsFilddle, there is nothing wrong with the original audio but the second one cannot play.
I found that it I just open the .html page, no server supported. There will be nothing wrong. But if I run it on a server locally, the duraionchange will not response. So does it mean that the problem happens on the server side, but not the js?
But it is unreasonable that an audio source cannot be assigned to another audio source with a running server. They are paths but essentially, they are still Strings, aren't they?
The thing is that browsers don't like having the several different players pointing to the very same mp3 on the same page.
So the trick can be to alter the url, to prevent caching, for example:
assign.src = original.src+"?foo="+(new Date().getTime());
jsfiddle

Why does dojo.xhrGet needs different kinds of url to work on different computers (pc/mac)?

i'm writing an greasemonkey script for somebody else. he is a moderator and i am not. and the script will help him do some moderating things.
now the script works for me. as far as it can work for me.(as i am not a mod)
but even those things that work for me are not working for him..
i checked his version of greasemonkey plugin and firefox and he is up to date.
only thing that's really different is that i'm on a mac and he is pc, but i wouldn't think that would be any problem.
this is one of the functions that is not working for him. he does gets the first and third GM_log message. but not the second one ("got some(1) ..").
kmmh.trackNames = function(){
GM_log("starting to get names from the first "+kmmh.topAmount+" page(s) from leaderboard.");
kmmh.leaderboardlist = [];
for (var p=1; p<=(kmmh.topAmount); p++){
var page = "http://www.somegamesite.com/leaderboard?page="+ p;
var boardHTML = "";
dojo.xhrGet({
url: page,
sync: true,
load: function(response){
boardHTML = response;
GM_log("got some (1) => "+boardHTML.length);
},
handleAs: "text"
});
GM_log("got some (2) => "+boardHTML.length);
//create dummy div and place leaderboard html in there
var dummy = dojo.create('div', { innerHTML: boardHTML });
//search through it
var searchN = dojo.query('.notcurrent', dummy).forEach(function(node,index){
if(index >= 10){
kmmh.leaderboardlist.push(node.textContent); // add names to array
}
});
}
GM_log("all names from "+ kmmh.topAmount +" page(s) of leaderboard ==> "+ kmmh.leaderboardlist);
does anyone have any idea what could be causing this ??
EDIT: i know i had to write according to what he would see on his mod screen. so i asked him to copy paste source of pages and so on. and besides that, this part of the script is not depending on being a mod or not.
i got everything else working for him. just this function still doesn't on neither of his pc's.
EDIT2 (changed question): OK. so after some more trial and error, i got it to work, but it's still weird.
when i removed the www-part of the url thats being use in the dojo.xhrGet() i got the finally the same error he got. so i had him add www to his and now it works.
the odd thing is he now uses a script with the url containing "www" and i'm using a script with an url without "www"...
so for me:
var page = "http://somegamesite.com/leaderboard?page="+ p;
and for him:
var page = "http://www.somegamesite.com/leaderboard?page="+ p;
Why don't you have him try logging into an account that is not a moderator account so that you eliminate one of your variables from your problem space.
It's possible that the DOM of the page is different for a moderator than for a regular user. If you're making assumptions about the page as a regular user that are not true as a moderator, that could cause problems.
I suspect that to fix it, you may need access to a moderator account so you can more easily replicate the behavior.
ooops. it seemed that the url of this gamesite is accessible as www.gamesite.com as well as gamesite.com (without the www.part). this caused the problem.
sorry to bother you'all.
i go hide in shame now...

Categories