exiftool_vendored 11.99.0 → 12.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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