Chrome Extension: Automatically download a screenshot taken with 'chrome.tabs.captureVisibleTab' - javascript

I'm new to the world of Chrome extensions / auto-downloading. I have a background page which takes a screenshot of the visible webpage with chrome.tabs.captureVisibleTab(). In my popup I have:
chrome.tabs.captureVisibleTab(null, {}, function (image) {
// Here I want to automatically download the image
});
I've done something similar with a blob before but I'm totally at a loss as to how to download an image as well as how to do it automatically.
In practice, I want my Chrome extension to screenshot + download the image automatically whenever a particular page is loaded (I'm guessing this will have to be achieved by having my content script talk to my background page, correct?)

Yes, as you said you can use Message Passing to get it done. By content scripts to detect the switch on particular pages, then chat with the background page in order to capture screenshot for that page. Your content script should send a message using chrome.runtime.sendMessage, and the background page should listen using chrome.runtime.onMessage.addListener:
Sample codes I created and tested it works with me:
Content script(myscript.js):
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
});
Background.js:
var screenshot = {
content : document.createElement("canvas"),
data : '',
init : function() {
this.initEvents();
},
saveScreenshot : function() {
var image = new Image();
image.onload = function() {
var canvas = screenshot.content;
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
// save the image
var link = document.createElement('a');
link.download = "download.png";
link.href = screenshot.content.toDataURL();
link.click();
screenshot.data = '';
};
image.src = screenshot.data;
},
initEvents : function() {
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.greeting == "hello") {
chrome.tabs.captureVisibleTab(null, {format : "png"}, function(data) {
screenshot.data = data;
screenshot.saveScreenshot();
});
}
});
}
};
screenshot.init();
Also keep in mind to register your content script's code and permissions in manifest file:
"permissions": ["<all_urls>","tabs"],
"content_scripts": [
{
"matches": ["http://www.particularpageone.com/*", "http://www.particularpagetwo.com/*"],
"js": ["myscript.js"]
}
]
It captures the screenshot and download the image automatically as .png whenever a particular page is loaded. Cheers!

Here's an alternative to message passing that should accomplish the same:
In my manifest.json
"manifest_version": 3,
"permissions": ["activeTab", "scripting"],
"background": {
"service_worker": "background.js"
},
...
In background.js
const download = (dataurl, filename) => {
const link = document.createElement("a");
link.href = dataurl;
link.download = filename;
link.click();
}
chrome.action.onClicked.addListener((tab) => {
chrome.tabs.captureVisibleTab(async (dataUrl) => {
await chrome.scripting.executeScript({
func: download,
target: { tabId: tab.id },
args: [dataUrl, 'test.png'],
});
});
});

Related

Chrome Extensions - navigator.mediaDevices.getUserMedia() failing with NotAllowedError: Failed due to shutdown

I am trying to develop a Chrome Extension to access webcam and record a video, and to download it once stopped.
Extension fails with an error on invoking the following line of code:
navigator.mediaDevices.getUserMedia({ audio: false, video: true }).then(handleSuccess).catch(function (err) {alert(err)});".
Error is :
NotAllowedError: Failed due to shutdown
I am using Chrome Version 80.0.3987.132 (Official Build) (64-bit) on Mac.
What am I doing wrong here? Can't we access device webcam via an extension? Any pointers?
Sample code below:
manifest.json
{
"manifest_version": 2,
"name": "Video Capture",
"version": "0.1",
"browser_action": {
"default_icon": "logo.png",
"default_popup": "popup.html"
}
}
popup.html
<html>
<head>
<title>Video Capture</title>
<script src="videoCapture.js"></script>
</head>
<body>
<button id="start">Start</button>
<button id="stop">Stop</button>
<a id="download">Download</a>
</body>
</html>
videoCapture.js
var shouldStop = false;
window.addEventListener('load', function showPopup() {
alert("Extension started");
startButton = document.getElementById('start');
stopButton = document.getElementById('stop');
downloadLink = document.getElementById('download');
shouldStop = false;
startButton.addEventListener('click', function() {
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
alert("This browser does not support the API yet");
}
alert("Media Devices Available..Starts recording.");
var handleSuccess = function(stream) {
alert("handling recording");
const options = {mimeType: 'video/mp4'};
const recordedChunks = [];
const mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start(5000); //capture video for 5 seconds
mediaRecorder.ondataavailable = function(e) {
if (e.data.size > 0) {
recordedChunks.push(e.data);
}
if(shouldStop === true && stopped === false) {
mediaRecorder.stop();
stopped = true;
stream.getTracks().forEach(function(track) {
track.stop();
});
}
};
mediaRecorder.onstop = function() {
downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
var currentTimestamp = Date.now();
downloadLink.download = 'recording-'+currentTimestamp+'.mp4';
alert("Click Download Link to download captured video");
};
};
navigator.mediaDevices.getUserMedia({ audio: false, video: true })
.then(handleSuccess).catch(function (err) {alert(err)});
});
stopButton.addEventListener('click', function() {
shouldStop = true;
alert("stopped");
});
});
Linking to this question as a possible solution (getUserMedia needed to be run in the website tab NOT from the popup or the background script): https://stackoverflow.com/a/51009577/4136722

