mirror of
https://github.com/moex3/flac2mp3.pl
synced 2024-11-21 19:44:15 -05:00
Some fixes and some new features
Fix: - Use TXXX for arranger, performer (because mid3v2 doesn't recognize the TIPL and other tags for this. :( - Map lyricist tag - Map origin and origintype to TXXX fields - Remoeavbe comment tag Adds: - Add option for embedded image art control - Enforce TXXX key uniqueness and merge them together with a ; character if there are more that one of them.
This commit is contained in:
parent
6be59c9036
commit
be6853c5da
90
flac2mp3.pl
90
flac2mp3.pl
@ -11,6 +11,7 @@ my $opt_no_genre;
|
|||||||
my $opt_comment;
|
my $opt_comment;
|
||||||
my $opt_catid;
|
my $opt_catid;
|
||||||
my $opt_rg;
|
my $opt_rg;
|
||||||
|
my $opt_embedcover;
|
||||||
|
|
||||||
# TODO fill this out
|
# TODO fill this out
|
||||||
my %genreMap = (
|
my %genreMap = (
|
||||||
@ -46,14 +47,16 @@ my %idLookup = (
|
|||||||
albumartistsort => 'TSO2', # Maybe?
|
albumartistsort => 'TSO2', # Maybe?
|
||||||
artist => 'TPE1',
|
artist => 'TPE1',
|
||||||
artistsort => 'TSOP',
|
artistsort => 'TSOP',
|
||||||
arranger => ['TIPL', 'arranger:'],
|
# arranger => ['TIPL', 'arranger:'],
|
||||||
|
arranger => ['TXXX', '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', "instrument:"], # Should be like this, but mid3v2 says it doesn't have this tag.
|
||||||
|
performer => ['TXXX', "PERFORMER:"],
|
||||||
producer => ['TIPL', 'producer:'],
|
producer => ['TIPL', 'producer:'],
|
||||||
publisher => 'TPUB',
|
publisher => 'TPUB',
|
||||||
organization => 'TPUB',
|
organization => 'TPUB',
|
||||||
@ -141,10 +144,13 @@ my %idLookup = (
|
|||||||
#replaygain_track_peak => 'TXXX=REPLAYGAIN_TRACK_PEAK',
|
#replaygain_track_peak => 'TXXX=REPLAYGAIN_TRACK_PEAK',
|
||||||
script => ['TXXX', 'SCRIPT:'],
|
script => ['TXXX', 'SCRIPT:'],
|
||||||
lyrics => 'USLT',
|
lyrics => 'USLT',
|
||||||
|
lyricist => 'TEXT',
|
||||||
circle => ['TXXX', 'CIRCLE:'],
|
circle => ['TXXX', 'CIRCLE:'],
|
||||||
event => ['TXXX', 'EVENT:'],
|
event => ['TXXX', 'EVENT:'],
|
||||||
discid => ['TXXX', 'DISCID:'],
|
discid => ['TXXX', 'DISCID:'],
|
||||||
originaltitle => ['TXXX', 'ORIGINALTITLE:'],
|
originaltitle => ['TXXX', 'ORIGINALTITLE:'],
|
||||||
|
origin => ['TXXX', 'ORIGIN:'],
|
||||||
|
origintype => ['TXXX', 'ORIGINTYPE:'],
|
||||||
);
|
);
|
||||||
sub tagmap_catalogid {
|
sub tagmap_catalogid {
|
||||||
my $t = shift;
|
my $t = shift;
|
||||||
@ -164,6 +170,7 @@ GetOptions(
|
|||||||
"help|h" => \$opt_help,
|
"help|h" => \$opt_help,
|
||||||
"catid=s" => \$opt_catid,
|
"catid=s" => \$opt_catid,
|
||||||
"comment=s" => \$opt_comment,
|
"comment=s" => \$opt_comment,
|
||||||
|
"cover=s" => \$opt_embedcover,
|
||||||
"tagreplace|t=s" => \@opt_tagreplace,
|
"tagreplace|t=s" => \@opt_tagreplace,
|
||||||
"320|3" => \$opt_cbr,
|
"320|3" => \$opt_cbr,
|
||||||
) or die("Error in command line option");
|
) or die("Error in command line option");
|
||||||
@ -213,6 +220,9 @@ sub iterFlac {
|
|||||||
}
|
}
|
||||||
|
|
||||||
argsToTags($tags, $flac_o);
|
argsToTags($tags, $flac_o);
|
||||||
|
#foreach (%$tags) {
|
||||||
|
#print("Copying tag '$_->[0]=$_->[1]'\n");
|
||||||
|
#}
|
||||||
my $tagopts = tagsToOpts($tags);
|
my $tagopts = tagsToOpts($tags);
|
||||||
|
|
||||||
#print("Debug: @$tagopts\n");
|
#print("Debug: @$tagopts\n");
|
||||||
@ -248,10 +258,25 @@ sub iterFlac {
|
|||||||
embedImageFromFlac($flac, $dest);
|
embedImageFromFlac($flac, $dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub getMimeType {
|
||||||
|
my $file = shift;
|
||||||
|
shellsan(\$file);
|
||||||
|
my $mime = qx(file -b --mime-type '$file');
|
||||||
|
chomp($mime);
|
||||||
|
return $mime;
|
||||||
|
}
|
||||||
|
|
||||||
sub embedImageFromFlac {
|
sub embedImageFromFlac {
|
||||||
my $flac = shift;
|
my $flac = shift;
|
||||||
my $mp3 = shift;
|
my $mp3 = shift;
|
||||||
|
|
||||||
|
return if ($opt_embedcover eq "");
|
||||||
|
if ($opt_embedcover ne "") {
|
||||||
|
my $cmime = getMimeType($opt_embedcover);
|
||||||
|
qx(mid3v2 -p '${opt_embedcover}:cover:3:$cmime' -- '$mp3');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
# I can't get the automatic deletion working :c
|
# I can't get the automatic deletion working :c
|
||||||
my (undef, $fname) = tempfile();
|
my (undef, $fname) = tempfile();
|
||||||
# Export image from flac
|
# Export image from flac
|
||||||
@ -278,8 +303,12 @@ sub argsToTags {
|
|||||||
if (defined($opt_genre)) {
|
if (defined($opt_genre)) {
|
||||||
$argTags->{genre} = [$opt_genre];
|
$argTags->{genre} = [$opt_genre];
|
||||||
}
|
}
|
||||||
if (defined($opt_comment) && $opt_comment ne "") {
|
if (defined($opt_comment)) {
|
||||||
$argTags->{comment} = [$opt_comment];
|
if ($opt_comment eq "") {
|
||||||
|
delete($argTags->{comment});
|
||||||
|
} else {
|
||||||
|
$argTags->{comment} = [$opt_comment];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (defined($opt_catid) && $opt_catid ne "") {
|
if (defined($opt_catid) && $opt_catid ne "") {
|
||||||
$argTags->{catalognumber} = [$opt_catid];
|
$argTags->{catalognumber} = [$opt_catid];
|
||||||
@ -295,10 +324,40 @@ sub argsToTags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub mergeDupeTxxx {
|
||||||
|
# Merge tags together, that we can't have multiples of
|
||||||
|
# Like TXXX with same key
|
||||||
|
|
||||||
|
my $tagsArr = shift;
|
||||||
|
|
||||||
|
for (my $i = 0; $i < scalar @$tagsArr; $i++) {
|
||||||
|
if (lc($tagsArr->[$i]->[0]) ne "txxx") {
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tagsArr->[$i]->[1] =~ m/^(.*?):(.*)$/;
|
||||||
|
my $txkeyFirst = $1;
|
||||||
|
my $txvalFirst = $2;
|
||||||
|
|
||||||
|
for (my $j = $i + 1; $j < scalar @$tagsArr; $j++) {
|
||||||
|
next if (lc($tagsArr->[$j]->[0]) ne "txxx");
|
||||||
|
$tagsArr->[$j]->[1] =~ m/^(.*?):(.*)$/;
|
||||||
|
my $txkeySecond = $1;
|
||||||
|
my $txvalSecond = $2;
|
||||||
|
|
||||||
|
next if ($txkeyFirst ne $txkeySecond);
|
||||||
|
# TXXX keys are equal, append the second to the first, and delete this entry
|
||||||
|
$tagsArr->[$i]->[1] .= ';' . $txvalSecond;
|
||||||
|
#print("DDDDDDDDDDDDD: Deleted $j index $txkeySecond:$txvalSecond\n");
|
||||||
|
splice(@$tagsArr, $j, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub tagsToOpts {
|
sub tagsToOpts {
|
||||||
my $tags = shift;
|
my $tags = shift;
|
||||||
my @tagopts;
|
my @tagopts;
|
||||||
|
|
||||||
# TODO escape stuff?
|
# TODO escape stuff?
|
||||||
foreach my $currKey (keys (%$tags)) {
|
foreach my $currKey (keys (%$tags)) {
|
||||||
if (!exists($idLookup{$currKey})) {
|
if (!exists($idLookup{$currKey})) {
|
||||||
@ -311,7 +370,7 @@ sub tagsToOpts {
|
|||||||
# If tag name is defined and tag contents exists (aka not silenced)
|
# If tag name is defined and tag contents exists (aka not silenced)
|
||||||
foreach my $tagCont (@{$tags->{$currKey}}) {
|
foreach my $tagCont (@{$tags->{$currKey}}) {
|
||||||
shellsan(\$tagCont);
|
shellsan(\$tagCont);
|
||||||
push(@tagopts, qq('--$tagMapping' '$tagCont'));
|
push(@tagopts, ["$tagMapping", "$tagCont"]);
|
||||||
}
|
}
|
||||||
} elsif ($type eq "ARRAY") {
|
} elsif ($type eq "ARRAY") {
|
||||||
my $mapKey = $tagMapping->[0];
|
my $mapKey = $tagMapping->[0];
|
||||||
@ -325,12 +384,12 @@ sub tagsToOpts {
|
|||||||
if ($mapContType eq "") {
|
if ($mapContType eq "") {
|
||||||
foreach my $tagValue (@{$tags->{$currKey}}) {
|
foreach my $tagValue (@{$tags->{$currKey}}) {
|
||||||
shellsan(\$tagValue);
|
shellsan(\$tagValue);
|
||||||
push(@tagopts, qq('--$mapKey' '$mapCont$tagValue'));
|
push(@tagopts, ["$mapKey", "$mapCont$tagValue"]);
|
||||||
}
|
}
|
||||||
} elsif ($mapContType eq "CODE") {
|
} elsif ($mapContType eq "CODE") {
|
||||||
my $tagValue = $mapCont->($tags);
|
my $tagValue = $mapCont->($tags);
|
||||||
shellsan(\$tagValue);
|
shellsan(\$tagValue);
|
||||||
push(@tagopts, qq('--$mapKey' '$tagValue'));
|
push(@tagopts, ["$mapKey", "$tagValue"]);
|
||||||
}
|
}
|
||||||
} elsif ($type eq 'CODE') {
|
} elsif ($type eq 'CODE') {
|
||||||
# If we have just a code reference
|
# If we have just a code reference
|
||||||
@ -345,10 +404,20 @@ sub tagsToOpts {
|
|||||||
my $mapKey = $codeRet->[0];
|
my $mapKey = $codeRet->[0];
|
||||||
my $mapCont = $codeRet->[1];
|
my $mapCont = $codeRet->[1];
|
||||||
shellsan(\$mapCont);
|
shellsan(\$mapCont);
|
||||||
push(@tagopts, qq('--$mapKey' '$mapCont'));
|
push(@tagopts, ["$mapKey", "$mapCont"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return \@tagopts;
|
|
||||||
|
mergeDupeTxxx(\@tagopts);
|
||||||
|
|
||||||
|
# Convert the tag array into an array of string to use with mid3v2
|
||||||
|
my @tagoptsStr;
|
||||||
|
|
||||||
|
foreach (@tagopts) {
|
||||||
|
push(@tagoptsStr, qq('--$_->[0]' '$_->[1]'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return \@tagoptsStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub getFlacTags {
|
sub getFlacTags {
|
||||||
@ -395,6 +464,7 @@ Usage:
|
|||||||
-r, --replay-gain use replay gain values
|
-r, --replay-gain use replay gain values
|
||||||
--catid STRING the catalog id to set (or "")
|
--catid STRING the catalog id to set (or "")
|
||||||
--comment STRING the comment to set (or "")
|
--comment STRING the comment to set (or "")
|
||||||
|
--cover STRING Use this image as cover (or "" to not copy from flac)
|
||||||
-t --tagreplace STR Replace flac tags for a specific file only
|
-t --tagreplace STR Replace flac tags for a specific file only
|
||||||
Like -t '02*flac/TITLE=Some other title'
|
Like -t '02*flac/TITLE=Some other title'
|
||||||
-3, --320 Convert into CBR 320 instead into the default V0
|
-3, --320 Convert into CBR 320 instead into the default V0
|
||||||
|
Loading…
Reference in New Issue
Block a user