diff --git a/lib/Audio/FLAC/Header.pm b/lib/Audio/FLAC/Header.pm old mode 100755 new mode 100644 diff --git a/lib/File/Find/Rule.pm b/lib/File/Find/Rule.pm old mode 100755 new mode 100644 diff --git a/lib/MP3/Tag.pm b/lib/MP3/Tag.pm old mode 100755 new mode 100644 index 87b4365..f3f8aa4 --- a/lib/MP3/Tag.pm +++ b/lib/MP3/Tag.pm @@ -27,6 +27,7 @@ use strict; return $self->{parent}->get_config(@_); } *get_config1 = \&MP3::Tag::Implemenation::get_config1; + *get_config1 = 0 if 0; # quiet a warning } use MP3::Tag::ID3v1; @@ -41,7 +42,7 @@ use MP3::Tag::ImageExifTool; use MP3::Tag::LastResort; use vars qw/$VERSION @ISA/; -$VERSION="1.13"; +$VERSION="1.14"; @ISA = qw( MP3::Tag::User MP3::Tag::Site MP3::Tag::Vendor MP3::Tag::Implemenation ); # Make overridable *config = \%MP3::Tag::Implemenation::config; @@ -89,6 +90,7 @@ use vars qw/%config/; # ExifTool says: ID3 may be in MP3/MPEG/AIFF/OGG/FLAC/APE/RealAudio (MPC). writable_extensions => [qw(mp3 mp2 id3 tag ogg mpg mpeg mp4 aiff flac ape ram mpc)], + ampersand_joiner => ['; '], ); { my %e; @@ -307,6 +309,7 @@ sub _get_tag { # keep old name for a while *getTags = \&get_tags; +*getTags = 0 if 0; # quiet a warning =item new_fake @@ -360,6 +363,7 @@ sub new_tag { # keep old name for a while *newTag = \&new_tag; +*newTag = 0 if 0; # quiet a warning #only as a shortcut to {filename}->close to explicitly close a file @@ -590,24 +594,50 @@ sub disk_alphanum ($) { my %ignore_0length = qw(ID3v1 1 CDDB_File 1 Inf 1 Cue 1 ImageSize 1 ImageExifTool 1); -sub auto_field($;$) { - my ($self, $elt, $from) = (shift, shift, shift); +sub _auto_field_from($$$;$$$$) { + my ($self, $check_only, $packs, $rwhat, $ret_from, $args, $all) = (shift, shift, shift, shift, shift, shift || [], shift); + my @what = ref $rwhat ? @$rwhat : $rwhat; + my @out; local $self->{__proxy}[0] = $self unless $self->{__proxy}[0] or $ENV{MP3TAG_TEST_WEAKEN}; - my $parts = $self->get_config($elt) || $self->get_config('autoinfo'); $self->get_tags; - my $do_can = ($elt =~ /^(cd\w+_id|height|width|bit_depth|mime_type|img_type|_duration)$/); - foreach my $part (@$parts) { - next unless exists $self->{$part}; - next if $do_can and not $self->{$part}->can($elt); - next unless defined (my $out = $self->{$part}->$elt()); - # Ignore 0-length answers from ID3v1, ImageExifTool, CDDB_File, Cue, ImageSize, and Inf - next if not length $out and $ignore_0length{$part}; # These return '' - return [$out, $part] if $from; + # ID3v1 has AUTOLOAD +# my $do_can = ($what =~ /^(cd\w+_id|height|width|bit_depth|mime_type|img_type|_duration)$/); + foreach my $pack (@$packs) { + next unless exists $self->{$pack}; + my $do_can = $pack ne 'ID3v1'; + my $out; + for my $what (@what) { + next if $pack eq 'ID3v1' and not $MP3::Tag::ID3v1::ok_length{$what}; # dup of a warning in AUTOLOAD + next if $do_can and not $self->{$pack}->can($what); + if ($check_only and $self->{$pack}->can(my $m = $what . '_have')) { + next unless $self->{$pack}->$m(@$args); + return $ret_from ? [1, $pack] : 1; + } + next unless defined ($out = $self->{$pack}->$what(@$args)); + # Ignore 0-length answers from ID3v1, ImageExifTool, CDDB_File, Cue, ImageSize, and Inf + undef $out, next if not length $out and $ignore_0length{$pack}; # These return '' + } + next unless defined $out; + $out = 1 if $check_only; + if ($all) { # Currently, @out is not used by our callers + push @out, ($ret_from ? [$out, $pack] : $out); + next; + } + return [$out, $pack] if $ret_from; return $out; } - return ''; + return @out if $all; + return; +} + +sub auto_field($;$$) { + my ($self, $what, $ret_from) = (shift, shift, shift); + my $packs = $self->get_config($what) || $self->get_config('autoinfo'); + my $o = $self->_auto_field_from(!'check_only', $packs, $what, $ret_from); + return '' unless defined $o; + $o; } for my $elt ( qw( title track artist album comment year genre ) ) { @@ -1182,7 +1212,7 @@ sub config { decode_encoding_filename decode_encoding_files decode_encoding_inf decode_encoding_cddb_file name_for_field_normalization is_writable writable_extensions - id3v2_frames_autofill local_cfg_file); + id3v2_frames_autofill local_cfg_file ampersand_joiner); my @tr = map "translate_$_", qw( title track artist album comment year genre comment_collection comment_track title_track @@ -1797,7 +1827,7 @@ conditionals too, with C<:>.) =item * -Strings of the form C are +Strings of the form C are replaced by the first FRAM frame with the descriptor "description" in the specified comma-separated list of languages. Instead of a language (ID3v2 uses lowercase 3-char ISO-639-2 language notations) one can use @@ -1815,13 +1845,27 @@ match is case-insensitive. =item * Several descriptors of the form -C discussed above may be +C discussed above may be combined together with C<&>; the non-empty expansions are joined -together with C<"; ">. Example: +together with the value of configuration variable C +(default C<"; ">). Example: %{TXXX[pre-title]&TIT1&TIT2&TIT3&TXXX[post-title]} +=item * + +Strings of the form C are replaced +by the result of C (with the given arguments) in one of the specified +known subpackages (e.g., for C, C is used). Arbitrary number +of arguments is supported. Instead of a long name C one can use its +standard shortcut (e.g., C for C). For example, + + $mp3->interpolate('%{t(ID3v1,Cue)}') + +returns the title from the ID3v1 tag, or (if not there) from a cue sheet. +One can use this in conditionals etc as well. + =item * C<d>I<NUMBER> is replaced by I<NUMBER>-th component of the directory name (with @@ -1884,7 +1928,7 @@ corresponding method. C<frames> is replaced by space-separated list of "long names" of ID3v2 frames (see id3v2_frame_descriptors()). (To use a different separator, -put it after slash, as in %{frames/, }, where separator is COMMA +put it after slash, as in C<%{frames/, }>, where separator is COMMA SPACE). =item * @@ -1981,6 +2025,8 @@ will result in C<39:05.620sec>. =cut +# ` + my %trans = qw( t title a artist l album @@ -2057,8 +2103,22 @@ my %trans = qw( t title # %O Original material flag (string) # %G Musical genre (integer) -my $frame_bra = # FRAM | FRAM03 | FRAM(lang)[ - qr{\w{4}(?:(?:\d\d)|(?:\([^()]*(?:\([^()]+\)[^()]*)*\))?(?:(\[)|(?=[\}:|&])))}s; # 1 group for begin-descr +# Made as tags: ParseData ID3v2 ID3v1 ImageExifTool Inf CDDB_File Cue ImageSize LastResort +my %handlers = map {($_, 1)} qw( + CDDB_File File ID3v2 ImageExifTool Inf ParseData + Cue ID3v1 ImageSize LastResort +); # Inf/Cue are not in language list: https://www.loc.gov/standards/iso639-2/php/code_list.php + +#$handler_r = qr/$handler_r/; +# +#my $frame_bra = # FRAM | FRAM03 | FRAM(lang)[ +# qr{\w{4}(?:(?:\d\d)|(?:\([^()]*(?:\([^()]+\)[^()]*)*\))?(?:(\[)|(?=[\}:|&])))}s; # 1 group for begin-descr + +my $at_end_frame_name = qr/(?=[\}:|&])/; +my $lang_or_handlers_rex = qr/\(([^()]*(?:\([^()]+\)[^()]*)*)\)/; +my $frame_bra = # FRAM | FRAM03 | FRAM(lang)[ | cmd(PACKAGES) | cmd(PACKAGES)[args] + qr{(?:\w{4}(?=\d\d\b|\b)|(?!I\b)\w+(?=\())(?:\d\d|$lang_or_handlers_rex?(?:(\[)|$at_end_frame_name))}s; # 2 groups for descr + bra + # used with offset by 1: 2: fill, 3: same, 4: $left, 5..6 width, 7: key my $pat_rx = qr/^%(?:(?:\((.)\)|([^-.1-9%a-zA-Z]))?(-)?(\d+))?(?:\.(\d+))?([talgcynfFeEABDNvLrqQSmsCpouMHwh{%])/s; # XXXX Partially repeated below, search for `talgc'??? vLrqQSmsCpouMH miss??? @@ -2067,6 +2127,16 @@ my $longer_f = qr(a[3CRI]|tT|c[TC]|i[DIT]|n[012]|m[A12TP]|bD); # (a[CR]|tT|c[TC]|[mMS]L|SML|i[DIT]|n[012]|m[A12T]|bD) # a[CR]|tT|c[TC]|i[DIT]|n[012]|m[A12T]|bD +sub process_handlers ($$$$;$$) { # only 1 level of parens allowed in flags + my ($self, $h, $handlers, $args, $cond, $set) = (shift, shift, shift, shift, shift, shift); +# die "Conditionals with handlers not supported yet" if $cond; + die "Handlers with arguments not supported yet" if @$args; + my (@f) = ($h =~ /^(\w+)/) or die "Panic: `$h' as a handler"; + push @f, $trans{$f[0]} if exists $trans{$f[0]}; + $set and $_ .= '__set' for @f; + $self->_auto_field_from($cond, $handlers, \@f, undef, $args, $set); # if $set, calls a method in all packages where possible +} + # $upto TRUE: parse the part including $upto char # Very restricted backslashitis: only $upto and \ before $upto-or-end # $upto defined but FALSE: interpolate only one %-escape. @@ -2079,8 +2149,8 @@ sub _interpolate ($$;$$) { my $ids; die "upto=`$upto' not supported" if $upto and $upto ne ']' and $upto ne'}'; die "upto=`$upto' not supported with skip" - if $upto and not defined $upto and $skip; - my $cnt = ($upto or not defined $upto) ? -1 : 1; # upto eq '': 1 escape + if $upto and not defined $upto and $skip; # XXXX Unreachable??? + my $cnt = ($upto or not defined $upto) ? -1 : 1; # upto eq '': 1 escape while ($cnt-- and ($upto # undef and '' use the same code ? ($upto eq ']' @@ -2115,49 +2185,53 @@ sub _interpolate ($$;$$) { next if $skip; my $meth = $trans{$1}; $str = $self->$meth(); - } elsif ($what eq '{' and # $frame_bra has 1 group, No. 5 + } elsif ($what eq '{' and # $frame_bra has 2 groups, No. 5, 6 # 2-char fields as above, except for [mMS]L|SML (XXX: vLrqQSmsCpouMH ???) $_[1] =~ s/^(!)?(([talgcynfFeEABDNvLrqQSmsCpouMHwh]|ID3v[12]|ID3v2-modified|$longer_f|U\d+)(:|\|\|?)|$frame_bra)//o) { # Alternation with simple/complicated stuff - my ($neg, $id, $simple, $delim) = ($1, $2, $3, $4); - if ($delim) { # Not a frame id... + my ($neg, $id, $simple, $delim, $lang_or_packages, $have_bra) = ($1, $2, $3, $4, $5, $6); + + my(@_handlers, @args) = split /,/, ($lang_or_packages || ''); + my @handlers = grep $handlers{$_}, @_handlers; + $delim or $id =~ /^[A-Z]{3}[A-Z\d](\d\d)?\b/ or @handlers and @handlers == @_handlers + or die "Cannot parse frame descriptor: <<<$id>>>"; + + if ($delim) { # Not a frame/cmd id... $id = $simple; - } else { # Frame: maybe trailed by :, |, ||, maybe not - $id .= ($self->_interpolate($_[1], ']', $skip) . ']') if $5; + } else { # Frame/cmd: maybe trailed by :, |, ||, maybe not + while (@handlers and $have_bra) { + push @args, $self->_interpolate($_[1], ']', $skip); + $have_bra = ($_[1] =~ s/^\[//); + } + $id .= ($self->_interpolate($_[1], ']', $skip) . ']') if $have_bra; # unreachable if handler present! $_[1] =~ s/^(:|\|\|?)// and $delim = $1; unless ($delim) { die "Can't parse negated conditional: I see `$_[1]'" if $neg; my $nonesuch = 0; - unless ($self->{ID3v2} or $neg) { + unless (@handlers or $self->{ID3v2} or $neg) { die "No ID3v2 present" if $self->get_config('id3v2_missing_fatal'); $nonesuch = 1; } - if ($_[1] =~ s/^}//) { # frame with optional (lang)/[descr] - next if $skip or $nonesuch; - $str = $self->select_id3v2_frame_by_descr($id); - #$str = $str->{_Data} if $str and ref $str and exists $str->{_Data}; - } elsif ($_[1] =~ /^&/o) { - # join of frames with optional (language)/[descriptor] - my @id = $id; - while ($_[1] =~ s/^&($frame_bra)//o) { - $id = $1; - $id .= ($self->_interpolate($_[1], ']', $skip) . ']') if $2; - next if $skip or $nonesuch; - push @id, $id; - } - die "Can't parse &-list; I see `$_[1]'" unless $_[1] =~ s/^}//; - next if $skip or $nonesuch; - my @out; - for my $in (@id) { - $in = $self->select_id3v2_frame_by_descr($in); - #$in = $in->{_Data} if $in and ref $in and exists $in->{_Data}; - push @out, $in if defined $in and length $in; + next if ($skip or $nonesuch) and $_[1] =~ s/^\}//; + if ($_[1] =~ /^[\}&]/) { # frame with optional (lang)/[descr], or a package-handled descriptor + if (@handlers) { + $str = $self->process_handlers($id, \@handlers, \@args) unless $skip; +# $str = '' if not defined $str and $1 eq '&'; + } else { + $str = $self->select_id3v2_frame_by_descr($id); } - $str = join '; ', @out; } else { die "unknown frame terminator; I see `$_[1]'"; } + if ($_[1] =~ s/^&/%\{/) { # join of frames with optional (language)/[descriptor], etc + my $rest = $self->_interpolate($_[1], '', $skip); + next if $skip; + my $joiner = $self->get_config1('ampersand_joiner'); # default '; ' + $str = join $joiner, map {(defined and length) ? $_ : ()} $str, $rest; + } else { + $_[1] =~ s/^\}//; + } } } if ($delim) { # Conditional @@ -2176,13 +2250,17 @@ sub _interpolate ($$;$$) { die "ID3v2 or ID3v1 as conditionals incompatible with $alt" if $alt; $have = !! $self->{$simple}; # Make logical - } else { - $have = $self->have_id3v2_frame_by_descr($id); - } - my $skipping = $skip || (not $alt and $1 ? $have : !$have); + } elsif (@handlers) { +# warn "\t!!! Handlers"; + $have = $self->process_handlers($id, \@handlers, \@args, 'cond'); + } else { + $have = $self->have_id3v2_frame_by_descr($id); +# warn "\t!!! Cond: <<$id>> <<$have>>"; + } + my $skipping = $skip || (not $alt and $neg ? $have : !$have); my $s; if ($alt and $alt ne '||') { # Need to prepend % - if ($_[1] =~ s/^([^\\])}//) { # One-char escape + if ($_[1] =~ s/^([^\\])\}//) { # One-char escape $s = $self->interpolate("%$1") unless $skipping; } else { # Understood with {}; prepend %{ $_[1] =~ s/^/%\{/ or die; @@ -2192,8 +2270,13 @@ sub _interpolate ($$;$$) { $s = $self->_interpolate($_[1], '}', $skipping); } next if $skipping; - $str = $self->select_id3v2_frame_by_descr($id) - if $alt and $have and not $simple; + if ($alt and $have and not $simple) { + if (@handlers) { + $str = $self->process_handlers($id, \@handlers, \@args); + } else { + $str = $self->select_id3v2_frame_by_descr($id); + } + } $str = $s unless $have and $alt; $str = $str->{_Data} if $str and ref $str and exists $str->{_Data}; @@ -2398,11 +2481,13 @@ sub interpolate_with_flags ($$$) { =item parse_rex($pattern, $string) Parse $string according to the regular expression $pattern with -C<%>-escapes C<%%, %a, %t, %l, %y, %g, %c, %n, %e, %E>. The meaning -of escapes is the same as for method L<"interpolate">(); but they are +C<%>-escapes C<%%, %a, %t, %l, %y, %g, %c, %n, %e, %E> etc. The meaning +of escapes is the same as for method L<"interpolate">(); but (with +the exception of C<%%>) they are used not for I<expansion>, but for I<matching> a part of $string suitable to be a value for these fields. Returns false on failure, a -hash reference with parsed fields otherwise. +hash reference with parsed fields otherwise (with C<%a> setting the +field C<author>, etc). Some more escapes are supported: C<%=a, %=t, %=l, %=y, %=g, %=c, %=n, %=e, %=E, %=A, %=B, %=D, %=f, %=F, %=N, %={WHATEVER}> I<match> @@ -2413,10 +2498,12 @@ configuration variable C<parse_filename_merge_dots> is true (default)) and are case-insensitive if configuration variable C<parse_filename_ignore_case> is true (default); moreover, C<%n>, C<%y>, C<%=n>, C<%=y> will not match if the string-to-match is -adjacent to a digit). +adjacent to a digit). Double C<=> if you want to match to fail when +the corresponding conditional C<%>-escape would fail (a missing field, +or a zero-length field for required fields). The escapes C<%{UE<lt>numberE<gt>}> and escapes of the forms -C<%{ABCD}>, C<%{ABCDE<lt>numberE<gt>}> match any string; the +C<%{ABCD}> match any string; the corresponding hash key in the result hash is what is inside braces; here C<ABCD> is a 4-letter word possibly followed by 2-digit number (as in names of ID3v2 tags), or what can be put in @@ -2433,6 +2520,13 @@ option year_is_timestamp is TRUE (default), year may be a range of timestamps in the format understood by ID3v2 method year() (see L<MP3::Tag::ID3v2/"year">). +The escape C<%E> matches the REx in the configuration variable C<extension>; +the escape C<%e> matches the part of %E after the leading dot. + +In list context, also returns an array reference with %{handler} groups +parsed (if present). Such groups can match everything, and a successful match gives an +array element with C<[$method, $packages, $args, $matched]>. + Currently the regular expressions with capturing parens are not supported. =item parse_rex_prepare($pattern) @@ -2487,9 +2581,17 @@ sub __pure_track_rex ($) { $t } -sub _parse_rex_microinterpolate { # $self->idem($code, $groups, $ecount) +sub _parse_rex_microinterpolate ($$$;$) { # $self->idem($code, $groups, $have_non_trivial__not_group) my ($self, $code, $groups) = (shift, shift, shift); - return '%' if $code eq '%'; + if ($_[1]) { # handler + my ($check, $fail, $id) = ($code =~ /^(=(=)?)?(\w+)/) or die "Panic: <<$code>>"; +# die "Setting via handler not suppored, handler=<<<$id>>>" unless $check; + (push @$groups, [$id, $_[1], $_[2]]), return $self->_parse_rex_anything($code) unless $check; + return '(?!)' if $fail and not (my($o) = $self->process_handlers($id, $_[1], $_[2])); + $o = '' unless defined $o; + $_[0]++, return quotemeta $o; + } + $_[0]++, return '%' if $code eq '%'; # In these two, allow setting to '', and to 123/789 too... push(@$groups, $code), return '((?<!\d)\d{1,3}(?:/\d{1,3})?(?!\d)|\A\Z)' if $code eq 'n'; (push @$groups, $code), return '((?<!\d)[12]\d{3}(?:(?:--|[-:/T\0,])\d(?:|\d|\d\d\d))*(?!\d)|\A\Z)' @@ -2500,15 +2602,17 @@ sub _parse_rex_microinterpolate { # $self->idem($code, $groups, $ecount) (push @$groups, $code), return $self->_parse_rex_anything($code) if $code =~ /^[talgc]$/; $_[0]++, return $self->_rex_protect_filename($self->interpolate("%$1"), $1) - if $code =~ /^=([ABDfFN]|{d\d+})$/; + if $code =~ /^=([ABDfFN]|\{d\d+\})$/; $_[0]++, return quotemeta($self->interpolate("%$1")) - if $code =~ /^=([talgceEwhvLrqQSmsCpouMH]|{.*})$/; + if $code =~ /^=([talgceEwhvLrqQSmsCpouMH]|\{.*\})$/; + $_[0]++, return $self->interpolate("%{$+:1}") ? quotemeta($self->interpolate("%$1")) : '(?!)' + if $code =~ /^==(([talgcynfFeEABDNvLrqQSmsCpouMHwh])|\{(.*)\})$/; $_[0]++, return '(?<!\d)0*' . $self->__pure_track_rex . '(?!\d)' if $code eq '=n'; $_[0]++, return '(?<!\d)' . quotemeta($self->year) . '(?!\d)' if $code eq '=y'; (push @$groups, $1), return $self->_parse_rex_anything() - if $code =~ /^{(U\d+|\w{4}(\d\d+|(?:\([^\)]*\))?(?:\[.*\])?)?)}$/s; + if $code =~ /^\{(U\d+|\w{4}(\d\d+|(?:\([^\)]*\))?(?:\[.*\])?)?)\}$/s; # What remains is extension my $e = $self->get_config('extension')->[0]; (push @$groups, $code), return "($e)" if $code eq 'E'; @@ -2520,32 +2624,61 @@ sub _parse_rex_microinterpolate { # $self->idem($code, $groups, $ecount) die "unknown escape `%$code'"; } -sub parse_rex_prepare { - my ($self, $pattern) = @_; - my ($codes, $exact, $p) = ([], 0, ''); +sub _parse_rex_prepare ($$$) { + my ($self, $is_rex, $pattern) = @_; + my ($codes, $exact, $p) = ([], 0, ($is_rex ? '' : '^')); my $o = $pattern; - # (=? is correct! Group 4 is inside $frame_bra - while ($pattern =~ s<^([^%]+)|%(=?{(?:($frame_bra)|[^}]+})|=?.)><>so) { + # (=? is correct! Groups 4(descr), 5(have_bra) are inside $frame_bra + while ($pattern =~ s< ^ ( [^%]+ ) # 1: no %-escapes + | % ( ={0,2} \{ # 2: %-group (beg-of-{FRAME}, or full {non-frame}), or single-letter + (?: ($frame_bra) # 3: beg-FRAME (up to leading [, if present) + | [^}]+ \} # or full non-frame + ) + | =? . # or single letter + ) + ><>sox) { if (defined $1) { - $p .= $1; + $p .= ($is_rex ? $1 : quotemeta $1); } else { my $group = $2; - # description begins - $group .= ($self->_interpolate($pattern, ']') . ']') if $4; if ($3) { + my ($id, $langs_or_packs, $have_bra) = ($3, $4, $5); + my(@_handlers, @args) = split /,/, ($4 || ''); + my @handlers = grep $handlers{$_}, @_handlers; + $id =~ /^[A-Z]{3}[A-Z\d](\d\d)?\b/ or @handlers and @handlers == @_handlers + or die "Cannot parse frame descriptor: <<<$id>>>"; + my ($meth) = ($id =~ /^(\w+)/) or die "Panic: meth"; + + while (@handlers and $have_bra) { # process []-arguments of a handler ($group is not terminated!) + push @args, $self->_interpolate($_[1], ']', !'skip'); + $have_bra = ($_[1] =~ s/^\[//); + } # append []-arguments of a frame: + $group .= ($self->_interpolate($pattern, ']') . ']') if $have_bra; $pattern =~ s/^}// or die "Can't find end of frame name, I see `$p'"; + $p .= $self->_parse_rex_microinterpolate($group, $codes, $exact, \@handlers, \@args), next if @handlers; $group .= '}'; } $p .= $self->_parse_rex_microinterpolate($group, $codes, $exact); } } + $p .= '$' unless $is_rex; die "Can't parse pattern, I see `$pattern'" if length $pattern; #$pattern =~ s<%(=?{(?:[^\\{}]|\\[\\{}])*}|{U\d+}|=?.)> # (=? is correct! # ( $self->_parse_rex_microinterpolate($1, $codes, $exact) )seg; - my @tags = map { length == 1 ? $trans{$_} : $_ } @$codes; + my @tags = map { (not ref and length == 1) ? $trans{$_} : $_ } @$codes; return [$o, $p, \@tags, $exact]; } +sub parse_rex_prepare ($$) { + my ($self) = shift; + $self->_parse_rex_prepare('REx', @_) +} + +sub parse_prepare ($$) { + my ($self) = shift; + $self->_parse_rex_prepare(!'REx', @_) +} + sub parse_rex_match { # pattern = [Original, Interpolated, Fields, NumExact] my ($self, $pattern, $data) = @_; return unless @{$pattern->[2]} or $pattern->[3]; @@ -2553,16 +2686,19 @@ sub parse_rex_match { # pattern = [Original, Interpolated, Fields, NumExact] my $cv = @vals - 1; die "Unsupported %-regular expression `$pattern->[0]' (catching parens? Got $cv vals) (converted to `$pattern->[1]')" unless $cv == @{$pattern->[2]}; - my ($c, %h) = 0; + my ($c, %h, @a) = 0; for my $k ( @{$pattern->[2]} ) { + next unless defined (my $v = $vals[$c++]); + push(@a, [@$k, $v]), next if ref $k; $h{$k} ||= []; - push @{ $h{$k} }, $vals[$c++]; # Support multiple occurences + push @{ $h{$k} }, $v; # Support multiple occurences } my $j = $self->get_config('parse_join')->[0]; for $c (keys %h) { $h{$c} = join $j, grep length, @{ $h{$c} }; } $h{track} =~ s/^0+(?=\d)// if exists $h{track}; + return \%h, \@a if wantarray and @a; return \%h; } @@ -2603,11 +2739,11 @@ against multiple $data. #my %unquote = ('\\%' => '%', '\\%\\=' => '%='); sub __unquote ($) { (my $k = shift) =~ s/\\(\W)/$1/g; $k } -sub parse_prepare { +sub __parse_prepare { # obsolete parse_prepare my ($self, $pattern) = @_; $pattern = "^\Q$pattern\E\$"; - # unquote %. and %=. and %={WHATEVER} and %{WHATEVER} - $pattern =~ s<(\\%(?:\\=)?(\w|\\{(?:\w|\\[^\w\\{}]|\\\\\\[\\{}])*\\}|\\\W))> + # unquote %. and %=. and %={WHATEVER} and %{WHATEVER}; look for quoted \w or [^\w\\{}] or \[\\{}] + $pattern =~ s<(\\%(?:\\=){0,2}(\w|\\\{(?:\w|\\[^\w\\{}]|\\\\\\[\\{}])*\\\}|\\[^\w=\{]))> ( __unquote($1) )ge; # $pattern =~ s/(\\%(?:\\=)?)(\w|\\(\W))/$unquote{$1}$+/g; return $self->parse_rex_prepare($pattern); @@ -3543,12 +3679,12 @@ configuration variable C<local_cfg_file>. L<MP3::Tag::ID3v1>, L<MP3::Tag::ID3v2>, L<MP3::Tag::File>, L<MP3::Tag::ParseData>, L<MP3::Tag::Inf>, L<MP3::Tag::CDDB_File>, -L<MP3::Tag::Cue>, L<mp3info2>, -L<typeset_audio_dir>. +L<MP3::Tag::Cue>, L<MP3::Tag::ImageExifTool>, L<MP3::Tag::ImageSize>, +L<MP3::Tag::LastResort>, L<mp3info2>, L<typeset_audio_dir>. =head1 COPYRIGHT -Copyright (c) 2000-2008 Thomas Geffert, Ilya Zakharevich. All rights reserved. +Copyright (c) 2000-2016 Thomas Geffert, Ilya Zakharevich. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the Artistic License, distributed diff --git a/lib/MP3/Tag/CDDB_File.pm b/lib/MP3/Tag/CDDB_File.pm old mode 100755 new mode 100644 diff --git a/lib/MP3/Tag/File.pm b/lib/MP3/Tag/File.pm old mode 100755 new mode 100644 diff --git a/lib/MP3/Tag/ID3v1.pm b/lib/MP3/Tag/ID3v1.pm old mode 100755 new mode 100644 diff --git a/lib/MP3/Tag/ID3v2-Data.pod b/lib/MP3/Tag/ID3v2-Data.pod deleted file mode 100644 index bc92cff..0000000 --- a/lib/MP3/Tag/ID3v2-Data.pod +++ /dev/null @@ -1,330 +0,0 @@ - -=head1 NAME - -MP3::Tag::ID3v2-Data - get_frame() data format and supported frames - -=head1 SYNOPSIS - - $mp3 = MP3::Tag->new($filename); - $mp3->get_tags(); - $id3v2 = $mp3->{ID3v2} if exists $mp3->{id3v2}; - - ($info, $long) = $id3v2->get_frame($id); # or - - ($info, $long) = $id3v2->get_frame($id, 'raw'); - - -=head1 DESCRIPTION - -This document describes how to use the results of the get_frame function of -MP3::Tag::ID3v2, thus the data format of frames retrieved with -MP3::Tag::ID3v2::get_frame(). - -It contains also a list of all supported ID3v2-Frames. - -=over 4 - -=head2 get_frame() - - ($info, $long) = $id3v2->get_frame($id); # or - - ($info, $long) = $id3v2->get_frame($id, 'raw'); - -$id has to be a name of a frame like "APIC". For more variants of calling -see L<get_frame()|MP3::Tag::ID3v2>. - -The names of all frames found in a tag can be retrieved with the L<get_frame_ids()|MP3::Tag::ID3v2> function. - -=head2 Using the returned data - -In the ID3v2.3 specifications 72 frames are defined, which can contain very -different information. That means that get_frame returns the information -of different frames also in different ways. - -=item Simple Frames - -A lot of the tags contain only a text string and encoding information. If -you call ($info, $long) = $id3v2->get_frame($id) for such a frame, $info will contain -the text string and $long will contain the english name of the frame. - -Example: - get_frame("TIT2"); # returns - - ("Birdhouse In Your Soul", "Title/songname/content description") - -=item Complex Frames - -For more complex frames the returned $info is a reference to a hash, where -each entry of the hash decribes a part of the information found in the -frame. The key of a hash entry contains the name of this part, the according -value contains the information itself. - -Example: - get_frame("APIC"); # returns - - ( { "Description" => "Flood", - "MIME Type" => "/image/jpeg", - "Picture Type" => "Cover (front)", - "_Data" => "..data of jpeg picture (binary).." - }, - "Attached Picture"); - -=item Other Frames - -Some frames are not supported at the moment, ie the data found in the frame -is not returned in a descriptive way. But you can read the data of this -frames (and also of all other frames too) in raw mode. Then the complete -data field of the frame is returned, without any modifications. This means -that the returned data will be almost binary data. - -Example: - get_frame("TIT2", 'raw'); # returns - - ("\x00Birdhouse In Your Soul", "Title/songname/content description") - -=back - -The frames which (in addition to C<Text>/C<URL>) contain only -C<Description> and C<Language> fields are in some intermediate position -between "simple" and "complex" frames. They can be handled very similarly -to "simple" frames by using "long names", such as C<COMM[description]> -or C<COMM(LANG)[description]>, and the corresponding "quick" API such -as frame_select(). - - - -=head2 List of Simple Frames - -Following Frames are supported -and return a single string (text). In the List you can find the frame IDs -and the long names of the frames as returned by $id3v2->get_frame(): - -=over 4 - - -=item IPLS : Involved people list - -=item MCDI : Music CD identifier - -=item PCNT : Play counter - -=item TALB : Album/Movie/Show title - -=item TBPM : BPM (beats per minute) - -=item TCOM : Composer - -=item TCON : Content type - -=item TCOP : Copyright message - -=item TDAT : Date - -=item TDLY : Playlist delay - -=item TENC : Encoded by - -=item TEXT : Lyricist/Text writer - -=item TFLT : File type - -=item TIME : Time - -=item TIPL : Involved people list - -=item TIT1 : Content group description - -=item TIT2 : Title/songname/content description - -=item TIT3 : Subtitle/Description refinement - -=item TKEY : Initial key - -=item TLAN : Language(s) - -=item TLEN : Length - -=item TMCL : Musician credits list - -=item TMED : Media type - -=item TOAL : Original album/movie/show title - -=item TOFN : Original filename - -=item TOLY : Original lyricist(s)/text writer(s) - -=item TOPE : Original artist(s)/performer(s) - -=item TORY : Original release year - -=item TOWN : File owner/licensee - -=item TPE1 : Lead performer(s)/Soloist(s) - -=item TPE2 : Band/orchestra/accompaniment - -=item TPE3 : Conductor/performer refinement - -=item TPE4 : Interpreted, remixed, or otherwise modified by - -=item TPOS : Part of a set - -=item TPUB : Publisher - -=item TRCK : Track number/Position in set - -=item TRDA : Recording dates - -=item TRSN : Internet radio station name - -=item TRSO : Internet radio station owner - -=item TSIZ : Size - -=item TSRC : ISRC (international standard recording code) - -=item TSSE : Software/Hardware and settings used for encoding - -=item TYER : Year - -=item WCOM : Commercial information - -=item WCOP : Copyright/Legal information - -=item WOAF : Official audio file webpage - -=item WOAR : Official artist/performer webpage - -=item WOAS : Official audio source webpage - -=item WORS : Official internet radio station homepage - -=item WPAY : Payment - -=item WPUB : Publishers official webpage - -=back - - - -=head2 List of Complex Frames - -Following frames are supported and return a reference to a hash. The -list shows which keys can be found in the returned hash: - -=over 4 - - -=item AENC : Audio encryption - - Keys: URL, Preview start, Preview length - -=item APIC : Attached picture - - Keys: MIME type, Picture Type, Description, _Data - -=item COMM : Comments - - Keys: Language, Description, Text - -=item COMR : Commercial frame - - Keys: Price, Valid until, URL, Received as, Name of Seller, Description, MIME type, _Logo - -=item ENCR : Encryption method registration - - Keys: Owner ID, Method symbol, _Data - -=item GEOB : General encapsulated object - - Keys: MIME type, Filename, Description, _Data - -=item GRID : Group identification registration - - Keys: Owner, Symbol, _Data - -=item LINK : Linked information - - Keys: ID, URL, Text - -=item OWNE : Ownership frame - - Keys: Price payed, Date of purchase, Text - -=item POPM : Popularimeter - - Keys: URL, Rating, Counter - -=item PRIV : Private frame - - Keys: Text, _Data - -=item RBUF : Recommended buffer size - - Keys: Buffer size, Embedded info flag, Offset to next tag - -=item RVRB : Reverb - - Keys: Reverb left (ms), Reverb right (ms), Reverb bounces (left), Reverb bounces (right), Reverb feedback (left to left), Reverb feedback (left to right), Reverb feedback (right to right), Reverb feedback (right to left), Premix left to right, Premix right to left - -=item SYTC : Synchronized tempo codes - - Keys: Time Stamp Format, _Data - -=item TXXX : User defined text information frame - - Keys: Description, Text - -=item UFID : Unique file identifier - - Keys: Text, _Data - -=item USER : Terms of use - - Keys: Language, Text - -=item USLT : Unsychronized lyric/text transcription - - Keys: Language, Description, Text - -=item WXXX : User defined URL link frame - - Keys: Description, URL - -=back - - - -=head2 List of Other Frames - -Following frames are only supported in raw mode: - -=over 4 - - -=item CRM : Encrypted meta frame - -=item EQUA : Equalization - -=item ETCO : Event timing codes - -=item LNK : Linked information - -=item MLLT : MPEG location lookup table - -=item PIC : Attached picture - -=item POSS : Position synchronisation frame - -=item RVAD : Relative volume adjustment - -=item SYLT : Synchronized lyric/text - -=back - - -=head1 SEE ALSO - -L<MP3::Tag>, L<MP3::Tag::ID3v2> - diff --git a/lib/MP3/Tag/ID3v2.pm b/lib/MP3/Tag/ID3v2.pm old mode 100755 new mode 100644 index d438576..685825b --- a/lib/MP3/Tag/ID3v2.pm +++ b/lib/MP3/Tag/ID3v2.pm @@ -8,7 +8,6 @@ package MP3::Tag::ID3v2; use strict; use File::Basename; -use File::Temp; # use Compress::Zlib; use vars qw /%format %long_names %res_inp @supported_majors %v2names_to_v3 @@ -16,7 +15,7 @@ use vars qw /%format %long_names %res_inp @supported_majors %v2names_to_v3 %back_splt %embedded_Descr /; -$VERSION = "1.12_TS"; # custom version of v1.12 w/ thread safe generation of temp files +$VERSION = "1.14"; @ISA = 'MP3::Tag::__hasparent'; my $trustencoding = $ENV{MP3TAG_DECODE_UNICODE}; @@ -627,26 +626,22 @@ sub build_tag { sub insert_space { my ($self, $insert) = @_; my $mp3obj = $self->{mp3}; - my $target = $mp3obj->{filename}; # !! use a specific tmp-dir here - my $tempfh = new File::Temp( - UNLINK => 1, - DIR => dirname($mp3obj->{filename}), - SUFFIX => '.tmp' - ); - my $tempfile = $tempfh->filename; - - # Change file permissions to default value. - # Permissions will otherwise (at least on *nix) - # be 0600 regardless of umask, due the File::Temp module. - chmod umask ^ 0666, $tempfile; - - if ($@) { + my $tempfile = dirname($mp3obj->{filename}) . "/TMPxx"; + my $count = 0; + while (-e $tempfile . $count . ".tmp") { + if ($count++ > 999) { + warn "Problems with tempfile\n"; + return undef; + } + } + $tempfile .= $count . ".tmp"; + unless (open (NEW, ">$tempfile")) { warn "Can't open '$tempfile' to insert tag\n"; return -1; } my ($buf, $pos_old); - binmode $tempfh; + binmode NEW; $pos_old=0; $mp3obj->seek(0,0); local $\ = ''; @@ -655,12 +650,12 @@ sub insert_space { if ($pos_old < $ins->[0]) { $pos_old += $ins->[0]; while ($mp3obj->read(\$buf,$ins->[0]<16384?$ins->[0]:16384)) { - print $tempfh $buf; + print NEW $buf; $ins->[0] = $ins->[0]<16384?0:$ins->[0]-16384; } } for (my $i = 0; $i<$ins->[2]; $i++) { - print $tempfh chr(0); + print NEW chr(0); } if ($ins->[1]) { $pos_old += $ins->[1]; @@ -669,23 +664,17 @@ sub insert_space { } while ($mp3obj->read(\$buf,16384)) { - print $tempfh $buf; + print NEW $buf; } - $tempfh->unlink_on_destroy(0); - $tempfh->close; - + close NEW; $mp3obj->close; # rename tmp-file to orig file - unless ( rename($tempfile,$target) ) { - my $delay_duration = 1; - select(undef, undef, undef, $delay_duration); - print("-------Sleeping for $delay_duration s\n"); # Hack to avoid some problems on MSWin systems - unless ( rename($tempfile, $target) ){ # second try at renaming - unlink($tempfile); - warn "******* Couldn't rename temporary file $tempfile to $target\n"; - return -1; - } + unless (( rename $tempfile, $mp3obj->{filename})|| + (system("mv",$tempfile,$mp3obj->{filename})==0)) { + unlink($tempfile); + warn "Couldn't rename temporary file $tempfile to $mp3obj->{filename}\n"; + return -1; } return 0; } @@ -905,26 +894,31 @@ directly, which will override an old tag. sub remove_tag { my $self = shift; my $mp3obj = $self->{mp3}; - - my ($tempfh, $tempfile) = eval { - tempfile("TMPXXXX", SUFFIX => ".tmp", DIR => dirname($mp3obj->{filename}), UNLINK => 0) - }; - if ($@) { - warn "Couldn't write temp file\n"; - return undef; - } + my $tempfile = dirname($mp3obj->{filename}) . "/TMPxx"; + my $count = 0; local $\ = ''; - my $buf; - binmode $tempfh; - $mp3obj->seek($self->{tagsize}+10,0); - while ($mp3obj->read(\$buf,16384)) { - print $tempfh $buf; + while (-e $tempfile . $count . ".tmp") { + if ($count++ > 999) { + warn "Problems with tempfile\n"; + return undef; + } } - close $tempfh; + $tempfile .= $count . ".tmp"; + if (open (NEW, ">$tempfile")) { + my $buf; + binmode NEW; + $mp3obj->seek($self->{tagsize}+10,0); + while ($mp3obj->read(\$buf,16384)) { + print NEW $buf; + } + close NEW; $mp3obj->close; unless (( rename $tempfile, $mp3obj->{filename})|| (system("mv",$tempfile,$mp3obj->{filename})==0)) { warn "Couldn't rename temporary file $tempfile\n"; + } + } else { + warn "Couldn't write temp file\n"; return undef; } return 1; @@ -1404,6 +1398,16 @@ sub title { return join '', @parts, $last; } +sub have_one_of_frames { + my $self = shift; + return grep $self->frame_have($_), @_; +} + +sub title_have { + my $self = shift; + $self->have_one_of_frames($self->v2title_order) +} + =item _comment([$language]) Returns the file comment (COMM with an empty 'Description') from the tag, or @@ -1412,8 +1416,8 @@ of the title). =cut -sub _comment { - my $self = shift; +sub __comment { + my($self, $check_have) = (shift, shift); my $language; $language = lc shift if @_; my @info = get_frames($self, "COMM"); @@ -1423,10 +1427,20 @@ sub _comment { next unless exists $comment->{Description} and not length $comment->{Description}; next if defined $language and (not exists $comment->{Language} or lc $comment->{Language} ne $language); - return $comment->{Text}; + return $check_have ? 1 : $comment->{Text} ; } return if grep $_ eq 'TIT3', $self->v2title_order; - return scalar $self->get_frame("TIT3"); + return $check_have ? $self->frame_have("TIT3") : scalar $self->get_frame("TIT3"); +} + +sub _comment { + my $self = shift; + $self->__comment(!'only_check', @_); +} + +sub comment_have { + my $self = shift; + $self->__comment('only_check', @_); } =item comment() @@ -1863,6 +1877,11 @@ sub year { return $y; } +sub year_have { + my $self = shift; + $self->have_one_of_frames(qw( TDRC TYER )) +} + =pod =item track( [$new_track] ) @@ -1884,6 +1903,11 @@ sub track { return scalar $self->get_frame("TRCK"); } +sub track_have { + my $self = shift; + $self->frame_have('TRCK') +} + =pod =item artist( [ $new_artist ] ) @@ -1917,6 +1941,11 @@ sub artist { return; } +sub artist_have { + my $self = shift; + $self->have_one_of_frames(qw( TPE1 TPE2 TCOM TPE3 TEXT )) +} + =pod =item album( [ $new_album ] ) @@ -1943,6 +1972,13 @@ sub album { return scalar $self->get_frame("TIT1"); } +sub album_have { + my $self = shift; + return 1 if $self->frame_have('TALB'); + return if grep $_ eq 'TIT1', $self->v2title_order; + return $self->frame_have('TIT1'); +} + =item genre( [ $new_genre ] ) Returns the genre string from TCON frame of the tag. @@ -1965,6 +2001,11 @@ sub genre { $g; } +sub genre_have { + my $self = shift; + $self->frame_have('TCON') +} + =item version() $version = $id3v2->version(); diff --git a/lib/MP3/Tag/ID3v2_Data.pod b/lib/MP3/Tag/ID3v2_Data.pod deleted file mode 100755 index bf0d30b..0000000 --- a/lib/MP3/Tag/ID3v2_Data.pod +++ /dev/null @@ -1,332 +0,0 @@ - -=head1 NAME - -MP3::Tag::ID3v2_Data - get_frame() data format and supported frames - -=head1 SYNOPSIS - - $mp3 = MP3::Tag->new($filename); - $mp3->get_tags(); - $id3v2 = $mp3->{ID3v2} if exists $mp3->{id3v2}; - - ($info, $long) = $id3v2->get_frame($id); # or - - ($info, $long) = $id3v2->get_frame($id, 'raw'); - - -=head1 DESCRIPTION - -This document describes how to use the results of the get_frame function of -MP3::Tag::ID3v2, thus the data format of frames retrieved with -MP3::Tag::ID3v2::get_frame(). - -It contains also a list of all supported ID3v2-Frames. - -=head2 get_frame() - - ($info, $long) = $id3v2->get_frame($id); # or - - ($info, $long) = $id3v2->get_frame($id, 'raw'); - -$id has to be a name of a frame like "APIC". For more variants of calling -see L<get_frame()|MP3::Tag::ID3v2>. - -The names of all frames found in a tag can be retrieved with the L<get_frame_ids()|MP3::Tag::ID3v2> function. - -=head2 Using the returned data - -In the ID3v2.3 specifications 73 frames are defined, which can contain very -different information. That means that get_frame returns the information -of different frames also in different ways. - -=over 4 - -=item Simple Frames - -A lot of the tags contain only a text string and encoding information. If -you call ($info, $long) = $id3v2->get_frame($id) for such a frame, $info will contain -the text string and $long will contain the english name of the frame. - -Example: - get_frame("TIT2"); # returns - - ("Birdhouse In Your Soul", "Title/songname/content description") - -=item Complex Frames - -For more complex frames the returned $info is a reference to a hash, where -each entry of the hash decribes a part of the information found in the -frame. The key of a hash entry contains the name of this part, the according -value contains the information itself. - -Example: - get_frame("APIC"); # returns - - ( { "Description" => "Flood", - "MIME Type" => "/image/jpeg", - "Picture Type" => "Cover (front)", - "_Data" => "..data of jpeg picture (binary).." - }, - "Attached Picture"); - -=item Other Frames - -Some frames are not supported at the moment, ie the data found in the frame -is not returned in a descriptive way. But you can read the data of this -frames (and also of all other frames too) in raw mode. Then the complete -data field of the frame is returned, without any modifications. This means -that the returned data will be almost binary data. - -Example: - get_frame("TIT2", 'raw'); # returns - - ("\x00Birdhouse In Your Soul", "Title/songname/content description") - -=back - -The frames which (in addition to C<Text>/C<URL>) contain only -C<Description> and C<Language> fields are in some intermediate position -between "simple" and "complex" frames. They can be handled very similarly -to "simple" frames by using "long names", such as C<COMM[description]> -or C<COMM(LANG)[description]>, and the corresponding "quick" API such -as frame_select(). - - - -=head2 List of Simple Frames - -Following Frames are supported -and return a single string (text). In the List you can find the frame IDs -and the long names of the frames as returned by $id3v2->get_frame(): - -=over 4 - - -=item IPLS : Involved people list - -=item MCDI : Music CD identifier - -=item PCNT : Play counter - -=item TALB : Album/Movie/Show title - -=item TBPM : BPM (beats per minute) - -=item TCOM : Composer - -=item TCON : Content type - -=item TCOP : Copyright message - -=item TDAT : Date - -=item TDLY : Playlist delay - -=item TDRC : Recording time - -=item TENC : Encoded by - -=item TEXT : Lyricist/Text writer - -=item TFLT : File type - -=item TIME : Time - -=item TIPL : Involved people list - -=item TIT1 : Content group description - -=item TIT2 : Title/songname/content description - -=item TIT3 : Subtitle/Description refinement - -=item TKEY : Initial key - -=item TLAN : Language(s) - -=item TLEN : Length - -=item TMCL : Musician credits list - -=item TMED : Media type - -=item TOAL : Original album/movie/show title - -=item TOFN : Original filename - -=item TOLY : Original lyricist(s)/text writer(s) - -=item TOPE : Original artist(s)/performer(s) - -=item TORY : Original release year - -=item TOWN : File owner/licensee - -=item TPE1 : Lead performer(s)/Soloist(s) - -=item TPE2 : Band/orchestra/accompaniment - -=item TPE3 : Conductor/performer refinement - -=item TPE4 : Interpreted, remixed, or otherwise modified by - -=item TPOS : Part of a set - -=item TPUB : Publisher - -=item TRCK : Track number/Position in set - -=item TRDA : Recording dates - -=item TRSN : Internet radio station name - -=item TRSO : Internet radio station owner - -=item TSIZ : Size - -=item TSRC : ISRC (international standard recording code) - -=item TSSE : Software/Hardware and settings used for encoding - -=item TYER : Year - -=item WCOM : Commercial information - -=item WCOP : Copyright/Legal information - -=item WOAF : Official audio file webpage - -=item WOAR : Official artist/performer webpage - -=item WOAS : Official audio source webpage - -=item WORS : Official internet radio station homepage - -=item WPAY : Payment - -=item WPUB : Publishers official webpage - -=back - - - -=head2 List of Complex Frames - -Following frames are supported and return a reference to a hash. The -list shows which keys can be found in the returned hash: - -=over 4 - - -=item AENC : Audio encryption - - Keys: URL, Preview start, Preview length, _Data - -=item APIC : Attached picture - - Keys: MIME type, Picture Type, Description, _Data - -=item COMM : Comments - - Keys: Language, Description, Text - -=item COMR : Commercial frame - - Keys: Price, Valid until, URL, Received as, Name of Seller, Description, MIME type, _Logo - -=item ENCR : Encryption method registration - - Keys: Owner ID, Method symbol, _Data - -=item GEOB : General encapsulated object - - Keys: MIME type, Filename, Description, _Data - -=item GRID : Group identification registration - - Keys: Owner, Symbol, _Data - -=item LINK : Linked information - - Keys: ID, URL, Text - -=item OWNE : Ownership frame - - Keys: Price payed, Date of purchase, Text - -=item POPM : Popularimeter - - Keys: URL, Rating, Counter - -=item PRIV : Private frame - - Keys: Text, _Data - -=item RBUF : Recommended buffer size - - Keys: Buffer size, Embedded info flag, Offset to next tag - -=item RVRB : Reverb - - Keys: Reverb left (ms), Reverb right (ms), Reverb bounces (left), Reverb bounces (right), Reverb feedback (left to left), Reverb feedback (left to right), Reverb feedback (right to right), Reverb feedback (right to left), Premix left to right, Premix right to left - -=item SYTC : Synchronized tempo codes - - Keys: Time Stamp Format, _Data - -=item TXXX : User defined text information frame - - Keys: Description, Text - -=item UFID : Unique file identifier - - Keys: Text, _Data - -=item USER : Terms of use - - Keys: Language, Text - -=item USLT : Unsychronized lyric/text transcription - - Keys: Language, Description, Text - -=item WXXX : User defined URL link frame - - Keys: Description, URL - -=back - - - -=head2 List of Other Frames - -Following frames are only supported in raw mode: - -=over 4 - - -=item CRM : Encrypted meta frame - -=item EQUA : Equalization - -=item ETCO : Event timing codes - -=item LNK : Linked information - -=item MLLT : MPEG location lookup table - -=item PIC : Attached picture - -=item POSS : Position synchronisation frame - -=item RVAD : Relative volume adjustment - -=item SYLT : Synchronized lyric/text - -=back - - -=head1 SEE ALSO - -L<MP3::Tag>, L<MP3::Tag::ID3v2> - diff --git a/lib/MP3/Tag/ImageExifTool.pm b/lib/MP3/Tag/ImageExifTool.pm index 909ac50..71d64a1 100644 --- a/lib/MP3/Tag/ImageExifTool.pm +++ b/lib/MP3/Tag/ImageExifTool.pm @@ -5,14 +5,14 @@ use File::Basename; #use File::Spec; use vars qw /$VERSION @ISA/; -$VERSION="0.01"; +$VERSION="1.14"; @ISA = 'MP3::Tag::__hasparent'; =pod =head1 NAME -MP3::Tag::ImageExifTool - extract size info from image files via L<Image::Size|Image::Size>. +MP3::Tag::ImageExifTool - extract size info from image files via L<Image::ExifTool|Image::ExifTool>. =head1 SYNOPSIS @@ -24,9 +24,12 @@ see L<MP3::Tag> MP3::Tag::ImageExifTool is designed to be called from the MP3::Tag module. -It implements width(), height() and mime_type() methods (sizes in pixels). +It implements the (standard) methods qw(title track artist album year genre comment), +as well as width(), height(), bit_depth(), _duration() and mime_type() methods (sizes in pixels). -They return C<undef> if C<Image::Size> is not available, or does not return valid data. +Use method C<field('FieldName')> to access a particular field provided by C<Image::ExifTool>. + +These methods return C<undef> if C<Image::ExifTool> is not available, or does not return valid data. =cut diff --git a/lib/MP3/Tag/Inf.pm b/lib/MP3/Tag/Inf.pm old mode 100755 new mode 100644 diff --git a/lib/MP3/Tag/LastResort.pm b/lib/MP3/Tag/LastResort.pm old mode 100755 new mode 100644 diff --git a/lib/MP3/Tag/ParseData.pm b/lib/MP3/Tag/ParseData.pm old mode 100755 new mode 100644 diff --git a/lib/Number/Compare.pm b/lib/Number/Compare.pm old mode 100755 new mode 100644