UNPKG

cod-dicomweb-server

Version:

A wadors server proxy that get data from a Cloud Optimized Dicom format.

105 lines (104 loc) 4.21 kB
import { ZSTDDecoder } from 'zstddec'; import { CustomError } from './classes/customClasses'; import { createMetadataJsonUrl } from './classes/utils'; import { medatata } from './constants'; import { createMetadataFileName, getDirectoryHandle, readFile, writeFile } from './fileAccessSystemUtils'; class MetadataManager { metadataPromises = {}; decoder; decoderInitPromise; constructor() { this.decoder = null; const decoder = new ZSTDDecoder(); this.decoderInitPromise = decoder .init() .then(() => { this.decoder = decoder; return true; }) .catch((error) => { console.error('Failed to initialize ZSTD WASM module:', error); return false; }); } async addDeidMetadata(jsonMetadata, url) { const { cod } = jsonMetadata; const [studyUID, _, seriesUID] = url.match(/studies\/(.*?)\/metadata/)?.[1].split('/') || []; if (!cod || !studyUID || !seriesUID) { console.warn('Missing required metadata properties: cod, studyUID, or seriesUID'); return; } for (const sopUID in cod.instances) { const instance = cod.instances[sopUID]; // For V2, convert the metadata to InstanceMetadata format. if (instance.version === medatata.METADATA_VERSION.V2 && typeof instance.metadata === 'string') { const parsedMetadata = await this.decodeDecompressAndParse(instance.metadata); if (!parsedMetadata) { throw new Error('Failed to decode, decompress, or parse JSON'); } instance.metadata = parsedMetadata; } const instanceMetadata = instance.metadata; instanceMetadata.DeidStudyInstanceUID = { Value: [studyUID] }; instanceMetadata.DeidSeriesInstanceUID = { Value: [seriesUID] }; instanceMetadata.DeidSopInstanceUID = { Value: [sopUID] }; } } getMetadataFromCache(url) { return this.metadataPromises[url]; } async getMetadata(params, headers) { const url = createMetadataJsonUrl(params); if (!url) { throw new CustomError('Error creating metadata json url'); } const cachedMetadata = this.getMetadataFromCache(url); if (cachedMetadata) { return await cachedMetadata; } const directoryHandle = await getDirectoryHandle(); const fileName = createMetadataFileName(url); const locallyCachedMetadata = (await readFile(directoryHandle, fileName, { isJson: true })); if (locallyCachedMetadata) { return locallyCachedMetadata; } try { this.metadataPromises[url] = fetch(url, { headers }) .then((response) => { if (!response.ok) { throw new CustomError(`Failed to fetch metadata: ${response.statusText}`); } return response.json(); }) .then(async (data) => { await this.addDeidMetadata(data, url); await writeFile(directoryHandle, fileName, data, true); return data; }); return await this.metadataPromises[url]; } catch (error) { console.error(error); throw error; } } async decodeDecompressAndParse(base64String) { if (!base64String) { return null; } try { if (!(await this.decoderInitPromise)) { throw new Error('WASM Decoder is not initialized. Cannot decompress data.'); } const compressedBytes = Uint8Array.from(atob(base64String), (c) => c.charCodeAt(0)); const decompressedBytes = this.decoder.decode(compressedBytes); const jsonString = new TextDecoder().decode(decompressedBytes); return JSON.parse(jsonString); } catch (error) { console.error('Failed to decode, decompress, or parse JSON:', error); return null; } } } export default MetadataManager;