Issues with Browserify and NodeJS Integration in Google Chrome Extension - javascript

I'm having issues with developing my Google Chrome extension. I need some specific npm modules in order to execute my code so I looked into Browserify. I followed all the steps without issue but the code still produces errors when run. The screenshot is attached below.
Error when Chrome extension is only loaded
All my files are located in the same project folder (popup.html, popup.js, bundle.js, etc.). I only have one html file and one javascript file (excluding bundle.js).
Here is my popup.html code:
document.addEventListener('DOMContentLoaded', function() {
var convertMP3Button = document.getElementById("getLinkAndConvert");
convertMP3Button.addEventListener("click", function() {
chrome.tabs.getSelected(null, function(tab) { // 'tab' has all the info
var fs = require('fs');
var ytdl = require('ytdl-core');
var ffmpeg = require('fluent-ffmpeg');
var ffmetadata = require("ffmetadata");
var request = require('request');
console.log(tab.url); //returns the url
convertMP3Button.textContent = tab.url;
var url = tab.url;
var stream = ytdl(url);
//.pipe(fs.createWriteStream('/Users/nishanth/Downloads/video.mp4'));
// Helper method for downloading
var download = function(uri, filename, callback){
request.head(uri, function(err, res, body){
request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
});
};
ytdl.getInfo(url, function(err, info) {
console.log("INFO: " + JSON.stringify(info, null, 2));
var process = new ffmpeg({source:stream})
process.save('/Users/nishanth/Downloads/' + info.title + '.mp3').on('end', function() {
console.log("PROCESSING FINISHED!");
download(info.thumbnail_url, "/Users/nishanth/Downloads/image.jpg", function() {
console.log("DOWNLOADED IMAGE");
var options = {
artist: info.author,
attachments: ["/Users/nishanth/Downloads/image.jpg"]
};
ffmetadata.write('/Users/nishanth/Downloads/' + info.title + '.mp3', {}, options, function(err) {
if (err)
console.error("Error writing cover art: " + err);
else
console.log("Cover art added");
});
});
});
});
});
});
});
<!doctype html>
<html>
<head>
<title>Youtube Music</title>
<script src="bundle.js"></script>
<script src="popup.js"></script>
</head>
<body>
<h1>Youtube Music</h1>
<button id="getLinkAndConvert">Download Song Now!</button>
</body>
</html>
It would be great if I could find out the reason why I have not been able to properly integrate browserify in order to use the npm modules.

Browsers don't allow you to access the file system, instead they usually have some storage mechanisms of their own (cookies, localstorage, or a browser-specific system like chrome.storage). Browserify has no way of getting around this, and doesn't provide a shim for require('fs'). Instead of writing directly to disk you'll need to have your app provide a downloadable version of your files, which the user will then have to save manually. If you don't need the files to be accessible outside the extension you can use the api I linked earlier, or drop in something like browserify-fs which creates a virtual file system in the browser's storage.

Related

How to download file from Cloud Storage [Firebase Hosting]