JavaScript Canvas Download

I have a problem downloading my drawing from a canvas. This is my code:
function downloadCanvas(link, canvasId, filename) {
link.href = document.getElementById(canvasId).toDataURL();
const le = link.href;
console.log(le);
le.download = filename;
};
download.addEventListener('mousedown', function() {
downloadCanvas(this, 'draw', 'Drawing.jpeg');
}, false);
The console show the value of le, which is the link to download, but the file isn't downloaded... What is wrong here? I'm a beginner.
The issue you have is you're attempting to download a link location, whereas you simply need to be downloading the link file.
To fix this, you should change the const le = link.href; to const le = link;. However, you might as well just get rid of the const le and change le.download to link.download.
Hope that helps!
Edit:
Here's a cleaned up version of the code:
function downloadCanvas(link, canvasId, filename) {
link.href = document.getElementById(canvasId).toDataURL();
link.download = filename;
};
download.addEventListener('mousedown', function() {
downloadCanvas(this, 'draw', 'Drawing.jpeg');
}, false);

How can I display the print option for a pdf stream returned by a wcf service?

My wcf service create a pdf stream and return it, through the ajax call I'm properly handling the display in a new window and the download of the pdf file.
There is an option that I need to add which is directly show the print options for the document.
I tried using iframe but the document preview doesn't properly show the pdf as expected.
This code works fine for download:
var blob = b64toBlob(base64PDF, { type: 'application/pdf;' });
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = f + ".pdf";
link.click();
This is the code I'm trying to use to send the document to the printer:
var blob = b64toBlob(base64PDF, { type: 'application/pdf;' });
var iUrl = window.URL.createObjectURL(blob);
iUrl.download = f + ".pdf";
printPdf(iUrl);
function printPdf(url) {
var iframe = this._printIframe;
if (!this._printIframe) {
iframe = this._printIframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.style.display = 'none';
iframe.onload = function () {
setTimeout(function () {
iframe.focus();
iframe.contentWindow.print();
}, 1);
};
}
iframe.src = url;
}
I also tried few other approaches that are not any better.
var blob = b64toBlob(base64PDF, { type: 'application/pdf;' });
var iUrl = window.URL.createObjectURL(blob);
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = iUrl;
iframe.onload = function () {
setTimeout(function () {
iframe.focus();
iframe.contentWindow.print()
}, 1);
};
document.body.appendChild(iframe);
This code open the printing window but the document looks wrong, I basically get This:
Pdf print preview
As my pdf stream is a base64 string I thought I can create the blob directly as base 64, so I tried this code
var blob = new Blob([base64PDF], { type: 'application/pdf;base64,' })
var iUrl = window.URL.createObjectURL(blob);
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = iUrl;
iframe.onload = function () {
setTimeout(function () {
iframe.focus();
iframe.contentWindow.print()
}, 1);
};
document.body.appendChild(iframe);
Now nothing happen and I don't get the print preview page but in the console I get "Resource interpreted as Document but transferred with MIME type application/pdf:"

Video stream to canvas to image using JavaScript for Project Oxford

