NodeJS: Set path to FFMPEG binaries for module Fluent-FFMPEG - node.js

I am building a application that uses the module node-fluent-ffmpeg. https://github.com/schaermu/node-fluent-ffmpeg
And I'm trying to package the ffmpeg binaries along with my application.
I want to do this so (especially on Windows) the user does not have to install FFMPEG manually.
Sadly everything I've tried results in errors. I've tried the following:
ffmpeg.setFfmpegPath : Gives an error saying setFfmpegPath is not a method
and:
proc.setFfmpegPath : Gives a createproces error.
It seems I'm doing something wrong. Could someone point out my mistake.
Thanks a lot.

I fix it!
I did not know I had to include the binary itself in the path. So I made something like this:
if(os.platform() === 'win32'){
var ffmpegPath = './bin/ffmpeg/ffmpeg.exe'
}else{
var ffmpegPath = './bin/ffmpeg/ffmpeg'
}
proc = new ffmpeg({ source: movieUrl, nolog: true, timeout: FFMPEG_TIMEOUT })
proc.setFfmpegPath(ffmpegPath)
proc.addOptions(opts)
proc.writeToStream(response, function(return_code, error){

In my case I have downloaded the npm i -S ffmpeg-binaries~ and after that I just set the process.env.FFMPEG_PATH to './node_modules/ffmpeg-binaries/bin/ffmpeg.exe'. This worked for me.

Related

Node.js & GM library

I'm working on a Node.js application and I'm facing a critical problem right now which it is Resizing img after uploading .. the thing is everything goes right starting from uploading to getting the path of the uploaded img but I can't understand why this is happening .. here's my code and the error
var fileName = req.file.filename;
var filpath = "/uploads/" + fileName;
gm(filpath)
.resize(240, 240)
.write('/uploads/'+'resized-'+fileName, function (err) {
if (err) console.log(err);
else console.log("HOOLA");
});
The error is
{ [Error: Command failed: Invalid Parameter - /uploaded_file-1474832730810.png
] code: 4, signal: null }
The log
UPDATE:
first of all I would like to thank #r-a-lucas,and here's the full answer for Windows users:
npm install gm --save
download the latest ImageMagick from here ImageMagic EXE
Make sure it is excusable (advance system settings -> Advance -> Environment Variables -> add Path most likely in C:\Program Files\ImageMagick-*** )
In your JS file make sure to add (__dirname) to get the full path of the img. gm(__dirname+filpath)
It looks like you may need to install imagemagick for Windows. Check out this answer here: imagemagick with nodejs not working. Hopefully that helps. Best.

How can I bundle a precompiled binary with electron

I am trying to include a precompiled binary with an electron app. I began with electron quick start app and modified my renderer.js file to include this code that is triggered when a file is dropped on the body:
spawn = require('child_process').spawn,
ffmpeg = spawn('node_modules/.bin/ffmpeg', ['-i', clips[0], '-an', '-q:v', '1', '-vcodec', 'libx264', '-y', '-pix_fmt', 'yuv420p', '-vf', 'setsar=1,scale=trunc(iw/2)*2:trunc(ih/2)*2,crop=in_w:in_h-50:0:50', '/tmp/out21321.mp4']);
ffmpeg.stdout.on('data', data => {
console.log(`stdout: ${data}`);
});
ffmpeg.stderr.on('data', data => {
console.log(`stderr: ${data}`);
});
I have placed my precompiled ffmpeg binary in node_modules/.bin/. Everything works great in the dev panel, but when I use electron-packager to set up the app, it throws a spawn error ENOENT to the console when triggered. I did find a very similar question on SO, but the question doesn't seem to be definitively answered. The npm page on electron-packager does show that they can be bundled, but I cannot find any documentation on how to do so.
The problem is that electron-builder or electron-packager will bundle your dependency into the asar file. It seems that if the dependency has a binary into node_modules/.bin it is smart enough to not package it.
This is the documentation for asar packaging for electron-builder on that topic. It says
Node modules, that must be unpacked, will be detected automatically
I understand that it is related to existing binaries in node_modules/.bin.
If the module you are using is not automatically unpacked you can disable asar archiving completely or explicitly tell electron-builder to not pack certain files. You do so in your package.json file like this:
"build": {
"asarUnpack": [
"**/app/node_modules/some-module/*"
],
For your particular case
I ran into the same issue with ffmpeg and this is what I've done:
Use ffmpeg-static. This package bundles statically compiled ffmpeg binaries for Windows, Mac and Linux. It also provides a way to get the full path of the binary for the OS you are running: require('ffmpeg-static').path
This will work fine in development, but we still need to troubleshoot the distribution problem.
Tell electron-builder to not pack the ffmpeg-static module:
"build": {
"asarUnpack": [
"**/app/node_modules/ffmpeg-static/*"
],
Now we need to slightly change the code to get the right path to ffmpeg with this code: require('ffmpeg-static').path.replace('app.asar', 'app.asar.unpacked') (if we are in development the replace() won't replace anything which is fine).
If you are using webpack (or other javascript bundler)
I ran into the issue that require('ffmpeg-static').path was returning a relative path in the renderer process. But the issue seemed to be that webpack changes the way the module is required and that prevents ffmpeg-static to provide a full path. In the Dev Tools the require('ffmpeg-static').path was working fine when run manually, but when doing the same in the bundled code I was always getting a relative path. So this is what I did.
In the main process add this before opening the BrowserWindow: global.ffmpegpath = require('ffmpeg-static').path.replace('app.asar', 'app.asar.unpacked'). The code that runs in the main process is not bundled by webpack so I always get a full path with this code.
In the renderer process pick the value this way: require('electron').remote.getGlobal('ffmpegpath')
I know I'm a bit late but just wanted to mention ffbinaries npm package I created a while ago exactly for this purpose.
It'll allow you to download ffmpeg/ffplay/ffserver/ffprobe binaries to specified location either during application boot (so you don't need to bundle it with your application) or in a CI setup. It can autodetect platform, you can also specify it manually.
If anyone happens to need an answer to this question: I do have a solution to this, but I have no idea if this is considered best practice. I couldn't find any good documentation for including 3rd party precompiled binaries, so I just fiddled with it until it finally worked. Here's what I did (starting with the electron quick start, node.js v6):
From the app directory I ran the following commands to include the ffmpeg binary as a module:
mkdir node_modules/ffmpeg
cp /usr/local/bin/ffmpeg node_modules/ffmpeg/
ln -s ../ffmpeg/ffmpeg node_modules/.bin/ffmpeg
(replace /usr/local/bin/ffmpeg with your current binary path, download it from here) Placing the link allowed electron-packager to include the binary I saved to node_modules/ffmpeg/.
Then to get the bundled app path I installed the npm package app-root-dir by running the following command:
npm i -S app-root-dir
Since I could then get the app path, I just appended the subfolder for my binary and spawned from there. This is the code that I placed in renderer.js:.
var appRootDir = require('app-root-dir').get();
var ffmpegpath=appRootDir+'/node_modules/ffmpeg/ffmpeg';
console.log(ffmpegpath);
const
spawn = require( 'child_process' ).spawn,
ffmpeg = spawn( ffmpegpath, ['-i',clips_input[0]]); //add whatever switches you need here
ffmpeg.stdout.on( 'data', data => {
console.log( `stdout: ${data}` );
});
ffmpeg.stderr.on( 'data', data => {
console.log( `stderr: ${data}` );
});
This is how I would do it:
Taking cues from tsuriga's answer, here is my code:
Note: replace or add OS path accordingly.
Create a directory ./resources/mac/bin
Place you binaries inside this folder
Create file ./app/binaries.js and paste the following code:
'use strict';
import path from 'path';
import { remote } from 'electron';
import getPlatform from './get-platform';
const IS_PROD = process.env.NODE_ENV === 'production';
const root = process.cwd();
const { isPackaged, getAppPath } = remote.app;
const binariesPath =
IS_PROD && isPackaged
? path.join(path.dirname(getAppPath()), '..', './Resources', './bin')
: path.join(root, './resources', getPlatform(), './bin');
export const execPath = path.resolve(path.join(binariesPath, './exec-file-name'));
Create file ./app/get-platform.js and paste the following code:
'use strict';
import { platform } from 'os';
export default () => {
switch (platform()) {
case 'aix':
case 'freebsd':
case 'linux':
case 'openbsd':
case 'android':
return 'linux';
case 'darwin':
case 'sunos':
return 'mac';
case 'win32':
return 'win';
}
};
Add the following code inside the ./package.json file:
"build": {
....
"extraFiles": [
{
"from": "resources/mac/bin",
"to": "Resources/bin",
"filter": [
"**/*"
]
}
],
....
},
import binary file path as:
import { execPath } from './binaries';
#your program code:
var command = spawn(execPath, arg, {});
Why this is better?
Most of the answers require an additional package called app-root-dir
The original answer doesn't handle the (env=production) build or the pre-packed versions properly. He/she has only taken care of development and post-packaged versions.

nodeJS:fs.write callback and fs.writeFile not working

I knew nothing about fs until I was learning to use casperjs to scrape some content from a website and save them to a file. Following some examples on the web, I write this file scrape.js (The json data has been tested so it has nothing to do with the issue):
var fs = require('fs');
var url = "http://w.nycweb.io/index.php?option=com_k2&view=itemlist&id=4&Itemid=209&format=json";
var casper = require('casper').create();
casper.start(url,function(){
var json = JSON.parse(this.fetchText('pre'));
var jsonOfItems={},items = json.items;
items.forEach(function(item){
jsonOfItems[item.id] = item.introtext.split('\n');
})
fs.write('videoLinks.json',JSON.stringify(jsonOfItems),function(err){
if (err) console.log(err);
console.log('videoLinks.json saved');
})
});
casper.run();
When I do casperjs scrape.js in command line of my Ubuntu 14.04 server, I won't see the file saved message as expected, although the file is properly saved. So this is the first question: why the callback isn't running at all?
Secondly, I also tried fs.writeFile, but when I replace fs.write with it, the file isn't saved at all, nor is there any error information.
I do notice that in casper documentation it's said that casper is not a node.js module and some module of node.js won't be available, but I doubt it has anything to do with my issues. And I think it worths to mention that previously when I run this script I only get a respond like
I'm 'fs' module.
I had to follow this question to reinstall fs module globally to get it working.
fs.write expects a file descriptor where you are trying to give it a filename. Try fs.writeFile. https://nodejs.org/dist/latest-v4.x/docs/api/fs.html#fs_fs_writefile_file_data_options_callback
Edit: Oh you tried that. Are you sure it didn't write it somewhere like the root directory? Tried a full path in there?
And what version of node are you running?

nodejitsu 400 Error: ENOENT, open '/opt/run/snapshot/package/images/tmp/72118-89rld0.png

I'm using nodejitsu to deploy a simple image upload program with express. In my code I've changed the default upload directory by following command
app.use(express.bodyParser({
uploadDir: __dirname + "/images/tmp"
}));
It's working fine on my localhost but when I'm using nodejitsu I'm getting this error
400 Error: ENOENT, open '/opt/run/snapshot/package/images/tmp/72118-89rld0.png.
Can anybody tell me how to make it work on nodejitsu as well? Actually I'm new to node as well as nodejitsu.
I had the same problem. Try to check directory at application start:
var fs = require('fs'),
upload = __dirname + "/images/tmp";
fs.exists(upload, function (exist) {
if (!exist) {
fs.mkdir(upload);
}
});
It was helpful for me, may be it would helpful for you.
make sure that directory /opt/run/snapshot/package/images/tmp/ exists. Otherways just mkdir those directory
At first check that the directory exist or not. If not then create it and follow the command
sudo jitsu deploy
I think the problem will be solved.

How to upload file using easy-ftp in node?

I am trying to upload a file into my hosting server using node and easy-ftp.
I try with the following code:
var EasyFtp = require ("easy-ftp");
var ftp = new EasyFtp();
var config = {
host:'homexxxxx.1and1-data.host',
type:'SFTP',
port:'22',
username:'u90xxxx',
password:"mypass"
};
ftp.connect(config);
ftp.upload("/test/test.txt", "/test.txt", function(err){
if (err) throw err;
ftp.close();
});
No error message but no file uploaded
I tried the same using promises
const EasyFTP = require('easy-ftp-extra')
const ftp = new EasyFTP()
const config = {
host:'homexxxxx.1and1-data.host',
type:'SFTP',
port:'22',
username:'u90xxxx',
password:"mypass"
};
ftp.connect(config);
ftp.upload('/test.txt', '/test.txt')
.then(console.log)
.catch(console.error)
ftp.upload()
The same issued. No file is uploaded. No error in node console.
The config is the same used in filezilla to transfer files. SFTP protocol. Everything working well with filezilla.
What I am doing wrong?
Looks like you may have a path problem over here.
"/test/test.txt"
The path specified will try to take file from root folder like "c:\test\test.txt".
Assuming you want the file to be taken from your project folder try this path:
"./test/test.txt"
Other things in your code are precisely the same as in mine and mine works.
For me, it was just silently failing, and intelli-sense was not available.
npm remove easy-ftp
npm install easy-ftp
npm audit fix --force until no more vulnerabilities
Afterwards, intelli-sense was available and it started working.

Resources