I would like to make a web app using Firebase Hosting.
make audio file using Cloud text to speech API
upload that audio file to Cloud Storage
download that audio file from Cloud Storage to a web browser
I passed step 1 and 2, but have a trouble with step 3.
I followed this turorial.
https://firebase.google.com/docs/storage/web/download-files
I deployed my Firebase project and tested my app. I could upload audio file to Cloud Storage, but I couldn't download it. I looked at browser's console, but I couldn't find any error message. There was no message in browser's console.
Could you give me any advice? Thank you in advance.
This is my main.js
'use strict';
// Saves a new message on the Cloud Firestore.
function saveMessage() {
// Add a new message entry to the Firebase database.
return firebase.firestore().collection('messages').add({
text: messageInputElement.value,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
}).catch(function(error) {
console.error('Error writing new message to Firebase Database', error);
});
}
// Checks that the Firebase SDK has been correctly setup and configured.
function checkSetup() {
if (!window.firebase || !(firebase.app instanceof Function) || !firebase.app().options) {
window.alert('You have not configured and imported the Firebase SDK. ' +
'Make sure you go through the codelab setup instructions and make ' +
'sure you are running the codelab using `firebase serve`');
}
}
// Checks that Firebase has been imported.
checkSetup();
// Shortcuts to DOM Elements.
var messageInputElement = document.getElementById('text');
var submitButtonElement = document.getElementById('download');
// Saves message on form submit.
submitButtonElement.addEventListener('click', saveMessage);
// Create a reference from a Google Cloud Storage URI
var storage = firebase.storage();
var gsReference = storage.refFromURL('gs://advan********8.appspot.com/audio/sub.mp3')
gsReference.getDownloadURL().then(function(url) {
// This can be downloaded directly:
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(event) {
var blob = xhr.response;
};
xhr.open('GET', url);
xhr.send();
}).catch(function(error) {
// A full list of error codes is available at
// https://firebase.google.com/docs/storage/web/handle-errors
switch (error.code) {
case 'storage/object-not-found':
console.log('storage/object-not-found')
break;
case 'storage/unauthorized':
console.log('storage/unauthorized')
break;
case 'storage/canceled':
console.log('storage/canceled')
break;
case 'storage/unknown':
console.log('storage/unknown')
break;
}
});
This is index.js (Cloud Functions)
const functions = require('firebase-functions');
var admin = require("firebase-admin");
admin.initializeApp();
const textToSpeech = require('#google-cloud/text-to-speech');
exports.myFunction = functions.firestore
.document('messages/{id}')
.onCreate((change, context) => {
const client = new textToSpeech.TextToSpeechClient();
async function quickStart() {
// The text to synthesize
const text = 'Hello world';
// Construct the request
const request = {
input: {text: text},
// Select the language and SSML voice gender (optional)
voice: {languageCode: 'en-US', ssmlGender: 'NEUTRAL'},
// select the type of audio encoding
audioConfig: {audioEncoding: 'MP3'},
};
var bucket = admin.storage().bucket('adva********.appspot.com');
var file = bucket.file('audio/sub.mp3')
// Create the file metadata
var metadata = {
contentType: 'audio/mpeg'
};
// Performs the text-to-speech request
const [response] = await client.synthesizeSpeech(request);
return await file.save(response.audioContent, metadata)
.then(() => {
console.log("File written to Firebase Storage.")
return;
})
.catch((error) => {
console.error(error);
});
}
quickStart();
});
This is index.html
<!--./advance/index.html-->
<!doctype html>
<html lang="ja">
<head>
<meta name="robots" content="noindex">
<title>音読アプリ アドバンス</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link href="https://fonts.googleapis.com/css?family=M+PLUS+Rounded+1c&display=swap" rel="stylesheet">
<style>
#text {width: 100%; height: 300px; font-family: 'M PLUS Rounded 1c', sans-serif; font-size: 22px;}
#download {font-family: 'M PLUS Rounded 1c', sans-serif; font-size: 28px;}
</style>
</head>
<body>
<textarea id="text" class="form-control" name="text" placeholder="ここに英文を入力してください。" maxlength="3000" minlength="1"></textarea>
<br>
<div style="text-align:center">
<input id="download" class="btn btn-primary" type="submit" value="音声をダウンロード">
</div>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<!-- Import and configure the Firebase SDK -->
<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->
<!-- If you do not want to serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->
<script src="/__/firebase/7.14.3/firebase-app.js"></script>
<script src="/__/firebase/7.14.3/firebase-auth.js"></script>
<script src="/__/firebase/7.14.3/firebase-storage.js"></script>
<script src="/__/firebase/7.14.3/firebase-messaging.js"></script>
<script src="/__/firebase/7.14.3/firebase-firestore.js"></script>
<script src="/__/firebase/7.14.3/firebase-performance.js"></script>
<script src="/__/firebase/7.14.3/firebase-functions.js"></script>
<script src="/__/firebase/init.js"></script>
<script src="scripts/main.js"></script>
</body>
</html>
browser's developer tool's Network tab
The problem is that you are likely trying to download before the file is created by your cloud function, since you are running the cloud function as a event trigger that automatically runs every time a document is created, but at the same time you are trying to download that file on your front end. This lack of syncronism creates this odd behaviour you are seeing.
In order to fix that there are a couple of things you should do:
Convert your cloud function to a http triggered function instead of a event triggered function.
This will make it so your function can be invoked by your front end after the creation of the document and before you are trying to download it, it could look like this:
exports.myFunction = (req, res) => {
//you can get the id of the document sent on the request here
const id=req.body;
...
};
Also, you might want to check this documentation for more details on this type of trigger
Create a download function and add all your code for the download to it and add synchronicity to your operations on your front-end.
With this your code will execute in the proper order, and you main.js will look like this:
// Saves a new message on the Cloud Firestore.
function saveMessage() {
// Add a new message entry to the Firebase database.
firebase.firestore().collection('messages').add({
text: messageInputElement.value,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
})
.then(function(docRef){
var obj = {
method: 'POST',
body: docRef.id
};
//calls function that adds to storage
fetch("YOUR_FUNTION_URL_HERE", obj).then({
//actually downloads
download();
}).catch(error) {
console.error('Failed to call cloud function', error);
});
}).catch(function(error) {
console.error('Error writing new message to Firebase Database', error);
});
}
function download(){
var storage = firebase.storage();
var gsReference = storage.refFromURL('gs://advan********8.appspot.com/audio/sub.mp3')
gsReference.getDownloadURL().then(function(url) {
// This can be downloaded directly:
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(event) {
var blob = xhr.response;
};
xhr.open('GET', url);
xhr.send();
}).catch(function(error) {
// A full list of error codes is available at
// https://firebase.google.com/docs/storage/web/handle-errors
switch (error.code) {
case 'storage/object-not-found':
console.log('storage/object-not-found')
break;
case 'storage/unauthorized':
console.log('storage/unauthorized')
break;
case 'storage/canceled':
console.log('storage/canceled')
break;
case 'storage/unknown':
console.log('storage/unknown')
break;
}
});
}
// Checks that Firebase has been imported.
checkSetup();
// Shortcuts to DOM Elements.
var messageInputElement = document.getElementById('text');
var submitButtonElement = document.getElementById('download');
// Saves message on form submit.
submitButtonElement.addEventListener('click', saveMessage);
NOTE: This is all untested but it will be a good starting point for your to start the changes necessary to your code.
I hope someone may still find this useful. A hack around is to create an express server/lambda/cloud function to download file and send it back to the browser via the download method. Check bellow code
const express = require("express");
const app = express();
var fs = require("fs");
const request = require("request");
app.get("/", (req, res) => {
const { filename, fileUrl } = req.body;
const file = fs.createWriteStream("filename");
request.get(fileUrl).on("response", function (response) {
var pipe = response.pipe(file);
pipe.on("finish", function () {
res.download(filename, function (err) {
if (err) {
console.log(err); // Check error if you want
}
fs.unlink(yourFilePath, function () {
console.log("File was deleted"); // Callback
});
});
});
});
});
app.listen(3000, () => console.log("Server ready"));

