@@ -12,7 +12,6 @@ import { FfprobeData } from 'fluent-ffmpeg';
1212import { FileHandle } from 'fs/promises' ;
1313import * as util from 'node:util' ;
1414import * as path from 'path' ;
15- import { IptcParser } from 'ts-node-iptc' ;
1615import { Utils } from '../../../common/Utils' ;
1716import { FFmpegFactory } from '../FFmpegFactory' ;
1817import { ExtensionDecorator } from '../extension/ExtensionDecorator' ;
@@ -181,7 +180,7 @@ export class MetadataLoader {
181180 icc : false ,
182181 jfif : false , //not needed and not supported for png
183182 ihdr : true ,
184- iptc : false , //exifr reads UTF8-encoded data wrongly, using IptcParser instead
183+ iptc : true ,
185184 exif : true ,
186185 gps : true ,
187186 reviveValues : false , //don't convert timestamps
@@ -221,46 +220,6 @@ export class MetadataLoader {
221220 await fileHandle . close ( ) ;
222221 }
223222 try {
224-
225-
226- try { //Parse iptc data using the IptcParser, which works correctly for both UTF-8 and ASCII
227- const iptcData = IptcParser . parse ( data ) ;
228- if ( iptcData . country_or_primary_location_name ) {
229- metadata . positionData = metadata . positionData || { } ;
230- metadata . positionData . country =
231- iptcData . country_or_primary_location_name
232- . replace ( / \0 / g, '' )
233- . trim ( ) ;
234- }
235- if ( iptcData . province_or_state ) {
236- metadata . positionData = metadata . positionData || { } ;
237- metadata . positionData . state = iptcData . province_or_state
238- . replace ( / \0 / g, '' )
239- . trim ( ) ;
240- }
241- if ( iptcData . city ) {
242- metadata . positionData = metadata . positionData || { } ;
243- metadata . positionData . city = iptcData . city
244- . replace ( / \0 / g, '' )
245- . trim ( ) ;
246- }
247- if ( iptcData . object_name ) {
248- metadata . title = iptcData . object_name . replace ( / \0 / g, '' ) . trim ( ) ;
249- }
250- if ( iptcData . caption ) {
251- metadata . caption = iptcData . caption . replace ( / \0 / g, '' ) . trim ( ) ;
252- }
253- if ( Array . isArray ( iptcData . keywords ) ) {
254- metadata . keywords = iptcData . keywords ;
255- }
256-
257- if ( iptcData . date_time ) {
258- metadata . creationDate = iptcData . date_time . getTime ( ) ;
259- }
260- } catch ( err ) {
261- // Logger.debug(LOG_TAG, 'Error parsing iptc data', fullPath, err);
262- }
263-
264223 try {
265224 const exif = await exifr . parse ( data , exifrOptions ) ;
266225 MetadataLoader . mapMetadata ( metadata , exif ) ;
@@ -370,20 +329,35 @@ export class MetadataLoader {
370329 }
371330 }
372331 }
332+ if ( exif . iptc &&
333+ exif . iptc . Keywords &&
334+ exif . iptc . Keywords . length > 0 ) {
335+ const subj = Array . isArray ( exif . iptc . Keywords ) ? exif . iptc . Keywords : [ exif . iptc . Keywords ] ;
336+ if ( metadata . keywords === undefined ) {
337+ metadata . keywords = [ ] ;
338+ }
339+ for ( let kw of subj ) {
340+ kw = Utils . asciiToUTF8 ( kw ) ;
341+ if ( metadata . keywords . indexOf ( kw ) === - 1 ) {
342+ metadata . keywords . push ( kw ) ;
343+ }
344+ }
345+ }
373346 }
374347
375348 private static mapTitle ( metadata : PhotoMetadata , exif : any ) {
376- metadata . title = exif . dc ?. title ?. value || metadata . title || exif . photoshop ?. Headline || exif . acdsee ?. caption ; //acdsee caption holds the title when data is saved by digikam. Used as last resort if iptc and dc do not contain the data
349+ metadata . title = exif . dc ?. title ?. value || Utils . asciiToUTF8 ( exif . iptc ?. ObjectName ) || metadata . title || exif . photoshop ?. Headline || exif . acdsee ?. caption ; //acdsee caption holds the title when data is saved by digikam. Used as last resort if iptc and dc do not contain the data
377350 }
378351
379352 private static mapCaption ( metadata : PhotoMetadata , exif : any ) {
380- metadata . caption = exif . dc ?. description ?. value || metadata . caption || exif . ifd0 ?. ImageDescription || exif . exif ?. UserComment ?. value || exif . Iptc4xmpCore ?. ExtDescrAccessibility ?. value || exif . acdsee ?. notes ;
353+ metadata . caption = exif . dc ?. description ?. value || Utils . asciiToUTF8 ( exif . iptc ?. Caption ) || metadata . caption || exif . ifd0 ?. ImageDescription || exif . exif ?. UserComment ?. value || exif . Iptc4xmpCore ?. ExtDescrAccessibility ?. value || exif . acdsee ?. notes ;
381354 }
382355
383356 private static mapTimestampAndOffset ( metadata : PhotoMetadata , exif : any ) {
384357 metadata . creationDate = Utils . timestampToMS ( exif ?. photoshop ?. DateCreated , null ) ||
385358 Utils . timestampToMS ( exif ?. xmp ?. CreateDate , null ) ||
386359 Utils . timestampToMS ( exif ?. xmp ?. ModifyDate , null ) ||
360+ Utils . timestampToMS ( Utils . toIsoTimestampString ( exif ?. iptc ?. DateCreated , exif ?. iptc ?. TimeCreated ) , null ) ||
387361 metadata . creationDate ;
388362
389363 metadata . creationDateOffset = Utils . timestampToOffsetString ( exif ?. photoshop ?. DateCreated ) ||
@@ -490,24 +464,15 @@ export class MetadataLoader {
490464
491465 private static mapToponyms ( metadata : PhotoMetadata , exif : any ) {
492466 //Function to convert html code for special characters into their corresponding character (used in exif.photoshop-section)
493- const unescape = ( tag : string ) => {
494- return tag . replace ( / & # ( [ 0 - 9 ] { 1 , 3 } ) ; / gi, function ( match , numStr ) {
495- return String . fromCharCode ( parseInt ( numStr , 10 ) ) ;
496- } ) ;
497- }
498- //photoshop section sometimes has City, Country and State
499- if ( exif . photoshop ) {
500- if ( ! metadata . positionData ?. country && exif . photoshop . Country ) {
501- metadata . positionData = metadata . positionData || { } ;
502- metadata . positionData . country = unescape ( exif . photoshop . Country ) ;
503- }
504- if ( ! metadata . positionData ?. state && exif . photoshop . State ) {
505- metadata . positionData = metadata . positionData || { } ;
506- metadata . positionData . state = unescape ( exif . photoshop . State ) ;
507- }
508- if ( ! metadata . positionData ?. city && exif . photoshop . City ) {
509- metadata . positionData = metadata . positionData || { } ;
510- metadata . positionData . city = unescape ( exif . photoshop . City ) ;
467+
468+ metadata . positionData = metadata . positionData || { } ;
469+ metadata . positionData . country = Utils . asciiToUTF8 ( exif . iptc ?. Country ) || Utils . decodeHTMLChars ( exif . photoshop ?. Country ) ;
470+ metadata . positionData . state = Utils . asciiToUTF8 ( exif . iptc ?. State ) || Utils . decodeHTMLChars ( exif . photoshop ?. State ) ;
471+ metadata . positionData . city = Utils . asciiToUTF8 ( exif . iptc ?. City ) || Utils . decodeHTMLChars ( exif . photoshop ?. City ) ;
472+ if ( metadata . positionData ) {
473+ Utils . removeNullOrEmptyObj ( metadata . positionData ) ;
474+ if ( Object . keys ( metadata . positionData ) . length === 0 ) {
475+ delete metadata . positionData ;
511476 }
512477 }
513478 }
0 commit comments