Merge pull request #891 from grasdk/feature/Clear-DateTime-Tag-Priority
Feature/clear date time tag priority
30
src/backend/model/fileaccess/MetadataCreationDate.ts
Normal file
@ -0,0 +1,30 @@
|
||||
type MainDateTag = string; //The main date tag, where the date is to be found. Typically has both date, time and offset. Except (see SecondaryDateTag and -Type)
|
||||
type SecondaryDateTag = string | undefined; //The secondary date tag, which is sometimes needed to get a full date (sometimes contains offset, sometimes the time part of a timestamp)
|
||||
type SecondaryDateTagType = 'O' | 'T' | undefined; //The secondary date tag type: 'O' for offset, 'T' for time undefined, if there is no secondary tag for the main tag
|
||||
|
||||
//Interesting exiftool forums posts about some of these tags:
|
||||
//https://exiftool.org/forum/index.php?topic=13170.msg71174#msg71174 - about the meaning of exif.DateTimeOriginal, exif.CreateDate/exif.DateTimeDigitized and exif.ModifyDate
|
||||
//https://exiftool.org/forum/index.php?topic=15555.msg83536#msg83536
|
||||
|
||||
//This is the PRIORITIZED LIST of tags which Pigallery2 uses to determine the date of creation of pictures.
|
||||
//The list is used for embedded picture metadata and xmp-sidecar files for both pictures and vidoes.
|
||||
|
||||
export const DateTags: [MainDateTag, SecondaryDateTag, SecondaryDateTagType][] = [
|
||||
// Date tag Offset or time tag Type //Description
|
||||
["exif.DateTimeOriginal", "exif.OffsetTimeOriginal", 'O'], //Date and time when the original image was taken - shutter close time
|
||||
["exif.CreateDate", "exif.OffsetTimeDigitized", 'O'], //Date and time when the image was created
|
||||
["exif.DateTimeDigitized", "exif.OffsetTimeDigitized", 'O'], //Same as exif.CreateDate but older and newer spec name
|
||||
["ifd0.ModifyDate", undefined, undefined], //The date and time of image creation. In Exif standard, it is the date and time the file was changed.
|
||||
["ihdr.Creation Time", undefined, undefined], //Time of original image creation for PNG files
|
||||
["photoshop.DateCreated", undefined, undefined], //The date the intellectual content of the document was created. Used and set by LightRoom among others
|
||||
["xmp.CreateDate", undefined, undefined], //Date and time when the image was created (XMP standard)
|
||||
["iptc.DateCreated", "iptc.TimeCreated", 'T'], //Designates the date and optionally the time the content of the image was created rather than the date of the creation of the digital representation
|
||||
["quicktime.CreationDate", undefined, undefined], //Date and time when the QuickTime movie was created"],
|
||||
["quicktime.CreateDate", undefined, undefined], //Date and time when the QuickTime movie was created in UTC"],
|
||||
["heic.ContentCreateDate", undefined, undefined], //Date and time when the HEIC image content was created"],
|
||||
["heic.CreationDate", undefined, undefined], //Date and time when the HEIC image was created"],
|
||||
["tiff.DateTime", undefined, undefined], //Date and time of image creation.
|
||||
["exif.ModifyDate", "exif.OffsetTime", 'O'], //Modification date
|
||||
["xmp.ModifyDate", undefined, undefined], //Date and time when the image was last modified (XMP standard)"]
|
||||
["xmp.MetadataDate", undefined, undefined], //The date and time that any metadata for this resource was last changed. It should be the same as or more recent than xmp:ModifyDate.
|
||||
];
|
@ -15,6 +15,7 @@ import * as path from 'path';
|
||||
import { Utils } from '../../../common/Utils';
|
||||
import { FFmpegFactory } from '../FFmpegFactory';
|
||||
import { ExtensionDecorator } from '../extension/ExtensionDecorator';
|
||||
import { DateTags } from './MetadataCreationDate';
|
||||
|
||||
const LOG_TAG = '[MetadataLoader]';
|
||||
const ffmpeg = FFmpegFactory.get();
|
||||
@ -248,7 +249,9 @@ export class MetadataLoader {
|
||||
if (fs.existsSync(sidecarPath)) {
|
||||
const sidecarData: any = await exifr.sidecar(sidecarPath, exifrOptions);
|
||||
if (sidecarData !== undefined) {
|
||||
//note that since side cars are loaded last, data loaded here overwrites embedded metadata (in Pigallery2, not in the actual files)
|
||||
MetadataLoader.mapMetadata(metadata, sidecarData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -361,50 +364,69 @@ export class MetadataLoader {
|
||||
}
|
||||
|
||||
private static mapTimestampAndOffset(metadata: PhotoMetadata, exif: any) {
|
||||
metadata.creationDate = Utils.timestampToMS(exif?.photoshop?.DateCreated, null) ||
|
||||
Utils.timestampToMS(exif?.xmp?.CreateDate, null) ||
|
||||
Utils.timestampToMS(exif?.xmp?.ModifyDate, null) ||
|
||||
Utils.timestampToMS(Utils.toIsoTimestampString(exif?.iptc?.DateCreated, exif?.iptc?.TimeCreated), null) ||
|
||||
metadata.creationDate;
|
||||
|
||||
metadata.creationDateOffset = Utils.timestampToOffsetString(exif?.photoshop?.DateCreated) ||
|
||||
Utils.timestampToOffsetString(exif?.xmp?.CreateDate) ||
|
||||
metadata.creationDateOffset;
|
||||
if (exif.exif) {
|
||||
let offset = undefined;
|
||||
//Preceedence of dates: exif.DateTimeOriginal, exif.CreateDate, ifd0.ModifyDate, ihdr["Creation Time"], xmp.MetadataDate, file system date
|
||||
//Filesystem is the absolute last resort, and it's hard to write tests for, since file system dates are changed on e.g. git clone.
|
||||
if (exif.exif.DateTimeOriginal) {
|
||||
//DateTimeOriginal is when the camera shutter closed
|
||||
offset = exif.exif.OffsetTimeOriginal; //OffsetTimeOriginal is the corresponding offset
|
||||
if (!offset) { //Find offset among other options if possible
|
||||
offset = exif.exif.OffsetTimeDigitized || exif.exif.OffsetTime || Utils.getTimeOffsetByGPSStamp(exif.exif.DateTimeOriginal, exif.exif.GPSTimeStamp, exif.gps);
|
||||
//This method looks for date tags matching the priorized list 'DateTags' of 'MetadataCreationDate'
|
||||
let ts: string, offset: string;
|
||||
for (let i = 0; i < DateTags.length; i++) {
|
||||
const [mainpath, extrapath, extratype] = DateTags[i];
|
||||
[ts, offset] = extractTSAndOffset(mainpath, extrapath, extratype);
|
||||
if (ts) {
|
||||
if (!offset) { //We don't have the offset from the timestamp or from extra tag, let's see if we can find it in another way
|
||||
//Check the explicit offset tags. Otherwise calculate from GPS
|
||||
offset = exif.exif?.OffsetTimeOriginal || exif.exif?.OffsetTimeDigitized || exif.exif?.OffsetTime || Utils.getTimeOffsetByGPSStamp(ts, exif.exif?.GPSTimeStamp, exif.gps);
|
||||
}
|
||||
metadata.creationDate = Utils.timestampToMS(exif.exif.DateTimeOriginal, offset) || metadata.creationDate;
|
||||
} else if (exif.exif.CreateDate) { //using else if here, because DateTimeOriginal has preceedence
|
||||
//Create is when the camera wrote the file (typically within the same ms as shutter close)
|
||||
offset = exif.exif.OffsetTimeDigitized; //OffsetTimeDigitized is the corresponding offset
|
||||
if (!offset) { //Find offset among other options if possible
|
||||
offset = exif.exif.OffsetTimeOriginal || exif.exif.OffsetTime || Utils.getTimeOffsetByGPSStamp(exif.exif.DateTimeOriginal, exif.exif.GPSTimeStamp, exif.gps);
|
||||
if (!offset) { //still no offset? let's look for a timestamp with offset in the rest of the DateTags list
|
||||
const [tsonly, tsoffset] = Utils.splitTimestampAndOffset(ts);
|
||||
for (let j = i+1; j < DateTags.length; j++) {
|
||||
const [exts, exOffset] = extractTSAndOffset(DateTags[j][0], DateTags[j][1], DateTags[j][2]);
|
||||
if (exts && exOffset && Math.abs(Utils.timestampToMS(tsonly, null) - Utils.timestampToMS(exts, null)) < 30000) {
|
||||
//if there is an offset and the found timestamp is within 30 seconds of the extra timestamp, we will use the offset from the found timestamp
|
||||
offset = exOffset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
metadata.creationDate = Utils.timestampToMS(exif.exif.CreateDate, offset) || metadata.creationDate;
|
||||
} else if (exif.ifd0?.ModifyDate) { //using else if here, because DateTimeOriginal and CreatDate have preceedence
|
||||
offset = exif.exif.OffsetTime; //exif.Offsettime is the offset corresponding to ifd0.ModifyDate
|
||||
if (!offset) { //Find offset among other options if possible
|
||||
offset = exif.exif.DateTimeOriginal || exif.exif.OffsetTimeDigitized || Utils.getTimeOffsetByGPSStamp(exif.ifd0.ModifyDate, exif.exif.GPSTimeStamp, exif.gps);
|
||||
}
|
||||
metadata.creationDate = Utils.timestampToMS(exif.ifd0.ModifyDate, offset) || metadata.creationDate;
|
||||
} else if (exif.ihdr && exif.ihdr["Creation Time"]) {// again else if (another fallback date if the good ones aren't there) {
|
||||
const any_offset = exif.exif.DateTimeOriginal || exif.exif.OffsetTimeDigitized || exif.exif.OffsetTime || Utils.getTimeOffsetByGPSStamp(exif.ifd0.ModifyDate, exif.exif.GPSTimeStamp, exif.gps);
|
||||
metadata.creationDate = Utils.timestampToMS(exif.ihdr["Creation Time"], any_offset);
|
||||
offset = any_offset;
|
||||
} else if (exif.xmp?.MetadataDate) {// again else if (another fallback date if the good ones aren't there - metadata date is probably later than actual creation date, but much better than file time) {
|
||||
const any_offset = exif.exif.DateTimeOriginal || exif.exif.OffsetTimeDigitized || exif.exif.OffsetTime || Utils.getTimeOffsetByGPSStamp(exif.ifd0.ModifyDate, exif.exif.GPSTimeStamp, exif.gps);
|
||||
metadata.creationDate = Utils.timestampToMS(exif.xmp.MetadataDate, any_offset) || metadata.creationDate;
|
||||
offset = any_offset;
|
||||
break; //timestamp is found, look no further
|
||||
}
|
||||
metadata.creationDateOffset = offset || metadata.creationDateOffset;
|
||||
}
|
||||
metadata.creationDate = Utils.timestampToMS(ts, offset) || metadata.creationDate;
|
||||
metadata.creationDateOffset = offset || metadata.creationDateOffset;
|
||||
//---- End of mapTimestampAndOffset logic ----
|
||||
|
||||
//---- Helper functions for mapTimestampAndOffset ----
|
||||
function getValue(exif: any, path: string): any {
|
||||
const pathElements = path.split('.');
|
||||
let currentObject: any = exif;
|
||||
for (const pathElm of pathElements) {
|
||||
const tmp = currentObject[pathElm];
|
||||
if (tmp === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
currentObject = tmp;
|
||||
}
|
||||
return currentObject;
|
||||
}
|
||||
|
||||
function extractTSAndOffset(mainpath: string, extrapath: string, extratype: string) {
|
||||
let ts: string | undefined = undefined;
|
||||
let offset: string | undefined = undefined;
|
||||
//line below is programmatic way of finding a timestamp in the exif object. For example "xmp.CreateDate", from the DateTags list
|
||||
//ts = exif.xmp?.CreateDate
|
||||
ts = getValue(exif, mainpath);
|
||||
if (ts) {
|
||||
if (!extratype || extratype == 'O') { //offset can be in the timestamp itself
|
||||
[ts, offset] = Utils.splitTimestampAndOffset(ts);
|
||||
if (extratype == 'O' && !offset) { //offset in the extra tag and not already extracted from main tag
|
||||
offset = getValue(exif, extrapath);
|
||||
}
|
||||
} else if (extratype == 'T') { //date only in main tag, time in the extra tag
|
||||
ts = Utils.toIsoTimestampString(ts, getValue(exif, extrapath));
|
||||
[ts, offset] = Utils.splitTimestampAndOffset(ts);
|
||||
}
|
||||
}
|
||||
return [ts, offset];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static mapCameraData(metadata: PhotoMetadata, exif: any) {
|
||||
|
@ -56,11 +56,16 @@ export class Utils {
|
||||
return c;
|
||||
}
|
||||
|
||||
static zeroPrefix(value: string | number, length: number): string {
|
||||
const ret = '00000' + value;
|
||||
return ret.substr(ret.length - length);
|
||||
static zeroPrefix(number: any, length: number): string {
|
||||
if (!isNaN(number)) {
|
||||
const zerosToAdd = Math.max(length - String(number).length, 0);
|
||||
return '0'.repeat(zerosToAdd) + number;
|
||||
} else {
|
||||
return '0'.repeat(number);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the two input (let them be objects or arrays or just primitives) are equal
|
||||
*/
|
||||
@ -118,7 +123,6 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static makeUTCMidnight(d: number | Date) {
|
||||
if (!(d instanceof Date)) {
|
||||
d = new Date(d);
|
||||
@ -162,22 +166,22 @@ export class Utils {
|
||||
return Date.parse(formattedTimestamp);
|
||||
}
|
||||
|
||||
//function to extract offset string from timestamp string, returns undefined if timestamp does not contain offset
|
||||
static timestampToOffsetString(timestamp: string) {
|
||||
try {
|
||||
const offsetRegex = /[+-]\d{2}:\d{2}$/;
|
||||
const match = timestamp.match(offsetRegex);
|
||||
if (match) {
|
||||
return match[0];
|
||||
} else if (timestamp.indexOf("Z") > 0) {
|
||||
return '+00:00';
|
||||
}
|
||||
return undefined;
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
static splitTimestampAndOffset(timestamp: string): [string|undefined, string|undefined] {
|
||||
if (!timestamp) {
|
||||
return [undefined, undefined];
|
||||
}
|
||||
// |---------------------TIMESTAMP WITH OPTIONAL MILLISECONDS--------------------||-OPTIONAL TZONE--|
|
||||
// |YYYY MM DD HH MM SS (MS optio)||(timezone offset)|
|
||||
const timestampWithOffsetRegex = /^(\d{4}[-.: ]\d{2}[-.: ]\d{2}[-.: T]\d{2}[-.: ]\d{2}[-.: ]\d{2}(?:\.\d+)?)([+-]\d{2}:\d{2})?$/;
|
||||
const match = timestamp.match(timestampWithOffsetRegex);
|
||||
if (match) {
|
||||
return [match[1], match[2]]; //match[0] is the full string, not interested in that.
|
||||
} else {
|
||||
return [undefined, undefined];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//function to calculate offset from exif.exif.gpsTimeStamp or exif.gps.GPSDateStamp + exif.gps.GPSTimestamp
|
||||
static getTimeOffsetByGPSStamp(timestamp: string, gpsTimeStamp: string, gps: any) {
|
||||
let UTCTimestamp = gpsTimeStamp;
|
||||
@ -186,7 +190,7 @@ export class Utils {
|
||||
gps.GPSDateStamp &&
|
||||
gps.GPSTimeStamp) { //else use exif.gps.GPS*Stamp if available
|
||||
//GPS timestamp is always UTC (+00:00)
|
||||
UTCTimestamp = gps.GPSDateStamp.replaceAll(':', '-') + " " + gps.GPSTimeStamp.map((num: any) => Utils.zeroPad(num ,2)).join(':');
|
||||
UTCTimestamp = gps.GPSDateStamp.replaceAll(':', '-') + " " + gps.GPSTimeStamp.map((num: any) => Utils.zeroPrefix(num ,2)).join(':');
|
||||
}
|
||||
if (UTCTimestamp && timestamp) {
|
||||
//offset in minutes is the difference between gps timestamp and given timestamp
|
||||
@ -202,22 +206,13 @@ export class Utils {
|
||||
if (-720 <= offsetMinutes && offsetMinutes <= 840) {
|
||||
//valid offset is within -12 and +14 hrs (https://en.wikipedia.org/wiki/List_of_UTC_offsets)
|
||||
return (offsetMinutes < 0 ? "-" : "+") + //leading +/-
|
||||
Utils.zeroPad(Math.trunc(Math.abs(offsetMinutes) / 60), 2) + ":" + //zeropadded hours and ':'
|
||||
Utils.zeroPad((Math.abs(offsetMinutes) % 60), 2); //zeropadded minutes
|
||||
Utils.zeroPrefix(Math.trunc(Math.abs(offsetMinutes) / 60), 2) + ":" + //zeropadded hours and ':'
|
||||
Utils.zeroPrefix((Math.abs(offsetMinutes) % 60), 2); //zeropadded minutes
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
static zeroPad(number: any, length: number): string {
|
||||
if (!isNaN(number)) {
|
||||
const zerosToAdd = Math.max(length - String(number).length, 0);
|
||||
return '0'.repeat(zerosToAdd) + number;
|
||||
} else {
|
||||
return '0'.repeat(number);
|
||||
}
|
||||
}
|
||||
|
||||
static getOffsetMinutes(offsetString: string) { //Convert offset string (+HH:MM or -HH:MM) into a minute value
|
||||
const regex = /^([+-](0[0-9]|1[0-4]):[0-5][0-9])$/; //checks if offset is between -14:00 and +14:00.
|
||||
//-12:00 is the lowest valid UTC-offset, but we allow down to -14 for efficiency
|
||||
@ -230,6 +225,11 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
static getLocalTimeMS(creationDate: number, creationDateOffset: string) {
|
||||
const offsetMinutes = Utils.getOffsetMinutes(creationDateOffset);
|
||||
return creationDate + (offsetMinutes ? (offsetMinutes * 60000) : 0);
|
||||
}
|
||||
|
||||
static isLeapYear(year: number) {
|
||||
return (0 == year % 4) && (0 != year % 100) || (0 == year % 400)
|
||||
}
|
||||
|
After Width: | Height: | Size: 61 KiB |
@ -0,0 +1,18 @@
|
||||
{
|
||||
"creationDate": 1713040470000,
|
||||
"creationDateOffset": "+12:00",
|
||||
"fileSize": 62150,
|
||||
"positionData": {
|
||||
"GPSData": {
|
||||
"latitude": -36.854351,
|
||||
"longitude": 174.755402
|
||||
},
|
||||
"city": "Arch Hill",
|
||||
"country": "New Zealand",
|
||||
"state": "Auckland"
|
||||
},
|
||||
"size": {
|
||||
"height": 1080,
|
||||
"width": 1920
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 62 KiB |
@ -0,0 +1,18 @@
|
||||
{
|
||||
"creationDate": 1713040530000,
|
||||
"creationDateOffset": "-03:00",
|
||||
"fileSize": 64033,
|
||||
"positionData": {
|
||||
"GPSData": {
|
||||
"latitude": -34.862412,
|
||||
"longitude": -56.154556
|
||||
},
|
||||
"city": "Jacinto Vera",
|
||||
"country": "Uruguay",
|
||||
"state": "Montevideo"
|
||||
},
|
||||
"size": {
|
||||
"height": 1080,
|
||||
"width": 1920
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 61 KiB |
@ -0,0 +1,17 @@
|
||||
{
|
||||
"creationDate": 1713040531000,
|
||||
"creationDateOffset": "+05:45",
|
||||
"fileSize": 62443,
|
||||
"positionData": {
|
||||
"GPSData": {
|
||||
"latitude": 27.693999,
|
||||
"longitude": 85.285262
|
||||
},
|
||||
"country": "Nepal",
|
||||
"state": "Bagmati Province"
|
||||
},
|
||||
"size": {
|
||||
"height": 1080,
|
||||
"width": 1920
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 59 KiB |
@ -0,0 +1,18 @@
|
||||
{
|
||||
"creationDate": 1713040590000,
|
||||
"creationDateOffset": "+01:00",
|
||||
"fileSize": 60679,
|
||||
"positionData": {
|
||||
"GPSData": {
|
||||
"latitude": 51.515618,
|
||||
"longitude": -0.091998
|
||||
},
|
||||
"city": "Bassishaw",
|
||||
"country": "Storbritannien",
|
||||
"state": "England"
|
||||
},
|
||||
"size": {
|
||||
"height": 1080,
|
||||
"width": 1920
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 59 KiB |
@ -0,0 +1,18 @@
|
||||
{
|
||||
"creationDate": 1713040640000,
|
||||
"creationDateOffset": "+09:00",
|
||||
"fileSize": 60451,
|
||||
"positionData": {
|
||||
"GPSData": {
|
||||
"latitude": 35.682194,
|
||||
"longitude": 139.762221
|
||||
},
|
||||
"city": "Kokyogaien",
|
||||
"country": "Japan",
|
||||
"state": "Tokyo To"
|
||||
},
|
||||
"size": {
|
||||
"height": 1080,
|
||||
"width": 1920
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 58 KiB |
@ -0,0 +1,17 @@
|
||||
{
|
||||
"creationDate": 1713040650000,
|
||||
"creationDateOffset": "+00:00",
|
||||
"fileSize": 59743,
|
||||
"positionData": {
|
||||
"GPSData": {
|
||||
"latitude": 64.141166,
|
||||
"longitude": -21.94313
|
||||
},
|
||||
"city": "Reykjavík - 1",
|
||||
"country": "Island"
|
||||
},
|
||||
"size": {
|
||||
"height": 1080,
|
||||
"width": 1920
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 61 KiB |
@ -0,0 +1,18 @@
|
||||
{
|
||||
"creationDate": 1713040710000,
|
||||
"creationDateOffset": "-04:00",
|
||||
"fileSize": 62918,
|
||||
"positionData": {
|
||||
"GPSData": {
|
||||
"latitude": 40.712728,
|
||||
"longitude": -74.006015
|
||||
},
|
||||
"city": "New York",
|
||||
"country": "USA",
|
||||
"state": "New York"
|
||||
},
|
||||
"size": {
|
||||
"height": 1080,
|
||||
"width": 1920
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"skip": [
|
||||
"creationDate"
|
||||
"creationDate",
|
||||
"creationDateOffset"
|
||||
],
|
||||
"fileSize": 5860,
|
||||
"size": {
|
||||
|
@ -4,7 +4,7 @@
|
||||
"height": 5
|
||||
},
|
||||
"caption": "Description of image",
|
||||
"creationDate": 328817998000,
|
||||
"creationDate": 328839598000,
|
||||
"creationDateOffset": "-06:00",
|
||||
"faces": [
|
||||
{
|
||||
|
@ -268,6 +268,7 @@ describe('MetadataLoader', () => {
|
||||
const expected = require(path.join(__dirname, '/../../../assets/orientation/Landscape.json'));
|
||||
delete data.fileSize;
|
||||
delete data.creationDate;
|
||||
delete data.creationDateOffset;
|
||||
expect(Utils.clone(data)).to.be.deep.equal(expected);
|
||||
});
|
||||
it('Portrait ' + i, async () => {
|
||||
@ -275,6 +276,7 @@ describe('MetadataLoader', () => {
|
||||
const expected = require(path.join(__dirname, '/../../../assets/orientation/Portrait.json'));
|
||||
delete data.fileSize;
|
||||
delete data.creationDate;
|
||||
delete data.creationDateOffset;
|
||||
expect(Utils.clone(data)).to.be.deep.equal(expected);
|
||||
});
|
||||
}
|
||||
@ -334,4 +336,25 @@ describe('MetadataLoader', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('should load metadata from files with times and coordinates in different parts of the world', () => {
|
||||
const root = path.join(__dirname, '/../../../assets/4MinsAroundTheWorld');
|
||||
const files = fs.readdirSync(root);
|
||||
for (const item of files) {
|
||||
const fullFilePath = path.join(root, item);
|
||||
if (PhotoProcessing.isPhoto(fullFilePath)) {
|
||||
it(item, async () => {
|
||||
const data = await MetadataLoader.loadPhotoMetadata(fullFilePath);
|
||||
const expected = require(fullFilePath.split('.').slice(0, -1).join('.') + '.json');
|
||||
expect(Utils.clone(data)).to.be.deep.equal(expected);
|
||||
});
|
||||
} else if (VideoProcessing.isVideo(fullFilePath)) {
|
||||
it(item, async () => {
|
||||
const data = await MetadataLoader.loadVideoMetadata(fullFilePath);
|
||||
const expected = require(fullFilePath.split('.').slice(0, -1).join('.') + '.json');
|
||||
expect(Utils.clone(data)).to.be.deep.equal(expected);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|