web worker throwing 404 when executing

My project structure looks like
js
/client.js
/script1.js
/webWoker.js
node_modules
.gitignore
index.html
The main.html has it included as well in
<script type="text/javascript" src="js/script1.js"></script>
<script type="text/javascript" src="js/client.js"></script>
<script type="text/javascript" src="js/webWoker.js"></script>
My script1.js looks like
if (window.Worker) {
console.log("uri: " + document.documentURI);
var myWorker = new Worker("myworker.js");
myWorker.postMessage("hello");
console.log(myWorker);
myWorker.onmessage = function (e) {
result.textContent = e.data;
console.log('Message received from worker: ' + result.textContent);
};
}
and my webWorker.js looks like
onmessage = function (e) {
console.log('Message received from main script');
var result = "#27ae60";
console.log('Posting message back to main script');
postMessage(result);
};
I use node.js for this project and run it via npm start, and when I run this in browser I see
script1.js:81 GET http://localhost:8080/webWorker.js 404 (Not Found)
execute # script1.js:81
img.onload # script1.js:64
What is going wrong here?
You don't have to include the worker script in your main page (it's even a bad idea), but the URI you pass to new Worker(URI) is relative to the current documentURI.
So in your case, it should be new Worker("/js/webWorker.js");.

Customize DocxJS to render local docx file

