-
-
Notifications
You must be signed in to change notification settings - Fork 45
/
ExiftoolPath.ts
100 lines (91 loc) · 3.32 KB
/
ExiftoolPath.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { Logger } from "batch-cluster"
import * as _fs from "node:fs"
import * as _path from "node:path"
import { isWin32 } from "./IsWin32"
import { Maybe } from "./Maybe"
import { which } from "./Which"
function vendorPackage() {
return "exiftool-vendored." + (isWin32() ? "exe" : "pl")
}
function tryRequire({
prefix = "",
logger,
}: { prefix?: string; logger?: Maybe<Logger> } = {}): Maybe<string> {
const id = prefix + vendorPackage()
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
return require(id)
} catch (error) {
logger?.warn(id + "not found: ", error)
return
}
}
/**
* This implementation relies on the fact that both `exiftool-vendored.pl` and
* `exiftool-vendored.exe` both export the path to their respective exiftool
* binary.
*
* When running in node, this method should suffice.
*
* When running in Electron, all bets are off, due to ASAR packaging and other
* nonsense. As perl can't run from within an ASAR archive, `electron-builder`
* must be configured to `asarUnpack`
* "**/node_modules/exiftool-vendored.{pl,exe}/". See
* <https://www.electron.build/generated/platformspecificbuildoptions#configuration-asarUnpack>
* for details.
*
* If you're using `electron-forge`, add something like the following to your
* ForgeConfig.packagerConfig.extraResource array: `fs.join(".", "node_modules",
* "exiftool-vendored." + (isWin ? "exe" : "pl"))` but then you must specify a
* custom exiftoolPath in your options hash, as subprocesses that use
* ELECTRON_RUN_AS_NODE will no longer have process.resourcesPath set.
*
* If none of the above work for your use case, you can provide your own
* `exiftoolPath` implementation to your instance of ExifTool
*
* @return the path to the exiftool binary, preferring the vendored version in
* node_modules.
*/
export async function exiftoolPath(logger?: Logger): Promise<string> {
const path = tryRequire({ prefix: "", logger })
// This s/app.asar/app.asar.unpacked/ path switch adds support for Electron
// apps whose modules are ASAR-packed (like by electron-builder).
const asarUnpackedPath = path
?.split(_path.sep)
.map((ea) => (ea === "app.asar" ? "app.asar.unpacked" : ea))
.join(_path.sep)
// NOTE: we must check for the fixedPath FIRST, because Electron's ASAR
// shenanigans will make existsSync return true for asar-packed resources
if (asarUnpackedPath != null && _fs.existsSync(asarUnpackedPath)) {
return asarUnpackedPath
}
if (path != null && _fs.existsSync(path)) {
return path
}
logger?.warn("Failed to find exiftool via " + vendorPackage())
// process.resourcesPath is set by electron-forge:
const electronResourcePath = (process as any).resourcesPath
if (electronResourcePath != null) {
const forgePath = _path.join(
electronResourcePath,
vendorPackage(),
"bin",
"exiftool" + (isWin32() ? ".exe" : "")
)
if (_fs.existsSync(forgePath)) {
return forgePath
} else {
logger?.warn(
"Failed to find exiftool in electron forge resources path: " + forgePath
)
}
}
// Last ditch: is there a globally installed exiftool in the PATH?
const fromPath = await which("exiftool")
if (fromPath != null) {
return fromPath
}
throw new Error(
`Failed to find ExifTool installation: set exiftoolPath explicitly.`
)
}