import { defineStore } from 'pinia'; import { ref } from 'vue'; import type { ExiftoolInitStatus } from '@/types/metadata/ExiftoolInitStatus'; import type { ExiftoolWorkerOutMessage } from '@/types/workers/exiftoolWorkerOutMessage'; export const useExiftoolStore = defineStore('exiftool', () => { const initStatus = ref('idle'); const initErrorMessage = ref(null); const worker = ref(null); let initPromise: Promise | null = null; let initPromiseResolve: (() => void) | null = null; let initPromiseReject: ((error: Error) => void) | null = null; const readMetadataWaiters = new Map< string, { resolve: (output: string) => void; reject: (error: Error) => void } >(); const initializeExiftool = async (): Promise => { if (initStatus.value === 'ready') { return; } if (initStatus.value === 'error') { throw new Error(initErrorMessage.value ?? 'ExifTool is unavailable'); } if (initStatus.value === 'idle') { initStatus.value = 'loading'; initErrorMessage.value = null; initPromise = new Promise((resolve, reject) => { initPromiseResolve = resolve; initPromiseReject = reject; }); if (!worker.value) { worker.value = new Worker(new URL('../workers/exiftoolWorker.ts', import.meta.url), { type: 'module' }); worker.value.addEventListener('message', handleWorkerMessage); } worker.value.postMessage({ type: 'init' }); } if (!initPromise) { throw new Error('ExifTool is not initializing'); } await initPromise; }; const handleWorkerMessage = (event: MessageEvent): void => { const message = event.data; if (message.type === 'init-done') { initStatus.value = 'ready'; initPromiseResolve?.(); initPromiseResolve = null; initPromiseReject = null; return; } if (message.type === 'init-error') { initStatus.value = 'error'; initErrorMessage.value = message.message; initPromiseReject?.(new Error(message.message)); initPromiseResolve = null; initPromiseReject = null; return; } if (message.type === 'read-metadata-done') { const waiter = readMetadataWaiters.get(message.id); if (!waiter) { return; } readMetadataWaiters.delete(message.id); waiter.resolve(message.output); return; } if (message.type === 'read-metadata-error') { const waiter = readMetadataWaiters.get(message.id); if (!waiter) { return; } readMetadataWaiters.delete(message.id); waiter.reject(new Error(message.message)); } }; const readMetadata = async (file: File): Promise => { await initializeExiftool(); const id = crypto.randomUUID(); const workerInstance = worker.value; if (!workerInstance) { throw new Error('ExifTool worker is unavailable'); } return new Promise((resolve, reject) => { readMetadataWaiters.set(id, { resolve, reject }); workerInstance.postMessage({ type: 'read-metadata', id, file }); }); }; return { initStatus, initErrorMessage, initializeExiftool, readMetadata }; });