diff --git a/apps/api/src/lib/heic-converter.ts b/apps/api/src/lib/heic-converter.ts
index 1510d1a0..b7f0d11d 100644
--- a/apps/api/src/lib/heic-converter.ts
+++ b/apps/api/src/lib/heic-converter.ts
@@ -8,18 +8,40 @@ import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
/**
- * Decode a HEIC/HEIF buffer to PNG using the system `heif-dec` CLI tool.
+ * Find the HEIF decode command. macOS (Homebrew) provides `heif-dec`,
+ * while Linux packages provide `heif-convert`. Both accept the same
+ * ` ` argument syntax.
+ */
+let cachedDecodeCmd: string | null = null;
+
+async function findDecodeCmd(): Promise {
+ if (cachedDecodeCmd) return cachedDecodeCmd;
+ for (const cmd of ["heif-convert", "heif-dec"]) {
+ try {
+ await execFileAsync(cmd, ["--version"], { timeout: 5_000 });
+ cachedDecodeCmd = cmd;
+ return cmd;
+ } catch {
+ // try next
+ }
+ }
+ throw new Error("No HEIF decoder found. Install libheif-examples (Linux) or libheif (macOS).");
+}
+
+/**
+ * Decode a HEIC/HEIF buffer to PNG using the system HEIF decoder CLI.
* This is needed because Sharp's bundled libheif does not include the
* HEVC decoder required for true HEIC files (iPhone photos).
*/
export async function decodeHeic(buffer: Buffer): Promise {
+ const cmd = await findDecodeCmd();
const id = randomUUID();
const inputPath = join(tmpdir(), `heic-in-${id}.heic`);
const outputPath = join(tmpdir(), `heic-out-${id}.png`);
try {
await writeFile(inputPath, buffer);
- await execFileAsync("heif-dec", [inputPath, outputPath], { timeout: 30_000 });
+ await execFileAsync(cmd, [inputPath, outputPath], { timeout: 30_000 });
return await readFile(outputPath);
} finally {
await rm(inputPath, { force: true }).catch(() => {});
@@ -47,16 +69,3 @@ export async function encodeHeic(buffer: Buffer, quality = 80): Promise
await rm(outputPath, { force: true }).catch(() => {});
}
}
-
-/**
- * Check whether the heif-enc and heif-dec CLI tools are available.
- */
-export async function isHeicToolAvailable(): Promise {
- try {
- await execFileAsync("heif-enc", ["--version"], { timeout: 5_000 });
- await execFileAsync("heif-dec", ["--version"], { timeout: 5_000 });
- return true;
- } catch {
- return false;
- }
-}