exiftool

package module
v0.0.0-...-88042be Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 5, 2026 License: MIT Imports: 19 Imported by: 0

README

go-exiftool: ExifTool in Go (zeroperl + wazero)

Go Reference

Go library that runs ExifTool in-process: embedded Perl via zeroperl (Perl compiled to WebAssembly), executed with wazero. You do not need a system exiftool binary or a separate Perl install.

Upstream ExifTool (Phil Harvey) and metadata: exiftool.org, exiftool/exiftool.

Why this shape

  • Self-contained — Perl stdlib, ExifTool, and the interpreter ship inside the module (embed/).
  • Same behavior everywhere — one WASM stack on Linux, macOS, Windows, etc.
  • Trade-off — cold start is heavy (on the order of ~7–10 seconds to compile/instantiate WASM and init Perl). For many operations, use NewServer and Server.Command so ExifTool stays open (-stay_open) and work is amortized.

Requirements

  • Go 1.26+
  • Dependency: github.com/tetratelabs/wazero (see go.mod).

Usage

// One-shot: paths are relative to the process working directory.
out, err := exiftool.Command(nil, "-json", "photo.jpg")

// Explicit tree at /work (e.g. tests or isolated FS).
out, err := exiftool.Run(ctx, os.DirFS("/path/to/photos"), "-json", "/work/photo.jpg")

// Batch: one WASM/Perl startup, many commands.
e, err := exiftool.NewServer("-fast")
if err != nil { /* ... */ }
defer e.Shutdown()
out, err = e.Command("-Artist", "photo.jpg")

Package docs, API details, and filesystem semantics (Command vs Run, temp dir, Arg1 / Config): run go doc -all or open pkg.go.dev.

Development

Tests
go test ./... -timeout 10m   # includes WASM tests; allow several minutes
Refreshing embed/ from zeroperl

The WASM module, Perl install prefix, and minified ExifTool script come from zeroperl. Follow that repository’s Build section (Docker or Apple Container): build the image, run the container, and copy /artifacts into a host directory (as shown there, e.g. ./output/).

From the build output directory, install these into this repo under embed/:

Artifact from zeroperl output Path in go-exiftool
zeroperl.wasm embed/zeroperl.wasm
perl-wasi-prefix/ (entire tree) embed/perl-wasi-prefix/
exiftool.min.pl embed/exiftool.min.pl

Use the zeroperl.wasm artifact (reactor with asyncify) unless you intentionally switch runtimes. If you disable ExifTool in zeroperl (BUILD_EXIFTOOL=false), you must supply exiftool.min.pl yourself.

zeroperl’s README also documents build arguments (PERL_VERSION, EXIFTOOL_VERSION, BUILD_EXIFTOOL, memory/stack, etc.). If you change PERL_VERSION, update the hard-coded PERL5LIB paths in exiftool.go and server.go so the version segment (e.g. 5.42.0) matches the tree under embed/perl-wasi-prefix/lib/.

License

See LICENSE. ExifTool and embedded components carry their own licenses under embed/.

Documentation

Overview

Package exiftool runs ExifTool in-process via an embedded Perl interpreter (zeroperl) compiled to WebAssembly and executed with wazero. No external exiftool binary or Perl installation is required.

Entry points:

  • Command / CommandContext: one-shot invocation; host paths are relative to the process working directory and are visible read-only under "/" inside the sandbox, with os.TempDir mounted read-write for ExifTool side effects.
  • Run / RunDebug: single invocation with an explicit fs.FS mounted at "/work"; pass paths such as "/work/photo.jpg".
  • NewServer: persistent ExifTool using the -stay_open protocol; amortizes WASM startup across many Server.Command calls. Call Server.Shutdown or Server.Close when finished.

Configuration package variables Arg1 and Config are forwarded into the ExifTool argument list for Command/CommandContext and NewServer. Exec is kept for API compatibility but is not used to spawn a process. Leave Arg1 empty unless you intend to pass a valid ExifTool option: it is prepended verbatim and a mistaken value can be interpreted as a file argument.

Index

Constants

This section is empty.

Variables

View Source
var Arg1 string

