Slave/metadata pipeline #6

Merged
nobswebdev merged 9 commits from slave/metadata-pipeline into master 2026-10-18 16:57:39 +00:00
3 changed files with 70 additions and 19 deletions
Showing only changes of commit 42f9a3eafc - Show all commits
+55 -18
View File
@@ -1,21 +1,20 @@
import { parseMetadata } from '@uswriting/exiftool'; import { defineStore, storeToRefs } from 'pinia';
import { defineStore } from 'pinia';
import { ref } from 'vue'; import { ref } from 'vue';
import { useExiftoolStore } from '@/stores/exiftool';
import type { MetadataPreviewEntry } from '@/types/metadata/MetadataPreview'; import type { MetadataPreviewEntry } from '@/types/metadata/MetadataPreview';
import { resolveErrorMessage } from '@/utils/resolveErrorMessage';
const EXIFTOOL_PREPARE_ERROR_MESSAGE = 'Could not prepare ExifTool';
const EXIFTOOL_INITIALIZING_MESSAGE = 'Initializing ExifTool';
const METADATA_READING_MESSAGE = 'Reading metadata';
const METADATA_READ_ERROR_MESSAGE = 'Could not read image metadata';
export const useMetadataStore = defineStore('metadata', () => { export const useMetadataStore = defineStore('metadata', () => {
const previewsByKey = ref<Record<string, MetadataPreviewEntry>>({}); const previewsByKey = ref<Record<string, MetadataPreviewEntry>>({});
const readExifToolStdout = async (file: File): Promise<string> => { const exiftoolStore = useExiftoolStore();
const result = await parseMetadata(file);
if (!result.success) { const { initStatus: exiftoolInitStatus } = storeToRefs(exiftoolStore);
throw new Error(result.error); const { initializeExiftool, readMetadata } = exiftoolStore;
}
return result.data;
};
const makeKeyFromFile = (file: File): string => `${file.name}\0${file.size}\0${file.lastModified}`; const makeKeyFromFile = (file: File): string => `${file.name}\0${file.size}\0${file.lastModified}`;
@@ -28,20 +27,59 @@ export const useMetadataStore = defineStore('metadata', () => {
const loadPreview = async (file: File): Promise<MetadataPreviewEntry> => { const loadPreview = async (file: File): Promise<MetadataPreviewEntry> => {
const key = makeKeyFromFile(file); const key = makeKeyFromFile(file);
previewsByKey.value = { ...previewsByKey.value, [key]: { status: 'loading' } }; if (exiftoolInitStatus.value === 'error') {
const entry: MetadataPreviewEntry = {
status: 'error',
message: EXIFTOOL_PREPARE_ERROR_MESSAGE
};
previewsByKey.value = { ...previewsByKey.value, [key]: entry };
return entry;
}
if (exiftoolInitStatus.value === 'ready') {
previewsByKey.value = {
...previewsByKey.value,
[key]: { status: 'loading', message: METADATA_READING_MESSAGE }
};
} else {
previewsByKey.value = {
...previewsByKey.value,
[key]: { status: 'pending', message: EXIFTOOL_INITIALIZING_MESSAGE }
};
try {
await initializeExiftool();
previewsByKey.value = {
...previewsByKey.value,
[key]: { status: 'loading', message: METADATA_READING_MESSAGE }
};
} catch {
const entry: MetadataPreviewEntry = {
status: 'error',
message: EXIFTOOL_PREPARE_ERROR_MESSAGE
};
previewsByKey.value = { ...previewsByKey.value, [key]: entry };
return entry;
}
}
try { try {
const output = await readExifToolStdout(file); const output = await readMetadata(file);
const entry: MetadataPreviewEntry = { status: 'ready', output }; const entry: MetadataPreviewEntry = { status: 'ready', output };
previewsByKey.value = { ...previewsByKey.value, [key]: entry }; previewsByKey.value = { ...previewsByKey.value, [key]: entry };
return entry; return entry;
} catch (error) { } catch {
const entry: MetadataPreviewEntry = { const entry: MetadataPreviewEntry = {
status: 'error', status: 'error',
message: resolveErrorMessage(error, 'Could not read image metadata') message: METADATA_READ_ERROR_MESSAGE
}; };
previewsByKey.value = { ...previewsByKey.value, [key]: entry }; previewsByKey.value = { ...previewsByKey.value, [key]: entry };
@@ -50,9 +88,8 @@ export const useMetadataStore = defineStore('metadata', () => {
} }
}; };
const loadPreviews = async (files: readonly File[]): Promise<void> => { const loadPreviews = (files: readonly File[]): Promise<MetadataPreviewEntry[]> =>
await Promise.all(files.map(file => loadPreview(file))); Promise.all(files.map(file => loadPreview(file)));
};
const clearPreview = (file: File): void => { const clearPreview = (file: File): void => {
const key = makeKeyFromFile(file); const key = makeKeyFromFile(file);
@@ -1,5 +1,6 @@
export type MetadataPreviewEntry = export type MetadataPreviewEntry =
| { status: 'idle' } | { status: 'idle' }
| { status: 'loading' } | { status: 'pending'; message: string }
| { status: 'loading'; message: string }
| { status: 'ready'; output: string } | { status: 'ready'; output: string }
| { status: 'error'; message: string }; | { status: 'error'; message: string };
+13
View File
@@ -4,3 +4,16 @@
<p class="mt-2 text-muted-foreground">Upload UI coming soon.</p> <p class="mt-2 text-muted-foreground">Upload UI coming soon.</p>
</main> </main>
</template> </template>
<script setup lang="ts">
import { onBeforeMount } from 'vue';
import { useExiftoolStore } from '@/stores/exiftool';
const exiftoolStore = useExiftoolStore();
const { initializeExiftool } = exiftoolStore;
onBeforeMount(() => {
initializeExiftool();
});
</script>