I just found a working docx to html converter using only javascript on github. The main code which converts docx to html is below. The issue is the page just has a button which on click or drag and choosing a word document, opens it as html. I want to specify a file location in the code so I can load it on the server for loading some documents from computer locally.
Code which converts docx to html and renders :
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DocxJS Example</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script type="text/javascript" src="https://www.docxjs.com/js/build/latest.docxjs.min.js"></script>
</head>
<body>
<input id="inputFiles" type="file" name="files[]" multiple="false">
<div id="loaded-layout" style="width:100%;height:800px;"></div>
<script>
$(document).ready(function(){
var $inputFiles = $('#inputFiles');
$inputFiles.on('change', function (e) {
var files = e.target.files;
var docxJS = new DocxJS();
docxJS.parse(
files[0],
function () {
docxJS.render($('#loaded-layout')[0], function (result) {
if (result.isError) {
console.log(result.msg);
} else {
console.log("Success Render");
}
});
}, function (e) {
console.log("Error!", e);
}
);
});
});
</script>
</body>
</html>
I tried changing var files = e.target.files; to var files = "C:/sda/path/to/docx"; but that didn't help.
I tried to change
var files = e.target.files;
to
var files = new Array(new File([""], "sample.docx"));
but it gives me OOXML parse error.
Update:
Lets say I have a file location variable in PHP and I wish to use that instead in the javascript code. How do I do it?
I also checked docx2html javascript code and here is the code for it:
<!DOCTYPE html>
<html>
<head>
<script src="index.js"></script>
<script>
function test(input){
require("docx2html")(input.files[0]).then(function(converted){
text.value=converted.toString()
})
}
</script>
</head>
<body>
<input type="file" style="position:absolute;top:0" onchange="test(this)">
<br/>
<br/>
<textarea id="text"></textarea>
</body>
</html>
Same issue need input.files[0] here as well
Update:
I am trying to use the method mentioned in the comments but encounter some errors:
var fil;
var getFileBlob = function (url, cb) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.addEventListener('load', function() {
cb(xhr.response);
});
xhr.send();
};
var blobToFile = function (blob, name) {
blob.lastModifiedDate = new Date();
blob.name = name;
return blob;
};
var getFileObject = function(filePathOrUrl, cb) {
getFileBlob(filePathOrUrl, function (blob) {
cb(blobToFile(blob, 'test.docx'));
});
};
getFileObject('demo.docx', function (fileObject) {
console.log(fileObject);
fil = fileObject;
});
The error primarily was “Cross origin requests are only supported for HTTP.” before I used https://calibre-ebook.com/downloads/demos/demo.docx instead of just demo.docx in above file path. This however gives another error:
Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.
which means chrome cannot load it. It needs to be working on a server. If someone can help providing a fix to make it work offline, let me know. The last method was asynchronous call.
In the browser, there is a sandbox policy.
It can not access files directly via Path.
Please access the file through drag & drop event or input file change event.

How do I define Watershed in Node.js?

