feat: add pipeline API and download utilities
- processImageWithPipeline for full pipeline processing - ZIP archive creation with JSZip - File download helpers - Output filename generation Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
102
src/lib/processing/pipeline.ts
Normal file
102
src/lib/processing/pipeline.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
// High-level pipeline API for image processing
|
||||
|
||||
import type { Device, ImageEntry, PipelineConfig } from '$lib/types';
|
||||
import { getExtension } from '$lib/types';
|
||||
import JSZip from 'jszip';
|
||||
import { initPhoton } from './photon';
|
||||
import { workerPool } from './workerPool';
|
||||
|
||||
let photonReady = false;
|
||||
|
||||
export async function ensurePhotonReady(): Promise<void> {
|
||||
if (photonReady) return;
|
||||
await initPhoton();
|
||||
photonReady = true;
|
||||
}
|
||||
|
||||
export async function processImage(file: File, device: Device): Promise<Blob> {
|
||||
const { blob } = await processImageWithPipeline(file, device, {
|
||||
crop: { enabled: true, mode: 'center' },
|
||||
resize: { enabled: true, mode: 'cover' },
|
||||
brightness: { enabled: false, value: 0 },
|
||||
contrast: { enabled: false, value: 0 },
|
||||
gamma: { enabled: false, value: 1.0 },
|
||||
autoLevels: { enabled: false, clipPercent: 0.5 },
|
||||
greyscale: { enabled: true, method: 'luminosity' },
|
||||
sharpen: { enabled: false, amount: 0 },
|
||||
quantize: { enabled: true, levels: device.greyLevels },
|
||||
dither: { enabled: false, algorithm: 'floyd-steinberg' }
|
||||
});
|
||||
return blob;
|
||||
}
|
||||
|
||||
export async function processImageWithPreview(
|
||||
file: File,
|
||||
device: Device
|
||||
): Promise<{ blob: Blob; dataUrl: string }> {
|
||||
return processImageWithPipeline(file, device, {
|
||||
crop: { enabled: true, mode: 'center' },
|
||||
resize: { enabled: true, mode: 'cover' },
|
||||
brightness: { enabled: false, value: 0 },
|
||||
contrast: { enabled: false, value: 0 },
|
||||
gamma: { enabled: false, value: 1.0 },
|
||||
autoLevels: { enabled: false, clipPercent: 0.5 },
|
||||
greyscale: { enabled: true, method: 'luminosity' },
|
||||
sharpen: { enabled: false, amount: 0 },
|
||||
quantize: { enabled: true, levels: device.greyLevels },
|
||||
dither: { enabled: false, algorithm: 'floyd-steinberg' }
|
||||
});
|
||||
}
|
||||
|
||||
export async function processImageWithPipeline(
|
||||
file: File,
|
||||
device: Device,
|
||||
config: PipelineConfig
|
||||
): Promise<{ blob: Blob; dataUrl: string }> {
|
||||
return workerPool.process(file, device, config);
|
||||
}
|
||||
|
||||
export function getOutputFilename(originalFilename: string, device: Device): string {
|
||||
const lastDot = originalFilename.lastIndexOf('.');
|
||||
const stem = lastDot > 0 ? originalFilename.substring(0, lastDot) : originalFilename;
|
||||
const ext = getExtension(device.outputFormat);
|
||||
return `${stem}_${device.id}.${ext}`;
|
||||
}
|
||||
|
||||
export async function createZipArchive(images: ImageEntry[], device: Device): Promise<Blob> {
|
||||
const zip = new JSZip();
|
||||
|
||||
for (const image of images) {
|
||||
if (image.processedBlob) {
|
||||
const filename = getOutputFilename(image.filename, device);
|
||||
zip.file(filename, image.processedBlob);
|
||||
}
|
||||
}
|
||||
|
||||
return zip.generateAsync({ type: 'blob' });
|
||||
}
|
||||
|
||||
export function downloadFile(blob: Blob, filename: string): void {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
export function downloadImage(image: ImageEntry, device: Device): void {
|
||||
if (!image.processedBlob) return;
|
||||
const filename = getOutputFilename(image.filename, device);
|
||||
downloadFile(image.processedBlob, filename);
|
||||
}
|
||||
|
||||
export async function downloadAllAsZip(images: ImageEntry[], device: Device): Promise<void> {
|
||||
const completedImages = images.filter((img) => img.status === 'complete' && img.processedBlob);
|
||||
if (completedImages.length === 0) return;
|
||||
|
||||
const zipBlob = await createZipArchive(completedImages, device);
|
||||
downloadFile(zipBlob, '2eink_covers.zip');
|
||||
}
|
||||
Reference in New Issue
Block a user