Skip to content

Commit

Permalink
Support async exiftool path search, and process.resourcesPath
Browse files Browse the repository at this point in the history
  • Loading branch information
mceachen committed Mar 26, 2024
1 parent 291afae commit 60d4bab
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 38 deletions.
4 changes: 2 additions & 2 deletions src/DefaultExifToolOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CapturedAtTagNames } from "./CapturedAtTagNames"
import { DefaultExiftoolArgs } from "./DefaultExiftoolArgs"
import { DefaultMaxProcs } from "./DefaultMaxProcs"
import { ExifToolOptions } from "./ExifToolOptions"
import { DefaultExifToolPath } from "./FindExiftool"
import { exiftoolPath } from "./ExiftoolPath"
import { geoTz } from "./GeoTz"
import { isWin32 } from "./IsWin32"
import { Omit } from "./Omit"
Expand All @@ -25,7 +25,7 @@ export const DefaultExifToolOptions: Omit<
taskTimeoutMillis: 20000,
onIdleIntervalMillis: 2000,
taskRetries: 1,
exiftoolPath: DefaultExifToolPath,
exiftoolPath,
exiftoolArgs: DefaultExiftoolArgs,
exiftoolEnv: {},
checkPerl: !isWin32(),
Expand Down
13 changes: 7 additions & 6 deletions src/ExifTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
MWGKeywordTags,
} from "./MWGTags"
import { Maybe } from "./Maybe"
import { isFunction } from "./Object"
import { Omit } from "./Omit"
import { pick } from "./Pick"
import { PreviewTag } from "./PreviewTag"
Expand Down Expand Up @@ -169,6 +170,7 @@ const whichPerl = lazy(async () => {
export class ExifTool {
readonly options: ExifToolOptions
private readonly batchCluster: bc.BatchCluster
readonly exiftoolPath: () => Promise<string>

constructor(options: Partial<ExifToolOptions> = {}) {
if (options != null && typeof options !== "object") {
Expand All @@ -193,23 +195,22 @@ export class ExifTool {
detached: false, // < no orphaned exiftool procs, please
env,
}
this.exiftoolPath = lazy(async () =>
isFunction(o.exiftoolPath) ? o.exiftoolPath(o.logger()) : o.exiftoolPath
)
const processFactory = async () =>
ignoreShebang
? _cp.spawn(
await whichPerl(),
[o.exiftoolPath, ...o.exiftoolArgs],
[await this.exiftoolPath(), ...o.exiftoolArgs],
spawnOpts
)
: _cp.spawn(o.exiftoolPath, o.exiftoolArgs, spawnOpts)
: _cp.spawn(await this.exiftoolPath(), o.exiftoolArgs, spawnOpts)

this.options = {
...o,
ignoreShebang,
processFactory,
exitCommand: o.exitCommand,
versionCommand: o.versionCommand,
// User options win:
...options,
}
this.batchCluster = new bc.BatchCluster(this.options)
}
Expand Down
78 changes: 78 additions & 0 deletions src/ExiftoolPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Logger } from "batch-cluster"
import * as _fs from "node:fs"
import * as _path from "node:path"
import process from "node:process"
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 {
return require(id)
} catch (error) {
logger?.warn(id + "not found: ", error)
return
}
}

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 that are ASAR-packed (like by electron-builder). This is necessary because
// Note that we can't reliably automatically detect that we're running in
// electron because child processes that are spawned by the main process will
// most likely need the ELECTRON_RUN_AS_NODE environment variable set, which
// will unset the process.versions.electron field.
const fixedPath = path
?.split(_path.sep)
.map((ea) => (ea === "app.asar" ? "app.asar.unpacked" : ea))
.join(_path.sep)

// Note also, that we must check for the fixedPath first, because Electron's
// ASAR shenanigans will make existsSync return true even for asar-packed
// resources.
if (fixedPath != null && _fs.existsSync(fixedPath)) {
return fixedPath
}
if (path != null && _fs.existsSync(path)) {
return path
}

logger?.warn("Failed to find exiftool via " + vendorPackage())

// 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.`
)
}
30 changes: 0 additions & 30 deletions src/FindExiftool.ts

This file was deleted.

0 comments on commit 60d4bab

Please sign in to comment.