2023-03-05 22:45:01 +08:00
/* eslint-disable @typescript-eslint/no-var-requires */
2018-12-21 06:02:49 +08:00
import { expect } from 'chai' ;
2023-10-14 23:54:21 +08:00
import { MetadataLoader } from '../../../../../src/backend/model/fileaccess/MetadataLoader' ;
2019-12-10 17:44:35 +08:00
import { Utils } from '../../../../../src/common/Utils' ;
2018-12-21 06:02:49 +08:00
import * as path from 'path' ;
2021-04-02 21:53:20 +08:00
import * as fs from 'fs' ;
2023-10-14 23:54:21 +08:00
import { PhotoProcessing } from '../../../../../src/backend/model/fileaccess/fileprocessing/PhotoProcessing' ;
2024-03-07 17:14:50 +08:00
import { VideoProcessing } from '../../../../../src/backend/model/fileaccess/fileprocessing/VideoProcessing' ;
2021-05-11 21:57:36 +08:00
import { Config } from '../../../../../src/common/config/private/Config' ;
2021-05-11 22:44:17 +08:00
import { DatabaseType } from '../../../../../src/common/config/private/PrivateConfig' ;
declare const before : any ;
2018-12-21 06:02:49 +08:00
describe ( 'MetadataLoader' , ( ) = > {
2021-05-11 22:44:17 +08:00
// loading default settings (this might have been changed by other tests)
before ( ( ) = > {
Config . loadSync ( ) ;
2022-12-29 02:12:18 +08:00
Config . Database . type = DatabaseType . sqlite ;
Config . Faces . enabled = true ;
Config . Faces . keywordsToPersons = true ;
2023-11-17 06:41:05 +08:00
Config . Extensions . enabled = false ;
2021-05-11 22:44:17 +08:00
} ) ;
2018-12-21 06:02:49 +08:00
it ( 'should load png' , async ( ) = > {
2020-01-08 05:28:59 +08:00
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/test_png.png' ) ) ;
2020-09-13 20:43:03 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/test_png.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
2018-12-21 06:02:49 +08:00
} ) ;
2024-02-11 22:55:26 +08:00
it ( 'should load png with faces and dates' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/png_with_faces_and_dates.png' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/png_with_faces_and_dates.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2024-04-05 05:53:07 +08:00
it ( 'should load png with description' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/title_caption/description.png' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/title_caption/description.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
it ( 'should load image with metadata saved by digikam' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/title_caption/digikam.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/title_caption/digikam.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2018-12-21 06:02:49 +08:00
it ( 'should load jpg' , async ( ) = > {
2020-01-08 05:28:59 +08:00
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/test image öüóőúéáű-.,.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/test image öüóőúéáű-.,.json' ) ) ;
2019-01-14 00:38:39 +08:00
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
2018-12-21 06:02:49 +08:00
} ) ;
2020-02-08 23:31:29 +08:00
it ( 'should load miss dated jpg' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/date_issue.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/date_issue.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2021-05-11 21:43:44 +08:00
it ( 'should load xmp section dc:subject into keywords' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/xmp/xmp_subject.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/xmp/xmp_subject.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
it ( 'should load Rating and not overwrite its value by RatingPercent' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/two_ratings.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/two_ratings.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2021-04-26 21:22:12 +08:00
2019-01-19 17:07:06 +08:00
it ( 'should load jpg 2' , async ( ) = > {
2020-01-08 05:28:59 +08:00
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/old_photo.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/old_photo.json' ) ) ;
2019-01-19 17:07:06 +08:00
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2024-02-06 05:35:25 +08:00
it ( 'should load jpg with special characters' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/Chars.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/Chars.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
it ( 'should load jpg with special characters saved by exiftool' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/Chars_exiftool.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/Chars_exiftool.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2019-01-19 17:07:06 +08:00
2024-02-11 22:55:26 +08:00
it ( 'should load jpg with timestamps, timezone AEST (UTC+10) and gps (UTC)' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/timestamps/sydney_opera_house.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/timestamps/sydney_opera_house.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
it ( 'should load jpg with timestamps and gps (UTC) and calculate offset +10' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/timestamps/sydney_opera_house_no_tsoffset_but_gps_utc.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/timestamps/sydney_opera_house_no_tsoffset_but_gps_utc.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
it ( 'should load jpg with timestamps, timezone BST (UTC+1) and gps (UTC)' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/timestamps/big_ben.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/timestamps/big_ben.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
it ( 'should load jpg with timestamps and gps (UTC) and calculate offset +1' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/timestamps/big_ben_no_tsoffset_but_gps_utc.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/timestamps/big_ben_no_tsoffset_but_gps_utc.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
it ( 'should load jpg with timestamps but no offset and no GPS to calculate it from' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/timestamps/big_ben_only_time.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/timestamps/big_ben_only_time.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2024-02-28 01:53:53 +08:00
it ( 'should load sidecar file with file extension for video' , async ( ) = > {
const data = await MetadataLoader . loadVideoMetadata ( path . join ( __dirname , '/../../../assets/sidecar/bunny_1sec.mp4' ) ) ;
2024-03-14 17:41:41 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/sidecar/bunny_1sec.json' ) ) ;
2024-02-28 01:53:53 +08:00
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' ) ) ;
2024-03-14 17:41:41 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/sidecar/bunny_1sec.json' ) ) ; //sidecar "bunny_1sec_v2.xmp" is identical to "bunny_1sec.mp4.xmp" so we expect the same result
2024-02-28 01:53:53 +08:00
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' ) ) ;
2024-03-14 17:41:41 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/sidecar/bunny_1sec.json' ) ) ;
2024-02-28 01:53:53 +08:00
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' ) ) ;
2024-03-14 17:41:41 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/sidecar/bunny_1sec_v3.json' ) ) ;
2024-02-28 01:53:53 +08:00
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' ) ) ;
2024-03-14 17:41:41 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/sidecar/no_metadata.json' ) ) ;
2024-02-28 01:53:53 +08:00
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' ) ) ;
2024-03-14 17:41:41 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/sidecar/no_metadata_v2.json' ) ) ;
2024-02-28 01:53:53 +08:00
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' ) ) ;
2024-03-14 17:41:41 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/sidecar/no_metadata.json' ) ) ;
2024-02-28 01:53:53 +08:00
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' ) ) ;
2024-03-14 17:41:41 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/sidecar/no_metadata_v3.json' ) ) ;
2024-02-28 01:53:53 +08:00
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2024-02-29 01:50:19 +08:00
it ( 'should read keywords from photo without sidecar file' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/sidecar/metadata.jpg' ) ) ;
2024-03-14 17:41:41 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/sidecar/metadata.json' ) ) ;
2024-02-29 01:50:19 +08:00
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' ) ) ;
2024-03-14 17:41:41 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/sidecar/metadata_v2.json' ) ) ; //"metadata_v2.jpg" is identical to "metadata.jpg" and "metadata_v2.xmp" contains 2 different keywords
2024-03-12 15:48:55 +08:00
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
2024-02-29 01:50:19 +08:00
} ) ;
2024-02-28 01:53:53 +08:00
2021-03-27 17:23:48 +08:00
describe ( 'should load jpg with proper height and orientation' , ( ) = > {
it ( 'jpg 1' , async ( ) = > {
2021-03-28 19:24:55 +08:00
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/orientation/broken_orientation_exif.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/orientation/broken_orientation_exif.json' ) ) ;
2021-03-27 17:23:48 +08:00
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
it ( 'jpg 2' , async ( ) = > {
2021-04-02 21:53:20 +08:00
const data = await MetadataLoader . loadPhotoMetadata (
2023-11-17 06:41:05 +08:00
path . join ( __dirname , '/../../../assets/orientation/broken_orientation_exif2.jpg' ) ) ;
2021-03-28 19:24:55 +08:00
const expected = require ( path . join ( __dirname , '/../../../assets/orientation/broken_orientation_exif2.json' ) ) ;
2021-03-27 17:23:48 +08:00
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
} ) ;
2024-03-07 16:29:44 +08:00
it ( 'should load wild-1-small image with CreateDate from 2015, but no DateTimeOriginal' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/wild-1-small.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/wild-1-small.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2024-04-04 04:50:49 +08:00
it ( 'should load wild-2-small image with xmp-CreateDate from 2015 and negative offset' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/wild-2-small.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/wild-2-small.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2021-03-27 17:23:48 +08:00
2021-04-02 21:53:20 +08:00
describe ( 'should load jpg with edge case exif data' , ( ) = > {
const root = path . join ( __dirname , '/../../../assets/edge_case_exif_data' ) ;
const files = fs . readdirSync ( root ) ;
2021-04-18 21:48:35 +08:00
for ( const item of files ) {
const fullFilePath = path . join ( root , item ) ;
2021-04-02 21:53:20 +08:00
if ( PhotoProcessing . isPhoto ( fullFilePath ) ) {
2021-04-18 21:48:35 +08:00
it ( item , async ( ) = > {
2021-04-02 21:53:20 +08:00
const data = await MetadataLoader . loadPhotoMetadata ( fullFilePath ) ;
const expected = require ( fullFilePath . split ( '.' ) . slice ( 0 , - 1 ) . join ( '.' ) + '.json' ) ;
if ( expected . skip ) {
expected . skip . forEach ( ( s : string ) = > {
2021-04-18 21:48:35 +08:00
delete ( data as any ) [ s ] ;
2021-04-02 21:53:20 +08:00
delete expected [ s ] ;
} ) ;
delete expected . skip ;
}
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
}
}
} ) ;
2022-03-26 04:51:42 +08:00
// TODO: deprecated tests. We do not save orientation anymore.
2021-03-28 19:24:55 +08:00
describe ( 'should read orientation' , ( ) = > {
for ( let i = 0 ; i <= 8 ; ++ i ) {
it ( 'Landscape ' + i , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/orientation/Landscape_' + i + '.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/orientation/Landscape.json' ) ) ;
delete data . fileSize ;
2021-03-28 19:41:54 +08:00
delete data . creationDate ;
2021-03-28 19:24:55 +08:00
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
it ( 'Portrait ' + i , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/orientation/Portrait_' + i + '.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/orientation/Portrait.json' ) ) ;
delete data . fileSize ;
2021-03-28 19:41:54 +08:00
delete data . creationDate ;
2021-03-28 19:24:55 +08:00
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
}
} ) ;
2019-02-16 05:19:30 +08:00
2020-12-27 21:35:30 +08:00
it ( 'should load jpg edited with exiftool' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/exiftool.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/exiftool.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2023-03-05 22:45:01 +08:00
it ( 'should load jpg with provided ImageWidth but missing imageSize' , async ( ) = > {
const data = await MetadataLoader . loadPhotoMetadata ( path . join ( __dirname , '/../../../assets/imageSizeError.jpg' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/imageSizeError.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2020-12-27 21:35:30 +08:00
2019-02-16 05:19:30 +08:00
it ( 'should load mp4' , async ( ) = > {
2020-01-08 05:28:59 +08:00
const data = await MetadataLoader . loadVideoMetadata ( path . join ( __dirname , '/../../../assets/video.mp4' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/video.json' ) ) ;
2019-02-16 05:19:30 +08:00
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2020-09-02 19:23:30 +08:00
it ( 'should respect mp4 rotate transformation' , async ( ) = > {
const data = await MetadataLoader . loadVideoMetadata ( path . join ( __dirname , '/../../../assets/video_rotate.mp4' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/video_rotate.json' ) ) ;
2021-03-27 17:23:48 +08:00
delete data . duration ;
delete expected . duration ;
2020-09-02 19:23:30 +08:00
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2023-10-14 23:54:21 +08:00
2023-09-03 21:35:19 +08:00
it ( 'should load mkv' , async ( ) = > {
const data = await MetadataLoader . loadVideoMetadata ( path . join ( __dirname , '/../../../assets/video_mkv.mkv' ) ) ;
const expected = require ( path . join ( __dirname , '/../../../assets/video_mkv.json' ) ) ;
expect ( Utils . clone ( data ) ) . to . be . deep . equal ( expected ) ;
} ) ;
2019-02-16 05:19:30 +08:00
2024-03-07 17:14:50 +08:00
describe ( 'should load metadata from sidecar files' , ( ) = > {
const root = path . join ( __dirname , '/../../../assets/sidecar' ) ;
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 ) ;
} ) ;
}
}
} ) ;
2018-12-21 06:02:49 +08:00
} ) ;