Ignore:
Timestamp:
2021-02-26T19:39:51+13:00 (3 years ago)
Author:
anupama
Message:

Committing the improvements to EmbeddedMetaPlugin's processing of Keywords vs other metadata fields. Keywords were literally stored as arrays of words rather than phrases in PDFs (at least in Diego's sample PDF), whereas other meta fields like Subjects and Creators stored them as arrays of phrases. To get both to work, Kathy updated EXIF to a newer version, to retrieve the actual EXIF values stored in the PDF. And Kathy and Dr Bainbridge came up with a new option that I added called apply_join_before_split_to_metafields that's a regex which can list the metadata fields to apply the join_before_split to and whcih previously always got applied to all metadata fields. Now it's applied to any *Keywords metafields by default, as that's the metafield we have experience of that behaves differently to the others, as it stores by word instead of phrases. Tested on Diego's sample PDF. Diego has double-checked it to works on his sample PDF too, setting the split char to ; and turning on the join_before_split and leaving apply_join_before_split_to_metafields at its default of .*Keywords. File changes are strings.properties for the tooltip, the plugin introducing the option and working with it and Kathy's EXIF updates affecting cpan/File and cpan/Image.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone2/perllib/cpan/Image/ExifTool/ID3.pm

    r24107 r34921  
    22# File:         ID3.pm
    33#
    4 # Description:  Read ID3 meta information
     4# Description:  Read ID3 and Lyrics3 meta information
    55#
    66# Revisions:    09/12/2005 - P. Harvey Created
     7#               09/08/2020 - PH Added Lyrics3 support
    78#
    89# References:   1) http://www.id3.org/
    910#               2) http://www.mp3-tech.org/
    1011#               3) http://www.fortunecity.com/underworld/sonic/3/id3tag.html
     12#               4) https://id3.org/Lyrics3
    1113#------------------------------------------------------------------------------
    1214
     
    1719use Image::ExifTool qw(:DataAccess :Utils);
    1820
    19 $VERSION = '1.28';
     21$VERSION = '1.55';
    2022
    2123sub ProcessID3v2($$$);
    2224sub ProcessPrivate($$$);
     25sub ProcessSynText($$$);
    2326sub ConvertID3v1Text($$);
     27sub ConvertTimeStamp($);
    2428
    2529# audio formats that we process after an ID3v2 header (in order)
    2630my @audioFormats = qw(APE MPC FLAC OGG MP3);
    2731
    28 # audio formats where the processing proc is in a different module
     32# audio formats where the processing proc is in a differently-named module
    2933my %audioModule = (
    3034    MP3 => 'ID3',
    31     OGG => 'Vorbis',
     35    OGG => 'Ogg',
    3236);
    3337
     
    6771    VARS => { NO_ID => 1 },
    6872    NOTES => q{
    69         ExifTool extracts ID3 information from MP3, MPEG, AIFF, OGG, FLAC, APE and
    70         RealAudio files.  ID3v2 tags which support multiple languages (ie. Comment
    71         and Lyrics) are extracted by specifying the tag name, followed by a dash
    72         ('-'), then a 3-character
    73         ISO 639-2
    74         language code (ie. "Comment-spa"). See L<http://www.id3.org/> for the
    75         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.
    7880    },
    7981    ID3v1 => {
     
    9799        SubDirectory => { TagTable => 'Image::ExifTool::ID3::v2_4' },
    98100    },
     101);
     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
    99119);
    100120
     
    141161     38 => 'Gospel',
    142162     39 => 'Noise',
    143      40 => 'AlternRock',
     163     40 => 'Alt. Rock', # (was AlternRock)
    144164     41 => 'Bass',
    145165     42 => 'Soul',
     
    160180     57 => 'Comedy',
    161181     58 => 'Cult',
    162      59 => 'Gangsta',
     182     59 => 'Gangsta Rap', # (was Gansta)
    163183     60 => 'Top 40',
    164184     61 => 'Christian Rap',
     
    168188     65 => 'Cabaret',
    169189     66 => 'New Wave',
    170      67 => 'Psychadelic',
     190     67 => 'Psychedelic', # (was misspelt)
    171191     68 => 'Rave',
    172192     69 => 'Showtunes',
     
    186206     82 => 'National Folk',
    187207     83 => 'Swing',
    188      84 => 'Fast Fusion',
    189      85 => 'Bebob',
     208     84 => 'Fast-Fusion', # (was Fast Fusion)
     209     85 => 'Bebop', # (was misspelt)
    190210     86 => 'Latin',
    191211     87 => 'Revival',
     
    225245    121 => 'Punk Rock',
    226246    122 => 'Drum Solo',
    227     123 => 'Acapella',
     247    123 => 'A Cappella', # (was Acapella)
    228248    124 => 'Euro-House',
    229249    125 => 'Dance Hall',
     
    236256    131 => 'Indie',
    237257    132 => 'BritPop',
    238     133 => 'Negerpunk',
     258    133 => 'Afro-Punk', # (was Negerpunk)
    239259    134 => 'Polsk Punk',
    240260    135 => 'Beat',
    241     136 => 'Christian Gangsta',
     261    136 => 'Christian Gangsta Rap', # (was Christian Gangsta)
    242262    137 => 'Heavy Metal',
    243263    138 => 'Black Metal',
    244264    139 => 'Crossover',
    245     140 => 'Contemporary C',
     265    140 => 'Contemporary Christian', # (was Contemporary C)
    246266    141 => 'Christian Rock',
    247267    142 => 'Merengue',
     
    250270    145 => 'Anime',
    251271    146 => 'JPop',
    252     147 => 'SynthPop',
     272    147 => 'Synthpop', # (was SynthPop)
     273    # ref http://alicja.homelinux.com/~mats/text/Music/MP3/ID3/Genres.txt
     274    # (also used to update some Genres above)
     275    148 => 'Abstract',
     276    149 => 'Art Rock',
     277    150 => 'Baroque',
     278    151 => 'Bhangra',
     279    152 => 'Big Beat',
     280    153 => 'Breakbeat',
     281    154 => 'Chillout',
     282    155 => 'Downtempo',
     283    156 => 'Dub',
     284    157 => 'EBM',
     285    158 => 'Eclectic',
     286    159 => 'Electro',
     287    160 => 'Electroclash',
     288    161 => 'Emo',
     289    162 => 'Experimental',
     290    163 => 'Garage',
     291    164 => 'Global',
     292    165 => 'IDM',
     293    166 => 'Illbient',
     294    167 => 'Industro-Goth',
     295    168 => 'Jam Band',
     296    169 => 'Krautrock',
     297    170 => 'Leftfield',
     298    171 => 'Lounge',
     299    172 => 'Math Rock',
     300    173 => 'New Romantic',
     301    174 => 'Nu-Breakz',
     302    175 => 'Post-Punk',
     303    176 => 'Post-Rock',
     304    177 => 'Psytrance',
     305    178 => 'Shoegaze',
     306    179 => 'Space Rock',
     307    180 => 'Trop Rock',
     308    181 => 'World Music',
     309    182 => 'Neoclassical',
     310    183 => 'Audiobook',
     311    184 => 'Audio Theatre',
     312    185 => 'Neue Deutsche Welle',
     313    186 => 'Podcast',
     314    187 => 'Indie Rock',
     315    188 => 'G-Funk',
     316    189 => 'Dubstep',
     317    190 => 'Garage Rock',
     318    191 => 'Psybient',
    253319    255 => 'None',
    254320    # ID3v2 adds some text short forms...
     
    358424        in the tables below are those extracted by ExifTool, and don't represent a
    359425        complete list of available ID3v2 tags.
    360        
     426
    361427        ID3 version 2.2 tags.  (These are the tags written by iTunes 5.0.)
    362428    },
     
    366432    PIC => {
    367433        Name => 'Picture',
    368         Groups => { 2 => 'Image' },
     434        Groups => { 2 => 'Preview' },
    369435        Binary => 1,
    370436        Notes => 'the 3 tags below are also extracted from this PIC frame',
     
    378444    },
    379445    'PIC-3' => { Name => 'PictureDescription', Groups => { 2 => 'Image' } },
    380   # POP => 'Popularimeter',
     446    POP => {
     447        Name => 'Popularimeter',
     448        PrintConv => '$val=~s/^(.*?) (\d+) (\d+)$/$1 Rating=$2 Count=$3/s; $val',
     449    },
    381450    SLT => {
    382         Name => 'SynchronizedLyricText',
    383         Binary => 1,
     451        Name => 'SynLyrics',
     452        SubDirectory => { TagTable => 'Image::ExifTool::ID3::SynLyrics' },
    384453    },
    385454    TAL => 'Album',
     
    391460        PrintConv => 'Image::ExifTool::ID3::PrintGenre($val)',
    392461    },
    393     TCP => 'Compilation', # not part of spec, but used by iTunes
     462    TCP => { Name => 'Compilation', PrintConv => { 0 => 'No', 1 => 'Yes' } }, # iTunes
    394463    TCR => { Name => 'Copyright', Groups => { 2 => 'Author' } },
    395464    TDA => { Name => 'Date', Groups => { 2 => 'Time' } },
     
    403472    TMT => 'Media',
    404473    TOA => { Name => 'OriginalArtist', Groups => { 2 => 'Author' } },
    405     TOF => 'OriginalFilename',
     474    TOF => 'OriginalFileName',
    406475    TOL => 'OriginalLyricist',
    407476    TOR => 'OriginalReleaseYear',
     
    432501    WPB => 'PublisherURL',
    433502    WXX => 'UserDefinedURL',
     503    # the following written by iTunes 10.5 (ref PH)
     504    RVA => 'RelativeVolumeAdjustment',
     505    TST => 'TitleSortOrder',
     506    TSA => 'AlbumSortOrder',
     507    TSP => 'PerformerSortOrder',
     508    TS2 => 'AlbumArtistSortOrder',
     509    TSC => 'ComposerSortOrder',
     510    ITU => { Name => 'iTunesU', Description => 'iTunes U', Binary => 1, Unknown => 1 },
     511    PCS => { Name => 'Podcast', Binary => 1, Unknown => 1 },
    434512);
    435513
     
    439517    APIC => {
    440518        Name => 'Picture',
    441         Groups => { 2 => 'Image' },
     519        Groups => { 2 => 'Preview' },
    442520        Binary => 1,
    443         Notes => 'the 3 tags below are also extracted from this PIC frame',
    444     },
    445     'APIC-1' => { Name => 'PictureMimeType',    Groups => { 2 => 'Image' } },
     521        Notes => 'the 3 tags below are also extracted from this APIC frame',
     522    },
     523    'APIC-1' => { Name => 'PictureMIMEType',    Groups => { 2 => 'Image' } },
    446524    'APIC-2' => {
    447525        Name => 'PictureType',
     
    460538    MCDI => { Name => 'MusicCDIdentifier', Binary => 1 },
    461539  # MLLT => 'MPEGLocationLookupTable',
    462   # OWNE => 'Ownership', # enc(1), _price, 00, _date(8), Seller
     540    OWNE => 'Ownership',
    463541    PCNT => 'PlayCounter',
    464   # POPM => 'Popularimeter', # _email, 00, rating(1), counter(4-N)
     542    POPM => {
     543        Name => 'Popularimeter',
     544        PrintConv => '$val=~s/^(.*?) (\d+) (\d+)$/$1 Rating=$2 Count=$3/s; $val',
     545    },
    465546  # POSS => 'PostSynchronization',
    466547    PRIV => {
     
    471552  # RVRB => 'Reverb',
    472553    SYLT => {
    473         Name => 'SynchronizedLyricText',
    474         Binary => 1,
     554        Name => 'SynLyrics',
     555        SubDirectory => { TagTable => 'Image::ExifTool::ID3::SynLyrics' },
    475556    },
    476557  # SYTC => 'SynchronizedTempoCodes',
     
    501582    TMED => 'Media',
    502583    TOAL => 'OriginalAlbum',
    503     TOFN => 'OriginalFilename',
     584    TOFN => 'OriginalFileName',
    504585    TOLY => 'OriginalLyricist',
    505586    TOPE => { Name => 'OriginalArtist', Groups => { 2 => 'Author' } },
     
    517598    TSSE => 'EncoderSettings',
    518599    TXXX => 'UserDefinedText',
    519   # UFID => 'UniqueFileID',
     600  # UFID => 'UniqueFileID', (not extracted because it is long and nasty and not very useful)
    520601    USER => 'TermsOfUse',
    521602    USLT => 'Lyrics',
     
    529610    WPUB => 'PublisherURL',
    530611    WXXX => 'UserDefinedURL',
     612#
     613# non-standard frames
     614#
     615    # the following are written by iTunes 10.5 (ref PH)
     616    TSO2 => 'AlbumArtistSortOrder',
     617    TSOC => 'ComposerSortOrder',
     618    ITNU => { Name => 'iTunesU', Description => 'iTunes U', Binary => 1, Unknown => 1 },
     619    PCST => { Name => 'Podcast', Binary => 1, Unknown => 1 },
     620    # other proprietary Apple tags (ref http://help.mp3tag.de/main_tags.html)
     621    TDES => 'PodcastDescription',
     622    TGID => 'PodcastID',
     623    WFED => 'PodcastURL',
     624    TKWD => 'PodcastKeywords',
     625    TCAT => 'PodcastCategory',
     626    # more non-standard tags (ref http://eyed3.nicfit.net/compliance.html)
     627    # NCON - unknown MusicMatch binary data
     628    XDOR => { Name => 'OriginalReleaseTime',Groups => { 2 => 'Time' }, %dateTimeConv },
     629    XSOA => 'AlbumSortOrder',
     630    XSOP => 'PerformerSortOrder',
     631    XSOT => 'TitleSortOrder',
     632    XOLY => {
     633        Name => 'OlympusDSS',
     634        SubDirectory => { TagTable => 'Image::ExifTool::Olympus::DSS' },
     635    },
     636    GRP1 => 'Grouping',
     637    MVNM => 'MovementName', # (NC)
     638    MVIN => 'MovementNumber', # (NC)
    531639);
    532640
     
    535643    PROCESS_PROC => \&Image::ExifTool::ID3::ProcessID3v2,
    536644    GROUPS => { 1 => 'ID3v2_3', 2 => 'Audio' },
    537     NOTES => 'ID3 version 2.3 tags',
     645    NOTES => q{
     646        ID3 version 2.3 tags.  Includes some non-standard tags written by other
     647        software.
     648    },
    538649    %id3v2_common,  # include common tags
    539650  # EQUA => 'Equalization',
     
    552663    PROCESS_PROC => \&Image::ExifTool::ID3::ProcessID3v2,
    553664    GROUPS => { 1 => 'ID3v2_4', 2 => 'Audio' },
    554     NOTES => 'ID3 version 2.4 tags',
     665    NOTES => q{
     666        ID3 version 2.4 tags.  Includes some non-standard tags written by other
     667        software.
     668    },
    555669    %id3v2_common,  # include common tags
    556670  # EQU2 => 'Equalization',
    557   # RVA2 => 'RelativeVolumeAdjustment',
     671    RVA2 => 'RelativeVolumeAdjustment',
    558672  # SEEK => 'Seek',
    559673  # SIGN => 'Signature',
     
    573687);
    574688
     689# Synchronized lyrics/text
     690%Image::ExifTool::ID3::SynLyrics = (
     691    GROUPS => { 1 => 'ID3', 2 => 'Audio' },
     692    VARS => { NO_ID => 1 },
     693    PROCESS_PROC => \&ProcessSynText,
     694    NOTES => 'The following tags are extracted from synchronized lyrics/text frames.',
     695    desc => { Name => 'SynchronizedLyricsDescription' },
     696    type => {
     697        Name => 'SynchronizedLyricsType',
     698        PrintConv => {
     699            0 => 'Other',
     700            1 => 'Lyrics',
     701            2 => 'Text Transcription',
     702            3 => 'Movement/part Name',
     703            4 => 'Events',
     704            5 => 'Chord',
     705            6 => 'Trivia/"pop-up" Information',
     706            7 => 'Web Page URL',
     707            8 => 'Image URL',
     708        },
     709    },
     710    text => {
     711        Name => 'SynchronizedLyricsText',
     712        List => 1,
     713        Notes => q{
     714            each list item has a leading time stamp in square brackets.  Time stamps may
     715            be in seconds with format [MM:SS.ss], or MPEG frames with format [FFFF],
     716            depending on how this information was stored
     717        },
     718        PrintConv => \&ConvertTimeStamp,
     719    },
     720);
     721
    575722# ID3 PRIV tags (ref PH)
    576723%Image::ExifTool::ID3::Private = (
    577724    PROCESS_PROC => \&Image::ExifTool::ID3::ProcessPrivate,
    578725    GROUPS => { 1 => 'ID3', 2 => 'Audio' },
    579     NOTES => 'ID3 private (PRIV) tags.',
     726    VARS => { NO_ID => 1 },
     727    NOTES => q{
     728        ID3 private (PRIV) tags.  ExifTool will decode any private tags found, even
     729        if they do not appear in this table.
     730    },
    580731    XMP => {
    581732        SubDirectory => {
     
    590741        ValueConv => 'length($val)==4 ? unpack("V",$val) : \$val',
    591742    },
     743    # Windows Media attributes ("/" in tag ID is converted to "_" by ProcessPrivate)
     744    WM_WMContentID => {
     745        Name => 'WM_ContentID',
     746        ValueConv => 'require Image::ExifTool::ASF; Image::ExifTool::ASF::GetGUID($val)',
     747    },
     748    WM_WMCollectionID => {
     749        Name => 'WM_CollectionID',
     750        ValueConv => 'require Image::ExifTool::ASF; Image::ExifTool::ASF::GetGUID($val)',
     751    },
     752    WM_WMCollectionGroupID => {
     753        Name => 'WM_CollectionGroupID',
     754        ValueConv => 'require Image::ExifTool::ASF; Image::ExifTool::ASF::GetGUID($val)',
     755    },
     756    WM_MediaClassPrimaryID => {
     757        ValueConv => 'require Image::ExifTool::ASF; Image::ExifTool::ASF::GetGUID($val)',
     758    },
     759    WM_MediaClassSecondaryID => {
     760        ValueConv => 'require Image::ExifTool::ASF; Image::ExifTool::ASF::GetGUID($val)',
     761    },
     762    WM_Provider => {
     763        ValueConv => '$self->Decode($val,"UCS2","II")', #PH (NC)
     764    },
     765    # there are lots more WM tags that could be decoded if I had samples or documentation - PH
     766    # WM/AlbumArtist
     767    # WM/AlbumTitle
     768    # WM/Category
     769    # WM/Composer
     770    # WM/Conductor
     771    # WM/ContentDistributor
     772    # WM/ContentGroupDescription
     773    # WM/EncodingTime
     774    # WM/Genre
     775    # WM/GenreID
     776    # WM/InitialKey
     777    # WM/Language
     778    # WM/Lyrics
     779    # WM/MCDI
     780    # WM/MediaClassPrimaryID
     781    # WM/MediaClassSecondaryID
     782    # WM/Mood
     783    # WM/ParentalRating
     784    # WM/Period
     785    # WM/ProtectionType
     786    # WM/Provider
     787    # WM/ProviderRating
     788    # WM/ProviderStyle
     789    # WM/Publisher
     790    # WM/SubscriptionContentID
     791    # WM/SubTitle
     792    # WM/TrackNumber
     793    # WM/UniqueFileIdentifier
     794    # WM/WMCollectionGroupID
     795    # WM/WMCollectionID
     796    # WM/WMContentID
     797    # WM/Writer
     798    # WM/Year
     799);
     800
     801# lookup to check for existence of tags in other ID3 versions
     802my %otherTable = (
     803    \%Image::ExifTool::ID3::v2_4 => \%Image::ExifTool::ID3::v2_3,
     804    \%Image::ExifTool::ID3::v2_3 => \%Image::ExifTool::ID3::v2_4,
    592805);
    593806
     
    640853sub ConvertID3v1Text($$)
    641854{
    642     my ($exifTool, $val) = @_;
    643     return $exifTool->Decode($val, $exifTool->Options('CharsetID3'));
     855    my ($et, $val) = @_;
     856    return $et->Decode($val, $et->Options('CharsetID3'));
     857}
     858
     859#------------------------------------------------------------------------------
     860# Re-format time stamp in synchronized lyrics
     861# Inputs: 0) synchronized lyrics entry (eg. "[84.030]Da do do do")
     862# Returns: entry with formatted timestamp (eg. "[01:24.03]Da do do do")
     863sub ConvertTimeStamp($)
     864{
     865    my $val = shift;
     866    # do nothing if this isn't a time stamp (frame count doesn't contain a decimal)
     867    return $val unless $val =~ /^\[(\d+\.\d+)\]/g;
     868    my $time = $1;
     869    # print hours only if more than 60 minutes
     870    my $h = int($time / 3600);
     871    if ($h) {
     872        $time -= $h * 3600;
     873        $h = "$h:";
     874    } else {
     875        $h = '';
     876    }
     877    my $m = int($time / 60);
     878    my $s = $time - $m * 60;
     879    my $ss = sprintf('%05.2f', $s);
     880    if ($ss >= 60) {
     881        $ss = '00.00';
     882        ++$m >= 60 and $m -= 60, ++$h;
     883    }
     884    return sprintf('[%s%.2d:%s]', $h, $m, $ss) . substr($val, pos($val));
     885}
     886
     887#------------------------------------------------------------------------------
     888# Process ID3 synchronized lyrics/text
     889# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
     890sub ProcessSynText($$$)
     891{
     892    my ($et, $dirInfo, $tagTablePtr) = @_;
     893    my $dataPt = $$dirInfo{DataPt};
     894
     895    $et->VerboseDir('SynLyrics', 0, length $$dataPt);
     896    return unless length $$dataPt > 6;
     897
     898    my ($enc,$lang,$timeCode,$type) = unpack('Ca3CC', $$dataPt);
     899    $lang = lc $lang;
     900    undef $lang if $lang !~ /^[a-z]{3}$/ or $lang eq 'eng';
     901    pos($$dataPt) = 6;
     902    my ($termLen, $pat);
     903    if ($enc == 1 or $enc == 2) {
     904        $$dataPt =~ /\G(..)*?\0\0/sg or return;
     905        $termLen = 2;
     906        $pat = '\G(?:..)*?\0\0(....)';
     907    } else {
     908        $$dataPt =~ /\0/g or return;
     909        $termLen = 1;
     910        $pat = '\0(....)';
     911    }
     912    my $desc = substr($$dataPt, 6, pos($$dataPt) - 6 - $termLen);
     913    $desc = DecodeString($et, $desc, $enc);
     914
     915    my $tagInfo = $et->GetTagInfo($tagTablePtr, 'desc');
     916    $tagInfo = Image::ExifTool::GetLangInfo($tagInfo, $lang) if $lang;
     917    $et->HandleTag($tagTablePtr, 'type', $type);
     918    $et->HandleTag($tagTablePtr, 'desc', $desc, TagInfo => $tagInfo);
     919    $tagInfo = $et->GetTagInfo($tagTablePtr, 'text');
     920    $tagInfo = Image::ExifTool::GetLangInfo($tagInfo, $lang) if $lang;
     921
     922    for (;;) {
     923        my $pos = pos $$dataPt;
     924        last unless $$dataPt =~ /$pat/sg;
     925        my $time = unpack('N', $1);
     926        my $text = substr($$dataPt, $pos, pos($$dataPt) - $pos - 4 - $termLen);
     927        $text = DecodeString($et, $text, $enc);
     928        my $timeStr;
     929        if ($timeCode == 2) { # time in ms
     930            $timeStr = sprintf('%.3f', $time / 1000);
     931        } else {              # time in MPEG frames
     932            $timeStr = sprintf('%.4d', $time);
     933            $timeStr .= '?' if $timeCode != 1;
     934        }
     935        $et->HandleTag($tagTablePtr, 'text', "[$timeStr]$text", TagInfo => $tagInfo);
     936    }
    644937}
    645938
     
    649942sub ProcessPrivate($$$)
    650943{
    651     my ($exifTool, $dirInfo, $tagTablePtr) = @_;
     944    my ($et, $dirInfo, $tagTablePtr) = @_;
    652945    my $dataPt = $$dirInfo{DataPt};
    653946    my ($tag, $start);
    654     if ($$dataPt =~ /^(.*?)\0/) {
     947    $et->VerboseDir('PRIV', 0, length $$dataPt);
     948    if ($$dataPt =~ /^(.*?)\0/s) {
    655949        $tag = $1;
    656950        $start = length($tag) + 1;
     
    663957        $tag = 'private' unless $tag =~ /^[-\w]{1,24}$/;
    664958        unless ($$tagTablePtr{$tag}) {
    665             Image::ExifTool::AddTagToTable($tagTablePtr, $tag,
     959            AddTagToTable($tagTablePtr, $tag,
    666960                { Name => ucfirst($tag), Binary => 1 });
    667961        }
    668962    }
    669     my $key = $exifTool->HandleTag($tagTablePtr, $tag, undef,
     963    my $key = $et->HandleTag($tagTablePtr, $tag, undef,
    670964        Size  => length($$dataPt) - $start,
    671965        Start => $start,
     
    673967    );
    674968    # set group1 name
    675     $exifTool->SetGroup($key, $$exifTool{ID3_Ver}) if $key;
     969    $et->SetGroup($key, $$et{ID3_Ver}) if $key;
    676970}
    677971
     
    690984    # (genre numbers are separated by nulls in ID3v2.4,
    691985    #  but nulls are converted to '/' by DecodeString())
    692     while ($val =~ /(?:^|\/)(\d+)/g) {
     986    while ($val =~ /(?:^|\/)(\d+)(\/|$)/g) {
    693987        $genre{$1} or $genre{$1} = "Unknown ($1)";
    694988    }
    695989    $val =~ s/\((\d+)\)/\($genre{$1}\)/g;
    696     $val =~ s/(^|\/)(\d+)/$1$genre{$2}/g;
     990    $val =~ s/(^|\/)(\d+)(?=\/|$)/$1$genre{$2}/g;
    697991    $val =~ s/^\(([^)]+)\)\1?$/$1/; # clean up by removing brackets and duplicates
    698992    return $val;
     993}
     994
     995#------------------------------------------------------------------------------
     996# Get Genre ID
     997# Inputs: 0) Genre name
     998# Returns: genre ID number, or undef
     999sub GetGenreID($)
     1000{
     1001    return Image::ExifTool::ReverseLookup(shift, \%genre);
    6991002}
    7001003
     
    7071010sub DecodeString($$;$)
    7081011{
    709     my ($exifTool, $val, $enc) = @_;
     1012    my ($et, $val, $enc) = @_;
    7101013    return '' unless length $val;
    7111014    unless (defined $enc) {
     
    7191022        @vals = split "\0", $val;
    7201023        foreach $val (@vals) {
    721             $val = $exifTool->Decode($val, $enc ? 'UTF8' : 'Latin');
     1024            $val = $et->Decode($val, $enc ? 'UTF8' : 'Latin');
    7221025        }
    7231026    } elsif ($enc == 1 or $enc == 2) {  # UTF-16 with BOM, or UTF-16BE
     
    7271030            my $v;
    7281031            # split string at null terminators on word boundaries
    729             if ($val =~ s/((..)*?)\0\0//) {
     1032            if ($val =~ s/((..)*?)\0\0//s) {
    7301033                $v = $1;
    7311034            } else {
     
    7351038            }
    7361039            $bom = $1 if $v =~ s/^(\xfe\xff|\xff\xfe)//;
    737             push @vals, $exifTool->Decode($v, 'UCS2', $order{$bom});
     1040            push @vals, $et->Decode($v, 'UCS2', $order{$bom});
    7381041        }
    7391042    } else {
     
    7641067sub ProcessID3v2($$$)
    7651068{
    766     my ($exifTool, $dirInfo, $tagTablePtr) = @_;
     1069    my ($et, $dirInfo, $tagTablePtr) = @_;
    7671070    my $dataPt  = $$dirInfo{DataPt};
    7681071    my $offset  = $$dirInfo{DirStart};
    7691072    my $size    = $$dirInfo{DirLen};
    7701073    my $vers    = $$dirInfo{Version};
    771     my $verbose = $exifTool->Options('Verbose');
     1074    my $verbose = $et->Options('Verbose');
    7721075    my $len;    # frame data length
    7731076
    774     $verbose and $exifTool->VerboseDir($tagTablePtr->{GROUPS}->{1}, 0, $size);
     1077    $et->VerboseDir($tagTablePtr->{GROUPS}->{1}, 0, $size);
     1078    $et->VerboseDump($dataPt, Len => $size, Start => $offset);
    7751079
    7761080    for (;;$offset+=$len) {
     
    7961100                $len =  UnSyncSafe($len);
    7971101                if (not defined $len or $offset + $len + 10 > $size) {
    798                     $exifTool->Warn('Invalid ID3 frame size');
     1102                    $et->Warn('Invalid ID3 frame size');
    7991103                    last;
    8001104                }
     
    8101114        }
    8111115        last if $offset + $len > $size;
    812         my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $id);
     1116        my $tagInfo = $et->GetTagInfo($tagTablePtr, $id);
    8131117        unless ($tagInfo) {
    814             next unless $verbose or $exifTool->Options('Unknown');
    815             $id =~ tr/-A-Za-z0-9_//dc;
    816             $id = 'unknown' unless length $id;
    817             unless ($$tagTablePtr{$id}) {
    818                 $tagInfo = { Name => "ID3_$id", Binary => 1 };
    819                 Image::ExifTool::AddTagToTable($tagTablePtr, $id, $tagInfo);
     1118            my $otherTable = $otherTable{$tagTablePtr};
     1119            $tagInfo = $et->GetTagInfo($otherTable, $id) if $otherTable;
     1120            if ($tagInfo) {
     1121                $et->WarnOnce("Frame '${id}' is not valid for this ID3 version", 1);
     1122            } else {
     1123                next unless $verbose or $et->Options('Unknown');
     1124                $id =~ tr/-A-Za-z0-9_//dc;
     1125                $id = 'unknown' unless length $id;
     1126                unless ($$tagTablePtr{$id}) {
     1127                    $tagInfo = { Name => "ID3_$id", Binary => 1 };
     1128                    AddTagToTable($tagTablePtr, $id, $tagInfo);
     1129                }
    8201130            }
    8211131        }
    8221132        # decode v2.3 and v2.4 flags
    823         my %flags;
     1133        my (%flags, %extra);
    8241134        if ($flags) {
    8251135            if ($vers < 0x0400) {
     
    8381148        }
    8391149        if ($flags{Encrypt}) {
    840             $exifTool->WarnOnce('Encrypted frames currently not supported');
     1150            $et->WarnOnce('Encrypted frames currently not supported');
    8411151            next;
    8421152        }
     
    8491159        # read grouping identity
    8501160        if ($flags{GroupID}) {
    851             length($val) >= 1 or $exifTool->Warn("Short $id frame"), next;
     1161            length($val) >= 1 or $et->Warn("Short $id frame"), next;
    8521162            $val = substr($val, 1); # (ignore it)
    8531163        }
     
    8551165        my $dataLen;
    8561166        if ($flags{DataLen} or $flags{Compress}) {
    857             length($val) >= 4 or $exifTool->Warn("Short $id frame"), next;
     1167            length($val) >= 4 or $et->Warn("Short $id frame"), next;
    8581168            $dataLen = unpack('N', $val);   # save the data length word
    8591169            $val = substr($val, 4);
     
    8611171        # uncompress data
    8621172        if ($flags{Compress}) {
    863             if (eval 'require Compress::Zlib') {
     1173            if (eval { require Compress::Zlib }) {
    8641174                my $inflate = Compress::Zlib::inflateInit();
    8651175                my ($buff, $stat);
     
    8681178                    $val = $buff;
    8691179                } else {
    870                     $exifTool->Warn("Error inflating $id frame");
     1180                    $et->Warn("Error inflating $id frame");
    8711181                    next;
    8721182                }
    8731183            } else {
    874                 $exifTool->WarnOnce('Install Compress::Zlib to decode compressed frames');
     1184                $et->WarnOnce('Install Compress::Zlib to decode compressed frames');
    8751185                next;
    8761186            }
     
    8791189        if (defined $dataLen) {
    8801190            $dataLen = UnSyncSafe($dataLen);
    881             defined $dataLen or $exifTool->Warn("Invalid length for $id frame"), next;
    882             $dataLen == length($val) or $exifTool->Warn("Wrong length for $id frame"), next;
    883         }
    884         $verbose and $exifTool->VerboseInfo($id, $tagInfo,
    885             Table   => $tagTablePtr,
    886             Value   => $val,
    887             DataPt  => $dataPt,
    888             DataPos => $$dirInfo{DataPos},
    889             Size    => $len,
    890             Start   => $offset,
    891         );
    892         next unless $tagInfo;
     1191            defined $dataLen or $et->Warn("Invalid length for $id frame"), next;
     1192            $dataLen == length($val) or $et->Warn("Wrong length for $id frame"), next;
     1193        }
     1194        unless ($tagInfo) {
     1195            next unless $verbose;
     1196            %flags and $extra{Extra} = ', Flags=' . join(',', sort keys %flags);
     1197            $et->VerboseInfo($id, $tagInfo,
     1198                Table   => $tagTablePtr,
     1199                Value   => $val,
     1200                DataPt  => $dataPt,
     1201                DataPos => $$dirInfo{DataPos},
     1202                Size    => $len,
     1203                Start   => $offset,
     1204                %extra
     1205            );
     1206            next;
     1207        }
    8931208#
    894 # decode data in this frame
     1209# decode data in this frame (it is bad form to hard-code these, but the ID3 frame formats
     1210# are so variable that it would be more work to define format types for each of them)
    8951211#
    8961212        my $lang;
     
    8981214        if ($id =~ /^(TXX|TXXX)$/) {
    8991215            # two encoded strings separated by a null
    900             my @vals = DecodeString($exifTool, $val);
     1216            my @vals = DecodeString($et, $val);
    9011217            foreach (0..1) { $vals[$_] = '' unless defined $vals[$_]; }
    9021218            ($val = "($vals[0]) $vals[1]") =~ s/^\(\) //;
    9031219        } elsif ($id =~ /^T/ or $id =~ /^(IPL|IPLS)$/) {
    904             $val = DecodeString($exifTool, $val);
     1220            $val = DecodeString($et, $val);
    9051221        } elsif ($id =~ /^(WXX|WXXX)$/) {
    9061222            # one encoded string and one Latin string separated by a null
     
    9081224            my $url;
    9091225            if ($enc == 1 or $enc == 2) {
    910                 ($val, $url) = ($val =~ /^(.(?:..)*?)\0\0(.*)/);
     1226                ($val, $url) = ($val =~ /^(.(?:..)*?)\0\0(.*)/s);
    9111227            } else {
    912                 ($val, $url) = ($val =~ /^(..*?)\0(.*)/);
     1228                ($val, $url) = ($val =~ /^(..*?)\0(.*)/s);
    9131229            }
    9141230            unless (defined $val and defined $url) {
    915                 $exifTool->Warn("Invalid $id frame value");
     1231                $et->Warn("Invalid $id frame value");
    9161232                next;
    9171233            }
    918             $val = DecodeString($exifTool, $val);
    919             $url =~ s/\0.*//;
     1234            $val = DecodeString($et, $val);
     1235            $url =~ s/\0.*//s;
    9201236            $val = length($val) ? "($val) $url" : $url;
    9211237        } elsif ($id =~ /^W/) {
    922             $val =~ s/\0.*//;   # truncate at null
     1238            $val =~ s/\0.*//s;  # truncate at null
    9231239        } elsif ($id =~ /^(COM|COMM|ULT|USLT)$/) {
    924             $valLen > 4 or $exifTool->Warn("Short $id frame"), next;
     1240            $valLen > 4 or $et->Warn("Short $id frame"), next;
    9251241            $lang = substr($val,1,3);
    926             my @vals = DecodeString($exifTool, substr($val,4), Get8u(\$val,0));
     1242            my @vals = DecodeString($et, substr($val,4), Get8u(\$val,0));
    9271243            foreach (0..1) { $vals[$_] = '' unless defined $vals[$_]; }
    9281244            $val = length($vals[0]) ? "($vals[0]) $vals[1]" : $vals[1];
    9291245        } elsif ($id eq 'USER') {
    930             $valLen > 4 or $exifTool->Warn('Short USER frame'), next;
     1246            $valLen > 4 or $et->Warn("Short $id frame"), next;
    9311247            $lang = substr($val,1,3);
    932             $val = DecodeString($exifTool, substr($val,4), Get8u(\$val,0));
     1248            $val = DecodeString($et, substr($val,4), Get8u(\$val,0));
    9331249        } elsif ($id =~ /^(CNT|PCNT)$/) {
    934             $valLen >= 4 or $exifTool->Warn("Short $id frame"), next;
    935             my $cnt = unpack('N', $val);
    936             my $i;
    937             for ($i=4; $i<$valLen; ++$i) {
    938                 $cnt = $cnt * 256 + unpack("x${i}C", $val);
    939             }
     1250            $valLen >= 4 or $et->Warn("Short $id frame"), next;
     1251            my ($cnt, @xtra) = unpack('NC*', $val);
     1252            $cnt = ($cnt << 8) + $_ foreach @xtra;
    9401253            $val = $cnt;
    9411254        } elsif ($id =~ /^(PIC|APIC)$/) {
    942             $valLen >= 4 or $exifTool->Warn("Short $id frame"), next;
     1255            $valLen >= 4 or $et->Warn("Short $id frame"), next;
    9431256            my ($hdr, $attr);
    9441257            my $enc = unpack('C', $val);
     
    9491262            }
    9501263            # remove header (encoding, image format or MIME type, picture type, description)
    951             $val =~ s/^$hdr//s or $exifTool->Warn("Invalid $id frame"), next;
    952             my @attrs = ($1, ord($2), DecodeString($exifTool, $3, $enc));
     1264            $val =~ s/^$hdr//s or $et->Warn("Invalid $id frame"), next;
     1265            my @attrs = ($1, ord($2), DecodeString($et, $3, $enc));
    9531266            my $i = 1;
    9541267            foreach $attr (@attrs) {
    9551268                # must store descriptions even if they are empty to maintain
    9561269                # sync between copy numbers when multiple images
    957                 $exifTool->HandleTag($tagTablePtr, "$id-$i", $attr);
     1270                $et->HandleTag($tagTablePtr, "$id-$i", $attr);
    9581271                ++$i;
    9591272            }
     1273        } elsif ($id eq 'POP' or $id eq 'POPM') {
     1274            # _email, 00, rating(1), counter(4-N)
     1275            my ($email, $dat) = ($val =~ /^([^\0]*)\0(.*)$/s);
     1276            unless (defined $dat and length($dat)) {
     1277                $et->Warn("Invalid $id frame");
     1278                next;
     1279            }
     1280            my ($rating, @xtra) = unpack('C*', $dat);
     1281            my $cnt = 0;
     1282            $cnt = ($cnt << 8) + $_ foreach @xtra;
     1283            $val = "$email $rating $cnt";
     1284        } elsif ($id eq 'OWNE') {
     1285            # enc(1), _price, 00, _date(8), Seller
     1286            my @strs = DecodeString($et, $val);
     1287            $strs[1] =~ s/^(\d{4})(\d{2})(\d{2})/$1:$2:$3 /s if $strs[1]; # format date
     1288            $val = "@strs";
     1289        } elsif ($id eq 'RVA' or $id eq 'RVAD') {
     1290            my @dat = unpack('C*', $val);
     1291            my $flag = shift @dat;
     1292            my $bits = shift @dat or $et->Warn("Short $id frame"), next;
     1293            my $bytes = int(($bits + 7) / 8);
     1294            my @parse = (['Right',0,2,0x01],['Left',1,3,0x02],['Back-right',4,6,0x04],
     1295                         ['Back-left',5,7,0x08],['Center',8,9,0x10],['Bass',10,11,0x20]);
     1296            $val = '';
     1297            while (@parse) {
     1298                my $elem = shift @parse;
     1299                my $j = $$elem[2] * $bytes;
     1300                last if scalar(@dat) < $j + $bytes;
     1301                my $i = $$elem[1] * $bytes;
     1302                $val .= ', ' if $val;
     1303                my ($rel, $pk, $b);
     1304                for ($rel=0, $pk=0, $b=0; $b<$bytes; ++$b) {
     1305                    $rel = $rel * 256 + $dat[$i + $b];
     1306                    $pk  = $pk  * 256 + $dat[$j + $b]; # (peak - not used in printout)
     1307                }
     1308                $rel =-$rel unless $flag & $$elem[3];
     1309                $val .= sprintf("%+.1f%% %s", 100 * $rel / ((1<<$bits)-1), $$elem[0]);
     1310            }
     1311        } elsif ($id eq 'RVA2') {
     1312            my ($pos, $id) = $val=~/^([^\0]*)\0/s ? (length($1)+1, $1) : (1, '');
     1313            my @vals;
     1314            while ($pos + 4 <= $valLen) {
     1315                my $type = Get8u(\$val, $pos);
     1316                my $str = ({
     1317                    0 => 'Other',
     1318                    1 => 'Master',
     1319                    2 => 'Front-right',
     1320                    3 => 'Front-left',
     1321                    4 => 'Back-right',
     1322                    5 => 'Back-left',
     1323                    6 => 'Front-centre',
     1324                    7 => 'Back-centre',
     1325                    8 => 'Subwoofer',
     1326                }->{$type} || "Unknown($type)");
     1327                my $db = Get16s(\$val,$pos+1) / 512;
     1328                # convert dB to percent as displayed by iTunes 10.5
     1329                # (not sure why I need to divide by 20 instead of 10 as expected - PH)
     1330                push @vals, sprintf('%+.1f%% %s', 10**($db/20+2)-100, $str);
     1331                # step to next channel (ignoring peak volume)
     1332                $pos += 4 + int((Get8u(\$val,$pos+3) + 7) / 8);
     1333            }
     1334            $val = join ', ', @vals;
     1335            $val .= " ($id)" if $id;
    9601336        } elsif ($id eq 'PRIV') {
    9611337            # save version number to set group1 name for tag later
    962             $exifTool->{ID3_Ver} = $tagTablePtr->{GROUPS}->{1};
    963             $exifTool->HandleTag($tagTablePtr, $id, $val);
     1338            $$et{ID3_Ver} = $$tagTablePtr{GROUPS}{1};
     1339            $et->HandleTag($tagTablePtr, $id, $val);
    9641340            next;
     1341        } elsif ($$tagInfo{Format} or $$tagInfo{SubDirectory}) {
     1342            $et->HandleTag($tagTablePtr, $id, undef, DataPt => \$val);
     1343            next;
     1344        } elsif ($id eq 'GRP1' or $id eq 'MVNM' or $id eq 'MVIN') {
     1345            $val =~ s/(^\0+|\0+$)//g;   # (PH guess)
    9651346        } elsif (not $$tagInfo{Binary}) {
    966             $exifTool->Warn("Don't know how to handle $id frame");
     1347            $et->Warn("Don't know how to handle $id frame");
    9671348            next;
    9681349        }
     
    9701351            $tagInfo = Image::ExifTool::GetLangInfo($tagInfo, lc $lang);
    9711352        }
    972         $exifTool->FoundTag($tagInfo, $val);
     1353        %flags and $extra{Extra} = ', Flags=' . join(',', sort keys %flags);
     1354        $et->HandleTag($tagTablePtr, $id, $val,
     1355            TagInfo => $tagInfo,
     1356            DataPt  => $dataPt,
     1357            DataPos => $$dirInfo{DataPos},
     1358            Size    => $len,
     1359            Start   => $offset,
     1360            %extra
     1361        );
    9731362    }
    9741363}
     
    9791368# Returns: 1 on success, 0 if this file didn't contain ID3 information
    9801369# - also processes audio data if any ID3 information was found
    981 # - 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
    9821371sub ProcessID3($$)
    9831372{
    984     my ($exifTool, $dirInfo) = @_;
    985 
    986     return 0 if $exifTool->{DoneID3};  # avoid infinite recursion
    987     $exifTool->{DoneID3} = 1;
     1373    my ($et, $dirInfo) = @_;
     1374
     1375    return 0 if $$et{DoneID3};  # avoid infinite recursion
     1376    $$et{DoneID3} = 1;
    9881377
    9891378    # allow this to be called with either RAF or DataPt
     
    10021391    while ($buff =~ /^ID3/) {
    10031392        $rtnVal = 1;
    1004         $raf->Read($hBuff, 7) == 7 or $exifTool->Warn('Short ID3 header'), last;
     1393        $raf->Read($hBuff, 7) == 7 or $et->Warn('Short ID3 header'), last;
    10051394        my ($vers, $flags, $size) = unpack('nCN', $hBuff);
    10061395        $size = UnSyncSafe($size);
    1007         defined $size or $exifTool->Warn('Invalid ID3 header'), last;
     1396        defined $size or $et->Warn('Invalid ID3 header'), last;
    10081397        my $verStr = sprintf("2.%d.%d", $vers >> 8, $vers & 0xff);
    10091398        if ($vers >= 0x0500) {
    1010             $exifTool->Warn("Unsupported ID3 version: $verStr");
     1399            $et->Warn("Unsupported ID3 version: $verStr");
    10111400            last;
    10121401        }
    10131402        unless ($raf->Read($hBuff, $size) == $size) {
    1014             $exifTool->Warn('Truncated ID3 data');
     1403            $et->Warn('Truncated ID3 data');
    10151404            last;
    10161405        }
     
    10231412        if ($flags & 0x40) {
    10241413            # skip the extended header
    1025             $size >= 4 or $exifTool->Warn('Bad ID3 extended header'), last;
     1414            $size >= 4 or $et->Warn('Bad ID3 extended header'), last;
    10261415            my $len = unpack('N', $hBuff);
    10271416            if ($len > length($hBuff) - 4) {
    1028                 $exifTool->Warn('Truncated ID3 extended header');
     1417                $et->Warn('Truncated ID3 extended header');
    10291418                last;
    10301419            }
     
    10581447# read ID3v1 trailer if it exists
    10591448#
     1449    my $trailSize = 0;
    10601450    if ($raf->Seek(-128, 2) and $raf->Read($tBuff, 128) == 128 and $tBuff =~ /^TAG/) {
    1061         $exifTool->{DoneID3} = 2;  # set to 2 as flag that trailer exists
     1451        $trailSize = 128;
    10621452        %id3Trailer = (
    10631453            DataPt   => \$tBuff,
     
    10691459        $rtnVal = 1;
    10701460        # load 'Enhanced TAG' information if available
    1071         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+/) {
    10721463            $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');
    10731514        }
    10741515    }
     
    10791520        # first process audio data if it exists
    10801521        if ($$dirInfo{RAF}) {
    1081             my $oldType = $exifTool->{FILE_TYPE};   # save file type
     1522            my $oldType = $$et{FILE_TYPE};   # save file type
    10821523            # check current file type first
    10831524            my @types = grep /^$oldType$/, @audioFormats;
     
    10881529                $raf->Seek($hdrEnd, 0);
    10891530                # set type for this file if we are successful
    1090                 $exifTool->{FILE_TYPE} = $type;
     1531                $$et{FILE_TYPE} = $type;
    10911532                my $module = $audioModule{$type} || $type;
    10921533                require "Image/ExifTool/$module.pm" or next;
     
    10941535                # process the file
    10951536                no strict 'refs';
    1096                 &$func($exifTool, $dirInfo) and last;
     1537                &$func($et, $dirInfo) and last;
    10971538                use strict 'refs';
    10981539            }
    1099             $exifTool->{FILE_TYPE} = $oldType;      # restore original file type
     1540            $$et{FILE_TYPE} = $oldType;      # restore original file type
    11001541        }
    11011542        # set file type to MP3 if we didn't find audio data
    1102         $exifTool->SetFileType('MP3');
    1103         # record the size if the ID3 metadata
    1104         $exifTool->FoundTag('ID3Size', $id3Len);
     1543        $et->SetFileType('MP3');
     1544        # record the size of the ID3 metadata
     1545        $et->FoundTag('ID3Size', $id3Len);
    11051546        # process ID3v2 header if it exists
    11061547        if (%id3Header) {
    1107             $exifTool->VPrint(0, "$id3Header{DirName}:\n");
    1108             $exifTool->ProcessDirectory(\%id3Header, $tagTablePtr);
     1548            $et->VPrint(0, "$id3Header{DirName}:\n");
     1549            $et->ProcessDirectory(\%id3Header, $tagTablePtr);
    11091550        }
    11101551        # process ID3v1 trailer if it exists
    11111552        if (%id3Trailer) {
    1112             $exifTool->VPrint(0, "ID3v1:\n");
     1553            $et->VPrint(0, "ID3v1:\n");
    11131554            SetByteOrder('MM');
    11141555            $tagTablePtr = GetTagTable('Image::ExifTool::ID3::v1');
    1115             $exifTool->ProcessDirectory(\%id3Trailer, $tagTablePtr);
     1556            $et->ProcessDirectory(\%id3Trailer, $tagTablePtr);
    11161557            # process "Enhanced TAG" information if available
    11171558            if ($id3Trailer{EnhancedTAG}) {
    1118                 $exifTool->VPrint(0, "ID3v1 Enhanced TAG:\n");
     1559                $et->VPrint(0, "ID3v1 Enhanced TAG:\n");
    11191560                $tagTablePtr = GetTagTable('Image::ExifTool::ID3::v1_Enh');
    11201561                $id3Trailer{DataPt} = $id3Trailer{EnhancedTAG};
    11211562                $id3Trailer{DataPos} -= 227; # (227 = length of Enhanced TAG block)
    11221563                $id3Trailer{DirLen} = 227;
    1123                 $exifTool->ProcessDirectory(\%id3Trailer, $tagTablePtr);
     1564                $et->ProcessDirectory(\%id3Trailer, $tagTablePtr);
    11241565            }
    11251566        }
     
    11361577sub ProcessMP3($$)
    11371578{
    1138     my ($exifTool, $dirInfo) = @_;
     1579    my ($et, $dirInfo) = @_;
     1580    my $rtnVal = 0;
    11391581
    11401582    # must first check for leading/trailing ID3 information
    1141     unless ($exifTool->{DoneID3}) {
    1142         ProcessID3($exifTool, $dirInfo) and return 1;
    1143     }
    1144     my $raf = $$dirInfo{RAF};
    1145     my $rtnVal = 0;
    1146     my $buff;
     1583    # (and process the rest of the file if found)
     1584    unless ($$et{DoneID3}) {
     1585        $rtnVal = ProcessID3($et, $dirInfo);
     1586    }
     1587
     1588    # check for MPEG A/V data if not already processed above
     1589    unless ($rtnVal) {
     1590        my $raf = $$dirInfo{RAF};
     1591        my $buff;
    11471592#
    11481593# extract information from first audio/video frame headers
    11491594# (if found in the first $scanLen bytes)
    11501595#
    1151     # scan further into a file that should be an MP3
    1152     my $scanLen = ($$exifTool{FILE_EXT} and $$exifTool{FILE_EXT} eq 'MP3') ? 8192 : 256;
    1153     if ($raf->Read($buff, $scanLen)) {
    1154         require Image::ExifTool::MPEG;
    1155         if ($buff =~ /\0\0\x01(\xb3|\xc0)/) {
    1156             # look for A/V headers in first 64kB
    1157             my $buf2;
    1158             $raf->Read($buf2, 0x10000 - $scanLen) and $buff .= $buf2;
    1159             $rtnVal = 1 if Image::ExifTool::MPEG::ParseMPEGAudioVideo($exifTool, \$buff);
    1160         } else {
    1161             # look for audio frame sync in first $scanLen bytes
    1162             $rtnVal = 1 if Image::ExifTool::MPEG::ParseMPEGAudio($exifTool, \$buff);
    1163         }
     1596        # scan further into a file that should be an MP3
     1597        my $scanLen = ($$et{FILE_EXT} and $$et{FILE_EXT} eq 'MP3') ? 8192 : 256;
     1598        if ($raf->Read($buff, $scanLen)) {
     1599            require Image::ExifTool::MPEG;
     1600            if ($buff =~ /\0\0\x01(\xb3|\xc0)/) {
     1601                # look for A/V headers in first 64kB
     1602                my $buf2;
     1603                $raf->Read($buf2, 0x10000 - $scanLen) and $buff .= $buf2;
     1604                $rtnVal = 1 if Image::ExifTool::MPEG::ParseMPEGAudioVideo($et, \$buff);
     1605            } else {
     1606                # look for audio frame sync in first $scanLen bytes
     1607                # (set MP3 flag to 1 so this will fail unless layer 3 audio)
     1608                my $ext = $$et{FILE_EXT} || '';
     1609                my $mp3 = ($ext eq 'MUS') ? 0 : 1;  # MUS files are MP2
     1610                $rtnVal = 1 if Image::ExifTool::MPEG::ParseMPEGAudio($et, \$buff, $mp3);
     1611            }
     1612        }
     1613    }
     1614
     1615    # check for an APE trailer if this was a valid A/V file and we haven't already done it
     1616    if ($rtnVal and not $$et{DoneAPE}) {
     1617        require Image::ExifTool::APE;
     1618        Image::ExifTool::APE::ProcessAPE($et, $dirInfo);
    11641619    }
    11651620    return $rtnVal;
     
    11861641=head1 AUTHOR
    11871642
    1188 Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
     1643Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
    11891644
    11901645This library is free software; you can redistribute it and/or modify it
     
    12011656=item L<http://www.fortunecity.com/underworld/sonic/3/id3tag.html>
    12021657
     1658=item L<https://id3.org/Lyrics3>
     1659
    12031660=back
    12041661
Note: See TracChangeset for help on using the changeset viewer.