I've been able to use Project Oxford, specifically the Emotions API by manually asking a user to upload pictures. However, I'm now looking to do this using a video stream (the webpage shows a live video stream from their webcam and the user can take a snapshot of the stream and then choose to have the emotions API run on that image, which will then run the API and show the scores in a dialogue window - this is the part which I am currently stuck with).
Below I've written the HTML and JavaScript code which sets up the video stream on the webpage and then allows the user to take a snapshot, which then converts the canvas to an image.
<video id="video" width="640" height="480" autoplay></video>
<button id="snap">Snap Photo</button>
<canvas id="canvas" width="640" height="480"></canvas>
<img id="file" src = "" alt="Emotions">
<!--input type="file" id="file" name="filename"-->
<button id="btn">Click here</button>
<script>
// Put event listeners into place
window.addEventListener("DOMContentLoaded", function() {
// Grab elements, create settings, etc.
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
video = document.getElementById("video"),
videoObj = { "video": true },
errBack = function(error) {
console.log("Video capture error: ", error.code);
};
// Put video listeners into place
if(navigator.getUserMedia) { // Standard
navigator.getUserMedia(videoObj, function(stream) {
video.src = stream;
video.play();
}, errBack);
} else if(navigator.webkitGetUserMedia) { // WebKit-prefixed
navigator.webkitGetUserMedia(videoObj, function(stream){
video.src = window.webkitURL.createObjectURL(stream);
video.play();
}, errBack);
} else if(navigator.mozGetUserMedia) { // WebKit-prefixed
navigator.mozGetUserMedia(videoObj, function(stream){
video.src = window.URL.createObjectURL(stream);
video.play();
}, errBack);
}
// Trigger photo take
document.getElementById("snap").addEventListener("click", function() {
context.drawImage(video, 0, 0, 640, 480);
var dataURL = document.getElementById("canvas");
if (dataURL.getContext){
var ctx = dataURL.getContext("2d");
var link = document.createElement('a');
link.download = "test.png";
var myImage = link.href = dataURL.toDataURL("image/png").replace("image/png", "image/octet-stream");
link.click();
}
var imageElement = document.getElementById("file");
imageElement.src = myImage;
//document.getElementById("file").src = dataURL;
$('#btn').click(function () {
//var file = document.getElementById('file').files[0];
$.ajax({
url: "https://api.projectoxford.ai/emotion/v1.0/recognize",
beforeSend: function(xhrObj) {
// Request headers
xhrObj.setRequestHeader("Content-Type", "application/octet-stream");
xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key", "my-key");
},
type: "POST",
data: imageElement,
processData: false
})
.done(function(data) {
JSON.stringify(data);
alert(JSON.stringify(data));
})
.fail(function(error) {
//alert(error.getAllResponseHeaders());
alert("Error");
});
});
});
}, false);
</script>
I'm not able to get the API running on the image after it's been converted as a canvas. After reading around, I believe this has something to do with the file extension that's given to the image after it's been converted as a canvas (there is none), as the API only allows for image file types. I believe this might be where the problem lies and any help would be very much appreciated. Many thanks!
This is the code I'm using to upload the picture to a backend API, which then use the .NET SDK after adding some business logic.
It is not the exact same scenario but the Javascript code might help you:
var url = 'Home/upload';
var data = new FormData();
data.append('file', imgData);
var imgData = canvas.msToBlob('image/jpeg');
$.ajax({
type: 'POST',
url: url,
data: data,
processData: false,
contentType: false
})
.done(function (response) {
loading.style.display = "none";
if (response.error != "") {
result.innerText = response.error;
}
else {
result.innerHTML = response.result;
}
})
.fail(function (response) {
alert('Error : ' + response.error);
})
.complete(function (response) {
restartButton.style.display = 'inline-block';
});
The following thread helped me: Pass Blob through ajax to generate a file

Print PDF directly from JavaScript

