fix: Having sop instance in a per-frame or shared attribute breaks load (#4560)
Co-authored-by: sedghi <ar.sedghi@gmail.com>
This commit is contained in:
parent
27c040fdda
commit
cded082617
@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^2.14.7",
|
||||
"@cornerstonejs/core": "^2.14.7",
|
||||
"@cornerstonejs/adapters": "^2.15.3",
|
||||
"@cornerstonejs/core": "^2.15.3",
|
||||
"@kitware/vtk.js": "32.1.1",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
|
||||
@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^2.14.7",
|
||||
"@cornerstonejs/core": "^2.14.7",
|
||||
"@cornerstonejs/adapters": "^2.15.3",
|
||||
"@cornerstonejs/core": "^2.15.3",
|
||||
"@kitware/vtk.js": "32.1.1",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
|
||||
@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^2.14.7",
|
||||
"@cornerstonejs/core": "^2.14.7",
|
||||
"@cornerstonejs/tools": "^2.14.7",
|
||||
"@cornerstonejs/adapters": "^2.15.3",
|
||||
"@cornerstonejs/core": "^2.15.3",
|
||||
"@cornerstonejs/tools": "^2.15.3",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/core": "^2.14.7",
|
||||
"@cornerstonejs/tools": "^2.14.7",
|
||||
"@cornerstonejs/core": "^2.15.3",
|
||||
"@cornerstonejs/tools": "^2.15.3",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^2.14.7",
|
||||
"@cornerstonejs/dicom-image-loader": "^2.15.3",
|
||||
"@icr/polyseg-wasm": "^0.4.0",
|
||||
"@ohif/core": "3.10.0-beta.59",
|
||||
"@ohif/ui": "3.10.0-beta.59",
|
||||
@ -55,9 +55,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^2.14.7",
|
||||
"@cornerstonejs/core": "^2.14.7",
|
||||
"@cornerstonejs/tools": "^2.14.7",
|
||||
"@cornerstonejs/adapters": "^2.15.3",
|
||||
"@cornerstonejs/core": "^2.15.3",
|
||||
"@cornerstonejs/tools": "^2.15.3",
|
||||
"@icr/polyseg-wasm": "^0.4.0",
|
||||
"@kitware/vtk.js": "32.1.1",
|
||||
"html2canvas": "^1.4.1",
|
||||
|
||||
@ -32,8 +32,8 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/core": "^2.14.7",
|
||||
"@cornerstonejs/tools": "^2.14.7",
|
||||
"@cornerstonejs/core": "^2.15.3",
|
||||
"@cornerstonejs/tools": "^2.15.3",
|
||||
"@ohif/core": "3.10.0-beta.59",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.59",
|
||||
"@ohif/extension-default": "3.10.0-beta.59",
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^2.14.7",
|
||||
"@cornerstonejs/dicom-image-loader": "^2.15.3",
|
||||
"@emotion/serialize": "^1.1.3",
|
||||
"@ohif/core": "3.10.0-beta.59",
|
||||
"@ohif/extension-cornerstone": "3.10.0-beta.59",
|
||||
|
||||
@ -8,7 +8,7 @@ window.config = {
|
||||
showLoadingIndicator: true,
|
||||
showWarningMessageForCrossOrigin: true,
|
||||
showCPUFallbackMessage: true,
|
||||
strictZSpacingForVolumeViewport: true,
|
||||
strictZSpacingForVolumeViewport: false,
|
||||
// filterQueryParam: false,
|
||||
defaultDataSourceName: 'orthanc',
|
||||
dataSources: [
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^2.14.7",
|
||||
"@cornerstonejs/dicom-image-loader": "^2.15.3",
|
||||
"@ohif/ui": "3.10.0-beta.59",
|
||||
"cornerstone-math": "0.1.9",
|
||||
"dicom-parser": "^1.8.21"
|
||||
|
||||
@ -545,6 +545,7 @@ const WADO_IMAGE_LOADER = {
|
||||
frameOfReferenceUID: instance.FrameOfReferenceUID,
|
||||
rows: toNumber(instance.Rows),
|
||||
columns: toNumber(instance.Columns),
|
||||
spacingBetweenSlices: toNumber(instance.SpacingBetweenSlices),
|
||||
imageOrientationPatient: toNumber(ImageOrientationPatient) || [0, 1, 0, 0, 0, -1],
|
||||
rowCosines: toNumber(rowCosines || [0, 1, 0]),
|
||||
isDefaultValueSetForRowCosine: toNumber(rowCosines) ? false : true,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { vec3 } from 'gl-matrix';
|
||||
import { dicomSplit } from './dicomSplit';
|
||||
|
||||
/**
|
||||
* Combine the Per instance frame data, the shared frame data
|
||||
@ -15,24 +16,13 @@ const combineFrameInstance = (frame, instance) => {
|
||||
PerFrameFunctionalGroupsSequence,
|
||||
SharedFunctionalGroupsSequence,
|
||||
NumberOfFrames,
|
||||
SpacingBetweenSlices,
|
||||
ImageType,
|
||||
} = instance;
|
||||
|
||||
instance.ImageType = dicomSplit(ImageType);
|
||||
|
||||
if (PerFrameFunctionalGroupsSequence || NumberOfFrames > 1) {
|
||||
const frameNumber = Number.parseInt(frame || 1);
|
||||
const shared = SharedFunctionalGroupsSequence
|
||||
? Object.values(SharedFunctionalGroupsSequence[0])
|
||||
.filter(Boolean)
|
||||
.map(it => it[0])
|
||||
.filter(it => typeof it === 'object')
|
||||
: [];
|
||||
|
||||
const perFrame = PerFrameFunctionalGroupsSequence
|
||||
? Object.values(PerFrameFunctionalGroupsSequence[frameNumber - 1])
|
||||
.filter(Boolean)
|
||||
.map(it => it[0])
|
||||
.filter(it => typeof it === 'object')
|
||||
: [];
|
||||
|
||||
// this is to fix NM multiframe datasets with position and orientation
|
||||
// information inside DetectorInformationSequence
|
||||
@ -44,8 +34,12 @@ const combineFrameInstance = (frame, instance) => {
|
||||
let ImagePositionPatientToUse = instance.ImagePositionPatient;
|
||||
|
||||
if (!instance.ImagePositionPatient && instance.DetectorInformationSequence) {
|
||||
const imagePositionPatient = instance.DetectorInformationSequence[0].ImagePositionPatient;
|
||||
const imageOrientationPatient = instance.ImageOrientationPatient;
|
||||
let imagePositionPatient = instance.DetectorInformationSequence[0].ImagePositionPatient;
|
||||
let imageOrientationPatient = instance.ImageOrientationPatient;
|
||||
|
||||
imagePositionPatient = imagePositionPatient.map(it => Number(it));
|
||||
imageOrientationPatient = imageOrientationPatient.map(it => Number(it));
|
||||
const SpacingBetweenSlices = Number(instance.SpacingBetweenSlices);
|
||||
|
||||
// Calculate the position for the current frame
|
||||
if (imageOrientationPatient && SpacingBetweenSlices) {
|
||||
@ -73,26 +67,62 @@ const combineFrameInstance = (frame, instance) => {
|
||||
ImagePositionPatientToUse = [position[0], position[1], position[2]];
|
||||
}
|
||||
}
|
||||
const sharedInstance = createCombinedValue(instance, SharedFunctionalGroupsSequence?.[0]);
|
||||
const newInstance = createCombinedValue(
|
||||
sharedInstance,
|
||||
PerFrameFunctionalGroupsSequence?.[frameNumber]
|
||||
);
|
||||
|
||||
const newInstance = Object.assign(instance, { frameNumber: frameNumber });
|
||||
|
||||
// merge the shared first then the per frame to override
|
||||
[...shared, ...perFrame].forEach(item => {
|
||||
Object.entries(item).forEach(([key, value]) => {
|
||||
newInstance[key] = value;
|
||||
});
|
||||
Object.defineProperty(newInstance, 'ImagePositionPatient', {
|
||||
value: ImagePositionPatientToUse ?? newInstance.ImagePositionPatient ?? [0, 0, frameNumber],
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
// Todo: we should cache this combined instance somewhere, maybe add it
|
||||
// back to the dicomMetaStore so we don't have to do this again.
|
||||
return {
|
||||
...newInstance,
|
||||
ImagePositionPatient: ImagePositionPatientToUse ??
|
||||
newInstance.ImagePositionPatient ?? [0, 0, frameNumber],
|
||||
};
|
||||
Object.defineProperty(newInstance, 'frameNumber', {
|
||||
value: frameNumber,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
return newInstance;
|
||||
} else {
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
function createCombinedValue(parent, functionalGroups) {
|
||||
const newInstance = Object.create(parent);
|
||||
if (!functionalGroups) {
|
||||
return newInstance;
|
||||
}
|
||||
if (functionalGroups._sharedValue) {
|
||||
return functionalGroups._sharedValue;
|
||||
}
|
||||
const shared = functionalGroups
|
||||
? Object.values(functionalGroups)
|
||||
.filter(Boolean)
|
||||
.map(it => it[0])
|
||||
.filter(it => typeof it === 'object')
|
||||
: [];
|
||||
|
||||
// merge the shared first then the per frame to override
|
||||
[...shared].forEach(item => {
|
||||
if (item.SOPInstanceUID) {
|
||||
// This sub-item is a previous value information item, so don't merge it
|
||||
return;
|
||||
}
|
||||
Object.entries(item).forEach(([key, value]) => {
|
||||
newInstance[key] = value;
|
||||
});
|
||||
});
|
||||
Object.defineProperty(functionalGroups, '_sharedValue', {
|
||||
value: newInstance,
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
});
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
export default combineFrameInstance;
|
||||
|
||||
5
platform/core/src/utils/dicomSplit.ts
Normal file
5
platform/core/src/utils/dicomSplit.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export function dicomSplit(value) {
|
||||
return (
|
||||
(Array.isArray(value) && value) || (typeof value === 'string' && value.split('\\')) || value
|
||||
);
|
||||
}
|
||||
@ -11,7 +11,6 @@ function LayoutPreset({
|
||||
classNames: classNameProps,
|
||||
disabled,
|
||||
}) {
|
||||
console.debug('🚀 ~ icon:', icon);
|
||||
return (
|
||||
<div
|
||||
className={classNames(classNameProps, disabled && 'ohif-disabled')}
|
||||
|
||||
74
yarn.lock
74
yarn.lock
@ -1393,10 +1393,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
||||
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
|
||||
|
||||
"@cornerstonejs/adapters@^2.14.7":
|
||||
version "2.14.7"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-2.14.7.tgz#ecdcbc9701b26ce5c0336a6252ed44b876b51d87"
|
||||
integrity sha512-3vbWSfo6k68UzwLQ9ZVgL8DmTJA4mOEXBkETAk2M3+5Jky1EBT5X84UFKVIRzByk/76pG08D/NmPk2/QaO+FUA==
|
||||
"@cornerstonejs/adapters@^2.15.3":
|
||||
version "2.15.3"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-2.15.3.tgz#0b29519748fd1a51522f5a0c362317d51af98a7f"
|
||||
integrity sha512-TeYSM+qUh+x1XrPR4Tqowp3+T6X+g+H9i2siF33gOw+cIDi4EQ14WNcvWSB2tHmqHvg/Rmta9Kvgha0dSoWTzA==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs2" "^7.17.8"
|
||||
buffer "^6.0.3"
|
||||
@ -1429,19 +1429,19 @@
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.5.tgz#8690b61a86fa53ef38a70eee9d665a79229517c0"
|
||||
integrity sha512-MZCUy8VG0VG5Nl1l58+g+kH3LujAzLYTfJqkwpWI2gjSrGXnP6lgwyy4GmPRZWVoS40/B1LDNALK905cNWm+sg==
|
||||
|
||||
"@cornerstonejs/core@^2.14.7":
|
||||
version "2.14.7"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-2.14.7.tgz#7092f5038ecd2b88ce1417a86885cd5711d04709"
|
||||
integrity sha512-92gTKuY+Ea0xsEJB1niC7FiNsaWDK7R274ZEEcu+4vb0u6P3eIQOKCut8aKVV5JpVk3C7kJ5QjCMWdG/RNp/eg==
|
||||
"@cornerstonejs/core@^2.15.3":
|
||||
version "2.15.3"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-2.15.3.tgz#62b82ac8ebc6a5cf30f96e358bf7654f4eee71b4"
|
||||
integrity sha512-qQDvDUtONz7ydI6yf2RPI+p7/846IxnSPgCPgqLaVohjWjHeZkyhV5oY6lN50ZxgHkWmBvinw6l2wjvh4x1bKw==
|
||||
dependencies:
|
||||
"@kitware/vtk.js" "32.1.1"
|
||||
"@kitware/vtk.js" "32.9.0"
|
||||
comlink "^4.4.1"
|
||||
gl-matrix "^3.4.3"
|
||||
|
||||
"@cornerstonejs/dicom-image-loader@^2.14.7":
|
||||
version "2.14.7"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-2.14.7.tgz#9de7a5b6fd7dee030f7da5d705de4341af318bb5"
|
||||
integrity sha512-l/Vbz+5Vd076N+3xFr55aqkHxItqWe4HXjuvLBqm4orb3jWULIoM/34RJg0vGqgXj/UrB4OXxZfwDcuYBDAgEg==
|
||||
"@cornerstonejs/dicom-image-loader@^2.15.3":
|
||||
version "2.15.3"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-2.15.3.tgz#1ab90d03fab2674cbdfd6a56f4d3da585f69abf6"
|
||||
integrity sha512-/njbngdKBV3DEcD5UrPD9B3QC/eXS8gx9U3hfAvABFihaSQFd7/rEgKte1EbShQ9Qdtx1FPiaX+uMmhGPxVsQA==
|
||||
dependencies:
|
||||
"@cornerstonejs/codec-charls" "^1.2.3"
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2"
|
||||
@ -1452,10 +1452,10 @@
|
||||
pako "^2.0.4"
|
||||
uuid "^9.0.0"
|
||||
|
||||
"@cornerstonejs/tools@^2.14.7":
|
||||
version "2.14.7"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-2.14.7.tgz#da0b4485aff8aa72cd88a74ad30d0a12d762f107"
|
||||
integrity sha512-8IorarVROvevrWfkKRFGjfDONa14i9V3auWsZTAyi8J0YQ2PKrLx2MLoXWsFYQS4AHhaWRAuVl3GsKR+V/QbzQ==
|
||||
"@cornerstonejs/tools@^2.15.3":
|
||||
version "2.15.3"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-2.15.3.tgz#b778f67f13faf62d625c6ce8bbe8eed656f29ba0"
|
||||
integrity sha512-NzEeII/xTc4kFjvCZ0HV3LNOtlkFvPobxwHePuMq9wa8/2M89+blDrbrm1IOkO0xKrKMNnTqt1wTzBjcy+TKGA==
|
||||
dependencies:
|
||||
"@types/offscreencanvas" "2019.7.3"
|
||||
comlink "^4.4.1"
|
||||
@ -2225,6 +2225,29 @@
|
||||
worker-loader "3.0.8"
|
||||
xmlbuilder2 "3.0.2"
|
||||
|
||||
"@kitware/vtk.js@32.9.0":
|
||||
version "32.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@kitware/vtk.js/-/vtk.js-32.9.0.tgz#c905c92f50a1236d771e7d3c637d0c49127a48c5"
|
||||
integrity sha512-Tk9O++q6J4Z47DQnYXMnSeTmSRTQeK0Wa3447iGivCt162swOkIFCHBRtiwwHjWbe8tKCS8N/7d+eHCxkK7JtA==
|
||||
dependencies:
|
||||
"@babel/runtime" "7.22.11"
|
||||
"@types/webxr" "^0.5.5"
|
||||
commander "9.2.0"
|
||||
d3-scale "4.0.2"
|
||||
fast-deep-equal "^3.1.3"
|
||||
fflate "0.7.3"
|
||||
gl-matrix "3.4.3"
|
||||
globalthis "1.0.3"
|
||||
seedrandom "3.0.5"
|
||||
shader-loader "1.3.1"
|
||||
shelljs "0.8.5"
|
||||
spark-md5 "3.0.2"
|
||||
stream-browserify "3.0.0"
|
||||
utif "3.1.0"
|
||||
webworker-promise "0.5.0"
|
||||
worker-loader "3.0.8"
|
||||
xmlbuilder2 "3.0.2"
|
||||
|
||||
"@lerna/child-process@7.4.2":
|
||||
version "7.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.4.2.tgz#a2fd013ac2150dc288270d3e0d0b850c06bec511"
|
||||
@ -15516,6 +15539,11 @@ pacote@^15.2.0:
|
||||
ssri "^10.0.0"
|
||||
tar "^6.1.11"
|
||||
|
||||
pako@^1.0.5, pako@~1.0.5:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||
|
||||
pako@^2.0.4:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
|
||||
@ -15526,11 +15554,6 @@ pako@~0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
|
||||
integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==
|
||||
|
||||
pako@~1.0.5:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||
|
||||
param-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
|
||||
@ -19915,6 +19938,13 @@ use-sync-external-store@1.2.2, use-sync-external-store@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9"
|
||||
integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==
|
||||
|
||||
utif@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/utif/-/utif-3.1.0.tgz#4abec2f5d9cafe7813c0191e007927c125401f1c"
|
||||
integrity sha512-WEo4D/xOvFW53K5f5QTaTbbiORcm2/pCL9P6qmJnup+17eYfKaEhDeX9PeQkuyEoIxlbGklDuGl8xwuXYMrrXQ==
|
||||
dependencies:
|
||||
pako "^1.0.5"
|
||||
|
||||
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user