diff --git a/extensions/default/src/DicomLocalDataSource/index.js b/extensions/default/src/DicomLocalDataSource/index.js
index f10d427ba..d7b3d7106 100644
--- a/extensions/default/src/DicomLocalDataSource/index.js
+++ b/extensions/default/src/DicomLocalDataSource/index.js
@@ -115,6 +115,18 @@ function createDicomLocalApi(dicomLocalConfig) {
},
},
retrieve: {
+ directURL: params => {
+ const { instance, tag, defaultType } = params;
+
+ const value = instance[tag];
+ if (value instanceof Array && value[0] instanceof ArrayBuffer) {
+ return URL.createObjectURL(
+ new Blob([value[0]], {
+ type: defaultType,
+ })
+ );
+ }
+ },
series: {
metadata: async ({ StudyInstanceUID, madeInClient = false } = {}) => {
if (!StudyInstanceUID) {
diff --git a/extensions/default/src/DicomWebDataSource/index.js b/extensions/default/src/DicomWebDataSource/index.js
index c5782375c..01943417e 100644
--- a/extensions/default/src/DicomWebDataSource/index.js
+++ b/extensions/default/src/DicomWebDataSource/index.js
@@ -178,7 +178,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
* or is already retrieved, or a promise to a URL for such use if a BulkDataURI
*/
directURL: params => {
- return getDirectURL(wadoRoot, params);
+ return getDirectURL({ wadoRoot, singlepart }, params);
},
bulkDataURI: async ({ StudyInstanceUID, BulkDataURI }) => {
const options = {
@@ -394,7 +394,13 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
};
// Todo: this needs to be from wado dicom web client
return qidoDicomWebClient.retrieveBulkData(options).then(val => {
- const ret = (val && val[0]) || undefined;
+ // There are DICOM PDF cases where the first ArrayBuffer in the array is
+ // the bulk data and DICOM video cases where the second ArrayBuffer is
+ // the bulk data. Here we play it safe and do a find.
+ const ret =
+ (val instanceof Array &&
+ val.find(arrayBuffer => arrayBuffer?.byteLength)) ||
+ undefined;
value.Value = ret;
return ret;
});
diff --git a/extensions/default/src/utils/getDirectURL.js b/extensions/default/src/utils/getDirectURL.js
index e90ff17af..4580b8d30 100644
--- a/extensions/default/src/utils/getDirectURL.js
+++ b/extensions/default/src/utils/getDirectURL.js
@@ -19,13 +19,13 @@ import {
* @returns an absolute URL to the resource, if the absolute URL can be retrieved as singlepart,
* or is already retrieved, or a promise to a URL for such use if a BulkDataURI
*/
-const getDirectURL = (wadoRoot, params) => {
+const getDirectURL = (config, params) => {
+ const { wadoRoot, singlepart } = config;
const {
instance,
tag = 'PixelData',
defaultPath = '/pixeldata',
defaultType = 'video/mp4',
- singlepart = null,
singlepart: fetchPart = 'video',
} = params;
const value = instance[tag];
@@ -53,11 +53,7 @@ const getDirectURL = (wadoRoot, params) => {
return undefined;
}
- const {
- StudyInstanceUID,
- SeriesInstanceUID,
- SOPInstanceUID,
- } = instance;
+ const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = instance;
const BulkDataURI =
(value && value.BulkDataURI) ||
`series/${SeriesInstanceUID}/instances/${SOPInstanceUID}${defaultPath}`;
@@ -66,7 +62,13 @@ const getDirectURL = (wadoRoot, params) => {
const acceptUri =
BulkDataURI +
(hasAccept ? '' : (hasQuery ? '&' : '?') + `accept=${defaultType}`);
- if (BulkDataURI.indexOf('http') === 0) return acceptUri;
+ if (BulkDataURI.indexOf('http') === 0) {
+ if (tag === 'PixelData' || tag === 'EncapsulatedDocument') {
+ return `${wadoRoot}/studies/${StudyInstanceUID}/series/${SeriesInstanceUID}/instances/${SOPInstanceUID}/rendered`;
+ } else {
+ return acceptUri;
+ }
+ }
if (BulkDataURI.indexOf('/') === 0) {
return wadoRoot + acceptUri;
}
diff --git a/extensions/dicom-pdf/src/viewports/OHIFCornerstonePdfViewport.tsx b/extensions/dicom-pdf/src/viewports/OHIFCornerstonePdfViewport.tsx
index 99488a21f..9da1904c1 100644
--- a/extensions/dicom-pdf/src/viewports/OHIFCornerstonePdfViewport.tsx
+++ b/extensions/dicom-pdf/src/viewports/OHIFCornerstonePdfViewport.tsx
@@ -14,15 +14,14 @@ function OHIFCornerstonePdfViewport({ displaySets }) {
useEffect(() => {
const load = async () => {
- await pdfUrl;
- setUrl(pdfUrl);
+ setUrl(await pdfUrl);
};
load();
}, [pdfUrl]);
return (
-
+
diff --git a/extensions/dicom-video/src/getSopClassHandlerModule.js b/extensions/dicom-video/src/getSopClassHandlerModule.js
index 4be185e74..72fc00884 100644
--- a/extensions/dicom-video/src/getSopClassHandlerModule.js
+++ b/extensions/dicom-video/src/getSopClassHandlerModule.js
@@ -37,7 +37,18 @@ const _getDisplaySetsFromSeries = (
metadata.AvailableTransferSyntaxUID ||
metadata.TransferSyntaxUID ||
metadata['00083002'];
- return supportedTransferSyntaxUIDs.includes(tsuid);
+
+ if (supportedTransferSyntaxUIDs.includes(tsuid)) {
+ return true;
+ }
+
+ // Assume that an instance with certain SOPClassUID and
+ // with at least 90 frames (i.e. typically 3 seconds of video) is indeed a video.
+ return (
+ metadata.SOPClassUID ===
+ SOP_CLASS_UIDS.MULTIFRAME_TRUE_COLOR_SECONDARY_CAPTURE_IMAGE_STORAGE &&
+ metadata.NumberOfFrames >= 90
+ );
})
.map(instance => {
const {
diff --git a/extensions/dicom-video/src/viewports/OHIFCornerstoneVideoViewport.tsx b/extensions/dicom-video/src/viewports/OHIFCornerstoneVideoViewport.tsx
index 56545b2a0..c6aa80b2f 100644
--- a/extensions/dicom-video/src/viewports/OHIFCornerstoneVideoViewport.tsx
+++ b/extensions/dicom-video/src/viewports/OHIFCornerstoneVideoViewport.tsx
@@ -14,8 +14,7 @@ function OHIFCornerstoneVideoViewport({ displaySets }) {
useEffect(() => {
const load = async () => {
- await videoUrl;
- setUrl(videoUrl);
+ setUrl(await videoUrl);
};
load();
diff --git a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx
index e81309ce2..ca1f383e2 100644
--- a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx
+++ b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx
@@ -549,6 +549,8 @@ const thumbnailNoImageModalities = [
'RTSTRUCT',
'RTPLAN',
'RTDOSE',
+ 'DOC',
+ 'OT',
];
function _getComponentType(Modality) {
diff --git a/platform/ui/src/components/ThumbnailNoImage/ThumbnailNoImage.tsx b/platform/ui/src/components/ThumbnailNoImage/ThumbnailNoImage.tsx
index 1a6e24952..83eb44536 100644
--- a/platform/ui/src/components/ThumbnailNoImage/ThumbnailNoImage.tsx
+++ b/platform/ui/src/components/ThumbnailNoImage/ThumbnailNoImage.tsx
@@ -18,7 +18,7 @@ const ThumbnailNoImage = ({
isActive,
}) => {
const [collectedProps, drag, dragPreview] = useDrag({
- type: "displayset",
+ type: 'displayset',
item: { ...dragData },
canDrag: function(monitor) {
return Object.keys(dragData).length !== 0;
@@ -39,6 +39,7 @@ const ThumbnailNoImage = ({
onDoubleClick={onDoubleClick}
role="button"
tabIndex="0"
+ data-cy={`study-browser-thumbnail-no-image`}
>
diff --git a/platform/viewer/cypress/integration/OHIFPdfDisplay.spec.js b/platform/viewer/cypress/integration/OHIFPdfDisplay.spec.js
index 954dca093..8027380f6 100644
--- a/platform/viewer/cypress/integration/OHIFPdfDisplay.spec.js
+++ b/platform/viewer/cypress/integration/OHIFPdfDisplay.spec.js
@@ -6,7 +6,7 @@ describe('OHIF PDF Display', function() {
});
it('checks if series thumbnails are being displayed', function() {
- cy.get('[data-cy="study-browser-thumbnail"]')
+ cy.get('[data-cy="study-browser-thumbnail-no-image"]')
.its('length')
.should('be.gt', 0);
});
diff --git a/platform/viewer/cypress/integration/OHIFVideoDisplay.spec.js b/platform/viewer/cypress/integration/OHIFVideoDisplay.spec.js
index b03ec0a42..e312144ee 100644
--- a/platform/viewer/cypress/integration/OHIFVideoDisplay.spec.js
+++ b/platform/viewer/cypress/integration/OHIFVideoDisplay.spec.js
@@ -5,13 +5,15 @@ describe('OHIF Video Display', function() {
});
it('checks if series thumbnails are being displayed', function() {
- cy.get('[data-cy="study-browser-thumbnail"]')
+ cy.get('[data-cy="study-browser-thumbnail-no-image"]')
.its('length')
.should('be.gt', 1);
});
it('performs double-click to load thumbnail in active viewport', () => {
- cy.get('[data-cy="study-browser-thumbnail"]:nth-child(2)').dblclick();
+ cy.get(
+ '[data-cy="study-browser-thumbnail-no-image"]:nth-child(2)'
+ ).dblclick();
//const expectedText = 'Ser: 3';
//cy.get('@viewportInfoBottomLeft').should('contains.text', expectedText);
diff --git a/platform/viewer/public/config/local_dcm4chee.js b/platform/viewer/public/config/local_dcm4chee.js
index d47c4c34a..682501fff 100644
--- a/platform/viewer/public/config/local_dcm4chee.js
+++ b/platform/viewer/public/config/local_dcm4chee.js
@@ -32,6 +32,7 @@ window.config = {
auth: 'admin:admin',
},
dicomUploadEnabled: true,
+ singlepart: 'pdf,video',
},
},
{
diff --git a/platform/viewer/src/routes/Local/dicomFileLoader.js b/platform/viewer/src/routes/Local/dicomFileLoader.js
index 1188e3169..d9e2ea92c 100644
--- a/platform/viewer/src/routes/Local/dicomFileLoader.js
+++ b/platform/viewer/src/routes/Local/dicomFileLoader.js
@@ -21,6 +21,10 @@ const DICOMFileLoader = new (class extends FileLoader {
dicomData.meta
);
+ dataset.AvailableTransferSyntaxUID =
+ dataset.AvailableTransferSyntaxUID ||
+ dataset._meta.TransferSyntaxUID?.Value?.[0];
+
return dataset;
}
})();