Project Files
src / documents / loader.ts
/**
* Document Loader for Draw Things
* Supports image files with optional embedded metadata.
*/
import { readFile } from 'fs/promises';
import { createHash } from 'crypto';
import path from 'path';
import type { DocumentFormat, ParsedDocument } from '../types';
import { PngMetadataParser } from './parsers/pngMetadataParser';
import { SUPPORTED_IMAGE_FORMAT_SET } from './imageFormats';
export class DocumentLoader {
/**
* Load and parse a document based on its format
*/
static async load(filePath: string): Promise<ParsedDocument & { hash: string }> {
const format = this.detectFormat(filePath);
const hash = await this.hashFile(filePath);
let parsed: ParsedDocument;
switch (format) {
case 'png':
case 'jpg':
case 'jpeg':
case 'tga':
case 'bmp':
case 'psd':
case 'gif':
case 'hdr':
case 'pic':
case 'ppm':
case 'pgm':
parsed = await PngMetadataParser.parse(filePath);
break;
default:
throw new Error(`Unsupported file format: ${format}`);
}
// Add filename as title if not present
if (!parsed.metadata.title) {
parsed.metadata.title = path.basename(filePath, path.extname(filePath));
}
return { ...parsed, hash };
}
/**
* Detect document format from file extension
*/
static detectFormat(filePath: string): DocumentFormat {
const format = path.extname(filePath).slice(1).toLowerCase();
return SUPPORTED_IMAGE_FORMAT_SET.has(format) ? format as DocumentFormat : 'unknown';
}
/**
* Calculate SHA-256 hash of file content
*/
private static async hashFile(filePath: string): Promise<string> {
const buffer = await readFile(filePath);
return createHash('sha256').update(buffer).digest('hex');
}
/**
* Check if file format is supported
*/
static isSupported(filePath: string): boolean {
const format = this.detectFormat(filePath);
return format !== 'unknown';
}
/**
* Check if file is an image format
*/
static isImage(filePath: string): boolean {
const format = this.detectFormat(filePath);
return format !== 'unknown';
}
}