1
0
mirror of https://github.com/moex3/flac2mp3.pl synced 2024-11-22 03:54:15 -05:00

Switch over to using mid3v2 for all tags

and also use multiple tags
This commit is contained in:
moex3 2022-12-08 19:13:44 +01:00
parent 7f36a00b66
commit e3dccbcf78
No known key found for this signature in database
GPG Key ID: ABC92E00CF59BB7A

View File

@ -23,6 +23,16 @@ my %genreMap = (
# https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html # https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html
# a lot of this may not work # a lot of this may not work
# TODO escape potential 's # TODO escape potential 's
#
# Format is:
# Vorbis tag string => Mp3 tag value
# where mp3 tag value may be:
# undef -> Skip this tag
# A string -> Use this as the mp3 tag, and use the vorbis tag value as value
# code -> Execute this function. This should return an array, where [0] is the tag, [1] is the value.
# An array (str, str) -> [0] is the mp3 tag to use, [1] is the value prefix
# An array (str, code) -> [0] is the mp3 tag to use, [1] is a function that is executed, and the result is the tag value
# The code-s here will be called with the flac tags hashmap
my %idLookup = ( my %idLookup = (
album => 'TALB', album => 'TALB',
albumsort => 'TSOA', albumsort => 'TSOA',
@ -36,15 +46,15 @@ my %idLookup = (
albumartistsort => 'TSO2', # Maybe? albumartistsort => 'TSO2', # Maybe?
artist => 'TPE1', artist => 'TPE1',
artistsort => 'TSOP', artistsort => 'TSOP',
arranger => 'TIPL=arranger', arranger => ['TIPL', 'arranger:'],
author => 'TEXT', author => 'TEXT',
composer => 'TCOM', composer => 'TCOM',
conductor => 'TPE3', conductor => 'TPE3',
engineer => 'TIPL=engineer', engineer => ['TIPL', 'engineer:'],
djmixer => 'TIPL=DJ-mix', djmixer => ['TIPL', 'DJ-mix:'],
mixer => 'TIPL=mix', mixer => ['TIPL', 'mix:'],
#performer => 'TMCL', # This produces some really weird tags #performer => 'TMCL', # This produces some really weird tags
producer => 'TIPL=producer', producer => ['TIPL', 'producer:'],
publisher => 'TPUB', publisher => 'TPUB',
organization => 'TPUB', organization => 'TPUB',
label => 'TPUB', label => 'TPUB',
@ -52,42 +62,42 @@ my %idLookup = (
discnumber => ['TPOS', sub { discnumber => ['TPOS', sub {
my $t = shift; my $t = shift;
my $totalkey = exists($t->{disctotal}) ? 'disctotal' : 'totaldiscs'; my $totalkey = exists($t->{disctotal}) ? 'disctotal' : 'totaldiscs';
return "$t->{discnumber}" if !exists($t->{$totalkey}); return "$t->{discnumber}[0]" if !exists($t->{$totalkey});
return "$t->{discnumber}/$t->{$totalkey}"; return "$t->{discnumber}[0]/$t->{$totalkey}[0]";
}], }],
totaldiscs => undef, totaldiscs => undef,
disctotal => undef, disctotal => undef,
tracknumber => ['TRCK', sub { tracknumber => ['TRCK', sub {
my $t = shift; my $t = shift;
my $totalkey = exists($t->{tracktotal}) ? 'tracktotal' : 'totaltracks'; my $totalkey = exists($t->{tracktotal}) ? 'tracktotal' : 'totaltracks';
return "$t->{tracknumber}" if !exists($t->{$totalkey}); return "$t->{tracknumber}[0]" if !exists($t->{$totalkey});
return "$t->{tracknumber}/$t->{$totalkey}"; return "$t->{tracknumber}[0]/$t->{$totalkey}[0]";
}], }],
totaltracks => undef, totaltracks => undef,
tracktotal => undef, tracktotal => undef,
#date => 'TDRC', # This is for id3v2.4 #date => 'TDRC', # This is for id3v2.4
#date => 'TYER', #date => 'TYER',
date => [undef, sub { date => sub {
my $t = shift; my $t = shift;
my $date = $t->{date}; my $date = $t->{date}[0];
if (length($date) == 4) { # Only year if (length($date) == 4) { # Only year
return "TYER=$date"; return ["TYER", "$date"];
} }
if (!($date =~ m/^\d{4}\.\d{2}\.\d{2}$/)) { if (!($date =~ m/^\d{4}\.\d{2}\.\d{2}$/)) {
print("Date format unknown: $date\n"); print("Date format unknown: $date\n");
exit 1; exit 1;
} }
$date =~ s/\./-/g; $date =~ s/\./-/g;
return "TDRL=$date"; # Release date return ["TDRL", "$date"]; # Release date
}], },
originaldate => 'TDOR', # Also for 2.4 only originaldate => 'TDOR', # Also for 2.4 only
'release date' => 'TDOR', # Also for 2.4 only 'release date' => 'TDOR', # Also for 2.4 only
isrc => 'TSRC', isrc => 'TSRC',
barcode => 'TXXX=BARCODE', barcode => ['TXXX', 'BARCODE:'],
catalog => ['TXXX=CATALOGNUMBER', sub { return tagmap_catalogid(shift, 'catalog'); } ], catalog => ['TXXX', sub { return "CATALOGNUMBER:" . tagmap_catalogid(shift, 'catalog'); } ],
catalognumber => ['TXXX=CATALOGNUMBER', sub { return tagmap_catalogid(shift, 'catalognumber'); } ], catalognumber => ['TXXX', sub { return "CATALOGNUMBER:" . tagmap_catalogid(shift, 'catalognumber'); } ],
catalogid => ['TXXX=CATALOGNUMBER', sub { return tagmap_catalogid(shift, 'catalogid'); } ], catalogid => ['TXXX', sub { return "CATALOGNUMBER:" . tagmap_catalogid(shift, 'catalogid'); } ],
labelno => ['TXXX=CATALOGNUMBER', sub { return tagmap_catalogid(shift, 'labelno'); } ], labelno => ['TXXX', sub { return "CATALOGNUMBER:" . tagmap_catalogid(shift, 'labelno'); } ],
'encoded-by' => 'TENC', 'encoded-by' => 'TENC',
encoder => 'TSSE', encoder => 'TSSE',
encoding => 'TSSE', encoding => 'TSSE',
@ -96,7 +106,7 @@ my %idLookup = (
genre => ['TCON', sub { genre => ['TCON', sub {
return undef if ($opt_no_genre); return undef if ($opt_no_genre);
my $genreName = shift->{genre}; my $genreName = shift->{genre}[0];
if (!exists($genreMap{lc($genreName)})) { if (!exists($genreMap{lc($genreName)})) {
# If no genre number exists, use the name # If no genre number exists, use the name
return $genreName; return $genreName;
@ -106,38 +116,41 @@ my %idLookup = (
#mood => ['TMOO', sub { #mood => ['TMOO', sub {
#}], #}],
bpm => 'TBPM', bpm => 'TBPM',
comment => ['COMM=Comment', sub { comment => ['COMM', sub {
return undef if (defined($opt_comment) && $opt_comment eq ""); return undef if (defined($opt_comment) && $opt_comment eq "");
return shift->{comment}; return "Comment:" . shift->{comment}[0];
}], }],
copyright => 'TCOP', copyright => 'TCOP',
language => 'TLAN', language => 'TLAN',
#replaygain_album_peak => 'TXXX=REPLAYGAIN_ALBUM_PEAK', #replaygain_album_peak => 'TXXX=REPLAYGAIN_ALBUM_PEAK',
#replaygain_album_gain => 'TXXX=REPLAYGAIN_ALBUM_GAIN', #replaygain_album_gain => 'TXXX=REPLAYGAIN_ALBUM_GAIN',
replaygain_track_gain => sub { replaygain_track_gain => sub {
print("EEEEEEERRRRRRRRROOOOOOOOOORRRRRRRRRRRE FIXXXXXXXXXXXX THIIIIIIIIISSSSSSSS\n");
exit(1);
return undef if (!$opt_rg); return undef if (!$opt_rg);
shift->{replaygain_track_gain} =~ /^(-?\d+\.\d+) dB$/; shift->{replaygain_track_gain}[0] =~ /^(-?\d+\.\d+) dB$/;
my $gain_db = $1; my $gain_db = $1;
exit(1) if ($gain_db eq ""); exit(1) if ($gain_db eq "");
return "--replaygain-accurate --gain $gain_db"; return "--replaygain-accurate --gain $gain_db";
# TODO this lulw
}, },
#replaygain_album_gain => 'TXXX=REPLAYGAIN_ALBUM_GAIN', #replaygain_album_gain => 'TXXX=REPLAYGAIN_ALBUM_GAIN',
#replaygain_album_peak => 'TXXX=REPLAYGAIN_ALBUM_PEAK', #replaygain_album_peak => 'TXXX=REPLAYGAIN_ALBUM_PEAK',
#replaygain_track_gain => 'TXXX=REPLAYGAIN_TRACK_GAIN', #replaygain_track_gain => 'TXXX=REPLAYGAIN_TRACK_GAIN',
#replaygain_track_peak => 'TXXX=REPLAYGAIN_TRACK_PEAK', #replaygain_track_peak => 'TXXX=REPLAYGAIN_TRACK_PEAK',
script => 'TXXX=SCRIPT', script => ['TXXX', 'SCRIPT:'],
lyrics => 'USLT', lyrics => 'USLT',
circle => 'TXXX=CIRCLE', circle => ['TXXX', 'CIRCLE:'],
event => 'TXXX=EVENT', event => ['TXXX', 'EVENT:'],
discid => 'TXXX=DISCID', discid => ['TXXX', 'DISCID:'],
originaltitle => 'TXXX=ORIGINALTITLE', originaltitle => ['TXXX', 'ORIGINALTITLE:'],
); );
sub tagmap_catalogid { sub tagmap_catalogid {
my $t = shift; my $t = shift;
my $own_tag_name = shift; my $own_tag_name = shift;
return undef if (defined($opt_catid) && $opt_catid eq ""); return undef if (defined($opt_catid) && $opt_catid eq "");
return $t->{$own_tag_name}; return $t->{$own_tag_name}[0];
} }
my $opt_genre; my $opt_genre;
@ -206,9 +219,9 @@ sub iterFlac {
shellsan(\$dest); shellsan(\$dest);
my $cmd; my $cmd;
if ($opt_cbr) { if ($opt_cbr) {
$cmd = "flac -cd -- '$flac' | lame -S -b 320 -q 0 --add-id3v2 @$tagopts - '$dest'"; $cmd = "flac -cd -- '$flac' | lame -S -b 320 -q 0 --add-id3v2 - '$dest'";
} else { } else {
$cmd = "flac -cd -- '$flac' | lame -S -V0 --vbr-new -q 0 --add-id3v2 @$tagopts - '$dest'"; $cmd = "flac -cd -- '$flac' | lame -S -V0 --vbr-new -q 0 --add-id3v2 - '$dest'";
} }
#print("Debug - CMD: [$cmd]\n"); #print("Debug - CMD: [$cmd]\n");
qx($cmd); qx($cmd);
@ -216,6 +229,22 @@ sub iterFlac {
exit(1); exit(1);
} }
my $mid3v2TagLine = "";
#print(Dumper(\@$tagopts));
# Add tags with mid3v2 instead of lame to better support multiple tag values
foreach my $tagItem (@$tagopts) {
$mid3v2TagLine = $mid3v2TagLine . $tagItem . " ";
}
#print(Dumper(\$mid3v2TagLine));
my $mid3v2TagCmd = "mid3v2 $mid3v2TagLine -- '$dest'";
#print("Mid3V2 Debug - CMD: [$mid3v2TagCmd]\n");
qx($mid3v2TagCmd);
if ($? != 0) {
print("ERROR: At mid3v2 tag set\n");
exit(1);
}
embedImageFromFlac($flac, $dest); embedImageFromFlac($flac, $dest);
} }
@ -270,52 +299,55 @@ sub tagsToOpts {
my $tags = shift; my $tags = shift;
my @tagopts; my @tagopts;
# TODO escape ' and =? # TODO escape stuff?
foreach my $currKey (keys (%$tags)) { foreach my $currKey (keys (%$tags)) {
if (!exists($idLookup{$currKey})) { if (!exists($idLookup{$currKey})) {
print("Tag: '$currKey' doesn't have a mapping, skipping\n"); print("Tag: '$currKey' doesn't have a mapping, skipping\n");
next; next;
} }
my $tagName = $idLookup{$currKey}; my $tagMapping = $idLookup{$currKey};
my $type = ref($tagName); my $type = ref($tagMapping);
if ($type eq "" && defined($tagName)) { if ($type eq "" && defined($tagMapping)) {
# If tag name is defined and tag contents exists # If tag name is defined and tag contents exists (aka not silenced)
my $tagCont = $tags->{$currKey}; foreach my $tagCont (@{$tags->{$currKey}}) {
shellsan(\$tagCont); shellsan(\$tagCont);
push(@tagopts, qq(--tv '$tagName=$tagCont')); push(@tagopts, qq('--$tagMapping' '$tagCont'));
}
} elsif ($type eq "ARRAY") { } elsif ($type eq "ARRAY") {
my $tagCont = $tagName->[1]->($tags); my $mapKey = $tagMapping->[0];
my $tagKey = $tagName->[0]; my $mapCont = $tagMapping->[1];
if (defined($tagCont)) { my $mapContType = ref($mapCont);
if (defined($tagKey)) { if (not defined($mapCont)) {
shellsan(\$tagCont); print("WHUT???\n");
push(@tagopts, qq(--tv '$tagName->[0]=$tagCont')); exit(1);
} else {
if (ref($tagCont) eq 'ARRAY') {
# If we have an array of tags
foreach my $tC (@$tagCont) {
shellsan(\$tC);
push(@tagopts, qq(--tv '$tC'));
}
} else {
# If we have only one
shellsan(\$tagCont);
push(@tagopts, qq(--tv '$tagCont'));
} }
if ($mapContType eq "") {
foreach my $tagValue (@{$tags->{$currKey}}) {
shellsan(\$tagValue);
push(@tagopts, qq('--$mapKey' '$mapCont$tagValue'));
} }
} elsif ($mapContType eq "CODE") {
my $tagValue = $mapCont->($tags);
shellsan(\$tagValue);
push(@tagopts, qq('--$mapKey' '$tagValue'));
} }
} elsif ($type eq 'CODE') { } elsif ($type eq 'CODE') {
# If we have just a code reference # If we have just a code reference
# do not assume, that this is a tag, rather a general cmd opt # do not assume, that this is a tag, rather a general cmd opt
my $opt = $tagName->($tags); #my $opt = $tagName->($tags);
if (defined($opt)) { #if (defined($opt)) {
shellsan(\$opt); #shellsan(\$opt);
push(@tagopts, qq($opt)); #push(@tagopts, qq($opt));
} #}
}
my $codeRet = $tagMapping->($tags);
my $mapKey = $codeRet->[0];
my $mapCont = $codeRet->[1];
shellsan(\$mapCont);
push(@tagopts, qq('--$mapKey' '$mapCont'));
}
} }
return \@tagopts; return \@tagopts;
} }
@ -333,7 +365,11 @@ sub getFlacTags {
print("Empty tag: $1\n"); print("Empty tag: $1\n");
next; next;
} }
$tags{lc($1)} = $2; if (not exists($tags{lc($1)})) {
@{$tags{lc($1)}} = ($2);
} else {
push(@{$tags{lc($1)}}, $2);
}
} }
} }
return \%tags; return \%tags;