I am building a list of PDFs in HTML. In the list I'd like to include a download link and a print button/link. Is there some way to directly open the Print dialog for the PDF without the user seeing the PDF or opening a PDF viewer?
Some variation of downloading the PDF into a hidden iframe and triggering it to print with JavaScript?
Based on comments below, it no longer works in modern browsers
This question demonstrates an approach that might be helpful to you: Silent print an embedded PDF
It uses the <embed> tag to embed the PDF in the document:
<embed
type="application/pdf"
src="path_to_pdf_document.pdf"
id="pdfDocument"
width="100%"
height="100%" />
Then you call the .print() method on the element in Javascript when the PDF is loaded:
function printDocument(documentId) {
var doc = document.getElementById(documentId);
//Wait until PDF is ready to print
if (typeof doc.print === 'undefined') {
setTimeout(function(){printDocument(documentId);}, 1000);
} else {
doc.print();
}
}
You could place the embed in a hidden iframe and print it from there, giving you a seamless experience.
Here is a function to print a PDF from an iframe.
You just need to pass the URL of the PDF to the function. It will create an iframe and trigger print once the PDF is load.
Note that the function doesn't destroy the iframe. Instead, it reuses it each time the function is call. It's hard to destroy the iframe because it is needed until the printing is done, and the print method doesn't has callback support (as far as I know).
printPdf = function (url) {
var iframe = this._printIframe;
if (!this._printIframe) {
iframe = this._printIframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.style.display = 'none';
iframe.onload = function() {
setTimeout(function() {
iframe.focus();
iframe.contentWindow.print();
}, 1);
};
}
iframe.src = url;
}
You can use Print.js (npm install print-js). It's 128kB unpacked and you can find the docs at http://printjs.crabbly.com/.
It doesn't print on IE though, in those cases you'll have to download the PDF instead.
$http({
url: "",
method: "GET",
headers: {
"Content-type": "application/pdf"
},
responseType: "arraybuffer"
}).success(function (data, status, headers, config) {
var pdfFile = new Blob([data], {
type: "application/pdf"
});
var pdfUrl = URL.createObjectURL(pdfFile);
//window.open(pdfUrl);
printJS(pdfUrl);
//var printwWindow = $window.open(pdfUrl);
//printwWindow.print();
}).error(function (data, status, headers, config) {
alert("Sorry, something went wrong")
});
https://github.com/mozilla/pdf.js/
for a live demo http://mozilla.github.io/pdf.js/
it's probably what you want, but I can't see the point of this since modern browsers include such functionality, also it will run terribly slow on low-powered devices like mobile devices that, by the way, have their own optimized plugins and apps.
Cross browser solution for printing pdf from base64 string:
Chrome: print window is opened
FF: new tab with pdf is opened
IE11: open/save prompt is opened
.
const blobPdfFromBase64String = base64String => {
const byteArray = Uint8Array.from(
atob(base64String)
.split('')
.map(char => char.charCodeAt(0))
);
return new Blob([byteArray], { type: 'application/pdf' });
};
const isIE11 = !!(window.navigator && window.navigator.msSaveOrOpenBlob); // or however you want to check it
const printPDF = blob => {
try {
isIE11
? window.navigator.msSaveOrOpenBlob(blob, 'documents.pdf')
: printJS(URL.createObjectURL(blob)); // http://printjs.crabbly.com/
} catch (e) {
throw PDFError;
}
};
printPDF(blobPdfFromBase64String(base64String))
BONUS - Opening blob file in new tab for IE11
If you're able to do some preprocessing of the base64 string on the server you could expose it under some url and use the link in printJS :)
I used this function to download pdf stream from server.
function printPdf(url) {
var iframe = document.createElement('iframe');
// iframe.id = 'pdfIframe'
iframe.className='pdfIframe'
document.body.appendChild(iframe);
iframe.style.display = 'none';
iframe.onload = function () {
setTimeout(function () {
iframe.focus();
iframe.contentWindow.print();
URL.revokeObjectURL(url)
// document.body.removeChild(iframe)
}, 1);
};
iframe.src = url;
// URL.revokeObjectURL(url)
}
You can download the pdf file using fetch, and print it with Print.js
fetch("url").then(function (response) {
response.blob().then(function (blob) {
var reader = new FileReader();
reader.onload = function () {
//Remove the data:application/pdf;base64,
printJS({
printable: reader.result.substring(28),
type: 'pdf',
base64: true
});
};
reader.readAsDataURL(blob);
})
});
function printFile(url) {
const iframe = document.createElement('iframe');
iframe.src = url;
iframe.style.display = 'none';
document.body.appendChild(iframe);
// Use onload to make pdf preview work on firefox
iframe.onload = () => {
iframe.contentWindow.focus();
iframe.contentWindow.print();
};
}
It will be easy this way:
function PrintPdf (pdf) {
var iframe = document.createElement('iframe');
iframe.style.display = "none";
iframe.src = pdf;
document.body.appendChild(iframe);
iframe.contentWindow.focus();
iframe.contentWindow.print();
}
Simplification of #Nicolas BADIA's answer:
function printPDF (url)
{
let pdfFrame = document.body.appendChild(document.createElement('iframe'));
pdfFrame.style.display = 'none';
pdfFrame.onload = () => (void pdfFrame.contentWindow.print());
pdfFrame.src = url;
}

Categories