exiftool_vendored 11.99.0 → 12.11.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of exiftool_vendored might be problematic. Click here for more details.

Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +201 -2
  3. data/bin/MANIFEST +8 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +43 -42
  7. data/bin/exiftool +172 -99
  8. data/bin/lib/Image/ExifTool.pm +170 -117
  9. data/bin/lib/Image/ExifTool.pod +132 -97
  10. data/bin/lib/Image/ExifTool/AIFF.pm +2 -2
  11. data/bin/lib/Image/ExifTool/APE.pm +2 -2
  12. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +21 -10
  13. data/bin/lib/Image/ExifTool/Canon.pm +202 -13
  14. data/bin/lib/Image/ExifTool/CanonCustom.pm +82 -16
  15. data/bin/lib/Image/ExifTool/DPX.pm +56 -2
  16. data/bin/lib/Image/ExifTool/DarwinCore.pm +22 -3
  17. data/bin/lib/Image/ExifTool/EXE.pm +8 -5
  18. data/bin/lib/Image/ExifTool/Exif.pm +15 -6
  19. data/bin/lib/Image/ExifTool/Font.pm +9 -2
  20. data/bin/lib/Image/ExifTool/GIF.pm +6 -1
  21. data/bin/lib/Image/ExifTool/GeoTiff.pm +2 -0
  22. data/bin/lib/Image/ExifTool/Geotag.pm +2 -2
  23. data/bin/lib/Image/ExifTool/GoPro.pm +48 -22
  24. data/bin/lib/Image/ExifTool/H264.pm +1 -1
  25. data/bin/lib/Image/ExifTool/ID3.pm +86 -12
  26. data/bin/lib/Image/ExifTool/IPTC.pm +1 -0
  27. data/bin/lib/Image/ExifTool/Import.pm +12 -9
  28. data/bin/lib/Image/ExifTool/JSON.pm +27 -4
  29. data/bin/lib/Image/ExifTool/Lang/de.pm +3 -1
  30. data/bin/lib/Image/ExifTool/Lang/es.pm +1 -1
  31. data/bin/lib/Image/ExifTool/M2TS.pm +1 -1
  32. data/bin/lib/Image/ExifTool/MPF.pm +2 -2
  33. data/bin/lib/Image/ExifTool/MacOS.pm +154 -38
  34. data/bin/lib/Image/ExifTool/Matroska.pm +3 -1
  35. data/bin/lib/Image/ExifTool/Minolta.pm +7 -2
  36. data/bin/lib/Image/ExifTool/Nikon.pm +143 -17
  37. data/bin/lib/Image/ExifTool/Olympus.pm +40 -17
  38. data/bin/lib/Image/ExifTool/PNG.pm +14 -3
  39. data/bin/lib/Image/ExifTool/PPM.pm +5 -5
  40. data/bin/lib/Image/ExifTool/Panasonic.pm +148 -14
  41. data/bin/lib/Image/ExifTool/PanasonicRaw.pm +34 -0
  42. data/bin/lib/Image/ExifTool/Parrot.pm +2 -1
  43. data/bin/lib/Image/ExifTool/Pentax.pm +11 -3
  44. data/bin/lib/Image/ExifTool/Photoshop.pm +2 -1
  45. data/bin/lib/Image/ExifTool/QuickTime.pm +240 -37
  46. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +419 -60
  47. data/bin/lib/Image/ExifTool/README +25 -21
  48. data/bin/lib/Image/ExifTool/RSRC.pm +17 -11
  49. data/bin/lib/Image/ExifTool/Radiance.pm +7 -2
  50. data/bin/lib/Image/ExifTool/Ricoh.pm +19 -1
  51. data/bin/lib/Image/ExifTool/Shift.pl +1 -0
  52. data/bin/lib/Image/ExifTool/SigmaRaw.pm +40 -33
  53. data/bin/lib/Image/ExifTool/Sony.pm +423 -39
  54. data/bin/lib/Image/ExifTool/Stim.pm +2 -2
  55. data/bin/lib/Image/ExifTool/TagLookup.pm +5798 -5675
  56. data/bin/lib/Image/ExifTool/TagNames.pod +575 -100
  57. data/bin/lib/Image/ExifTool/Validate.pm +4 -4
  58. data/bin/lib/Image/ExifTool/WriteExif.pl +1 -0
  59. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +30 -21
  60. data/bin/lib/Image/ExifTool/Writer.pl +49 -24
  61. data/bin/lib/Image/ExifTool/XMP.pm +99 -17
  62. data/bin/lib/Image/ExifTool/XMP2.pl +1 -0
  63. data/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
  64. data/bin/lib/Image/ExifTool/ZISRAW.pm +123 -0
  65. data/bin/perl-Image-ExifTool.spec +42 -41
  66. data/lib/exiftool_vendored/version.rb +1 -1
  67. metadata +9 -8
