1#!/usr/bin/perl -w
2# $Id: id3tofilename,v 1.7 2003/12/12 06:30:10 ianb Exp $
3# Ian Beckwith <ianb@nessie.mcc.ac.uk>
4# v1 20030509
5# v2 20031024 MP3::Archive support, more options.
6
7use strict;
8use MP3::Tag;
9use Cwd;
10
11use vars qw($me);
12$me=($0=~/(?:.*\/)?(.*)/)[0];
13
14my $archive=undef;
15if(eval("require MP3::Archive;"))
16{
17	$archive=new MP3::Archive;
18}
19else
20{
21	warn("$me: cannot find MP3::Archive. Carrying on anyway...\n");
22}
23
24use vars qw($USEEITHER $USEONE $USETWO $ALBUM $TRACK $GUESS);
25$USEEITHER=0;
26$USEONE=1;
27$USETWO=2;
28my $use=$USEEITHER;
29
30$ALBUM=0;
31$TRACK=1;
32$GUESS=2;
33my $tracktype=$GUESS;
34my $verbose=0;
35my $dryrun=0;
36my $doneargs=0;
37my ($dotracknum,$doartist,$doalbum,$dotrack)=(0,0,0,0);
38my $doall=1;
39my $donesomething=0;
40
41while($_=shift)
42{
43	if(/^-/ && !$doneargs)
44	{
45		if(/-1/)    { $use=$USEONE;      }
46		elsif(/-2/) { $use=$USETWO;      }
47		elsif(/-v/) { $verbose=1;        }
48		elsif(/-q/) { $verbose=0;        }
49		elsif(/-d/) { $dryrun=1;         }
50		elsif(/-a/) { $tracktype=$ALBUM; }
51		elsif(/-t/) { $tracktype=$TRACK; }
52		elsif(/-g/) { $tracktype=$GUESS; }
53		elsif(/-N/) { $dotracknum=1; $doall=0; }
54		elsif(/-A/) { $doartist=1;   $doall=0; }
55		elsif(/-L/) { $doalbum=1;    $doall=0; }
56		elsif(/-T/) { $dotrack=1;    $doall=0; }
57		elsif(/-h/) { usage();           }
58		elsif(/--/) { $doneargs=1;       }
59		else { usage(); }
60		next;
61	}
62	$donesomething=1;
63	my $file=$_;
64	unless(-e $file)
65	{
66		warn("$me: $file: not found\n");
67		next;
68	}
69
70	if($dotracknum && $doartist && $doalbum && $dotrack)
71	{
72		$doall=1;
73	}
74
75	if((!defined($archive)) && (!$doall))
76	{
77		die("$me: -N, -A, -L and -T only supported with MP3::Archive\n");
78	}
79
80	my $mp3=MP3::Tag->new($file);
81	unless($mp3)
82	{
83		warn("$me: cannot open $file: $!\n");
84		next;
85	}
86
87	$mp3->get_tags;
88
89	my $id3=undef;
90	my $ver=undef;
91	if(exists($mp3->{ID3v2}))
92	{
93		unless($use==$USEONE)
94		{
95			$ver="ID3v2";
96			$id3=$mp3->{ID3v2};
97		}
98	}
99	elsif(exists($mp3->{ID3v1}))
100	{
101		unless($use==$USETWO)
102		{
103			$ver="ID3v1";
104			$id3=$mp3->{ID3v1};
105		}
106	}
107	else
108	{
109		warn("$me: $file: no id3 tags found\n");
110		next;
111	}
112
113	unless(defined $id3)
114	{
115		warn("$me:$file:cannot find requested ",
116			 ("","ID3v1 ","ID3v2 ")[$use],
117			 "tag\n");
118		next;
119	}
120	my $id3tracknum=$id3->track;
121	my $id3artist=$id3->artist;
122	my $id3album=$id3->album;
123	my $id3track=$id3->song;
124
125	$mp3->close;
126
127	unless(defined ($id3tracknum))
128	{
129		warn("$me:$file:no tracknumber defined in $ver tag\n");
130		next;
131	}
132
133	unless(defined ($id3artist))
134	{
135		warn("$me:$file:no artist defined in $ver tag\n");
136		next;
137	}
138
139	unless(defined ($id3album))
140	{
141		warn("$me:$file:no album defined in $ver tag\n");
142		next;
143	}
144
145	unless(defined ($id3track))
146	{
147		warn("$me:$file:no track defined in $ver tag\n");
148		next;
149	}
150
151	if($id3tracknum=~/^\d$/)
152	{
153		$id3tracknum="0$id3tracknum";
154	}
155
156	# default if MP3::Archive not installed
157	my $newfile="$id3tracknum - $id3artist - $id3album - $id3track.mp3";
158
159	if(defined($archive))
160	{
161		my ($ftracknum,$fartist,$falbum,$ftrack);
162		if((!$dotracknum) && (!$doall))
163		{
164			$ftracknum=$archive->tracknum($file,$tracktype);
165			unless(defined($ftracknum))
166			{
167				print("$me: $file: cannot extract tracknum, skipping.\n");
168				next;
169			}
170			$id3tracknum=$ftracknum;
171		}
172		if((!$doartist) && (!$doall))
173		{
174			$fartist=$archive->artist($file,$tracktype);
175			unless(defined($fartist))
176			{
177				print("$me: $file: cannot extract artist, skipping.\n");
178				next;
179			}
180			$id3artist=$fartist;
181		}
182		if((!$doalbum) && (!$doall))
183		{
184			$falbum=$archive->album($file,$tracktype);
185			unless(defined($falbum))
186			{
187				print("$me: $file: cannot extract album, skipping.\n");
188				next;
189			}
190			$id3album=$falbum;
191		}
192		if((!$dotrack) && (!$doall))
193		{
194			$ftrack=$archive->track($file,$tracktype);
195			unless(defined($ftrack))
196			{
197				print("$me: $file: cannot extract track, skipping.\n");
198				next;
199			}
200			$id3track=$ftrack;
201		}
202
203		$newfile=$archive->makefilename($file,$id3tracknum,$id3artist,$id3album,$id3track,"mp3",$tracktype);
204	}
205
206	if($newfile eq $file)
207	{
208		next;
209	}
210
211	if(-e $newfile)
212	{
213		warn("$me:$file: cannot rename: file exists: $newfile\n");
214		next;
215	}
216
217	my $skip=0;
218	unless($dryrun)
219	{
220		unless(rename($file,$newfile))
221		{
222			$skip=1;
223			warn("$me:$file: cannot rename to $newfile:$!\n");
224		}
225	}
226
227	if((!$skip) && ($verbose || $dryrun))
228	{
229		print("$file -> $newfile\n");
230	}
231}
232
233usage() unless($donesomething);
234
235sub usage
236{
237	die("Usage: $me [-vqd12atgNALT] file.mp3..\n".
238		" -v\tVerbose.\n".
239		" -q\tQuiet (default).\n".
240		" -d\tDry run (just show what it would do).\n".
241		" -1\tRead ID3v1 tag only.\n".
242		" -2\tRead ID3v2 tag only.\n".
243		" -a\tTreat as album tracks.\n".
244		" -t\tTreat as non-album tracks.\n".
245		" -g\tGuess track type from file location (default).\n".
246		" -N\tSet trackNumber.\n".
247		" -A\tSet Artist.\n".
248		" -L\tSet aLbum.\n".
249		" -T\tSet Track.\n".
250		" -h\tThis help.\n".
251		" --\tEnd of options.\n".
252		"Default: set all four data types.\n".
253		"-N/-A/-L/-T require the current filename to be parsable.\n");
254}
255
256
257__DATA__
258
259=head1 NAME
260
261id3tofilename - renames mp3 files based on id3 metadata
262
263=head1 SYNOPSIS
264
265B<id3tofilename> [I<-v>] [I<-q>] [I<-d>] [I<-1>] [I<-2>] [I<-a>] [I<-t>] [I<-g>] [I<-N>] [I<-A>] [I<-L>] [I<-T>] [I<-h>] [I<file.mp3>...]
266
267=head1 DESCRIPTION
268
269id3tofilename renames mp3 files based on the metadata in id3 tags.
270For details of how to specify the resulting filename formats, by
271configuring F<.mp3archiverc>, see L<MP3::Archive::Config(3)>.
272
273If any of the B<-N>/B<-A>/B<-L>/B<-T> options are set, it gets the
274remaining data from the current filename, so the filename must already
275be parsable by L<MP3::Archive(3)>. If none of those options are
276supplied, it defaults to setting all data in the filename from the id3
277tags, so the current filename does not have to be in any particular
278format.
279
280=head1 OPTIONS
281
282=over 4
283
284=item B<-v>
285
286Verbose
287
288=item B<-q>
289
290Quiet (no output). This is the default.
291
292=item B<-d>
293
294Dry run. Just show what it would do.
295
296=item B<-1>
297
298Use ID3V1 tag only
299
300=item B<-2>
301
302Use ID3V2 tag only
303
304=item B<-a>
305
306Treat files as album tracks.
307
308=item B<-t>
309
310Treat files as non-album tracks.
311
312=item B<-g>
313
314Guess whether to treat files as album tracks or not, based on the
315current directory of the file.
316
317=item B<-N>
318
319Only set the TRKNUM field from id3 tags.
320
321=item B<-A>
322
323Only set the ARTIST field from id3 tags.
324
325=item B<-L>
326
327Only set the ALBUM field from id3 tags.
328
329=item B<-T>
330
331Only set the TRACK field from id3 tags.
332
333=item B<-h>
334
335Show a short help message.
336
337=back
338
339=head1 BUGS
340
341None known. Please report any found to ianb@nessie.mcc.ac.uk
342
343=head1 SEE ALSO
344
345L<filenametoid3(1)>, L<MP3::Archive(3)>, L<MP3::Archive::Config(3)>,
346L<mp3-archive-tools(1)>, L<mp3lint(1)>
347
348=head1 AUTHOR
349
350Ian Beckwith <ianb@nessie.mcc.ac.uk>
351
352=head1 AVAILABILITY
353
354id3tofilename is part of the mp3-archive-tools package.
355
356The latest version can be found at:
357
358B<http://nessie.mcc.ac.uk/~ianb/projects/mp3-archive-tools/>
359
360=head1 COPYRIGHT
361
362Copyright 2003 Ian Beckwith <ianb@nessie.mcc.ac.uk>
363
364This program is free software; you can redistribute it and/or modify
365it under the terms of the GNU General Public License as published by
366the Free Software Foundation; either version 2 of the License, or (at
367your option) any later version.
368
369This program is distributed in the hope that it will be useful, but
370WITHOUT ANY WARRANTY; without even the implied warranty of
371MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
372General Public License for more details.
373
374You should have received a copy of the GNU General Public License
375along with this program; if not, write to the Free Software
376Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
377
378=cut
379
380
381
382