1
0
mirror of https://github.com/moex3/flac2mp3.pl synced 2024-11-25 12:41:09 -05:00

Compare commits

..

2 Commits

Author SHA1 Message Date
moex3
ccc5ba6af9
Add option for 320 CBR transcode 2022-12-07 16:50:39 +01:00
moex3
3d31b6842b
Some updates 2022-12-07 16:01:46 +01:00

View File

@ -9,6 +9,7 @@ use File::Basename;
my $opt_no_genre; my $opt_no_genre;
my $opt_comment; my $opt_comment;
my $opt_catid; my $opt_catid;
my $opt_rg;
# TODO fill this out # TODO fill this out
my %genreMap = ( my %genreMap = (
@ -18,6 +19,7 @@ my %genreMap = (
# this is a godsent page # this is a godsent page
# https://wiki.hydrogenaud.io/index.php?title=Tag_Mapping # https://wiki.hydrogenaud.io/index.php?title=Tag_Mapping
# 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
my %idLookup = ( my %idLookup = (
@ -43,6 +45,7 @@ my %idLookup = (
#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',
label => 'TPUB', label => 'TPUB',
remixer => 'TPE4', remixer => 'TPE4',
discnumber => ['TPOS', sub { discnumber => ['TPOS', sub {
@ -62,22 +65,33 @@ my %idLookup = (
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 {
my $t = shift;
my $date = $t->{date};
if (length($date) == 4) { # Only year
return "TYER=$date";
}
if (!($date =~ m/^\d{4}\.\d{2}\.\d{2}$/)) {
print("Date format unknown: $date\n");
exit 1;
}
$date =~ s/\./-/g;
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
isrc => 'TSRC', isrc => 'TSRC',
barcode => 'TXXX=BARCODE', barcode => 'TXXX=BARCODE',
catalog => ['TXXX=CATALOGNUMBER', sub { return tagmap_catalogid(shift, 'catalog'); } ], catalog => ['TXXX=CATALOGNUMBER', sub { return tagmap_catalogid(shift, 'catalog'); } ],
catalognumber => ['TXXX=CATALOGNUMBER', sub { return tagmap_catalogid(shift, 'catalognumber'); } ], catalognumber => ['TXXX=CATALOGNUMBER', sub { return tagmap_catalogid(shift, 'catalognumber'); } ],
catalogid => ['TXXX=CATALOGNUMBER', sub { return tagmap_catalogid(shift, 'catalogid'); } ], catalogid => ['TXXX=CATALOGNUMBER', sub { return tagmap_catalogid(shift, 'catalogid'); } ],
labelno => ['TXXX=CATALOGNUMBER', sub { return tagmap_catalogid(shift, 'labelno'); } ],
'encoded-by' => 'TENC', 'encoded-by' => 'TENC',
encoder => 'TSSE', encoder => 'TSSE',
encoding => 'TSSE', encoding => 'TSSE',
'encoder settings' => 'TSSE', 'encoder settings' => 'TSSE',
media => 'TMED', media => 'TMED',
replaygain_album_gain => 'TXXX=REPLAYGAIN_ALBUM_GAIN',
replaygain_album_peak => 'TXXX=REPLAYGAIN_ALBUM_PEAK',
replaygain_track_gain => 'TXXX=REPLAYGAIN_TRACK_GAIN',
replaygain_track_peak => 'TXXX=REPLAYGAIN_TRACK_PEAK',
genre => ['TCON', sub { genre => ['TCON', sub {
return undef if ($opt_no_genre); return undef if ($opt_no_genre);
@ -97,9 +111,26 @@ my %idLookup = (
}], }],
copyright => 'TCOP', copyright => 'TCOP',
language => 'TLAN', language => 'TLAN',
#replaygain_album_peak => 'TXXX=REPLAYGAIN_ALBUM_PEAK',
#replaygain_album_gain => 'TXXX=REPLAYGAIN_ALBUM_GAIN',
replaygain_track_gain => sub {
return undef if (!$opt_rg);
shift->{replaygain_track_gain} =~ /^(-?\d+\.\d+) dB$/;
my $gain_db = $1;
exit(1) if ($gain_db eq "");
return "--replaygain-accurate --gain $gain_db";
},
#replaygain_album_gain => 'TXXX=REPLAYGAIN_ALBUM_GAIN',
#replaygain_album_peak => 'TXXX=REPLAYGAIN_ALBUM_PEAK',
#replaygain_track_gain => 'TXXX=REPLAYGAIN_TRACK_GAIN',
#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',
discid => 'TXXX=DISCID',
originaltitle => 'TXXX=ORIGINALTITLE',
); );
sub tagmap_catalogid { sub tagmap_catalogid {
my $t = shift; my $t = shift;
@ -110,12 +141,17 @@ sub tagmap_catalogid {
my $opt_genre; my $opt_genre;
my $opt_help; my $opt_help;
my @opt_tagreplace;
my $opt_cbr = 0;
GetOptions( GetOptions(
"genre|g=s" => \$opt_genre, "genre|g=s" => \$opt_genre,
"no-genre|G" => \$opt_no_genre, "no-genre|G" => \$opt_no_genre,
"replay-gain|r" => \$opt_rg,
"help|h" => \$opt_help, "help|h" => \$opt_help,
"catid=s" => \$opt_catid, "catid=s" => \$opt_catid,
"comment=s" => \$opt_comment, "comment=s" => \$opt_comment,
"tagreplace|t=s" => \@opt_tagreplace,
"320|3" => \$opt_cbr,
) or die("Error in command line option"); ) or die("Error in command line option");
if ($opt_help) { if ($opt_help) {
@ -140,9 +176,12 @@ sub iterFlac {
return if (!-f || !/\.flac$/); return if (!-f || !/\.flac$/);
my @required_tags = ("artist", "title", "album", "tracknumber"); my @required_tags = ("artist", "title", "album", "tracknumber");
my $flacDir = substr($File::Find::name, length($IDIR));
my $flac = $_; my $flac = $_;
my $flac_o = $flac;
shellsan(\$flac); shellsan(\$flac);
my $dest = "$ODIR/" . basename($flac); my $dest = "$ODIR/" . $flacDir;
#print("DEBUG: $dest\n");
$dest =~ s/\.flac$/\.mp3/; $dest =~ s/\.flac$/\.mp3/;
my $tags = getFlacTags($flac); my $tags = getFlacTags($flac);
@ -155,24 +194,50 @@ sub iterFlac {
} }
if (!$has_req_tags) { if (!$has_req_tags) {
print("WARNING: File: '$flac' does not have all the required tags. Skipping\n"); print("WARNING: File: '$flac' does not have all the required tags. Skipping\n");
exit(1);
return; return;
} }
argsToTags($tags); argsToTags($tags, $flac_o);
my $tagopts = tagsToOpts($tags); my $tagopts = tagsToOpts($tags);
qx(flac -cd -- '$flac' | lame -V0 -S --vbr-new --add-id3v2 @$tagopts - '$dest'); #print("Debug: @$tagopts\n");
shellsan(\$dest);
my $cmd;
if ($opt_cbr) {
$cmd = "flac -cd -- '$flac' | lame -S -b 320 -q 0 --add-id3v2 @$tagopts - '$dest'";
} else {
$cmd = "flac -cd -- '$flac' | lame -S -V0 --vbr-new -q 0 --add-id3v2 @$tagopts - '$dest'";
}
#print("Debug - CMD: [$cmd]\n");
qx($cmd);
if ($? != 0) {
exit(1);
}
} }
sub argsToTags { sub argsToTags {
my $argTags = shift; my $argTags = shift;
my $fname = shift;
$fname =~ s!^.*/!!;
if (defined($opt_genre)) { if (defined($opt_genre)) {
$argTags->{genre} = $opt_genre; $argTags->{genre} = $opt_genre;
} elsif (defined($opt_comment) && $opt_comment ne "") { }
if (defined($opt_comment) && $opt_comment ne "") {
$argTags->{comment} = $opt_comment; $argTags->{comment} = $opt_comment;
} elsif (defined($opt_catid) && $opt_catid ne "") { }
if (defined($opt_catid) && $opt_catid ne "") {
$argTags->{catalognumber} = $opt_catid; $argTags->{catalognumber} = $opt_catid;
} }
if (scalar @opt_tagreplace > 0) {
foreach my $trepl (@opt_tagreplace) {
$trepl =~ m!(.*?)/(.*?)=(.*)!;
my ($freg, $tag, $tagval) = ($1, $2, $3);
if ($fname =~ m!$freg!) {
$argTags->{lc($tag)} = $tagval;
}
}
}
} }
sub tagsToOpts { sub tagsToOpts {
@ -188,14 +253,38 @@ sub tagsToOpts {
my $tagName = $idLookup{$currKey}; my $tagName = $idLookup{$currKey};
my $type = ref($tagName); my $type = ref($tagName);
if ($type eq "" && defined($tagName)) { if ($type eq "" && defined($tagName)) {
# If tag name is defined and tag contents exists
my $tagCont = $tags->{$currKey}; my $tagCont = $tags->{$currKey};
shellsan(\$tagCont); shellsan(\$tagCont);
push(@tagopts, qq(--tv '$tagName=$tagCont')); push(@tagopts, qq(--tv '$tagName=$tagCont'));
} elsif ($type eq "ARRAY") { } elsif ($type eq "ARRAY") {
my $tagCont = $tagName->[1]->($tags); my $tagCont = $tagName->[1]->($tags);
my $tagKey = $tagName->[0];
if (defined($tagCont)) { if (defined($tagCont)) {
if (defined($tagKey)) {
shellsan(\$tagCont); shellsan(\$tagCont);
push(@tagopts, qq(--tv '$tagName->[0]=$tagCont')); push(@tagopts, qq(--tv '$tagName->[0]=$tagCont'));
} 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'));
}
}
}
} elsif ($type eq 'CODE') {
# If we have just a code reference
# do not assume, that this is a tag, rather a general cmd opt
my $opt = $tagName->($tags);
if (defined($opt)) {
shellsan(\$opt);
push(@tagopts, qq($opt));
} }
} }
@ -209,8 +298,15 @@ sub getFlacTags {
my %tags; my %tags;
my @tagtxt = qx(metaflac --list --block-type=VORBIS_COMMENT -- '$flac'); my @tagtxt = qx(metaflac --list --block-type=VORBIS_COMMENT -- '$flac');
if ($? != 0) {
exit(1);
}
foreach my $tagline (@tagtxt) { foreach my $tagline (@tagtxt) {
if ($tagline =~ /comment\[\d+\]:\s(.*?)=(.*)/) { if ($tagline =~ /comment\[\d+\]:\s(.*?)=(.*)/) {
if ($2 eq '') {
print("Empty tag: $1\n");
next;
}
$tags{lc($1)} = $2; $tags{lc($1)} = $2;
} }
} }
@ -222,7 +318,7 @@ sub shellsan {
} }
sub usage { sub usage {
print("Usage: flac2mp3.pl [-h | --help] [-g | --genre NUM] <input_dir> <output_dir>\n"); print("Usage: flac2mp3.pl [-h | --help] [-r] [-3] [-g | --genre NUM] <input_dir> <output_dir>\n");
exit 1; exit 1;
} }
@ -234,8 +330,12 @@ Usage:
-h, --help print this help text -h, --help print this help text
-g, --genre NUM force this genre as a tag (lame --genre-list) -g, --genre NUM force this genre as a tag (lame --genre-list)
-G, --no-genre ignore genre in flac file -G, --no-genre ignore genre in flac file
-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 "")
-t --tagreplace STR Replace flac tags for a specific file only
Like -t '02*flac/TITLE=Some other title'
-3, --320 Convert into CBR 320 instead into the default V0
EOF EOF
print($h); print($h);
exit 0; exit 0;