@@ -20,7 +20,7 @@ use strict;
20
20
  use vars qw($VERSION);
21
21
  use Image::ExifTool qw(:DataAccess :Utils);
22
22
 
23
- $VERSION = '1.17';
23
+ $VERSION = '1.18';
24
24
 
25
25
  # road map of directory locations in GIF images
26
26
  my %gifMap = (
@@ -115,6 +115,11 @@ my %gifMap = (
115
115
  ValueConv => '$val + 1',
116
116
  },
117
117
  5 => 'BackgroundColor',
118
+ 6 => {
119
+ Name => 'PixelAspectRatio',
120
+ RawConv => '$val ? $val : undef',
121
+ ValueConv => '($val + 15) / 64',
122
+ },
118
123
  );
119
124
 
120
125
  # GIF Netscape 2.0 animation extension (ref 3)
@@ -573,6 +573,8 @@ my %epsg_vertcs = (
573
573
  3053 => 'Hjorsey 1955 Lambert',
574
574
  3057 => 'ISN93 Lambert 1993',
575
575
  3300 => 'Estonian Coordinate System of 1992',
576
+ 3786 => 'Popular Visualisation CRS / Mercator', #PH (NC)
577
+ 3857 => 'WGS 84 / Pseudo-Mercator', #PH (NC)
576
578
  20137 => 'Adindan UTM zone 37N',
577
579
  20138 => 'Adindan UTM zone 38N',
578
580
  20248 => 'AGD66 AMG zone 48',
@@ -27,7 +27,7 @@ use vars qw($VERSION);
27
27
  use Image::ExifTool qw(:Public);
28
28
  use Image::ExifTool::GPS;
29
29
 
30
- $VERSION = '1.62';
30
+ $VERSION = '1.63';
31
31
 
32
32
  sub JITTER() { return 2 } # maximum time jitter
33
33
 
@@ -217,7 +217,7 @@ sub LoadTrackLog($$;$)
217
217
  # (don't set format yet because we want to read HFDTE first)
218
218
  $nmeaStart = 'B' ;
219
219
  next;
220
- } elsif (/^HFDTE(\d{2})(\d{2})(\d{2})/) {
220
+ } elsif (/^HFDTE(?:DATE:)?(\d{2})(\d{2})(\d{2})/) {
221
221
  my $year = $3 + ($3 >= 70 ? 1900 : 2000);
222
222
  $dateFlarm = Time::Local::timegm(0,0,0,$1,$2-1,$year);
223
223
  $nmeaStart = 'B' ;
@@ -16,7 +16,7 @@ use vars qw($VERSION);
16
16
  use Image::ExifTool qw(:DataAccess :Utils);
17
17
  use Image::ExifTool::QuickTime;
18
18
 
19
- $VERSION = '1.03';
19
+ $VERSION = '1.06';
20
20
 
21
21
  sub ProcessGoPro($$$);
22
22
  sub ProcessString($$$);
@@ -77,6 +77,9 @@ my %addUnits = (
77
77
  Notes => 'accelerator readings in m/s2',
78
78
  Binary => 1,
79
79
  },
80
+ # ANGX (GPMF-GEOC) - seen -0.05 (fmt d, Max)
81
+ # ANGY (GPMF-GEOC) - seen 179.9 (fmt d, Max)
82
+ # ANGZ (GPMF-GEOC) - seen 0.152 (fmt d, Max)
80
83
  ALLD => 'AutoLowLightDuration', #1 (gpmd) (untested)
81
84
  # APTO (GPMF) - seen: 'RAW', 'DYNM' (fmt c)
82
85
  ATTD => { #PH (Karma)
@@ -105,16 +108,14 @@ my %addUnits = (
105
108
  },
106
109
  # BRID (GPMF) - seen: 0 (fmt B)
107
110
  # BROD (GPMF) - seen: 'ASK','' (fmt c)
111
+ # CALH (GPMF-GEOC) - seen 3040 (fmt L, Max)
112
+ # CALW (GPMF-GEOC) - seen 4056 (fmt L, Max)
108
113
  CASN => 'CameraSerialNumber', #PH (GPMF - seen: 'C3221324545448', fmt c)
109
- # CINF (GPMF) - seen: 0x67376be7709bc8876a8baf3940908618, 0xe230988539b30cf5f016627ae8fc5395 (fmt B) (Camera INFormation?)
110
- # CMOD (GPMF) - seen: 12,13,17 [13 time-laps video, 17 JPEG] (fmt B)
111
- CYTS => { #PH (Karma)
112
- Name => 'CoyoteStatus',
113
- # UNIT=s,,,,,rad,rad,rad,,
114
- # TYPE=LLLLLfffBB
115
- # SCAL=1000 1 1 1 1 1 1 1 1 1
116
- Binary => 1,
117
- },
114
+ # CINF (GPMF) - seen: 0x67376be7709bc8876a8baf3940908618, 0xe230988539b30cf5f016627ae8fc5395,
115
+ # 0x8bcbe424acc5b37d7d77001635198b3b (fmt B) (Camera INFormation?)
116
+ # CMOD (GPMF) - seen: 12,13,17 [12 360 video, 13 time-laps video, 17 JPEG] (fmt B)
117
+ # CRTX (GPMF-BACK/FRNT) - double[1]
118
+ # CRTY (GPMF-BACK/FRNT) - double[1]
118
119
  CSEN => { #PH (Karma)
119
120
  Name => 'CoyoteSense',
120
121
  # UNIT=s,rad/s,rad/s,rad/s,g,g,g,,,,
@@ -122,13 +123,27 @@ my %addUnits = (
122
123
  # SCAL=1000 1 1 1 1 1 1 1 1 1 1
123
124
  Binary => 1,
124
125
  },
126
+ CYTS => { #PH (Karma)
127
+ Name => 'CoyoteStatus',
128
+ # UNIT=s,,,,,rad,rad,rad,,
129
+ # TYPE=LLLLLfffBB
130
+ # SCAL=1000 1 1 1 1 1 1 1 1 1
131
+ Binary => 1,
132
+ },
125
133
  DEVC => { #PH (gpmd,GPMF, fmt \0)
126
134
  Name => 'DeviceContainer',
127
135
  SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPMF' },
128
- },
129
- # DVID (GPMF) - DeviceID; seen: 1 (fmt L), HLMT (fmt F)
136
+ # (Max) DVID=1,DVNM='Global Settings',VERS,FMWR,LINF,CINF,CASN,MINF,MUID,CMOD,MTYP,OREN,
137
+ # DZOM,DZST,SMTR,PRTN,PTWB,PTSH,PTCL,EXPT,PIMX,PIMN,PTEV,RATE,SROT,ZFOV,VLTE,VLTA,
138
+ # EISE,EISA,AUPT,AUDO,BROD,BRID,PVUL,PRJT,SOFF
139
+ # (Max) DVID='GEOC',DVNM='Geometry Calibrations',SHFX,SHFY,SHFZ,ANGX,ANGY,ANGZ,CALW,CALH
140
+ # (Max) DVID='BACK',DVNM='Back Lens',KLNS,CTRX,CTRY,MFOV,SFTR
141
+ # (Max) DVID='FRNT',DVNM='Front Lens',KLNS,CTRX,CTRY,MFOV,SFTR
142
+ # (Max) DVID='HLMT',DVNM='Highlights'
143
+ },
144
+ # DVID (GPMF) - DeviceID; seen: 1 (fmt L), HLMT (fmt F), GEOC (fmt F), 'BACK' (fmt F, Max)
130
145
  DVID => { Name => 'DeviceID', Unknown => 1 }, #2 (gpmd)
131
- # DVNM (GPMF) seen: 'Video Global Settings' (fmt c), 'Highlights' (fmt c)
146
+ # DVNM (GPMF) seen: 'Video Global Settings' (fmt c), 'Highlights' (fmt c), 'Geometry Calibrations' (Max)
132
147
  # DVNM (gpmd) seen: 'Camera' (Hero5), 'Hero6 Black' (Hero6), 'GoPro Karma v1.0' (Karma)
133
148
  DVNM => 'DeviceName', #PH (n/c)
134
149
  DZOM => { #PH (GPMF - seen: 'Y', fmt c)
@@ -136,10 +151,10 @@ my %addUnits = (
136
151
  PrintConv => { N => 'No', Y => 'Yes' },
137
152
  },
138
153
  # DZST (GPMF) - seen: 0 (fmt L) (something to do with digital zoom maybe?)
139
- EISA => { #PH (GPMF) - seen: 'Y','N', 'HS EIS' (fmt c) [N was for a time-lapse video]
154
+ EISA => { #PH (GPMF) - seen: 'Y','N','HS EIS','N/A' (fmt c) [N was for a time-lapse video]
140
155
  Name => 'ElectronicImageStabilization',
141
156
  },
142
- # EISE (GPMF) - seen: 'Y' (fmt c)
157
+ # EISE (GPMF) - seen: 'Y','N' (fmt c)
143
158
  EMPT => { Name => 'Empty', Unknown => 1 }, #2 (gpmd)
144
159
  ESCS => { #PH (Karma)
145
160
  Name => 'EscapeStatus',
@@ -215,7 +230,8 @@ my %addUnits = (
215
230
  RawConv => '$val', # necessary to use scaled value instead of raw data as subdir data
216
231
  SubDirectory => { TagTable => 'Image::ExifTool::GoPro::KBAT' },
217
232
  },
218
- # LINF (GPMF) - seen: LAJ7061916601668, C3341326002180 (fmt c) (Lens INFormation?)
233
+ # KLNS (GPMF-BACK/FRNT) - double[5] (fmt d, Max)
234
+ # LINF (GPMF) - seen: LAJ7061916601668,C3341326002180,C33632245450981 (fmt c) (Lens INFormation?)
219
235
  LNED => { #PH (Karma)
220
236
  Name => 'LocalPositionNED',
221
237
  # UNIT=s,m,m,m,m/s,m/s,m/s
@@ -224,12 +240,13 @@ my %addUnits = (
224
240
  Binary => 1,
225
241
  },
226
242
  MAGN => 'Magnetometer', #1 (gpmd) (units of uT)
243
+ # MFOV (GPMF-BACK/FRNT) - seen: 100 (fmt d, Max)
227
244
  MINF => { #PH (GPMF - seen: HERO6 Black, fmt c)
228
245
  Name => 'Model',
229
246
  Groups => { 2 => 'Camera' },
230
247
  Description => 'Camera Model Name',
231
248
  },
232
- # MTYP (GPMF) - seen: 0,1,11 [1 for time-lapse video, 11 for JPEG] (fmt B)
249
+ # MTYP (GPMF) - seen: 0,1,5,11 [1 for time-lapse video, 5 for 360 video, 11 for JPEG] (fmt B)
233
250
  # MUID (GPMF) - seen: 3882563431 2278071152 967805802 411471936 0 0 0 0 (fmt L)
234
251
  OREN => { #PH (GPMF - seen: 'U', fmt c)
235
252
  Name => 'AutoRotation',
@@ -245,7 +262,7 @@ my %addUnits = (
245
262
  PIMX => 'AutoISOMax', #PH (GPMF - seen: 1600, fmt L)
246
263
  # PRAW (APP6) - seen: 0, 'N', 'Y' (fmt c)
247
264
  PRES => 'PhotoResolution', #PH (APP6 - seen: '12MP_W')
248
- # PRJT (APP6) - seen: 'GPRO' (fmt F, Hero8)
265
+ # PRJT (APP6) - seen: 'GPRO','EACO' (fmt F, Hero8, Max)
249
266
  PRTN => { #PH (GPMF - seen: 'N', fmt c)
250
267
  Name => 'ProTune',
251
268
  PrintConv => {
@@ -257,7 +274,7 @@ my %addUnits = (
257
274
  PTEV => 'ExposureCompensation', #PH (GPMF - seen: '0.0', fmt c)
258
275
  PTSH => 'Sharpness', #PH (GPMF - seen: 'HIGH', fmt c)
259
276
  PTWB => 'WhiteBalance', #PH (GPMF - seen: 'AUTO', fmt c)
260
- # PVUL (APP6) - seen: 'F' (fmt c, Hero8)
277
+ # PVUL (APP6) - seen: 'F' (fmt c, Hero8, Max)
261
278
  RATE => 'Rate', #PH (GPMF - seen: '0_5SEC', fmt c; APP6 - seen: '4_1SEC')
262
279
  RMRK => { #2 (gpmd)
263
280
  Name => 'Comments',
@@ -274,6 +291,10 @@ my %addUnits = (
274
291
  # SCAL=1000 0.00999999977648258 0.00999999977648258 100
275
292
  %addUnits,
276
293
  },
294
+ # SFTR (GPMF-BACK/FRNT) - seen 0.999,1.00004 (fmt d, Max)
295
+ # SHFX (GPMF-GEOC) - seen 22.92 (fmt d, Max)
296
+ # SHFY (GPMF-GEOC) - seen 0.123 (fmt d, Max)
297
+ # SHFZ (GPMF-GEOC) - seen 36.06 (fmt d, Max)
277
298
  SHUT => { #2 (gpmd)
278
299
  Name => 'ExposureTimes',
279
300
  PrintConv => q{
@@ -295,7 +316,8 @@ my %addUnits = (
295
316
  ValueConv => '$self->Decode($val, "Latin")',
296
317
  },
297
318
  # SMTR (GPMF) - seen: 'N' (fmt c)
298
- # SOFF (APP6) - seen: 0 (fmt L, Hero8)
319
+ # SOFF (APP6) - seen: 0 (fmt L, Hero8, Max)
320
+ # SROT (GPMF) - seen 20.60 (fmt f, Max)
299
321
  STMP => { #1 (gpmd)
300
322
  Name => 'TimeStamp',
301
323
  ValueConv => '$val / 1e6',
@@ -338,7 +360,10 @@ my %addUnits = (
338
360
  Unknown => 1,
339
361
  ValueConv => '$self->Decode($val, "Latin")',
340
362
  },
341
- # VERS (APP6) - seen: '7 6 4' (fmt B, Hero8)
363
+ VERS => {
364
+ Name => 'MetadataVersion',
365
+ PrintConv => '$val =~ tr/ /./; $val',
366
+ },
342
367
  VFOV => { #PH (GPMF - seen: 'W', fmt c)
343
368
  Name => 'FieldOfView',
344
369
  PrintConv => {
@@ -360,7 +385,7 @@ my %addUnits = (
360
385
  Name => 'WhiteBalanceRGB',
361
386
  Binary => 1,
362
387
  },
363
- # ZFOV (APP6) - seen: 148.34 (fmt f, Hero8)
388
+ # ZFOV (APP6,GPMF) - seen: 148.34, 0 (fmt f, Hero8, Max)
364
389
  );
365
390
 
366
391
  # GoPro GPS5 tags (ref 2) (Hero5,Hero6)
@@ -590,6 +615,7 @@ sub ProcessString($$$)
590
615
  # Process GoPro metadata (gpmd samples, GPMF box, or APP6) (ref PH/1/2)
591
616
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
592
617
  # Returns: 1 on success
618
+ # - with hack to check for encrypted text in gpmd data (Rove Stealth 4K)
593
619
  sub ProcessGoPro($$$)
594
620
  {
595
621
  my ($et, $dirInfo, $tagTablePtr) = @_;
@@ -890,7 +890,7 @@ sub ParsePictureTiming($$)
890
890
 
891
891
  #------------------------------------------------------------------------------
892
892
  # Process H.264 Supplementary Enhancement Information (ref 1/PH)
893
- # Inputs: 0) Exiftool ref, 1) dirInfo ref, 2) tag table ref
893
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
894
894
  # Returns: 1 if we processed payload type 5
895
895
  # Payload types:
896
896
  # 0 - buffer period
@@ -1,13 +1,15 @@
1
1
  #------------------------------------------------------------------------------
2
2
  # File: ID3.pm
3
3
  #
4
- # Description: Read ID3 meta information
4
+ # Description: Read ID3 and Lyrics3 meta information
5
5
  #
6
6
  # Revisions: 09/12/2005 - P. Harvey Created
7
+ # 09/08/2020 - PH Added Lyrics3 support
7
8
  #
8
9
  # References: 1) http://www.id3.org/
9
10
  # 2) http://www.mp3-tech.org/
10
11
  # 3) http://www.fortunecity.com/underworld/sonic/3/id3tag.html
12
+ # 4) https://id3.org/Lyrics3
11
13
  #------------------------------------------------------------------------------
12
14
 
13
15
  package Image::ExifTool::ID3;
@@ -16,7 +18,7 @@ use strict;
16
18
  use vars qw($VERSION);
17
19
  use Image::ExifTool qw(:DataAccess :Utils);
18
20
 
19
- $VERSION = '1.54';
21
+ $VERSION = '1.55';
20
22
 
21
23
  sub ProcessID3v2($$$);
22
24
  sub ProcessPrivate($$$);
@@ -68,13 +70,13 @@ my %dateTimeConv = (
68
70
  %Image::ExifTool::ID3::Main = (
69
71
  VARS => { NO_ID => 1 },
70
72
  NOTES => q{
71
- ExifTool extracts ID3 information from MP3, MPEG, AIFF, OGG, FLAC, APE, MPC
72
- and RealAudio files. ID3v2 tags which support multiple languages (eg.
73
- Comment and Lyrics) are extracted by specifying the tag name, followed by a
74
- dash ('-'), then a 3-character ISO 639-2 language code (eg. "Comment-spa").
75
- See L<http://www.id3.org/> for the official ID3 specification and
76
- L<http://www.loc.gov/standards/iso639-2/php/code_list.php> for a list of ISO
77
- 639-2 language codes.
73
+ ExifTool extracts ID3 and Lyrics3 information from MP3, MPEG, AIFF, OGG,
74
+ FLAC, APE, MPC and RealAudio files. ID3v2 tags which support multiple
75
+ languages (eg. Comment and Lyrics) are extracted by specifying the tag name,
76
+ followed by a dash ('-'), then a 3-character ISO 639-2 language code (eg.
77
+ "Comment-spa"). See L<http://www.id3.org/> for the official ID3
78
+ specification and L<http://www.loc.gov/standards/iso639-2/php/code_list.php>
79
+ for a list of ISO 639-2 language codes.
78
80
  },
79
81
  ID3v1 => {
80
82
  Name => 'ID3v1',
@@ -98,6 +100,24 @@ my %dateTimeConv = (
98
100
  },
99
101
  );
100
102
 
103
+ # Lyrics3 tags (ref 4)
104
+ %Image::ExifTool::ID3::Lyrics3 = (
105
+ GROUPS => { 1 => 'Lyrics3', 2 => 'Audio' },
106
+ NOTES => q{
107
+ ExifTool extracts Lyrics3 version 1.00 and 2.00 tags from any file that
108
+ supports ID3. See L<https://id3.org/Lyrics3> for the specification.
109
+ },
110
+ IND => 'Indications',
111
+ LYR => 'Lyrics',
112
+ INF => 'AdditionalInfo',
113
+ AUT => { Name => 'Author', Groups => { 2 => 'Author' } },
114
+ EAL => 'ExtendedAlbumName',
115
+ EAR => 'ExtendedArtistName',
116
+ ETT => 'ExtendedTrackTitle',
117
+ IMG => 'AssociatedImageFile',
118
+ CRC => 'CRC', #PH
119
+ );
120
+
101
121
  # Mapping for ID3v1 Genre numbers
102
122
  my %genre = (
103
123
  0 => 'Blues',
@@ -1347,7 +1367,7 @@ sub ProcessID3v2($$$)
1347
1367
  # Inputs: 0) ExifTool object reference, 1) dirInfo reference
1348
1368
  # Returns: 1 on success, 0 if this file didn't contain ID3 information
1349
1369
  # - also processes audio data if any ID3 information was found
1350
- # - sets ExifTool DoneID3 to 1 when called, or to 2 if an ID3v1 trailer exists
1370
+ # - sets ExifTool DoneID3 to 1 when called, or to trailer size if an ID3v1 trailer exists
1351
1371
  sub ProcessID3($$)
1352
1372
  {
1353
1373
  my ($et, $dirInfo) = @_;
@@ -1426,8 +1446,9 @@ sub ProcessID3($$)
1426
1446
  #
1427
1447
  # read ID3v1 trailer if it exists
1428
1448
  #
1449
+ my $trailSize = 0;
1429
1450
  if ($raf->Seek(-128, 2) and $raf->Read($tBuff, 128) == 128 and $tBuff =~ /^TAG/) {
1430
- $$et{DoneID3} = 2; # set to 2 as flag that trailer exists
1451
+ $trailSize = 128;
1431
1452
  %id3Trailer = (
1432
1453
  DataPt => \$tBuff,
1433
1454
  DataPos => $raf->Tell() - 128,
@@ -1437,8 +1458,59 @@ sub ProcessID3($$)
1437
1458
  $id3Len += length($tBuff);
1438
1459
  $rtnVal = 1;
1439
1460
  # load 'Enhanced TAG' information if available
1440
- if ($raf->Seek(-355, 2) and $raf->Read($eBuff, 227) == 227 and $eBuff =~ /^TAG+/) {
1461
+ my $eSize = 227; # size of ID3 Enhanced TAG info
1462
+ if ($raf->Seek(-$trailSize - $eSize, 2) and $raf->Read($eBuff, $eSize) == $eSize and $eBuff =~ /^TAG+/) {
1441
1463
  $id3Trailer{EnhancedTAG} = \$eBuff;
1464
+ $trailSize += $eSize;
1465
+ }
1466
+ $$et{DoneID3} = $trailSize; # save trailer size
1467
+ }
1468
+ #
1469
+ # read Lyrics3 trailer if it exists
1470
+ #
1471
+ if ($raf->Seek(-$trailSize-15, 2) and $raf->Read($buff, 15) == 15 and $buff =~ /^(.{6})LYRICS(END|200)$/) {
1472
+ my $ver = $2; # Lyrics3 version ('END' for version 1)
1473
+ my $len = ($ver eq 'END') ? 5100 : $1 + 15; # max Lyrics3 length
1474
+ my $tbl = GetTagTable('Image::ExifTool::ID3::Lyrics3');
1475
+ $len = $raf->Tell() if $len > $raf->Tell();
1476
+ if ($raf->Seek(-$len, 1) and $raf->Read($buff, $len) == $len and $buff =~ /LYRICSBEGIN/g) {
1477
+ my $pos = pos($buff);
1478
+ $$et{DoneID3} = $trailSize + $len - $pos + 11; # update trailer length
1479
+ my $oldIndent = $$et{INDENT};
1480
+ $$et{INDENT} .= '| ';
1481
+ if ($et->Options('Verbose')) {
1482
+ $et->VPrint(0, "Lyrics3:\n");
1483
+ $et->VerboseDir('Lyrics3', undef, $len);
1484
+ if ($pos > 11) {
1485
+ $buff = substr($buff, $pos - 11);
1486
+ $pos = 11;
1487
+ }
1488
+ $et->VerboseDump(\$buff);
1489
+ }
1490
+ if ($ver eq 'END') {
1491
+ # Lyrics3 v1.00
1492
+ my $val = substr($buff, $pos, $len - $pos - 9);
1493
+ $et->HandleTag($tbl, 'LYR', $et->Decode($val, 'Latin'));
1494
+ } else {
1495
+ # Lyrics3 v2.00
1496
+ for (;;) {
1497
+ # (note: the size field is 5 digits,, not 6 as per the documentation)
1498
+ last unless $buff =~ /\G(.{3})(\d{5})/g;
1499
+ my ($tag, $size) = ($1, $2);
1500
+ $pos += 8;
1501
+ last if $pos + $size > length($buff);
1502
+ unless ($$tbl{$tag}) {
1503
+ AddTagToTable($tbl, $tag, { Name => Image::ExifTool::MakeTagName("Lyrics3_$tag") });
1504
+ }
1505
+ $et->HandleTag($tbl, $tag, $et->Decode(substr($buff, $pos, $size), 'Latin'));
1506
+ $pos += $size;
1507
+ pos($buff) = $pos;
1508
+ }
1509
+ $pos == length($buff) - 15 or $et->Warn('Malformed Lyrics3 v2.00 block');
1510
+ }
1511
+ $$et{INDENT} = $oldIndent;
1512
+ } else {
1513
+ $et->Warn('Error reading Lyrics3 trailer');
1442
1514
  }
1443
1515
  }
1444
1516
  #
@@ -1583,6 +1655,8 @@ under the same terms as Perl itself.
1583
1655
 
1584
1656
  =item L<http://www.fortunecity.com/underworld/sonic/3/id3tag.html>
1585
1657
 
1658
+ =item L<https://id3.org/Lyrics3>
1659
+
1586
1660
  =back
1587
1661
 
1588
1662
  =head1 SEE ALSO
@@ -497,6 +497,7 @@ my %fileFormat = (
497
497
  103 => {
498
498
  Name => 'OriginalTransmissionReference',
499
499
  Format => 'string[0,32]',
500
+ Notes => 'now used as a job identifier',
500
501
  },
501
502
  105 => {
502
503
  Name => 'Headline',
@@ -23,14 +23,15 @@ my $charset;
23
23
 
24
24
  #------------------------------------------------------------------------------
25
25
  # Read CSV file
26
- # Inputs: 0) CSV file name, file ref or RAF ref, 1) database hash ref, 2) missing tag value
26
+ # Inputs: 0) CSV file name, file ref or RAF ref, 1) database hash ref,
27
+ # 2) missing tag value, 3) delimiter if other than ','
27
28
  # Returns: undef on success, or error string
28
29
  # Notes: There are various flavours of CSV, but here we assume that only
29
30
  # double quotes are escaped, and they are escaped by doubling them
30
- sub ReadCSV($$;$)
31
+ sub ReadCSV($$;$$)
31
32
  {
32
33
  local ($_, $/);
33
- my ($file, $database, $missingValue) = @_;
34
+ my ($file, $database, $missingValue, $delim) = @_;
34
35
  my ($buff, @tags, $found, $err, $raf, $openedFile);
35
36
 
36
37
  if (UNIVERSAL::isa($file, 'File::RandomAccess')) {
@@ -45,6 +46,7 @@ sub ReadCSV($$;$)
45
46
  $openedFile = 1;
46
47
  $raf = new File::RandomAccess(\*CSVFILE);
47
48
  }
49
+ $delim = ',' unless defined $delim;
48
50
  # set input record separator by first newline found in the file
49
51
  # (safe because first line should contain only tag names)
50
52
  while ($raf->Read($buff, 65536)) {
@@ -53,18 +55,18 @@ sub ReadCSV($$;$)
53
55
  $raf->Seek(0,0);
54
56
  while ($raf->ReadLine($buff)) {
55
57
  my (@vals, $v, $i, %fileInfo);
56
- my @toks = split ',', $buff;
58
+ my @toks = split /\Q$delim/, $buff;
57
59
  while (@toks) {
58
60
  ($v = shift @toks) =~ s/^ +//; # remove leading spaces
59
61
  if ($v =~ s/^"//) {
60
62
  # quoted value must end in an odd number of quotes
61
63
  while ($v !~ /("+)\s*$/ or not length($1) & 1) {
62
64
  if (@toks) {
63
- $v .= ',' . shift @toks;
65
+ $v .= $delim . shift @toks;
64
66
  } else {
65
67
  # read another line from the file
66
68
  $raf->ReadLine($buff) or last;
67
- @toks = split ',', $buff;
69
+ @toks = split /\Q$delim/, $buff;
68
70
  last unless @toks;
69
71
  $v .= shift @toks;
70
72
  }
@@ -330,9 +332,10 @@ Read CSV or JSON file into a database hash.
330
332
  2) Optional string used to represent an undefined (missing) tag value.
331
333
  (Used for deleting tags.)
332
334
 
333
- 3) [ReadJSON only] Optional character set for converting Unicode escape
334
- sequences in strings. Defaults to "UTF8". See the ExifTool Charset option
335
- for a list of valid settings.
335
+ 3) For ReadCSV this gives the delimiter for CSV entries, with a default of
336
+ ",". For ReadJSON this is the character set for converting Unicode escape
337
+ sequences in strings, with a default of "UTF8". See the ExifTool Charset
338
+ option for a list of valid character sets.
336
339
 
337
340
  =item Return Value:
338
341