When I execute the following code, I get the error: Reference Error: Watershed is not defined. How can I define it? Do I need a module to be installed for it?
var restify=require('restify');
var ws= new Watershed();
var server=restify.createServer();
server.get('websocket/attach', function upgradeRoute(req, res, next){
if(!res.claimUpgrade){
next(new Error("Connection must be upgraded."));
return;
}
var upgrade=res.claimUpgrade();
var shed=ws.accept(req, upgrade.socket, upgrade.head);
shed.on('text', function (msg){
console.log("The message is: "+msg);
});
shed.send("hello there");
next(false);
});
server.listen(8081, function(){
console.log('%s listening at %s', server.name, server.url);
});
There is also a section of the restify doc that mentioned how to handle the ability to upgrade sockets. I just struggled with this for an emarrassingly long time and thought I'd share the simple solution. In addtion the #Dibu Raj reply, you also need to create your restify server with the handleUpgrades option set to true. Here is a complete example to make restify work with websocket upgrades and watershed:
'use strict';
var restify = require('restify');
var watershed = require('watershed');
var ws = new watershed.Watershed();
var server = restify.createServer({
handleUpgrades: true
});
server.get('/websocket/attach', function (req, res, next) {
if (!res.claimUpgrade) {
next(new Error('Connection Must Upgrade For WebSockets'));
return;
}
console.log("upgrade claimed");
var upgrade = res.claimUpgrade();
var shed = ws.accept(req, upgrade.socket, upgrade.head);
shed.on('text', function(msg) {
console.log('Received message from websocket client: ' + msg);
});
shed.send('hello there!');
next(false);
});
//For a complete sample, here is an ability to serve up a subfolder:
server.get(/\/test\/?.*/, restify.serveStatic({
directory: './static',
default: 'index.html'
}));
server.listen(8080, function() {
console.log('%s listening at %s', server.name, server.url);
});
For an html page to test your new nodejs websocket server: write this html below into a file at ./static/test/index.html - point your browser to http://localhost:8080/test/index.html - open your browser debug console to see the message exchange.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Web Socket test area</title>
<meta name="description" content="Web Socket tester">
<meta name="author" content="Tim">
</head>
<body>
Test Text.
<script>
(function() {
console.log("Opening connection");
var exampleSocket = new WebSocket("ws:/localhost:8080/websocket/attach");
exampleSocket.onopen = function (event) {
console.log("Opened socket!");
exampleSocket.send("Here's some text that the server is urgently awaiting!");
};
exampleSocket.onmessage = function (event) {
console.log("return:", event.data);
exampleSocket.close();
}
})();
</script>
</body>
</html>
Your browser log will look something like this:
07:05:05.357 index.html:18 Opening connection
07:05:05.480 index.html:22 Opened socket!
07:05:05.481 index.html:26 return: hello there!
And your node log will look like:
restify listening at http://[::]:8080
client connected!
Rest service called started
upgrade claimed
Received message from websocket client: Here's some text that the server is urgently awaiting!
Documentation for this found at:
http://restify.com/#upgrade-requests
You should include the watershed library
var Watershed = require('lib/watershed').Watershed;

Adding images from url to a zip file using jsZip

I am trying to create a zip file using jsZip . The contents of the zip file are images from the web.
I have created the following code. But when i run it all am getting is an empty zip file of 22kb.
<html>
<body>
<script type="text/javascript" src="jszip.js"></script>
<script type="text/javascript" src="FileSaver.js"></script>
<script type="text/javascript" src="jszip-utils.min.js"></script>
<script>
var imgLinks = ["url1", "url2", "url3"];
function create_zip() {
var zip = new JSZip();
for (var i = 0; i < imgLinks.length; i++) {
JSZipUtils.getBinaryContent(imgLinks[i], function(err, data) {
if (err) {
alert("Problem happened when download img: " + imgLink[i]);
console.erro("Problem happened when download img: " + imgLink[i]);
deferred.resolve(zip); // ignore this error: just logging
// deferred.reject(zip); // or we may fail the download
} else {
zip.file("picture" + i + ".jpg", data, {
binary: true
});
deferred.resolve(zip);
}
});
}
var content = zip.generate({
type: "blob"
});
saveAs(content, "downloadImages.zip");
}
</script>
<br/>
<br/>
<center>
Click the button to generate a ZIP file
<br/>
<input id="button" type="button" onclick="create_zip()" value="Create Zip" />
</center>
</body>
</html>
(url1 , url2 and url3 replaced by image urls i want to download).
Why am i getting these empty zip files?
JSZipUtils.getBinaryContent is asynchronous : the content will be added after the call to zip.generate(). You should wait for all images before generating the zip file.
Edit :
If the files you are loading are on a different server, this server need to send additional HTTP headers, like Access-Control-Allow-Origin: server-hosting-the-page.com. The Firefox or Chrome debug console will show an error in that case. The js library can't detect a CORS issue (see here) and will trigger a success with an empty content.

Categories

Resources