Arg1, if non-empty, is prepended to every ExifTool argv built by Command/CommandContext and by NewServer. It must be a valid ExifTool argument (for example a global option). Do not set it to a host path unless that path is intended as an input file for ExifTool.

View Source
var CacheDir string

CacheDir controls where wazero stores its persistent compilation cache. If empty, a per-user default cache directory is used when available. On Unix, this follows os.UserCacheDir, which respects XDG cache conventions.

The underlying wazero cache is segregated by version, architecture, and OS, so it is safe to point multiple systems at a shared home directory as long as the cache root itself is shared.

If set to "off", the package disables the persistent on-disk cache and uses only the in-memory cache for the current process.

View Source
var Config string

Config, if non-empty, is passed as "-config" and the path value to ExifTool.

View Source
var Exec = "exiftool"

Exec is retained for compatibility with the original go-exiftool API. This implementation does not execute an external program; the value is ignored.

Functions

func Command

func Command(stdin io.Reader, arg ...string) ([]byte, error)

Command runs ExifTool once with the given arguments and returns combined stdout. stdin may be nil; when ExifTool is invoked with "-@ -", lines from stdin supply extra arguments. Host paths in arg are resolved relative to the process working directory (mounted at "/" inside the WASM sandbox together with embedded Perl libs; see package documentation).

Command is equivalent to CommandContext with context.Background.

func CommandContext

func CommandContext(ctx context.Context, stdin io.Reader, arg ...string) (out []byte, err error)

CommandContext is like Command but honors ctx for cancellation only before the WASM runtime is created; a context deadline or cancel during eval is not guaranteed to interrupt an in-flight ExifTool run.

Non-empty stderr from ExifTool is returned as an error. Some failures may still return partial stdout together with an error.

func Run

func Run(ctx context.Context, workFS fs.FS, args ...string) (out []byte, err error)

Run executes ExifTool once with embedded Perl at "/" and workFS (if non-nil) mounted at "/work". Pass file paths in args as "/work/..." for files that live in workFS. Any non-empty stderr after a successful eval is reported as an error.

func RunDebug

func RunDebug(ctx context.Context, workFS fs.FS, args ...string) (out []byte, err error)

RunDebug is like Run but prints stderr to the host process's standard error and does not treat stderr alone as a failure after a successful WASM eval.

func Unmarshal

func Unmarshal(data []byte, m map[string][]byte) error

Unmarshal parses line-oriented ExifTool text output: each line must contain a key, the substring ": ", and a value (covers the default format and -s / short output). It does not parse JSON (-json), XML (-X), or other structured outputs.

Values stored in m are subslices of data; if data is reused or modified after the call, entries in m may change. Copy with append([]byte(nil), v...) when retaining values past the lifetime of data.

Types

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server keeps one ExifTool instance (embedded zeroperl WASM module) alive and exchanges commands using ExifTool's -stay_open protocol. Server.Command may be called from multiple goroutines; commands are serialized internally.

func NewServer

func NewServer(commonArg ...string) (*Server, error)

NewServer compiles and starts ExifTool in stay_open mode. Arguments in commonArg are appended after built-in options (-stay_open, -@ -, -common_args, ready echo); package-level Arg1 and Config are applied when non-empty. The server uses the same filesystem layout as Command: embedded Perl at "/" and the host working directory overlaid, with os.TempDir mounted read-write.

func (*Server) Close

func (e *Server) Close() error

Close forcibly stops the server: closes stdin and the WASM module, waits for the long-running eval goroutine, then closes the wazero runtime. Idempotent; safe after [Shutdown]. A second Close returns nil.

func (*Server) Command

func (e *Server) Command(arg ...string) ([]byte, error)

Command runs one ExifTool request: arguments are sent as lines on the server's stdin, then an -execute boundary. On success, returns a copy of stdout for that response (stderr is checked for errors). After [Shutdown] or [Close], Command returns an error and does not restart the server.

func (*Server) Shutdown

func (e *Server) Shutdown() error

Shutdown stops ExifTool cleanly by sending -stay_open false and closing stdin, waits for processing to finish, then releases resources like [Close]. Returns an error if the server was already closed. If [Close] runs concurrently, Shutdown may return nil after the other caller has finalized the runtime.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL