an image's header, by path, because ffprobe cannot read a HEIC
`/v1/probe` is ffprobe, which is the wrong tool for a still and fails at it loudly: a HEIC out of an iPhone is not a container it understands, so it rejects the file outright with "moov atom not found" rather than reporting no dimensions. So anything needing a photograph's size had nowhere to ask — and a caller building a width ladder needs exactly that, to know which rungs would be an upscale. sharp reads it, HEIC included, because `metadata()` only parses the header. That is precisely the part of a HEIC that works; the pixels are what its bundled libheif cannot reach, which is what decodable.ts routes around. No decode happens, so this needs no job slot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+33
-1
@@ -1,6 +1,6 @@
|
|||||||
import { timingSafeEqual } from "node:crypto";
|
import { timingSafeEqual } from "node:crypto";
|
||||||
import { ASSETS_DIR, MAX_CONCURRENT_JOBS, MAX_UPLOAD_BYTES, PORT, requireToken } from "./config";
|
import { ASSETS_DIR, MAX_CONCURRENT_JOBS, MAX_UPLOAD_BYTES, PORT, requireToken } from "./config";
|
||||||
import { ApiError, invalid, notFound, toErrorResponse } from "./errors";
|
import { ApiError, invalid, notFound, toErrorResponse, unprocessable } from "./errors";
|
||||||
import { contentPath, deleteAsset, putAsset, readAsset } from "./assets/store";
|
import { contentPath, deleteAsset, putAsset, readAsset } from "./assets/store";
|
||||||
import { ffprobe } from "./probe/ffprobe";
|
import { ffprobe } from "./probe/ffprobe";
|
||||||
import { runSharp, type SharpRequest } from "./sharp/run";
|
import { runSharp, type SharpRequest } from "./sharp/run";
|
||||||
@@ -225,6 +225,38 @@ async function route(request: Request): Promise<Response> {
|
|||||||
return Response.json(await withSlot(() => ffprobe(target)));
|
return Response.json(await withSlot(() => ffprobe(target)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
An image's header, by path.
|
||||||
|
|
||||||
|
Separate from `/v1/probe` because ffprobe is the wrong tool for a still and fails at it loudly: a
|
||||||
|
HEIC out of an iPhone is not a media container it understands, and it rejects the file outright
|
||||||
|
("moov atom not found") rather than reporting no dimensions. Anything that needs a photograph's
|
||||||
|
size — a caller building a width ladder, and therefore needing to know which rungs would be an
|
||||||
|
upscale — had nowhere to ask.
|
||||||
|
|
||||||
|
sharp reads it, including HEIC, because `metadata()` only parses the header. That is precisely the
|
||||||
|
part of a HEIC that works; the pixels are what its bundled libheif cannot reach, which is what
|
||||||
|
`decodable.ts` exists to route around. No decode happens here, so this is cheap and needs no job
|
||||||
|
slot: it is a file header read, not an image operation.
|
||||||
|
*/
|
||||||
|
if (path === "/v1/image" && request.method === "GET") {
|
||||||
|
const target = await safeInputPath(url.searchParams.get("path"), "path");
|
||||||
|
const meta = await sharp(target)
|
||||||
|
.metadata()
|
||||||
|
.catch(() => {
|
||||||
|
throw unprocessable(`Could not read that image: ${target}`);
|
||||||
|
});
|
||||||
|
return Response.json({
|
||||||
|
format: meta.format ?? null,
|
||||||
|
width: meta.width ?? null,
|
||||||
|
height: meta.height ?? null,
|
||||||
|
space: meta.space ?? null,
|
||||||
|
hasAlpha: meta.hasAlpha ?? false,
|
||||||
|
pages: meta.pages ?? 1,
|
||||||
|
orientation: meta.orientation ?? null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const asset = path.match(/^\/v1\/assets\/([^/]+)(\/content|\/probe)?$/);
|
const asset = path.match(/^\/v1\/assets\/([^/]+)(\/content|\/probe)?$/);
|
||||||
if (asset) {
|
if (asset) {
|
||||||
const [, id, suffix] = asset;
|
const [, id, suffix] = asset;
|
||||||
|
|||||||
Reference in New Issue
Block a user