node-av 0.0.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/install/check.js CHANGED
@@ -1,113 +1,123 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { getFFmpegLibraryVersions, globalFFmpegVersion, log, spawnRebuild, useGlobalFFmpeg } from './ffmpeg.js';
3
+ import { existsSync } from 'node:fs';
4
+ import { createRequire } from 'node:module';
5
+ import { join } from 'node:path';
4
6
 
5
- const buildFromSource = async (msg) => {
6
- log(msg);
7
- log('Building from source requires:');
8
- log(' - FFmpeg 7.1+ with development headers');
9
- log(' - Python 3.12+');
10
- log(' - node-gyp and node-addon-api');
7
+ import { log, spawnRebuild, useGlobalFFmpeg } from './ffmpeg.js';
11
8
 
12
- // Check for node-addon-api and node-gyp
13
- let hasNodeAddonApi = false;
14
- let hasNodeGyp = false;
9
+ const require = createRequire(import.meta.url);
10
+
11
+ const tryLoadPrebuilt = () => {
12
+ // Try to load from platform-specific package (optionalDependencies)
13
+ const platform = process.platform;
14
+ const arch = process.arch;
15
+ const packageName = `@seydx/node-av-${platform}-${arch}`;
15
16
 
16
17
  try {
17
- // @ts-expect-error
18
- await import('node-addon-api');
19
- hasNodeAddonApi = true;
18
+ const packagePath = require.resolve(`${packageName}/node-av.node`);
19
+ if (existsSync(packagePath)) {
20
+ log(`Using prebuilt binary from ${packageName}`);
21
+ return true;
22
+ }
20
23
  } catch {
21
- // Not installed
24
+ // Package not installed or file not found
25
+ }
26
+
27
+ // Try local binary folder (for development)
28
+ const localBinary = join(process.cwd(), 'binary', 'node-av.node');
29
+ if (existsSync(localBinary)) {
30
+ log('Using local binary from binary/node-av.node');
31
+ return true;
22
32
  }
23
33
 
34
+ return false;
35
+ };
36
+
37
+ const buildFromSource = () => {
38
+ log('Building from source...');
39
+
40
+ // Check for required build dependencies
41
+ const missingDeps = [];
42
+
24
43
  try {
25
- // @ts-expect-error
26
- await import('node-gyp');
27
- hasNodeGyp = true;
44
+ require('node-addon-api');
28
45
  } catch {
29
- // Not installed
46
+ missingDeps.push('node-addon-api');
30
47
  }
31
48
 
32
- if (!hasNodeAddonApi || !hasNodeGyp) {
49
+ try {
50
+ require('node-gyp');
51
+ } catch {
52
+ missingDeps.push('node-gyp');
53
+ }
54
+
55
+ if (missingDeps.length > 0) {
33
56
  log('');
34
- log('Missing build dependencies. Please install:');
35
- log(' npm install --save-dev node-addon-api node-gyp');
57
+ log(`Missing build dependencies: ${missingDeps.join(', ')}`);
58
+ log('Please install:');
59
+ log(` npm install --save-dev ${missingDeps.join(' ')}`);
36
60
  log('');
37
61
  log('Then run npm install again.');
38
62
  process.exit(1);
39
63
  }
40
64
 
41
- log('Found required build dependencies');
42
65
  log('Building native bindings...');
43
66
 
44
67
  const status = spawnRebuild();
45
68
  if (status !== 0) {
69
+ log('');
46
70
  log('Build failed. Please ensure you have:');
47
71
  log(' - FFmpeg 7.1+ libraries and headers installed');
48
72
  log(' - Python 3.12+ installed');
49
73
  log(' - A C++ compiler with C++17 support');
74
+ log('');
50
75
  log('See https://github.com/seydx/av for detailed requirements');
51
76
  process.exit(status);
52
77
  }
78
+
79
+ log('Build completed successfully!');
53
80
  };
54
81
 
55
82
  (async () => {
56
83
  try {
57
- // Check if we should build from source
58
- const shouldBuildFromSource = process.env.npm_config_build_from_source;
59
-
60
- // Try to load the prebuilt binary first (unless explicitly building from source)
61
- if (!shouldBuildFromSource) {
62
- try {
63
- // Check if platform-specific package exists (optionalDependencies)
64
- const platform = process.platform;
65
- const arch = process.arch;
66
- const packageName = `@seydx/node-av-${platform}-${arch}`;
67
-
68
- // Try to resolve the package
69
- await import(`${packageName}/package.json`);
70
- log(`Using prebuilt binary from ${packageName}`);
84
+ const shouldBuildFromSource = process.env.npm_config_build_from_source === 'true';
85
+
86
+ // Priority 1: User explicitly wants to build from source
87
+ if (shouldBuildFromSource) {
88
+ if (!useGlobalFFmpeg()) {
89
+ log('--build-from-source specified but no FFmpeg libraries found');
90
+ log('Please install FFmpeg 7.1+ with development headers');
91
+ process.exit(1);
92
+ }
93
+ // Fall through to build logic below
94
+ } else {
95
+ // Priority 2: Try to use prebuilt binary if not forcing source build
96
+ if (tryLoadPrebuilt()) {
71
97
  return;
72
- } catch {
73
- // No prebuilt binary available, will build from source
74
98
  }
75
99
  }
76
100
 
77
- // No prebuilt binary or --build-from-source flag, check for system FFmpeg
78
- if (useGlobalFFmpeg(log)) {
79
- const versions = getFFmpegLibraryVersions();
80
- if (versions && versions.length > 0) {
81
- log('Detected globally-installed FFmpeg libraries:');
82
- for (const lib of versions) {
83
- log(` ✓ ${lib.name.padEnd(20)} v${lib.version} (${lib.description})`);
84
- }
85
- await buildFromSource('Building with system FFmpeg');
86
- } else {
87
- await buildFromSource(`Detected globally-installed FFmpeg v${globalFFmpegVersion()}`);
88
- }
89
- } else {
90
- // No system FFmpeg found
101
+ // Build from source (either requested or as fallback)
102
+ if (useGlobalFFmpeg()) {
103
+ // Determine why we're building
91
104
  if (shouldBuildFromSource) {
92
- log('--build-from-source specified but no FFmpeg libraries found');
93
- log('Please install FFmpeg 7.1+ with development headers');
94
- process.exit(1);
105
+ log('Building from source as requested');
95
106
  } else {
96
107
  log('No prebuilt binary available for your platform');
97
- log('No system FFmpeg libraries found (requires v7.1+)');
98
- log('');
99
- log('Please install FFmpeg 7.1+ and build dependencies:');
100
- log(' - FFmpeg development libraries');
101
- log(' - Python 3.12+');
102
- log(' - npm install --save-dev node-addon-api node-gyp');
103
- log('');
104
- log('Then run npm install again.');
105
- process.exit(1);
108
+ log('System FFmpeg detected, building from source automatically');
106
109
  }
110
+
111
+ buildFromSource();
112
+ } else {
113
+ // No FFmpeg found and no prebuilt available
114
+ log('⚠️ No prebuilt binary and no system FFmpeg found');
115
+ log('Please install FFmpeg 7.1+ with development headers');
116
+ log('See https://github.com/seydx/av for installation instructions');
117
+ process.exit(1);
107
118
  }
108
119
  } catch (err) {
109
- const summary = err.message.split(/\n/).slice(0, 1);
110
- console.log(`node-av: installation error: ${summary}`);
120
+ console.error(`node-av: Installation error: ${err.message}`);
111
121
  process.exit(1);
112
122
  }
113
123
  })();
package/install/ffmpeg.js CHANGED
@@ -1,29 +1,13 @@
1
- // https://github.com/lovell/sharp/blob/main/lib/libvips.js
1
+ // Adapted from https://github.com/lovell/sharp/blob/main/lib/libvips.js
2
2
 
3
3
  import detectLibc from 'detect-libc';
4
4
  import { spawnSync } from 'node:child_process';
5
- import { readFileSync } from 'node:fs';
6
- import { dirname, join } from 'node:path';
7
- import { fileURLToPath } from 'node:url';
8
- import semverCoerce from 'semver/functions/coerce.js';
9
- import semverGreaterThanOrEqualTo from 'semver/functions/gte.js';
10
- import semverSatisfies from 'semver/functions/satisfies.js';
11
-
12
- const __dirname = dirname(fileURLToPath(import.meta.url));
13
- const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
14
- const { engines } = packageJson;
15
5
 
16
6
  const spawnSyncOptions = {
17
7
  encoding: 'utf8',
18
8
  shell: true,
19
9
  };
20
10
 
21
- // Minimum FFmpeg version required
22
- export const minimumFFmpegVersion = '7.1.0';
23
-
24
- // Minimum Python version required for node-gyp
25
- export const minimumPythonVersion = '3.12.0';
26
-
27
11
  export const log = (item) => {
28
12
  if (item instanceof Error) {
29
13
  console.error(`node-av: Installation error: ${item.message}`);
@@ -42,122 +26,52 @@ export const buildPlatformArch = () => {
42
26
  return `${npm_config_platform ?? process.platform}${libc}-${npm_config_arch ?? process.arch}`;
43
27
  };
44
28
 
45
- export const isUnsupportedNodeRuntime = () => {
46
- if (process.release?.name === 'node' && process.versions) {
47
- if (!semverSatisfies(process.versions.node, engines.node)) {
48
- return { found: process.versions.node, expected: engines.node };
49
- }
50
- }
51
- };
52
-
53
29
  export const spawnRebuild = () =>
54
30
  spawnSync('node-gyp rebuild', {
55
31
  ...spawnSyncOptions,
56
32
  stdio: 'inherit',
57
33
  }).status;
58
34
 
59
- export const globalFFmpegVersion = () => {
60
- if (process.platform !== 'win32') {
61
- const ffmpegVersion =
62
- spawnSync('pkg-config --modversion libavcodec', {
63
- ...spawnSyncOptions,
64
- env: {
65
- ...process.env,
66
- PKG_CONFIG_PATH: pkgConfigPath(),
67
- },
68
- }).stdout || '';
69
- return ffmpegVersion.trim();
70
- } else {
35
+ export const pkgConfigPath = () => {
36
+ if (process.platform === 'win32') {
71
37
  return '';
72
38
  }
73
- };
74
39
 
75
- export const getFFmpegLibraryVersions = () => {
76
- if (process.platform === 'win32') {
77
- return null;
78
- }
40
+ const paths = [];
79
41
 
80
- const libraries = [
81
- { name: 'libavcodec', description: 'codec library' },
82
- { name: 'libavformat', description: 'format library' },
83
- { name: 'libavutil', description: 'utility library' },
84
- { name: 'libswscale', description: 'video scaling library' },
85
- { name: 'libswresample', description: 'audio resampling library' },
86
- { name: 'libavfilter', description: 'filter library' },
87
- { name: 'libavdevice', description: 'device library' },
88
- { name: 'libpostproc', description: 'post-processing library' },
89
- ];
90
-
91
- const versions = [];
92
-
93
- for (const lib of libraries) {
94
- const version = spawnSync(`pkg-config --modversion ${lib.name}`, {
95
- ...spawnSyncOptions,
96
- env: {
97
- ...process.env,
98
- PKG_CONFIG_PATH: pkgConfigPath(),
99
- },
100
- }).stdout;
101
-
102
- if (version && version.trim()) {
103
- versions.push({
104
- name: lib.name,
105
- description: lib.description,
106
- version: version.trim(),
107
- });
42
+ // Try homebrew path on macOS
43
+ if (process.platform === 'darwin') {
44
+ const brewPath = spawnSync('brew --prefix 2>/dev/null', spawnSyncOptions).stdout;
45
+ if (brewPath) {
46
+ paths.push(`${brewPath.trim()}/lib/pkgconfig`);
108
47
  }
109
48
  }
110
49
 
111
- return versions.length > 0 ? versions : null;
112
- };
50
+ // Add standard paths
51
+ paths.push(process.env.PKG_CONFIG_PATH, '/usr/local/lib/pkgconfig', '/usr/lib/pkgconfig', '/usr/local/libdata/pkgconfig', '/usr/libdata/pkgconfig');
113
52
 
114
- export const pkgConfigPath = () => {
115
- if (process.platform !== 'win32') {
116
- const brewPkgConfigPath = spawnSync('which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2', spawnSyncOptions).stdout || '';
117
- return [
118
- brewPkgConfigPath.trim(),
119
- process.env.PKG_CONFIG_PATH,
120
- '/usr/local/lib/pkgconfig',
121
- '/usr/lib/pkgconfig',
122
- '/usr/local/libdata/pkgconfig',
123
- '/usr/libdata/pkgconfig',
124
- ]
125
- .filter(Boolean)
126
- .join(':');
127
- } else {
128
- return '';
129
- }
53
+ return paths.filter(Boolean).join(':');
130
54
  };
131
55
 
132
- export const useGlobalFFmpeg = (logger) => {
133
- const globalVersion = globalFFmpegVersion();
134
- if (!globalVersion) {
56
+ export const useGlobalFFmpeg = () => {
57
+ if (process.platform === 'win32') {
135
58
  return false;
136
59
  }
137
60
 
138
- // Check if all required libraries are present
139
- const requiredLibs = ['libavcodec', 'libavformat', 'libavutil', 'libswscale', 'libswresample'];
140
- for (const lib of requiredLibs) {
141
- const version =
142
- spawnSync(`pkg-config --modversion ${lib}`, {
143
- ...spawnSyncOptions,
144
- env: {
145
- ...process.env,
146
- PKG_CONFIG_PATH: pkgConfigPath(),
147
- },
148
- }).stdout || '';
149
- if (!version?.trim()) {
150
- if (logger) {
151
- logger(`Missing required FFmpeg library: ${lib}`);
152
- }
153
- return false;
154
- }
155
- }
61
+ let ffmpegVersion =
62
+ spawnSync('pkg-config --modversion libavcodec', {
63
+ ...spawnSyncOptions,
64
+ env: {
65
+ ...process.env,
66
+ PKG_CONFIG_PATH: pkgConfigPath(),
67
+ },
68
+ }).stdout || '';
69
+
70
+ ffmpegVersion = ffmpegVersion.trim();
156
71
 
157
- const coercedVersion = semverCoerce(globalVersion);
158
- if (!coercedVersion) {
72
+ if (!ffmpegVersion) {
159
73
  return false;
160
74
  }
161
75
 
162
- return semverGreaterThanOrEqualTo(coercedVersion.version, minimumFFmpegVersion);
76
+ return true;
163
77
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-av",
3
- "version": "0.0.1",
3
+ "version": "1.0.1",
4
4
  "description": "FFmpeg bindings for Node.js",
5
5
  "author": "seydx (https://github.com/seydx/av)",
6
6
  "type": "module",
@@ -32,17 +32,16 @@
32
32
  }
33
33
  },
34
34
  "scripts": {
35
- "build": "npm run build:native && npm run build:tsc",
36
- "build:all": "npm run build:native && npm run build:tests && npm run build:examples && npm run build:tsc",
35
+ "build": "npm run generate && npm run build:tests && npm run build:examples && npm run build:tsc && npm run build:native",
37
36
  "build:examples": "tsc -p tsconfig.examples.json",
38
37
  "build:native": "node-gyp rebuild && cpy --flat build/Release/node-av.node binary/",
39
38
  "build:tests": "tsc -p tsconfig.tests.json",
40
- "build:tsc": "rimraf dist && npm run generate:constants && npm run generate:layouts && tsc -p tsconfig.build.json",
41
- "clean": "node-gyp clean && rimraf dist",
42
- "clean:tsc": "npm run format && npm run lint:fix && npm run generate:constants && npm run build:tests && npm run build:examples && npm run build:tsc",
43
- "format": "prettier --write 'src/' --write 'test/' --write 'scripts/' --write 'examples/' --ignore-unknown --no-error-on-unmatched-pattern",
44
- "generate:constants": "node scripts/generate-constants.js && prettier --write 'src/lib/constants.ts'",
45
- "generate:layouts": "node scripts/generate-channel-layouts.js && prettier --write 'src/lib/channel-layouts.ts'",
39
+ "build:tsc": "rimraf dist && tsc -p tsconfig.build.json",
40
+ "clean": "rimraf dist && node-gyp clean && npm run generate && npm run format && npm run lint:fix && npm run build:tests && npm run build:examples && npm run build:tsc",
41
+ "format": "prettier --write src/ --write test/ --write scripts/ --write examples/ --ignore-unknown --no-error-on-unmatched-pattern",
42
+ "generate": "npm run generate:constants && npm run generate:layouts",
43
+ "generate:constants": "node scripts/generate-constants.js && prettier --write src/lib/constants.ts",
44
+ "generate:layouts": "node scripts/generate-channel-layouts.js && prettier --write src/lib/channel-layouts.ts",
46
45
  "install": "node install/check.js",
47
46
  "install-updates": "npm i --save",
48
47
  "lint": "eslint .",
@@ -50,20 +49,19 @@
50
49
  "release:patch": "node scripts/prepare-release.js patch",
51
50
  "release:minor": "node scripts/prepare-release.js minor",
52
51
  "release:major": "node scripts/prepare-release.js major",
53
- "test:all": "npm run build:tests && npm run build:tsc && tsx --test test/**/*.test.ts",
52
+ "test:all": "npm run generate && npm run build:tests && npm run build:tsc && tsx --test test/*.test.ts",
54
53
  "update": "updates --update ./"
55
54
  },
56
55
  "dependencies": {
57
- "detect-libc": "^2.0.4",
58
- "semver": "^7.7.2"
56
+ "detect-libc": "^2.0.4"
59
57
  },
60
58
  "optionalDependencies": {
61
- "@seydx/node-av-darwin-arm64": "^0.0.1",
62
- "@seydx/node-av-darwin-x64": "^0.0.1",
63
- "@seydx/node-av-linux-arm64": "^0.0.1",
64
- "@seydx/node-av-linux-x64": "^0.0.1",
65
- "@seydx/node-av-win32-arm64": "^0.0.1",
66
- "@seydx/node-av-win32-x64": "^0.0.1"
59
+ "@seydx/node-av-darwin-arm64": "^1.0.1",
60
+ "@seydx/node-av-darwin-x64": "^1.0.1",
61
+ "@seydx/node-av-linux-arm64": "^1.0.1",
62
+ "@seydx/node-av-linux-x64": "^1.0.1",
63
+ "@seydx/node-av-win32-arm64": "^1.0.1",
64
+ "@seydx/node-av-win32-x64": "^1.0.1"
67
65
  },
68
66
  "devDependencies": {
69
67
  "@stylistic/eslint-plugin": "^5.2.3",
@@ -0,0 +1,11 @@
1
+ ## node-av Release v1.0.1
2
+
3
+ ### 📦 Updated Packages
4
+
5
+ The following platform packages have been updated to v1.0.1:
6
+ - `@seydx/node-av-darwin-arm64@v1.0.1`
7
+ - `@seydx/node-av-darwin-x64@v1.0.1`
8
+ - `@seydx/node-av-linux-arm64@v1.0.1`
9
+ - `@seydx/node-av-linux-x64@v1.0.1`
10
+ - `@seydx/node-av-win32-arm64@v1.0.1`
11
+ - `@seydx/node-av-win32-x64@v1.0.1`