Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

"Intelligent" ID3v2 Tagger

by The Mad Hatter (Priest)
on Jul 21, 2003 at 05:29 UTC ( [id://276144]=sourcecode: print w/replies, xml ) Need Help??
Category: Audio Related Programs
Author/Contact Info The Mad Hatter
Description:

Given a directory, this will go thru and try to figure out artist, album, and title from the directory structure and filename. A few options allow you to tweak how it gets the information. Once it has gathered the info, it then writes an ID3v2 tag. I would support Ogg Vorbis, but I couldn't find a module on CPAN that could write tags to Oggs. Requires MP3::Tag, File::Find::Rule, File::Spec, and Cwd. (The last two are in the core.)

Nota Bene: Use at your own risk. If this messes up your pristine music collection, makes your socks smell, computer implode, or generally screws up anything, I am not liable.

#!/usr/bin/perl
#
# "Intelligent" ID3v2 tagger for MP3s
#

our $VERSION = 0.04;

use strict;
use warnings;

use MP3::Tag;
use File::Find::Rule;
use File::Spec;
use Cwd;

my $dir   = "";                 # Search by default nowhere
my $ext   = '.mp3';             # Grab files with the extension of .mp
+3
my $noSplit = 0;                # If the file does NOT contain  a hyph
+en, split into artist/title
my $titleFirst = 0;             # Format: title - artist instead of: a
+rtist - title
my $artistFromName = 0;         # Grab artist from filename
my $noOverwrite = 0;            # Don't overwrite existing tags
my $help  = 0;                  # Show help

my $cwd = Cwd::getcwd();

use Getopt::Long;
GetOptions("dir=s"              => \$dir,
           "ext=s"              => \$ext,
           "no-split"           => \$noSplit,
           "title-first"        => \$titleFirst,
           "artist-from-name"   => \$artistFromName,
           "no-overwrite"       => \$noOverwrite,
           "help"               => \$help);

if ($help || !$dir) {
    print <<"    HELP";
$0 - "intelligent" ID3v2 tagger for MP3s, version $VERSION

Usage (defaults in brackets):
  -d, --directory=DIR    directory to start search in [$dir]
  -e, --extension=EXT    extension to search for [$ext]
      --no-split         do NOT split the filename into: artist - titl
+e [$noSplit]
  -t, --title-first      if not --no-split, use: title - artist instea
+d [$titleFirst]
  -a, --artist-from-name get the artist from the filename [$artistFrom
+Name]
      --no-overwrite     don't overwrite any existing info [$noOverwri
+te]

    HELP
    exit;
}


my @mp3s = File::Find::Rule->file()
                           ->name("*$ext")
                           ->in($dir);

foreach my $mp3 (@mp3s) {
    my @spec = File::Spec->splitdir(File::Spec->abs2rel($mp3, $dir));
    
    my %file;
    $file{name} = $spec[$#spec];
    
    if (@spec >= 4) {
        $file{genre}  = $spec[$#spec - 3];
        $file{artist} = $spec[$#spec - 2];
        $file{album}  = $spec[$#spec - 1];
    }
    elsif (@spec == 3) {
        $file{artist} = $spec[$#spec - 2];
        $file{album}  = $spec[$#spec - 1];
    }
    elsif (@spec == 2) {
        $file{artist} = $spec[$#spec - 1];
    }

    my $sansExtension = $file{name};
    $sansExtension =~ s/\Q$ext\E\z//;
    
    if (!$noSplit && $sansExtension =~ /\s*-\s*/) {
        my ($artist, $title);

        unless ($titleFirst) {
            ($artist, $title) = split /\s*-\s*/, $sansExtension, 2;
        }
        else {
            ($title, $artist) = split /\s*-\s*/, $sansExtension, 2;
        }
        
        $file{title}  = $title;
        $file{artist} = $artist if !$file{artist} || $artistFromName;
    }
    else {
        $file{title} = $sansExtension;
    }
    
    my $tag = MP3::Tag->new($mp3);

    # read an existing tag
    $tag->get_tags();
    my $id3v2;
    
    $id3v2 = exists $tag->{ID3v2}
                ? $tag->{ID3v2}
                : $tag->new_tag("ID3v2");    
    
    unless ($noOverwrite) {
        $id3v2->remove_tag;
        $id3v2 = $tag->new_tag("ID3v2");
        
        $id3v2->add_frame("TIT2", $file{title})     if $file{title};
        $id3v2->add_frame("TPE1", $file{artist})    if $file{artist};
        $id3v2->add_frame("TALB", $file{album})     if $file{album};
        $id3v2->add_frame("TCON", $file{genre})     if $file{genre};
        $id3v2->write_tag;
    }
    else {
        $id3v2->remove_frame($_) for qw/TIT2 TPE1 TALB TCON/;
        
        $id3v2->add_frame("TIT2", $file{title})     if $file{title};
        $id3v2->add_frame("TPE1", $file{artist})    if $file{artist};
        $id3v2->add_frame("TALB", $file{album})     if $file{album};
        $id3v2->add_frame("TCON", $file{genre})     if $file{genre};
    }
    $id3v2->write_tag;
}
Replies are listed 'Best First'.
Re: "Intelligent" ID3v2 Tagger
by Mr_Person (Hermit) on Jul 21, 2003 at 13:14 UTC
    Ogg::Vorbis::Header will write to Ogg comment tags. I've been using it for a while without any problems. One thing that's a little different about Ogg comment fields is that unlike ID3 tags, there is no set requirement of what the tags must be called. However, there's a list here that has the most common usage for field and most players will follow that.
      Ah, I saw that, but didn't know Ogg comment tags were freeform like that. Thanks for enlightening me, now I'll get around to adding Ogg support sometime.
Re: "Intelligent" ID3v2 Tagger
by Chady (Priest) on Jul 21, 2003 at 13:44 UTC

    Hi,
    I'm writing something to categorize MP3s, I was thinking of something to correctly "parse"(?) the MP3 into good metadata: If the file has ID3 tags it's good, I read those and use them, if not, I have to rely on parsing the filename, folder/filename, folder/folder/filename... etc.

    Instead of assuming that the files are always /(title) - (artist)/, I was thinking of something like checking $1 and $2 against a list of the artists already in the db to see if there IS an artist with that name and mark that as a possible artist, if not, require input from the user, but there are obvious flaws in that. (different ways to type the artist name, typos, punctuations...etc)

    Does anyone use a similar technique? or something to achieve the same results?


    He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.

    Chady | http://chady.net/
      I have a similar problem regarding the naming convention, I have:

      /(artist)/(album)/(tracknum) - (title)

      What I thought might be better would be to have a templating string, then that could be used to extract the relevant fields. Thus using keywords, such as ARTIST, ALBUM, etc you should be able to extract the right field from the right portion of the path. So my template would look like:

      /ARTIST/ALBUM/TRACKNUM - TITLE

      I already do the extraction using File::Find::Rule (ain't it grand ... thanks Rich), but haven't gotten round to doing all the MP3 tagging. So I'll be having a jolly good play with this script once I'm back from YAPC::Europe.

      A fine effort.

      --
      Barbie | Birmingham Perl Mongers | http://birmingham.pm.org/

        Yeah, this script is by far no where near complete, but it works for me at the moment. I don't know if I plan on extending it for format strings, etc, but at least it provides somewhat of a base...

        Now that I think of it, it would also be nice to be able to exclude directories...hmmmmm

Re: "Intelligent" ID3v2 Tagger
by Anonymous Monk on Dec 23, 2004 at 11:53 UTC

    Hi

    Having spent a good couple of hours playing with ID3v2 under Windows, MP3::Tag is the way foward. I had some problems adding long string comments with some other modules, so for completeness, in the context above comments could be added using the code below.

    $id3v2->add_frame("COMM", "ENG", "Short text", "A nice long comment with lots of long text");

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://276144]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (8)
As of 2024-04-19 09:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found