1

我正在尝试编写一个 Python 脚本来读取我的 MP3 的文件名,当它发现缺少的 Artist 标签时,它将从文件名的第一部分获取它。我的大多数 MP3 的标题都是“艺术家 - Title.mp3”。

但是没有一个 ID3 标签阅读器在 Python 中工作得很好。ID3 不会读取 1.1 之后的任何标签,并且自 2002 年以来就没有开发过。当文件缺少标签时,Mutagen 会引发异常。Eye3D 需要安装二进制文件才能使库正常工作,pylibid3 也是如此。

我是否使用了错误的语言?我听说 Perl 有一些很棒的 ID3 标签库。我是一个新手,所以切换语言(我已经有一段时间没有读过关于 Perl 的书了)意味着从头开始。但如果 Python 是错误的语言,我愿意这样做。

有什么想法吗?

4

2 回答 2

6

使用诱变剂处理异常很容易:

from mutagen.id3 import ID3, TPE1, ID3NoHeaderError
try:
    audio = ID3(filename)
except ID3NoHeaderError:
    audio = ID3()

audio.add(TPE1(encoding=3, text=u'Artist'))
audio.save(filename)
于 2012-05-31T16:38:38.587 回答
2

这是一个执行您想要的 Perl 脚本。我经常用这个。它甚至可以在进行更改之前预览更改。

您确实需要安装几个 Perl 模块(MP3::Info 和 MP4::Info),尽管您可以从代码中删除 MP4 行并跳过该模块。

#!/usr/bin/perl

use strict;
use warnings;
use Cwd;
use File::Copy;
use File::Basename;

# Load MP3/MP4 modules and set them up to use UTF-8 characters
# (Unicode-like support, to see accents, etc.).
use MP3::Info qw(:all);
use_mp3_utf8(1);
use MP4::Info qw(:all);
use_mp4_utf8(1);

my @ARGS;
################################################################################
# Subroutine: RemoveIllegalFilenameCharacters
# Inputs:     $filename
# Outputs:    $filename
################################################################################
sub RemoveIllegalFilenameCharacters {
    my $filename = shift;

    if ($filename =~ m/\\/) { $filename =~ s/\\//g }
    if ($filename =~ m/\//) { $filename =~ s/ \/ / - /g }
    if ($filename =~ m/\//) { $filename =~ s/\///g }
    if ($filename =~ m/:/) { $filename =~ s/://g }
    if ($filename =~ m/\*/) { $filename =~ s/\*//g }
    if ($filename =~ m/\?/) { $filename =~ s/\?//g }
    if ($filename =~ m/"/) { $filename =~ s/"//g }
    if ($filename =~ m/</) { $filename =~ s/<//g }
    if ($filename =~ m/>/) { $filename =~ s/>//g }
    if ($filename =~ m/\|/) { $filename =~ s/\|//g }

    return $filename;
}

################################################################################
# Subroutine: Rename
# Inputs:     $test (indicates test mode)
# Outputs:    number of files to be changed (in test mode)
################################################################################
sub Rename {
  my $test = shift;
  my $destDir = ""; # hard-coded permanent destination, if desired

  my @tests;
  foreach my $file (@ARGS) {
    # Get rid of the Mac OS resource fork files.
    if ($file =~ m/^\._/) {
      unlink "$file";
      next;
    }

    if (! -f $file) {
      warn "'$file' does not exist!\n";
    }
    else {
      # If $destDir wasn't set above, then that means it should be
      # set to the original dir of each file.
      if (!($destDir)) {
    $destDir = dirname($file);
      }

      my $extension = $file;
      $extension =~ s/.*\.//;

      my $tag;
      if ($extension =~ m/^mp3$/i) {
    ($tag = get_mp3tag($file)) || warn "'$file' does not contain ID3 tags (it may not even be an MP3 file!)\n";
      }
      else {
    # If it's not MP3, try MP4
    ($tag = get_mp4tag($file)) || warn "'$file' does not contain ID3 tags (it may not even be an AAC/M4A/M4P file!)\n";
      }
      if (!($tag)) {
    # No $tag was returned.  Go to the next $file.
    next;
      }

      # DEBUG!  Show all the tag names.  Could be modifed to show all the tag values too.
      if (0) {
    print join("\n", keys %{$tag}) . "\n";
      }

      # Set the rename format depending on if we're under the $DOWNLOAD_DIR or not.
      my $newFile = $$tag{"ARTIST"} . " - " . $$tag{"TITLE"} . ".$extension";

      if (($$tag{"ARTIST"} eq "") || ($$tag{"TITLE"} eq "")) { warn "\n*** WARNING! This track is missing some info:\n\tOriginal Name: $file\n\tNew Name: $newFile\n\n" }
      $newFile = RemoveIllegalFilenameCharacters($newFile);

      # If current filename ($file) already matches the new filename ($newFile),
      # don't bother continuing (filename is already in the correct format).
      if ($file eq $newFile) {
    # If we're not choosing to move all files,
    # and if the filename is already in the correct format,
    # go to the next $file.
    next;
      }
      if ($destDir ne ".") { $newFile = $destDir . "/" . $newFile }
      if (!($test) && -f $newFile) {
    die "Unable to move '$file' to '$newFile',\nsince there's already a file named '$newFile'!\nStopped";
      }
      if ($test) {
    push @tests, $newFile;
      } else {
    print "Moving '$file' to '$newFile'\n";
    move($file, $newFile) || die "Unable to move file!\n";
      }
    } # End of if-then-else checking if file exists
  } # End of FOR loop looping through files in @ARGS.

  if ($test && scalar(@tests)) {
    print "Test run - new filenames: \n  ";
    print join("\n  ", sort(@tests));
  }

  return scalar(@tests);
}

################################################################################
# MAIN ROUTINE

if (scalar(@ARGV) == 0) {
    # If no args, use every music file in current directory.
    opendir(DIR, ".");
    @ARGS = sort(grep(/\.mp3$|\.aac$|\.m4a$|\.m4p$/i, readdir(DIR)));
    closedir(DIR);

    if (scalar(@ARGS) == 0) {
      print "USAGE: " . basename($0) . " <music-files>\n\n";
      print "If no filenames are specified, any music files (MP3/AAC/M4A/M4P) in the current directory will be used.\n";
      exit 1;
    }
}
else { @ARGS = @ARGV }

if (Rename(1)) {
    print "\n\nDo the test results look good?\n";
    print " n  - No they don't.  Do not rename the files!\n";
    print "[y] - Yes they do.  Rename the files and leave them in their original folder(s).\n";
    print " ";
    my $choice = <STDIN>;
    chomp $choice;
    # Only do the move if we're in the $DOWNLOAD_DIR area.
    # This allows us to use the default (null) response to process renames in a non-download dir.
    if (("$choice" eq "") || ("$choice" eq "y")) {
    Rename(0);
    }
}
else {
    print "No actions needed.\n";
}
于 2012-05-31T16:46:24.790 回答