如何将预编译的二进制文件与电子捆绑在一起

时间:2016-07-13 21:28:14

标签: node.js ffmpeg electron

我正在尝试使用电子应用程序包含预编译的二进制文件。我从电子快速启动应用程序开始,修改了我的renderer.js文件,以包含在文件被放置在正文上时触发的代码:

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}`);
});

我已将预编译的 ffmpeg 二进制文件放在node_modules/.bin/中。在开发面板中一切都很好用,但是当我使用电子打包器来设置应用程序时,它会在触发时向控制台抛出spawn error ENOENT。我确实在SO上找到了very similar question,但这个问题似乎没有得到明确的回答。 npm page on electron-packager确实显示它们可以捆绑,但我找不到任何有关如何操作的文档。

4 个答案:

答案 0 :(得分:23)

问题是electron-builderelectron-packager会将您的依赖项捆绑到asar文件中。似乎如果依赖项有一个二进制文件node_modules/.bin,它就足够聪明,不能打包它。

这是该主题electron-builder的{​​{3}}。它说

  

将自动检测必须解压缩的节点模块

我了解它与node_modules/.bin中的现有二进制文件有关。

如果您使用的模块未自动解压缩,您可以完全禁用asar存档,或明确告诉electron-builder不要打包某些文件。您可以在package.json文件中执行此操作:

  "build": {
    "asarUnpack": [
      "**/app/node_modules/some-module/*"
    ],

针对您的具体情况

我与ffmpeg遇到了同样的问题,这就是我所做的:

  • 使用documentation for asar packaging。该软件包捆绑了Windows,Mac和Linux的静态编译的ffmpeg二进制文件。它还提供了一种方法来获取正在运行的操作系统的二进制文件的完整路径:require('ffmpeg-static').path
  • 这在开发中可以正常工作,但我们仍然需要解决分发问题。
  • 告诉electron-builder不要打包ffmpeg-static模块:

    " build":{     " asarUnpack":[       " ** /应用/ node_modules / ffmpeg的静电/ *"     ],

  • 现在我们需要稍微更改代码,以便使用以下代码获取ffmpeg的正确路径:require('ffmpeg-static').path.replace('app.asar', 'app.asar.unpacked')(如果我们正在开发replace()赢了&#39} t替换任何好的东西。)

如果您使用的是webpack(或其他javascript捆绑器)

我遇到了require('ffmpeg-static').path在渲染器进程中返回相对路径的问题。但问题似乎是webpack改变了模块的需求方式,并阻止ffmpeg-static提供完整路径。在开发工具中,require('ffmpeg-static').path在手动运行时工作正常,但是当在捆绑代码中执行相同操作时,我总是得到相对路径。所以这就是我所做的。

  • 在主要流程中,在打开BrowserWindowglobal.ffmpegpath = require('ffmpeg-static').path.replace('app.asar', 'app.asar.unpacked')之前添加此内容。在主进程中运行的代码没有被webpack捆绑,所以我总是得到这个代码的完整路径。
  • 在渲染器过程中以这种方式选择值:require('electron').remote.getGlobal('ffmpegpath')

答案 1 :(得分:3)

我知道我有点迟了但只是想提一下我刚才为此创建的ffbinaries npm包。

它允许您在应用程序启动期间(因此您不需要将其与应用程序捆绑在一起)或在CI设置中将ffmpeg / ffplay / ffserver / ffprobe二进制文件下载到指定位置。它可以自动检测平台,您也可以手动指定它。

答案 2 :(得分:2)

如果有人碰巧需要回答这个问题:我确实有解决方法,但我不知道这是否是最佳实践。我无法找到包含第三方预编译二进制文件的任何好文档,所以我只是摆弄它直到它最终起作用。这就是我所做的(从电子快速启动开始,node.js v6):

在app目录中,我运行了以下命令,将ffmpeg二进制文件包含为模块:

mkdir node_modules/ffmpeg
cp /usr/local/bin/ffmpeg node_modules/ffmpeg/
ln -s ../ffmpeg/ffmpeg node_modules/.bin/ffmpeg

(将/ usr / local / bin / ffmpeg替换为您当前的二进制路径,从此处下载)放置链接允许electron-packager包含我保存到node_modules / ffmpeg /的二进制文件。

然后,为了获得捆绑的应用程序路径,我通过运行以下命令安装了npm包app-root-dir:

npm i -S app-root-dir

因为我可以获得应用程序路径,所以我只是附加了我的二进制文件的子文件夹并从那里生成。这是我放在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}` );
    });

答案 3 :(得分:0)

这就是我要做的:

根据tsuriga's answer的提示,这是我的代码:

注意:相应地替换或添加 OS 路径。

  • 创建目录。/resources / mac / bin
  • 将二进制文件放在此文件夹中
  • 创建文件 ./ app / binaries.js 并粘贴以下代码:
'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'));
  • 创建文件 ./ app / get-platform.js 并粘贴以下代码:
'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';
  }
};
  • ./ package.json 文件内添加以下代码:
"build": {
....

 "extraFiles": [
      {
        "from": "resources/mac/bin",
        "to": "Resources/bin",
        "filter": [
          "**/*"
        ]
      }
    ],

....
},
  • 将二进制文件路径导入为:
import { execPath } from './binaries';

#your program code:
var command = spawn(execPath, arg, {});

为什么这样更好?

  • 大多数答案都需要一个名为 app-root-dir

  • 的附加软件包。
  • 原始答案无法正确处理(env = production) build 预包装版本。他/她只负责开发和后期打包版本。