imgscribe
v0.1.1
Published
AI-powered product image processor that generates SEO metadata and outputs structured JSON
Maintainers
Readme
imgscribe
AI-powered product image processor that generates SEO metadata and outputs structured JSON.
Drop product images into a folder — imgscribe analyses each one with a vision AI model, generates titles, descriptions, keywords, alt text, and SEO metadata, converts to WebP, and returns structured JSON.
Install
npm install imgscribeQuick Start (CLI)
Create a config file:
// imgscribe.config.js
module.exports = {
aiApiKey: process.env.NVIDIA_API_KEY,
skuPrefix: 'NXT',
storageDir: './storage/products',
};Run:
npx imgscribe --config ./imgscribe.config.jsThe CLI watches ./products for new images, processes them one by one in test mode (pauses after each), and outputs JSON for each processed image.
Quick Start (Programmatic)
const imgscribe = require('imgscribe');
imgscribe.configure({
aiApiKey: 'nvapi-your-key',
skuPrefix: 'NXT',
categories: ['Lighting', 'Furniture', 'Decor'],
allowNewCategories: false,
});
const result = await imgscribe.process('./products/lamp.jpg');
console.log(result);API
configure(config)
Merges your config into the defaults. Call before process() or start().
process(imagePath)
Processes a single image. Returns a promise resolving to the output object, or null if the image was flagged or failed.
start()
Starts the interactive CLI watcher mode.
Config Reference
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| watchDir | string | './products' | Directory to watch for new images |
| storageDir | string | './storage/products' | Base output directory for WebP files |
| isolation | boolean | true | Group outputs into run folders |
| isolationMode | string | 'new' | 'new', 'merge', or 'increment' |
| skuPrefix | string | 'IMG' | Prefix for generated SKU numbers |
| aiBaseUrl | string | 'https://integrate.api.nvidia.com/v1' | OpenAI-compatible API base URL |
| aiApiKey | string | '' | API key for the vision model |
| aiModel | string | 'meta/llama-4-maverick-17b-128e-instruct' | Model identifier |
| targetMarket | string | '' | Market context injected into AI prompt |
| siteKeywords | string[] | [] | SEO keywords for AI context (used only if relevant) |
| categories | string[] | [] | Constrain AI to these category names |
| allowNewCategories | boolean | true | Let AI create categories outside the list |
| categoryMode | string | 'path,ai' | Category resolution priority chain |
Output
Each successfully processed image returns:
{
"sku": "NXT0005",
"slug": "elegant-black-crystal-chandelier",
"title": "Elegant Black Crystal Chandelier",
"category": "Lighting",
"altText": "Black crystal chandelier with elegant curved arms",
"metaTitle": "Elegant Black Crystal Chandelier | Premium Lighting",
"metaDescription": "Premium black crystal chandelier featuring curved arms and refined design. Ideal for modern and classic interiors.",
"description": "This elegant black crystal chandelier combines modern aesthetics with classic craftsmanship...",
"keywords": ["chandelier", "crystal", "lighting", "black", "elegant"],
"clarity": 9,
"webpPath": "storage/products/run_2026-06-25_001/lighting/elegant-black-crystal-chandelier-NXT0005.webp"
}| Field | Type | Description |
|-------|------|-------------|
| sku | string | Generated SKU (prefix + 4-digit number) |
| slug | string | URL-friendly slug derived from title |
| title | string | AI-generated product title (4-6 words) |
| category | string | Resolved category (see Category System) |
| altText | string | Descriptive alt text for accessibility |
| metaTitle | string | SEO meta title (50-60 chars) |
| metaDescription | string | SEO meta description (140-155 chars) |
| description | string | Product description (2-3 sentences) |
| keywords | string[] | SEO keywords array |
| clarity | number | Image clarity score (1-10) |
| webpPath | string | Path to the converted WebP file |
imgscribe returns structured JSON — persist it to any database, CMS, or file using your own application code.
Category System
Categories are resolved using a priority chain configured by categoryMode:
| Mode | Behavior |
|------|----------|
| path,ai | Folder name first, falls back to AI if no subfolder |
| ai,path | AI response first, falls back to folder name |
| ai | AI only — folder structure ignored |
| path | Folder only — AI category ignored |
Folder-based (path): Derived from subfolder name in the watch directory. products/lighting/lamp.jpg resolves to lighting. Images in the root resolve to uncategorised.
AI-based (ai): The AI assigns a category. Constrain it by passing a categories array:
imgscribe.configure({
categories: ['Lighting', 'Furniture', 'Textiles', 'Decor'],
allowNewCategories: false, // forces 'Other' if nothing fits
categoryMode: 'ai',
});AI Provider
imgscribe uses the OpenAI SDK, so any OpenAI-compatible API works. Swap providers by changing aiBaseUrl, aiApiKey, and aiModel.
NVIDIA NIM (default):
imgscribe.configure({
aiBaseUrl: 'https://integrate.api.nvidia.com/v1',
aiApiKey: 'nvapi-...',
aiModel: 'meta/llama-4-maverick-17b-128e-instruct',
});Groq:
imgscribe.configure({
aiBaseUrl: 'https://api.groq.com/openai/v1',
aiApiKey: 'gsk_...',
aiModel: 'llama-3.2-90b-vision-preview',
});OpenRouter:
imgscribe.configure({
aiBaseUrl: 'https://openrouter.ai/api/v1',
aiApiKey: 'sk-or-...',
aiModel: 'meta-llama/llama-4-maverick:free',
});Ollama (local):
imgscribe.configure({
aiBaseUrl: 'http://localhost:11434/v1',
aiApiKey: 'ollama',
aiModel: 'llava',
});Laravel Integration
Call the CLI from PHP and decode the JSON output:
$config = base_path('imgscribe.config.js');
$image = storage_path('app/uploads/product.jpg');
$output = shell_exec("npx imgscribe --config {$config} --once {$image} 2>/dev/null");
// Extract JSON block from output
preg_match('/\{[\s\S]*\}/', $output, $matches);
$result = json_decode($matches[0] ?? '{}', true);
echo $result['title']; // "Elegant Black Crystal Chandelier"
echo $result['sku']; // "NXT0005"
echo $result['webpPath']; // path to converted WebPOr use the programmatic API via a Node.js microservice and call it over HTTP from Laravel.
SKU Persistence
SKU counters are stored in .imgscribe-state.json in the project root. Each prefix tracks its own counter independently. Changing the prefix starts a new sequence.
Run Isolation
When isolation: true, each run creates a timestamped subfolder:
storage/products/
run_2026-06-25_001/
lighting/
elegant-chandelier-NXT0001.webp
furniture/
oak-dining-table-NXT0002.webp
run_2026-06-25_002/
...Modes:
new— Creates a fresh run folder each startupmerge— Reuses the most recent run folderincrement— Same as merge (SKU counter continues globally)
Set isolation: false to skip run folders entirely.
Requirements
- Node.js 18+
- sharp (installed automatically)
- A vision-capable AI model API key (NVIDIA NIM, Groq, OpenRouter, Ollama, etc.)
License
MIT
