110 lines
2.9 KiB
TypeScript
Executable File
110 lines
2.9 KiB
TypeScript
Executable File
#!/usr/bin/env runts -- --runtime-bun
|
|
|
|
import {parseArgs} from "util";
|
|
|
|
const SUPPORTED_FORMATS = [".heic", ".jpeg", ".jpg", ".png", ".webp", ".gif", ".bmp", ".avif", ".tiff", ".tif"];
|
|
|
|
function printUsage() {
|
|
console.log(`Usage: bun run show-image.ts <image1> [image2] ...
|
|
|
|
Display detailed information about image files.
|
|
|
|
Arguments:
|
|
<image> ... Input image file(s)
|
|
|
|
Options:
|
|
--help Show this help message
|
|
|
|
Supported formats:
|
|
${SUPPORTED_FORMATS.join(", ")}
|
|
|
|
Examples:
|
|
bun run show-image.ts photo.jpg
|
|
bun run show-image.ts a.jpg b.heic c.png`);
|
|
}
|
|
|
|
function formatBytes(bytes: number): string {
|
|
if (bytes < 1024) return `${bytes} B`;
|
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
}
|
|
|
|
function getFormat(ext: string): string {
|
|
switch (ext) {
|
|
case ".jpg":
|
|
case ".jpeg":
|
|
return "JPEG";
|
|
case ".tif":
|
|
case ".tiff":
|
|
return "TIFF";
|
|
default:
|
|
return ext.slice(1).toUpperCase();
|
|
}
|
|
}
|
|
|
|
async function showImage(path: string) {
|
|
const file = Bun.file(path);
|
|
|
|
if (!(await file.exists())) {
|
|
console.error(`File not found: ${path}`);
|
|
return false;
|
|
}
|
|
|
|
const ext = "." + file.name?.split(".").pop()?.toLowerCase();
|
|
if (!SUPPORTED_FORMATS.includes(ext)) {
|
|
console.error(`Unsupported format: ${ext} (${path})`);
|
|
return false;
|
|
}
|
|
|
|
const image = file.image();
|
|
const meta = await image.metadata();
|
|
const size = file.size;
|
|
|
|
console.log(`File: ${path}`);
|
|
console.log(`Format: ${getFormat(ext)}`);
|
|
console.log(`Size: ${formatBytes(size)} (${size.toLocaleString()} bytes)`);
|
|
console.log(`Dimensions: ${meta.width} x ${meta.height}`);
|
|
console.log(`Aspect ratio: ${(meta.width / meta.height).toFixed(2)}`);
|
|
console.log(`Megapixels: ${((meta.width * meta.height) / 1_000_000).toFixed(2)} MP`);
|
|
console.log();
|
|
return true;
|
|
}
|
|
|
|
async function main() {
|
|
const args = Bun.argv.slice(2);
|
|
|
|
if (args.length === 0 || args.includes("--help")) {
|
|
printUsage();
|
|
process.exit(0);
|
|
}
|
|
|
|
const {positionals} = parseArgs({args, allowPositionals: true, strict: false});
|
|
|
|
if (positionals.length === 0) {
|
|
console.error("Error: No input files specified.\n");
|
|
printUsage();
|
|
process.exit(1);
|
|
}
|
|
|
|
let successCount = 0;
|
|
for (const path of positionals) {
|
|
const ok = await showImage(path);
|
|
if (ok) successCount++;
|
|
}
|
|
|
|
if (positionals.length > 1) {
|
|
console.log(`Total: ${successCount}/${positionals.length} file(s) processed.`);
|
|
}
|
|
|
|
if (successCount === 0) {
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err);
|
|
});
|
|
|
|
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20260526T004141+08:00.MEQCIBjY8Rshew3TyWpojRAp
|
|
// /WewTldgEjhANbtyjw8Sn2VBAiARxfPLnmHlfkXZlVnN0lJoQmg1sqMmqouiHEjcxWQf3Q==
|