diff --git a/src/backend/model/fileaccess/MetadataLoader.ts b/src/backend/model/fileaccess/MetadataLoader.ts
index 02a0d949..d13a3796 100644
--- a/src/backend/model/fileaccess/MetadataLoader.ts
+++ b/src/backend/model/fileaccess/MetadataLoader.ts
@@ -127,7 +127,7 @@ export class MetadataLoader {
try {
// search for sidecar and merge metadata
- const fullPathWithoutExt = path.parse(fullPath).name;
+ const fullPathWithoutExt = path.join(path.parse(fullPath).dir, path.parse(fullPath).name);
const sidecarPaths = [
fullPath + '.xmp',
fullPath + '.XMP',
@@ -143,7 +143,11 @@ export class MetadataLoader {
if (metadata.keywords === undefined) {
metadata.keywords = [];
}
- for (const kw of (sidecarData as SideCar).dc.subject) {
+ let keywords = (sidecarData as SideCar).dc.subject || [];
+ if (typeof keywords === 'string') {
+ keywords = [keywords];
+ }
+ for (const kw of keywords) {
if (metadata.keywords.indexOf(kw) === -1) {
metadata.keywords.push(kw);
}
@@ -194,7 +198,7 @@ export class MetadataLoader {
translateValues: false, //don't translate orientation from numbers to strings etc.
mergeOutput: false //don't merge output, because things like Microsoft Rating (percent) and xmp.rating will be merged
};
-
+
//function to convert timestamp into milliseconds taking offset into account
const timestampToMS = (timestamp: string, offset: string) => {
if (!timestamp) {
@@ -606,7 +610,7 @@ export class MetadataLoader {
try {
// search for sidecar and merge metadata
- const fullPathWithoutExt = path.parse(fullPath).name;
+ const fullPathWithoutExt = path.join(path.parse(fullPath).dir, path.parse(fullPath).name);
const sidecarPaths = [
fullPath + '.xmp',
fullPath + '.XMP',
@@ -623,7 +627,11 @@ export class MetadataLoader {
if (metadata.keywords === undefined) {
metadata.keywords = [];
}
- for (const kw of (sidecarData as SideCar).dc.subject) {
+ let keywords = (sidecarData as SideCar).dc.subject || [];
+ if (typeof keywords === 'string') {
+ keywords = [keywords];
+ }
+ for (const kw of keywords) {
if (metadata.keywords.indexOf(kw) === -1) {
metadata.keywords.push(kw);
}
@@ -632,6 +640,9 @@ export class MetadataLoader {
if ((sidecarData as SideCar).xmp.Rating !== undefined) {
metadata.rating = (sidecarData as SideCar).xmp.Rating;
}
+ if ((sidecarData as SideCar).xmp.CreateDate) {
+ metadata.creationDate = timestampToMS((sidecarData as SideCar).xmp.CreateDate, null);
+ }
}
}
}
diff --git a/src/common/entities/MediaDTO.ts b/src/common/entities/MediaDTO.ts
index 7bcdcd99..c1d9f8e9 100644
--- a/src/common/entities/MediaDTO.ts
+++ b/src/common/entities/MediaDTO.ts
@@ -40,6 +40,7 @@ export interface SideCarDc {
export interface SideCarXmp {
Rating?: RatingTypes;
+ CreateDate?: string;
}
export const MediaDTOUtils = {
diff --git a/test/backend/assets/sidecar/bunny_1sec.mp4 b/test/backend/assets/sidecar/bunny_1sec.mp4
new file mode 100644
index 00000000..18af94e6
Binary files /dev/null and b/test/backend/assets/sidecar/bunny_1sec.mp4 differ
diff --git a/test/backend/assets/sidecar/bunny_1sec.mp4.json b/test/backend/assets/sidecar/bunny_1sec.mp4.json
new file mode 100644
index 00000000..29d2cb74
--- /dev/null
+++ b/test/backend/assets/sidecar/bunny_1sec.mp4.json
@@ -0,0 +1,16 @@
+{
+ "size": {
+ "width": 640,
+ "height": 360
+ },
+ "bitRate": 1794127,
+ "duration": 290,
+ "creationDate": 1709052692000,
+ "fileSize": 65073,
+ "fps": 40000,
+ "keywords": [
+ "rabbit",
+ "test"
+ ],
+ "rating": 4
+}
diff --git a/test/backend/assets/sidecar/bunny_1sec.mp4.xmp b/test/backend/assets/sidecar/bunny_1sec.mp4.xmp
new file mode 100644
index 00000000..707ec9f3
--- /dev/null
+++ b/test/backend/assets/sidecar/bunny_1sec.mp4.xmp
@@ -0,0 +1,119 @@
+
+
+
+
+
+ 16
+
+
+
+ 0.0.0
+
+
+
+ H.264
+
+
+ rabbit
+ test
+
+
+
+
+
+ 480
+ 852
+ 72/1
+ 72/1
+
+
+
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+ 4
+
+
+
+
+ 25Timecode
+ 00:00:00:00
+
+ Stereo
+ 48000
+ 16Int
+
+ 1/90000
+ 2928000
+
+ 1
+ 25
+
+ 25Timecode
+ 00:00:00:00
+
+ Progressive
+ 25.000000
+
+ 480
+ pixel
+ 852
+
+ 1/1
+
+
+
+
+ d47681ee-e57e-2256-f455-43c40000004b
+ 2a5a623f-09cc-ea32-5014-869400000078
+ xmp.did:5af8e6dd-2af0-e94e-8f3c-767794b3efa1
+
+ b2c5763f-bfb9-7ee1-6893-05220000004b
+
+
+
+ saved
+ /
+ 63c4c09c-9648-ee43-720e-644600000078
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:27:31+01:00
+
+
+ saved
+ /
+ 2a5a623f-09cc-ea32-5014-869400000078
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:26:54+01:00
+
+
+ saved
+ /
+ xmp.iid:6e43712c-17f5-cc4c-b90d-766dca2590dc
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:27:31+01:00
+
+
+ saved
+ /metadata
+ xmp.iid:4d4376d3-c650-354b-b4eb-0fc2d21bcaa4
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:27:31+01:00
+
+
+
+ xmp.iid:4d4376d3-c650-354b-b4eb-0fc2d21bcaa4
+ xmp.did:1aa2d671-bf82-2043-9053-ec864079866c
+
+
+
+
\ No newline at end of file
diff --git a/test/backend/assets/sidecar/bunny_1sec_v2.mp4 b/test/backend/assets/sidecar/bunny_1sec_v2.mp4
new file mode 100644
index 00000000..18af94e6
Binary files /dev/null and b/test/backend/assets/sidecar/bunny_1sec_v2.mp4 differ
diff --git a/test/backend/assets/sidecar/bunny_1sec_v2.xmp b/test/backend/assets/sidecar/bunny_1sec_v2.xmp
new file mode 100644
index 00000000..707ec9f3
--- /dev/null
+++ b/test/backend/assets/sidecar/bunny_1sec_v2.xmp
@@ -0,0 +1,119 @@
+
+
+
+
+
+ 16
+
+
+
+ 0.0.0
+
+
+
+ H.264
+
+
+ rabbit
+ test
+
+
+
+
+
+ 480
+ 852
+ 72/1
+ 72/1
+
+
+
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+ 4
+
+
+
+
+ 25Timecode
+ 00:00:00:00
+
+ Stereo
+ 48000
+ 16Int
+
+ 1/90000
+ 2928000
+
+ 1
+ 25
+
+ 25Timecode
+ 00:00:00:00
+
+ Progressive
+ 25.000000
+
+ 480
+ pixel
+ 852
+
+ 1/1
+
+
+
+
+ d47681ee-e57e-2256-f455-43c40000004b
+ 2a5a623f-09cc-ea32-5014-869400000078
+ xmp.did:5af8e6dd-2af0-e94e-8f3c-767794b3efa1
+
+ b2c5763f-bfb9-7ee1-6893-05220000004b
+
+
+
+ saved
+ /
+ 63c4c09c-9648-ee43-720e-644600000078
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:27:31+01:00
+
+
+ saved
+ /
+ 2a5a623f-09cc-ea32-5014-869400000078
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:26:54+01:00
+
+
+ saved
+ /
+ xmp.iid:6e43712c-17f5-cc4c-b90d-766dca2590dc
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:27:31+01:00
+
+
+ saved
+ /metadata
+ xmp.iid:4d4376d3-c650-354b-b4eb-0fc2d21bcaa4
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:27:31+01:00
+
+
+
+ xmp.iid:4d4376d3-c650-354b-b4eb-0fc2d21bcaa4
+ xmp.did:1aa2d671-bf82-2043-9053-ec864079866c
+
+
+
+
\ No newline at end of file
diff --git a/test/backend/assets/sidecar/bunny_1sec_v3.mp4 b/test/backend/assets/sidecar/bunny_1sec_v3.mp4
new file mode 100644
index 00000000..18af94e6
Binary files /dev/null and b/test/backend/assets/sidecar/bunny_1sec_v3.mp4 differ
diff --git a/test/backend/assets/sidecar/bunny_1sec_v3.mp4.json b/test/backend/assets/sidecar/bunny_1sec_v3.mp4.json
new file mode 100644
index 00000000..8cd3db65
--- /dev/null
+++ b/test/backend/assets/sidecar/bunny_1sec_v3.mp4.json
@@ -0,0 +1,15 @@
+{
+ "size": {
+ "width": 640,
+ "height": 360
+ },
+ "bitRate": 1794127,
+ "duration": 290,
+ "creationDate": 1709052692000,
+ "fileSize": 65073,
+ "fps": 40000,
+ "keywords": [
+ "rabbit"
+ ],
+ "rating": 4
+}
diff --git a/test/backend/assets/sidecar/bunny_1sec_v3.xmp b/test/backend/assets/sidecar/bunny_1sec_v3.xmp
new file mode 100644
index 00000000..d56f5385
--- /dev/null
+++ b/test/backend/assets/sidecar/bunny_1sec_v3.xmp
@@ -0,0 +1,118 @@
+
+
+
+
+
+ 16
+
+
+
+ 0.0.0
+
+
+
+ H.264
+
+
+ rabbit
+
+
+
+
+
+ 480
+ 852
+ 72/1
+ 72/1
+
+
+
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+ 4
+
+
+
+
+ 25Timecode
+ 00:00:00:00
+
+ Stereo
+ 48000
+ 16Int
+
+ 1/90000
+ 2928000
+
+ 1
+ 25
+
+ 25Timecode
+ 00:00:00:00
+
+ Progressive
+ 25.000000
+
+ 480
+ pixel
+ 852
+
+ 1/1
+
+
+
+
+ d47681ee-e57e-2256-f455-43c40000004b
+ 2a5a623f-09cc-ea32-5014-869400000078
+ xmp.did:5af8e6dd-2af0-e94e-8f3c-767794b3efa1
+
+ b2c5763f-bfb9-7ee1-6893-05220000004b
+
+
+
+ saved
+ /
+ 63c4c09c-9648-ee43-720e-644600000078
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:27:31+01:00
+
+
+ saved
+ /
+ 2a5a623f-09cc-ea32-5014-869400000078
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:26:54+01:00
+
+
+ saved
+ /
+ xmp.iid:6e43712c-17f5-cc4c-b90d-766dca2590dc
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:27:31+01:00
+
+
+ saved
+ /metadata
+ xmp.iid:4d4376d3-c650-354b-b4eb-0fc2d21bcaa4
+ Adobe Adobe Media Encoder CC 2017.1 (Windows)
+ 2018-11-17T20:27:31+01:00
+
+
+
+ xmp.iid:4d4376d3-c650-354b-b4eb-0fc2d21bcaa4
+ xmp.did:1aa2d671-bf82-2043-9053-ec864079866c
+
+
+
+
\ No newline at end of file
diff --git a/test/backend/assets/sidecar/metadata.jpg b/test/backend/assets/sidecar/metadata.jpg
new file mode 100644
index 00000000..6afec581
Binary files /dev/null and b/test/backend/assets/sidecar/metadata.jpg differ
diff --git a/test/backend/assets/sidecar/metadata.jpg.json b/test/backend/assets/sidecar/metadata.jpg.json
new file mode 100644
index 00000000..3593766b
--- /dev/null
+++ b/test/backend/assets/sidecar/metadata.jpg.json
@@ -0,0 +1,12 @@
+{
+ "size": {
+ "width": 10,
+ "height": 5
+ },
+ "creationDate": 1710188754000,
+ "fileSize": 5095,
+ "keywords": [
+ "floor",
+ "book"
+ ]
+}
diff --git a/test/backend/assets/sidecar/metadata_v2.jpg b/test/backend/assets/sidecar/metadata_v2.jpg
new file mode 100644
index 00000000..6afec581
Binary files /dev/null and b/test/backend/assets/sidecar/metadata_v2.jpg differ
diff --git a/test/backend/assets/sidecar/metadata_v2.jpg.json b/test/backend/assets/sidecar/metadata_v2.jpg.json
new file mode 100644
index 00000000..e66ce699
--- /dev/null
+++ b/test/backend/assets/sidecar/metadata_v2.jpg.json
@@ -0,0 +1,15 @@
+{
+ "size": {
+ "width": 10,
+ "height": 5
+ },
+ "creationDate": 1710188754000,
+ "fileSize": 5095,
+ "keywords": [
+ "floor",
+ "book",
+ "first",
+ "second"
+ ],
+ "rating": 0
+}
diff --git a/test/backend/assets/sidecar/metadata_v2.xmp b/test/backend/assets/sidecar/metadata_v2.xmp
new file mode 100644
index 00000000..532af7a5
--- /dev/null
+++ b/test/backend/assets/sidecar/metadata_v2.xmp
@@ -0,0 +1,18 @@
+
+
+
+ 0
+ 0
+
+
+ first
+ second
+
+
+
+
+
diff --git a/test/backend/assets/sidecar/no_metadata.jpg b/test/backend/assets/sidecar/no_metadata.jpg
new file mode 100644
index 00000000..c119808d
Binary files /dev/null and b/test/backend/assets/sidecar/no_metadata.jpg differ
diff --git a/test/backend/assets/sidecar/no_metadata.jpg.json b/test/backend/assets/sidecar/no_metadata.jpg.json
new file mode 100644
index 00000000..2ce48ad6
--- /dev/null
+++ b/test/backend/assets/sidecar/no_metadata.jpg.json
@@ -0,0 +1,12 @@
+{
+ "size": {
+ "width": 10,
+ "height": 5
+ },
+ "creationDate": 1542482851000,
+ "fileSize": 1430,
+ "keywords": [
+ "first",
+ "second"
+ ]
+}
diff --git a/test/backend/assets/sidecar/no_metadata.jpg.xmp b/test/backend/assets/sidecar/no_metadata.jpg.xmp
new file mode 100644
index 00000000..68dc1368
--- /dev/null
+++ b/test/backend/assets/sidecar/no_metadata.jpg.xmp
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+ first
+ second
+
+
+
+
+
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+
+
+
+
\ No newline at end of file
diff --git a/test/backend/assets/sidecar/no_metadata_v2.jpg b/test/backend/assets/sidecar/no_metadata_v2.jpg
new file mode 100644
index 00000000..c119808d
Binary files /dev/null and b/test/backend/assets/sidecar/no_metadata_v2.jpg differ
diff --git a/test/backend/assets/sidecar/no_metadata_v2.jpg.json b/test/backend/assets/sidecar/no_metadata_v2.jpg.json
new file mode 100644
index 00000000..2ce48ad6
--- /dev/null
+++ b/test/backend/assets/sidecar/no_metadata_v2.jpg.json
@@ -0,0 +1,12 @@
+{
+ "size": {
+ "width": 10,
+ "height": 5
+ },
+ "creationDate": 1542482851000,
+ "fileSize": 1430,
+ "keywords": [
+ "first",
+ "second"
+ ]
+}
diff --git a/test/backend/assets/sidecar/no_metadata_v2.xmp b/test/backend/assets/sidecar/no_metadata_v2.xmp
new file mode 100644
index 00000000..07376ab3
--- /dev/null
+++ b/test/backend/assets/sidecar/no_metadata_v2.xmp
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ first
+ second
+
+
+
+
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+
+
+
+
diff --git a/test/backend/assets/sidecar/no_metadata_v3.jpg b/test/backend/assets/sidecar/no_metadata_v3.jpg
new file mode 100644
index 00000000..c119808d
Binary files /dev/null and b/test/backend/assets/sidecar/no_metadata_v3.jpg differ
diff --git a/test/backend/assets/sidecar/no_metadata_v3.jpg.json b/test/backend/assets/sidecar/no_metadata_v3.jpg.json
new file mode 100644
index 00000000..7f904f44
--- /dev/null
+++ b/test/backend/assets/sidecar/no_metadata_v3.jpg.json
@@ -0,0 +1,11 @@
+{
+ "size": {
+ "width": 10,
+ "height": 5
+ },
+ "creationDate": 1542482851000,
+ "fileSize": 1430,
+ "keywords": [
+ "first"
+ ]
+}
diff --git a/test/backend/assets/sidecar/no_metadata_v3.xmp b/test/backend/assets/sidecar/no_metadata_v3.xmp
new file mode 100644
index 00000000..906cbf96
--- /dev/null
+++ b/test/backend/assets/sidecar/no_metadata_v3.xmp
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ first
+
+
+
+
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+ 2018-11-17T20:27:31+01:00
+
+
+
+
diff --git a/test/backend/unit/model/threading/MetaDataLoader.spec.ts b/test/backend/unit/model/threading/MetaDataLoader.spec.ts
index 30542f2b..e0b0b8c6 100644
--- a/test/backend/unit/model/threading/MetaDataLoader.spec.ts
+++ b/test/backend/unit/model/threading/MetaDataLoader.spec.ts
@@ -99,6 +99,67 @@ describe('MetadataLoader', () => {
const expected = require(path.join(__dirname, '/../../../assets/timestamps/big_ben_only_time.json'));
expect(Utils.clone(data)).to.be.deep.equal(expected);
});
+ it('should load sidecar file with file extension for video', async () => {
+ const data = await MetadataLoader.loadVideoMetadata(path.join(__dirname, '/../../../assets/sidecar/bunny_1sec.mp4'));
+ const expected = require(path.join(__dirname, '/../../../assets/sidecar/bunny_1sec.mp4.json'));
+ expect(Utils.clone(data)).to.be.deep.equal(expected);
+ });
+
+ it('should load sidecar file without file extension for video', async () => {
+ const data = await MetadataLoader.loadVideoMetadata(path.join(__dirname, '/../../../assets/sidecar/bunny_1sec_v2.mp4'));
+ const expected = require(path.join(__dirname, '/../../../assets/sidecar/bunny_1sec.mp4.json'));//sidecar "bunny_1sec_v2.xmp" is identical to "bunny_1sec.mp4.xmp" so we expect the same result
+ expect(Utils.clone(data)).to.be.deep.equal(expected);
+ });
+
+ it('should retrieve both keywords from sidecar file for video', async () => {
+ const data = await MetadataLoader.loadVideoMetadata(path.join(__dirname, '/../../../assets/sidecar/bunny_1sec.mp4'));
+ const expected = require(path.join(__dirname, '/../../../assets/sidecar/bunny_1sec.mp4.json'));
+ expect(Utils.clone(data)).to.be.deep.equal(expected);
+ });
+
+ it('should retrieve one keyword from sidecar file for video', async () => {
+ const data = await MetadataLoader.loadVideoMetadata(path.join(__dirname, '/../../../assets/sidecar/bunny_1sec_v3.mp4'));
+ const expected = require(path.join(__dirname, '/../../../assets/sidecar/bunny_1sec_v3.mp4.json'));
+ expect(Utils.clone(data)).to.be.deep.equal(expected);
+ });
+
+ it('should load sidecar file with file extension for photo', async () => {
+ const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/sidecar/no_metadata.jpg'));
+ const expected = require(path.join(__dirname, '/../../../assets/sidecar/no_metadata.jpg.json'));
+ expect(Utils.clone(data)).to.be.deep.equal(expected);
+ });
+
+ it('should load sidecar file without file extension for photo', async () => {
+ const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/sidecar/no_metadata_v2.jpg'));
+ const expected = require(path.join(__dirname, '/../../../assets/sidecar/no_metadata_v2.jpg.json'));
+ expect(Utils.clone(data)).to.be.deep.equal(expected);
+ });
+
+ it('should retrieve both keywords from sidecar file for photo', async () => {
+ const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/sidecar/no_metadata.jpg'));
+ const expected = require(path.join(__dirname, '/../../../assets/sidecar/no_metadata.jpg.json'));
+ expect(Utils.clone(data)).to.be.deep.equal(expected);
+ });
+
+ it('should retrieve one keyword from sidecar file for photo', async () => {
+ const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/sidecar/no_metadata_v3.jpg'));
+ const expected = require(path.join(__dirname, '/../../../assets/sidecar/no_metadata_v3.jpg.json'));
+ expect(Utils.clone(data)).to.be.deep.equal(expected);
+ });
+
+ it('should read keywords from photo without sidecar file', async () => {
+ const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/sidecar/metadata.jpg'));
+ const expected = require(path.join(__dirname, '/../../../assets/sidecar/metadata.jpg.json'));
+ expect(Utils.clone(data)).to.be.deep.equal(expected);
+ });
+
+ it('should merge keywords from photo with keywords from sidecar', async () => {
+ const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/sidecar/metadata_v2.jpg'));
+ const expected = require(path.join(__dirname, '/../../../assets/sidecar/metadata_v2.jpg.json')); //"metadata_v2.jpg" is identical to "metadata.jpg" and "metadata_v2.xmp" contains 2 different keywords
+ expect(Utils.clone(data)).to.be.deep.equal(expected);
+ });
+
+
describe('should load jpg with proper height and orientation', () => {
it('jpg 1', async () => {
const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/orientation/broken_orientation_exif.jpg'));