@remedia/node 0.1.0-dev.4 → 0.1.0-dev.5

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/README.md CHANGED
@@ -34,18 +34,32 @@ Requires Node.js **22.18.0 or newer** (ESM).
34
34
  npm install @remedia/node
35
35
  ```
36
36
 
37
- Install FFmpeg 9 shared libraries separately before importing the package. npm does not install
38
- FFmpeg or compile the addon. Required ABI majors are avcodec 63, avformat 63, avutil 61,
39
- avfilter 12, and swresample 7. libopus is statically included; FFmpeg is dynamically linked.
37
+ Install FFmpeg 9 shared libraries separately on the Node.js host. npm does not install FFmpeg, and
38
+ importing the package never downloads anything. The Remedia installer provisions FFmpeg 9.0.2 only
39
+ (no server) into `~/.remedia/ffmpeg9`, which the package finds automatically:
40
+
41
+ ```sh
42
+ curl -fsSL https://raw.githubusercontent.com/twlite/remedia/main/scripts/install.sh | sh -s -- --ffmpeg-only
43
+ ```
44
+
45
+ ```powershell
46
+ & ([scriptblock]::Create((irm https://raw.githubusercontent.com/twlite/remedia/main/scripts/install.ps1))) -FFmpegOnly
47
+ ```
48
+
49
+ No `LD_LIBRARY_PATH`, `DYLD_LIBRARY_PATH`, `PATH` change, or launcher is needed. Discovery order:
50
+ `REMEDIA_FFMPEG_DIR` (an explicit override that is never silently replaced), an existing system
51
+ FFmpeg 9, then `~/.remedia/ffmpeg9`. Required ABI majors are avcodec 63, avformat 63, avutil 61,
52
+ avfilter 12, and swresample 7. `getFfmpegRuntime()` reports which installation was loaded, and a
53
+ failed import lists the paths considered. libopus is statically included; FFmpeg is dynamically linked.
54
+ FFmpeg is needed on the Node.js host in embedded mode; in remote mode it is needed on the
55
+ `remedia-server` host instead.
40
56
 
41
57
  Native runtime CI covers Linux x64/ARM64 glibc, macOS x64/ARM64, Windows x64 MSVC, and
42
58
  FreeBSD x64. Additional build-only packages cover Linux x64/ARM64 musl, Linux ARMv7 hard-float GNU,
43
59
  Windows x86/ARM64 MSVC, and Android ARM64/ARMv7 (API 24). Build-only targets have no native runtime
44
60
  integration coverage. See the [distribution guide](https://github.com/twlite/remedia/blob/main/apps/docs/content/docs/node/distribution.mdx)
45
- for verification limits and library discovery: `LD_LIBRARY_PATH` on Linux/FreeBSD,
46
- `DYLD_LIBRARY_PATH` on macOS, and DLL directories on `PATH` on Windows. For load errors, check
47
- matching architecture/ABI, transitive FFmpeg dependencies, and that optional npm dependencies were
48
- installed. Windows also needs the Visual C++ v14 runtime.
61
+ and [native library troubleshooting](https://github.com/twlite/remedia/blob/main/apps/docs/content/docs/node/troubleshooting.mdx)
62
+ for verification limits and load errors. Windows also needs the Visual C++ v14 runtime.
49
63
 
50
64
  ```js
51
65
  import { Remedia } from '@remedia/node';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,104 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { dirname, join } from 'node:path';
4
+ const require = createRequire(import.meta.url);
5
+ /**
6
+ * Whether Linux uses musl rather than glibc, mirroring the generated napi-rs loader's decision so
7
+ * that the bootstrap comes from the same platform package as the engine addon.
8
+ */
9
+ function isMusl() {
10
+ // @types/node does not declare `excludeNetwork`, which keeps the report from doing DNS lookups.
11
+ const report = process.report;
12
+ if (typeof report?.getReport === 'function') {
13
+ const previous = report.excludeNetwork;
14
+ try {
15
+ report.excludeNetwork = true;
16
+ const data = report.getReport();
17
+ if (data.header?.glibcVersionRuntime)
18
+ return false;
19
+ if (data.sharedObjects?.some((object) => object.includes('libc.musl-') || object.includes('ld-musl-'))) {
20
+ return true;
21
+ }
22
+ }
23
+ catch {
24
+ // Fall through to the filesystem check.
25
+ }
26
+ finally {
27
+ report.excludeNetwork = previous;
28
+ }
29
+ }
30
+ try {
31
+ return readFileSync('/usr/bin/ldd', 'utf8').includes('musl');
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ }
37
+ /**
38
+ * napi-rs platform suffixes that may hold this machine's addons, in the generated loader's order.
39
+ *
40
+ * @param platform - Node.js platform string.
41
+ * @param arch - Node.js architecture string.
42
+ * @param musl - Whether a Linux system uses musl; only consulted on Linux.
43
+ * @returns Suffixes such as `linux-x64-gnu` or `darwin-arm64`; empty when unsupported.
44
+ * @internal
45
+ */
46
+ export function platformSuffixes(platform, arch, musl = isMusl) {
47
+ switch (platform) {
48
+ case 'linux': {
49
+ const libc = musl() ? 'musl' : 'gnu';
50
+ if (arch === 'x64')
51
+ return [`linux-x64-${libc}`];
52
+ if (arch === 'arm64')
53
+ return [`linux-arm64-${libc}`];
54
+ if (arch === 'arm')
55
+ return [libc === 'musl' ? 'linux-arm-musleabihf' : 'linux-arm-gnueabihf'];
56
+ return [];
57
+ }
58
+ case 'darwin':
59
+ return ['darwin-universal', `darwin-${arch}`];
60
+ case 'win32':
61
+ return arch === 'x64'
62
+ ? ['win32-x64-msvc']
63
+ : arch === 'ia32'
64
+ ? ['win32-ia32-msvc']
65
+ : arch === 'arm64'
66
+ ? ['win32-arm64-msvc']
67
+ : [];
68
+ case 'freebsd':
69
+ return [`freebsd-${arch}`];
70
+ case 'android':
71
+ return arch === 'arm64' ? ['android-arm64'] : arch === 'arm' ? ['android-arm-eabi'] : [];
72
+ default:
73
+ return [];
74
+ }
75
+ }
76
+ /**
77
+ * File locations where this platform's bootstrap addon could be: next to `native.js` in a source
78
+ * checkout, then inside the installed `@remedia/node-<suffix>` platform package.
79
+ *
80
+ * @param packageRoot - Directory of the `@remedia/node` package.
81
+ * @param suffixes - Platform suffixes from {@link platformSuffixes}.
82
+ * @param resolvePackage - Resolves a module specifier to a file path; throws if absent.
83
+ * @returns Existing file paths in preference order.
84
+ * @internal
85
+ */
86
+ export function bootstrapCandidates(packageRoot, suffixes, resolvePackage = (specifier) => require.resolve(specifier)) {
87
+ const candidates = [];
88
+ for (const suffix of suffixes) {
89
+ const fileName = `bootstrap.${suffix}.node`;
90
+ const local = join(packageRoot, fileName);
91
+ if (existsSync(local))
92
+ candidates.push(local);
93
+ try {
94
+ const manifest = resolvePackage(`@remedia/node-${suffix}/package.json`);
95
+ const packaged = join(dirname(manifest), fileName);
96
+ if (existsSync(packaged))
97
+ candidates.push(packaged);
98
+ }
99
+ catch {
100
+ // The optional platform package is not installed.
101
+ }
102
+ }
103
+ return candidates;
104
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Where the loaded FFmpeg installation came from.
3
+ *
4
+ * - `explicit`: the `REMEDIA_FFMPEG_DIR` environment variable.
5
+ * - `system`: an installation the platform's library search finds (package manager, Homebrew,
6
+ * `LD_LIBRARY_PATH`, `DYLD_LIBRARY_PATH`, `PATH`, ...).
7
+ * - `managed`: the Remedia installer's `~/.remedia/ffmpeg9` prefix.
8
+ * - `unmanaged`: the platform has no bootstrap addon, so the operating system's loader resolved
9
+ * FFmpeg on its own and Remedia cannot report which installation it chose.
10
+ */
11
+ export type FfmpegSource = 'explicit' | 'system' | 'managed' | 'unmanaged';
12
+ /**
13
+ * Why an FFmpeg installation candidate was rejected, where that can be determined.
14
+ *
15
+ * - `not-found`: the directory does not exist.
16
+ * - `missing-libraries`: required FFmpeg libraries are absent from the directory.
17
+ * - `abi-mismatch`: libraries exist but have a different ABI major than FFmpeg 9 requires.
18
+ * - `architecture-mismatch`: the libraries were built for a different CPU architecture.
19
+ * - `unresolved-dependency`: a library or one of its own dependencies could not be resolved.
20
+ * - `mixed-installations`: the five libraries did not all come from one directory.
21
+ * - `load-failed`: the loader rejected a library for another reason.
22
+ */
23
+ export type FfmpegFailureReason = 'not-found' | 'missing-libraries' | 'abi-mismatch' | 'architecture-mismatch' | 'unresolved-dependency' | 'mixed-installations' | 'load-failed';
24
+ /** One FFmpeg shared library loaded by Remedia. */
25
+ export interface FfmpegLibraryInfo {
26
+ /** FFmpeg library name without the `lib` prefix, for example `avcodec`. */
27
+ readonly name: string;
28
+ /** Absolute path of the loaded file as reported by the operating system. */
29
+ readonly path: string;
30
+ /** Library version reported by the library itself, as `major.minor.micro`. */
31
+ readonly version: string;
32
+ /** ABI major version, which must match the FFmpeg 9 requirement for `name`. */
33
+ readonly major: number;
34
+ }
35
+ /** What discovery concluded about one FFmpeg installation candidate. */
36
+ export interface FfmpegCandidateReport {
37
+ /** Which discovery step produced this candidate. */
38
+ readonly source: Exclude<FfmpegSource, 'unmanaged'>;
39
+ /** Directory that was inspected, or `null` when the step looked at several directories. */
40
+ readonly directory: string | null;
41
+ /** Whether this candidate was loaded (`selected`) or not. */
42
+ readonly outcome: 'selected' | 'rejected';
43
+ /** Classified rejection cause when `outcome` is `rejected` and the cause is known. */
44
+ readonly reason?: FfmpegFailureReason;
45
+ /** Human-readable explanation, including the loader's own message where available. */
46
+ readonly detail?: string;
47
+ }
48
+ /** The FFmpeg runtime that `@remedia/node` loaded. */
49
+ export interface FfmpegRuntimeInfo {
50
+ /** How the installation was found. */
51
+ readonly source: FfmpegSource;
52
+ /** Directory containing the loaded libraries, or `null` when `source` is `unmanaged`. */
53
+ readonly directory: string | null;
54
+ /** The five loaded libraries in load order; empty when `source` is `unmanaged`. */
55
+ readonly libraries: readonly FfmpegLibraryInfo[];
56
+ /** Every candidate discovery examined, in order, including the selected one. */
57
+ readonly considered: readonly FfmpegCandidateReport[];
58
+ }
59
+ /** Command that installs FFmpeg 9 on Linux and macOS with the Remedia installer. */
60
+ export declare const UNIX_INSTALL_COMMAND = "curl -fsSL https://raw.githubusercontent.com/twlite/remedia/main/scripts/install.sh | sh -s -- --ffmpeg-only";
61
+ /** Command that installs FFmpeg 9 on Windows PowerShell with the Remedia installer. */
62
+ export declare const WINDOWS_INSTALL_COMMAND = "& ([scriptblock]::Create((irm https://raw.githubusercontent.com/twlite/remedia/main/scripts/install.ps1))) -FFmpegOnly";
63
+ /**
64
+ * Raised when no compatible FFmpeg 9 installation could be loaded, or when an explicit
65
+ * `REMEDIA_FFMPEG_DIR` override is unusable. The message lists every path considered and how to
66
+ * install or point to FFmpeg.
67
+ */
68
+ export declare class FfmpegLoadError extends Error {
69
+ /** Every candidate that was examined, in order. */
70
+ readonly candidates: readonly FfmpegCandidateReport[];
71
+ /**
72
+ * @param candidates - Reports for each installation candidate that was examined.
73
+ * @param explicitOverride - Whether `REMEDIA_FFMPEG_DIR` was set, which disables fallbacks.
74
+ * @param platform - Platform used to choose the install command shown in the message.
75
+ */
76
+ constructor(candidates: readonly FfmpegCandidateReport[], explicitOverride: boolean, platform?: NodeJS.Platform);
77
+ }
78
+ /** FFmpeg 9 shared libraries in dependency order (leaf libraries first) with their ABI majors. */
79
+ export declare const FFMPEG_LIBRARIES: readonly [{
80
+ readonly name: "avutil";
81
+ readonly major: 61;
82
+ }, {
83
+ readonly name: "swresample";
84
+ readonly major: 7;
85
+ }, {
86
+ readonly name: "avcodec";
87
+ readonly major: 63;
88
+ }, {
89
+ readonly name: "avformat";
90
+ readonly major: 63;
91
+ }, {
92
+ readonly name: "avfilter";
93
+ readonly major: 12;
94
+ }];