From 86ea5305affaf049bdb321829eb91ade04915cf2 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 8 Mar 2026 17:28:22 +0800 Subject: [PATCH] update image scale --- image-scale/main.ts | 71 ++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 23 deletions(-) mode change 100644 => 100755 image-scale/main.ts diff --git a/image-scale/main.ts b/image-scale/main.ts old mode 100644 new mode 100755 index 62a2b37..c9b20d5 --- a/image-scale/main.ts +++ b/image-scale/main.ts @@ -1,6 +1,7 @@ +#!/usr/bin/env -S deno run -A + import sharp from "sharp"; -import { parseArgs } from "@std/cli/parse-args"; -import { basename, extname, join } from "@std/path"; +import {parseArgs} from "@std/cli/parse-args"; /** * Resize an image so that its smallest dimension equals the specified minimum pixels. @@ -14,6 +15,7 @@ export async function scaleImage( inputPath: string, outputPath: string, minPixels: number, + quality?: number, ): Promise { if (minPixels <= 0) { throw new Error("minPixels must be a positive number"); @@ -27,6 +29,13 @@ export async function scaleImage( throw new Error("Could not determine image dimensions"); } + if (originalWidth < minPixels || originalHeight < minPixels) { + // original width or height is less than min pixels, should not scale image + throw new Error( + `Image width ${originalWidth} and/or height ${originalHeight} less then ${minPixels}, cannot scale image`, + ); + } + // Calculate the scale factor based on the smaller dimension const minDimension = Math.min(originalWidth, originalHeight); const scaleFactor = minPixels / minDimension; @@ -36,7 +45,7 @@ export async function scaleImage( await sharp(inputPath) .resize(newWidth, newHeight) - .jpeg({ quality: 90 }) + .jpeg({ quality: quality ?? 90 }) .toFile(outputPath); console.log( @@ -48,10 +57,20 @@ export async function scaleImage( * Generate output path for the scaled image. * Changes the extension to .jpg */ -export function getOutputPath(inputPath: string, outputDir?: string): string { - const base = basename(inputPath, extname(inputPath)); - const dir = outputDir || join(inputPath, ".."); - return join(dir, `${base}.jpg`); +export function getOutputPath( + inputPath: string, + unsafeReplaceFile: boolean, +): string { + if (unsafeReplaceFile) { + const lowerInputPath = inputPath.toLowerCase(); + if (lowerInputPath.endsWith(".jpg") || lowerInputPath.endsWith(".jpeg")) { + return inputPath; + } + } + const lastDot = inputPath.lastIndexOf("."); + return lastDot !== -1 + ? `${inputPath.slice(0, lastDot)}${unsafeReplaceFile ? "" : "_scaled"}.jpg` + : `${inputPath}${unsafeReplaceFile ? "" : "_scaled"}.jpg`; } function printUsage() { @@ -75,15 +94,17 @@ Examples: if (import.meta.main) { const flags = parseArgs(Deno.args, { - string: ["output", "o", "min-pixels", "m"], - boolean: ["help", "h"], + string: ["output", "o", "min-pixels", "m", "quality", "q"], + boolean: ["help", "h", "unsafe-replace-file"], alias: { "min-pixels": "m", output: "o", help: "h", + quality: "q", }, default: { "min-pixels": "1200", + "quality": "90", }, }); @@ -92,26 +113,30 @@ if (import.meta.main) { Deno.exit(0); } - const inputPath = flags._[0] as string | undefined; + const imagePaths = flags._; - if (!inputPath) { - console.error("Error: Input image path is required"); + if (imagePaths.length == 0) { + console.error("No image paths found."); printUsage(); Deno.exit(1); } const minPixels = parseInt(flags["min-pixels"] as string, 10); - const outputPath = flags.output - ? flags.output.endsWith(".jpg") || flags.output.endsWith(".jpeg") - ? flags.output as string - : getOutputPath(inputPath, flags.output as string) - : getOutputPath(inputPath); + const quality = parseInt(flags["quality"] as string, 10); + const unsafeReplaceFile = flags["unsafe-replace-file"]; + console.log( + `Image scale config, min-pixels: ${minPixels}, quality: ${quality}, unsafe-replace-file: ${unsafeReplaceFile}`, + ); - try { - await scaleImage(inputPath, outputPath, minPixels); - console.log(`Output saved to: ${outputPath}`); - } catch (error) { - console.error("Error:", error instanceof Error ? error.message : error); - Deno.exit(1); + for (const imagePath of imagePaths) { + const imagePathStr = imagePath as string; + const outputPath = getOutputPath(imagePathStr, unsafeReplaceFile); + + try { + await scaleImage(imagePathStr, outputPath, minPixels, quality); + console.log(`Output saved to: ${outputPath}`); + } catch (error) { + console.error("Error:", error instanceof Error ? error.message : error); + } } }