您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

194 行
4.7KB

  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;
  4. use Getopt::Long;
  5. use File::Find;
  6. use Data::Dumper;
  7. use File::Basename;
  8. # this is a godsent page
  9. # https://wiki.hydrogenaud.io/index.php?title=Tag_Mapping
  10. # a lot of this may not work
  11. my %idLookup = (
  12. album => 'TALB',
  13. albumsort => 'TSOA',
  14. discsubtitle => 'TSST',
  15. grouping => 'TIT1',
  16. title => 'TIT2',
  17. titlesort => 'TSOT',
  18. subtitle => 'TIT3',
  19. subtitle => 'TIT3',
  20. albumartist => 'TPE2',
  21. albumartistsort => 'TSO2', # Maybe?
  22. artist => 'TPE1',
  23. artistsort => 'TSOP',
  24. arranger => 'TIPL=arranger',
  25. author => 'TEXT',
  26. composer => 'TCOM',
  27. conductor => 'TPE3',
  28. engineer => 'TIPL=engineer',
  29. djmixer => 'TIPL=DJ-mix',
  30. mixer => 'TIPL=mix',
  31. #performer => 'TMCL', # This produces some really weird tags
  32. producer => 'TIPL=producer',
  33. publisher => 'TPUB',
  34. label => 'TPUB',
  35. remixer => 'TPE4',
  36. discnumber => ['TPOS', sub {
  37. my $t = shift;
  38. return "$t->{discnumber}" if !exists($t->{disctotal});
  39. return "$t->{discnumber}/$t->{disctotal}";
  40. }],
  41. disctotal => undef,
  42. tracknumber => ['TRCK', sub {
  43. my $t = shift;
  44. return "$t->{tracknumber}" if !exists($t->{tracktotal});
  45. return "$t->{tracknumber}/$t->{tracktotal}";
  46. }],
  47. tracktotal => undef,
  48. date => 'TDRC', # This is for id3v2.4
  49. originaldate => 'TDOR', # Also for 2.4 only
  50. isrc => 'TSRC',
  51. barcode => 'TXXX=BARCODE',
  52. catalognumber => 'TXXX=CATALOGNUMBER',
  53. catalog => 'TXXX=CATALOGNUMBER',
  54. catalogid => 'TXXX=CATALOGNUMBER',
  55. 'encoded-by' => 'TENC',
  56. encoder => 'TSSE',
  57. encoding => 'TSSE',
  58. 'encoder settings' => 'TSSE',
  59. media => 'TMED',
  60. replaygain_album_gain => 'TXXX=REPLAYGAIN_ALBUM_GAIN',
  61. replaygain_album_peak => 'TXXX=REPLAYGAIN_ALBUM_PEAK',
  62. replaygain_track_gain => 'TXXX=REPLAYGAIN_TRACK_GAIN',
  63. replaygain_track_peak => 'TXXX=REPLAYGAIN_TRACK_PEAK',
  64. genre => ['TCON', sub {
  65. return 24;
  66. }],
  67. #mood => ['TMOO', sub {
  68. #}],
  69. bpm => 'TBPM',
  70. comment => 'COMM',
  71. copyright => 'TCOP',
  72. language => 'TLAN',
  73. script => 'TXXX=SCRIPT',
  74. lyrics => 'USLT',
  75. );
  76. my $opt_genre;
  77. my $opt_help;
  78. GetOptions(
  79. "genre=s" => \$opt_genre,
  80. "help" => \$opt_help
  81. ) or die("Error in command line option");
  82. if ($opt_help) {
  83. help();
  84. }
  85. if (scalar(@ARGV) != 2) {
  86. print("Bad arguments\n");
  87. usage();
  88. }
  89. my ($IDIR, $ODIR) = @ARGV;
  90. if (!-e $ODIR) {
  91. mkdir $ODIR;
  92. }
  93. find({ wanted => \&iterFlac, no_chdir => 1 }, $IDIR);
  94. sub iterFlac {
  95. # Return if file is not a file, or if it's not a flac
  96. return if (!-f || !/\.flac$/);
  97. my @required_tags = ("artist", "title", "album", "tracknumber");
  98. my $flac = $_;
  99. my $dest = "$ODIR/" . basename($flac);
  100. $dest =~ s/\.flac$/\.mp3/;
  101. my $tags = getFlacTags($flac);
  102. my $has_req_tags = 1;
  103. foreach (@required_tags) {
  104. if (!exists($tags->{lc($_)})) {
  105. $has_req_tags = 0;
  106. last;
  107. }
  108. }
  109. if (!$has_req_tags) {
  110. print("WARNING: File: '$flac' does not have all the required tags. Skipping\n");
  111. return;
  112. }
  113. argsToTags($tags);
  114. my $tagopts = tagsToOpts($tags);
  115. $flac =~ s!'!'\\''!g;
  116. qx(flac -cd '$flac' | lame -V0 -S --vbr-new --add-id3v2 @$tagopts - "$dest");
  117. }
  118. sub argsToTags {
  119. my $argTags = shift;
  120. if (defined($opt_genre)) {
  121. $argTags->{genre} = $opt_genre;
  122. }
  123. }
  124. sub tagsToOpts {
  125. my $tags = shift;
  126. my @tagopts;
  127. # TODO escape ' and =?
  128. foreach my $currKey (keys (%$tags)) {
  129. if (!exists($idLookup{$currKey})) {
  130. print("Tag: '$currKey' doesn't have a mapping, skipping\n");
  131. next;
  132. }
  133. my $tagName = $idLookup{$currKey};
  134. my $type = ref($tagName);
  135. if ($type eq "" && defined($tagName)) {
  136. push(@tagopts, qq(--tv '$tagName=$tags->{$currKey}'));
  137. } elsif ($type eq "ARRAY") {
  138. my $tagCont = $tagName->[1]->($tags);
  139. push(@tagopts, qq(--tv '$tagName->[0]=$tagCont'));
  140. }
  141. }
  142. #print(Dumper(\@tagopts));
  143. return \@tagopts;
  144. }
  145. sub getFlacTags {
  146. my $flac = shift;
  147. my %tags;
  148. my @tagtxt = qx(metaflac --list --block-type=VORBIS_COMMENT -- "$flac");
  149. foreach my $tagline (@tagtxt) {
  150. if ($tagline =~ /comment\[\d+\]:\s(.*?)=(.*)/) {
  151. $tags{lc($1)} = $2;
  152. }
  153. }
  154. return \%tags;
  155. }
  156. sub usage {
  157. print("Usage: flac2mp3.pl [-h | --help] [-g | --genre NUM] <input_dir> <output_dir>\n");
  158. exit 1;
  159. }
  160. sub help {
  161. my $h = <<EOF;
  162. Usage:
  163. flac2mp3.pl [options] <input_dir> <output_dir>
  164. -h, --help print this help text
  165. -g, --genre NUM force this genre as a tag (lame --genre-list)
  166. EOF
  167. print($h);
  168. exit 0;
  169. }
  170. # vim: ts=4 sw=4 et sta