id3fs-tag: don't print filename on tags line
[id3fs.git] / bin / id3fs-tag
1 #!/usr/bin/perl -w
2 #
3 # id3fs - a FUSE-based filesystem for browsing audio metadata
4 # Copyright (C) 2010  Ian Beckwith <ianb@erislabs.net>
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19 use lib '/home/ianb/projects/id3fs/id3fs/lib'; # FIXME: remove
20 use strict;
21 use Getopt::Long qw(Configure);
22 use ID3FS::AudioFile;
23 use vars qw($me);
24 $me=($0=~/(?:.*\/)?(.*)/)[0];
25
26 my $verbose=0;
27 my $help=0;
28 my ($artist, $album, $track, $tracknum, $year, $v1genre, $comment,
29     $delete_artist, $delete_album, $delete_track, $delete_tracknum,
30     $delete_year, $delete_v1genre, $delete_comment, $delete_all,
31     $delete_genre, $genre, $add_tags, $delete_tags, $overwrite_tagvals,
32     $delete_tagvals);
33
34 Configure(qw(bundling no_ignore_case));
35 my $optret=GetOptions(
36     "verbose|v"                      => \$verbose,
37     "help|h"                         => \$help,
38     "artist|a=s"                     => \$artist,
39     "album|l=s"                      => \$album,
40     "song|s=s"                       => \$track,
41     "tracknum|n=s"                   => \$tracknum,
42     "year|y=i"                       => \$year,
43     "v1genre|1=s"                    => \$v1genre,
44     "comment|c=s"                    => \$comment,
45     "delete-artist|A"                => \$delete_artist,
46     "delete-album|L"                 => \$delete_album,
47     "delete-song|S"                  => \$delete_track,
48     "delete-tracknum|N"              => \$delete_tracknum,
49     "delete-year|Y"                  => \$delete_year,
50     "delete-v1genre|0"               => \$delete_v1genre,
51     "delete-comment|C"               => \$delete_comment,
52     "delete|delete-all|D"            => \$delete_all,
53     "delete-genre|delete-all-tags|G" => \$delete_genre,
54     "genre|g|replace-all-tags|R=s"   => \$genre,
55     "add-tags|tags|t=s"              => \$add_tags,
56     "overwrite-tagvals|tagvals|o=s"  => \$overwrite_tagvals,
57     "delete-tags|T=s"                => \$delete_tags,
58     "delete-tags-with-values|O=s"    => \$delete_tagvals,
59     );
60
61 usage() if(!@ARGV || !$optret || $help);
62
63 while(my $filename=shift @ARGV)
64 {
65     unless(-f $filename)
66     {
67         warn("$me: $filename: not found\n");
68         next;
69     }
70     my $file=ID3FS::AudioFile->new($filename);
71     next unless($file);
72     my $changes=0;
73     $changes =  do_deletes($file);
74     $changes += do_adds($file);
75     if($changes)
76     {
77         do_write($file);
78     }
79     else
80     {
81         do_display($file);
82     }
83 }
84
85 sub do_deletes
86 {
87     my($file)=@_;
88     if($delete_all)
89     {
90         $file->delete_all();
91         # we don't want to save the tag if we've deleted it
92         return 0;
93     }
94     $file->delete_artist()   if($delete_artist);
95     $file->delete_album()    if($delete_album);
96     $file->delete_track()    if($delete_track);
97     $file->delete_tracknum() if($delete_tracknum);
98     $file->delete_year()     if($delete_year);
99     $file->delete_v1genre()  if($delete_v1genre);
100     $file->delete_comment()  if($delete_comment);
101     $file->delete_genre()    if($delete_genre || $genre);
102     $file->delete_tags($delete_tags, 0)       if($delete_tags);
103     $file->delete_tags($delete_tagvals, 1)    if($delete_tagvals);
104     $file->delete_tags($overwrite_tagvals, 1) if($overwrite_tagvals);
105
106     my $donesomething=($delete_artist   || $delete_album   || $delete_track   ||
107                        $delete_tracknum || $delete_year    || $delete_v1genre ||
108                        $delete_comment  || $delete_genre   || $delete_tags    ||
109                        $delete_tagvals  || defined($genre) || $overwrite_tagvals);
110     return($donesomething ? 1 : 0);
111 }
112
113 sub do_adds
114 {
115     my($file)=@_;
116     $file->artist($artist)     if($artist);
117     $file->album($album)       if($album);
118     $file->track($track)       if($track);
119     $file->tracknum($tracknum) if($tracknum);
120     $file->year($year)         if($year);
121     $file->v1genre($v1genre)   if($v1genre);
122     $file->comment($comment)   if($comment);
123     $file->add_tags($add_tags) if($add_tags);
124     $file->add_tags($genre)    if($genre);
125     $file->add_tags($overwrite_tagvals) if($overwrite_tagvals);
126
127     my $donesomething=(defined($artist)   || defined($album) ||
128                        defined($track)    || defined($tracknum) ||
129                        defined($year)     || defined($v1genre) ||
130                        defined($comment)  || defined($genre) ||
131                        defined($add_tags) || defined($overwrite_tagvals));
132     return( $donesomething ? 1 : 0 );
133 }
134
135 sub do_write
136 {
137     my($file)=@_;
138     $file->write();
139 }
140
141 sub do_display
142 {
143     my($file)=@_;
144     my $artist=$file->artist();
145     my $album=$file->album();
146     my $track=$file->track();
147     my $tracknum=$file->tracknum();
148     my $year=$file->year();
149     my $comment=$file->comment();
150     my $v1genre=$file->v1genre();
151     my @tags=$file->tags();
152     @tags = map { (ref($_) eq "ARRAY") ? join('/', grep {defined;} @{$_}) : $_; } @tags;
153     if($verbose)
154     {
155         print $file->path(), ":\n";
156         print "  tracknum: $tracknum\n" if($tracknum);
157         print "  artist: $artist\n"     if($artist);
158         print "  album: $album\n"       if($album);
159         print "  song: $track\n"        if($track);
160         print "  year: $year\n"         if($year);
161         print "  v1genre: $v1genre\n"   if($v1genre);
162         print "  comment: $comment\n"   if($comment);
163     }
164     else
165     {
166         my @fields=($file->path(), $tracknum, $artist, $album, $track,
167                     $year, $v1genre, $comment);
168         @fields=map { defined($_) ? $_ : ""; } @fields;
169         print join(':', @fields), "\n";
170     }
171     if(@tags)
172     {
173         if($verbose) { print "  tags: "; }
174 #       else         { print $file->path() . ":tags:"; }
175         else         { print "tags:"; }
176         print join(", ", @tags), "\n";
177     }
178 }
179
180 sub usage
181 {
182     die("Usage: $me [-vhALSNY0CDG] [-a ARTIST] [-l ALBUM] [-s SONG] [-n TRACKNUM] FILES...\n".
183         "       $me [-y YEAR] [-g GENRE] [-1 V1GENRE] [-c COMMENT] [--] FILES...\n".
184         "       $me [-t TAGS,TO,ADD] [-T TAGS,TO,DELETE]  FILES...\n".
185         "       $me [-r TAGS,TO,DELETE TAGS,TO,ADD] [-R TAGS,TO,OVERWRITE,WITH] FILES...\n".
186         "With no options, displays current info in tag\n".
187         "Options:\n".
188         " -a|--artist=ARTIST                    Set artist\n".
189         " -l|--album=ALBUM                      Set album\n".
190         " -s|--song=SONG                        Set song\n".
191         " -n|--tracknum=NUM                     Set tracknum\n".
192         " -y|--year=NUM                         Set year\n".
193         " -1|--v1genre=GENRE                    Set ID3v1 genre\n".
194         " -c|--comment=COMMENT                  Set comment\n".
195         " -A|--delete-artist                    Delete artist\n".
196         " -L|--delete-album                     Delete album\n".
197         " -S|--delete-song                      Delete song\n".
198         " -N|--delete-tracknum                  Delete tracknum\n".
199         " -Y|--delete-year                      Delete year\n".
200         " -0|--delete-v1genre                   Delete ID3v1 genre\n".
201         " -C|--delete-comment                   Delete comment\n".
202         " -D|--delete|delete-all                Delete entire ID3 tag\n".
203         " -G|--delete-genre|--delete-all-tags   Delete all tags stored in genre\n".
204         " -g|-R|replace-all-tags|--genre=GENRE  Replace all tags in genre tag\n".
205         " -t|--add-tags|tags=TAG1,TAG2          Add tags to genre tag, merging with existing ones\n".
206         " -T|--delete-tags=TAG1,TAG2            Delete tags from genre\n".
207         " -r|--replace-tags TAGS1 TAGS2         Replace TAGS1 in genre with TAGS2\n".
208         " -v|--verbose                          Verbose display\n".
209         " -h|--help                             This help\n".
210         " --                                    End of options\n");
211 }
212
213 __END__
214
215 =head1 NAME
216
217 id3fs-tag - Add files to id3fs index
218
219 =head1 SYNOPSIS
220
221 B<id3fs-tag> [B<-lvh>] S<[B<-d >I<basedir>]> S<[B<-f >I<dbpath>]> S<[B<-e >I<mp3,ogg,flac>]> [B<-->] [I<DIR>...]
222
223 =head1 DESCRIPTION
224
225 Extracts id3 tags from mp3 files (and comment tags from ogg and flac
226 files) and adds them to a sqlite database, ready for mounting
227 with L<id3fsd(8)>.
228
229 =head1 OPTIONS
230
231 =over 4
232
233 =item B<-l> | B<--list>
234
235 List tags in use in specified database.
236
237 =item S<B<-d >I<PATH>> | S<B<--dir=>I<PATH>>
238
239 Specify base directory of source files. All files will be indexed
240 relative to this point.
241
242 If not specified, defaults to the first non-option argument on the
243 command line. Note that to avoid ambiguities, if more than one
244 directory is specified on the command line, the base directory must
245 be specified explicitly.
246
247 All files indexed must be under the base directory.
248
249 =item S<B<-f >I<FILE>> | S<B<--database=>I<FILE>>
250
251 Database file to use. If not specified, defaults to
252 a hidden file called B<".id3fs"> under the base directory.
253
254 =item S<B<-e >I<EXT1,EXT2>> | S<B<--extensions=>I<EXT1,EXT2>>
255
256 File extensions to consider when indexing.
257 Defaults to B<.mp3>, B<.ogg> and B<.flac>.
258
259 =item B<-v>
260
261 Enable verbose operation.
262
263 =item B<-h>
264
265 Show a short help message.
266
267 =item B<-->
268
269 End of options.
270
271 =back
272
273 =head1 EXAMPLES
274
275 Index all files in the current directory:
276
277     id3fs-tag .
278
279 Index current directory, printing each subdirectory as it recurses
280 into it:
281
282     id3fs-tag -v .
283
284 Just index some sub-directories:
285
286     id3fs-tag -d . dir1 dir2
287
288 Store the database in a custom location:
289
290     id3fs-tag -f ~/.id3fs/index.sqlite .
291
292 Only index .mp3 and .flac files:
293
294     id3fs-tag -e mp3,flac .
295
296 =head1 BUGS
297
298 Please report any found to ianb@erislabs.net
299
300 =head1 SEE ALSO
301
302 L<id3fsd(8)>, L<MP3::Tag>, L<Audio::Flac::Header>, L<Ogg::Vorbis::Header>
303
304 =head1 AUTHOR
305
306 Ian Beckwith <ianb@erislabs.net>
307
308 Many thanks to Aubrey Stark-Toller for help wrangling SQL.
309
310 =head1 AVAILABILITY
311
312 The latest version can be found at:
313
314 L<http://erislabs.net/ianb/projects/id3fs/>
315
316 =head1 COPYRIGHT
317
318 Copyright (C) 2010  Ian Beckwith <ianb@erislabs.net>
319
320 This program is free software: you can redistribute it and/or modify
321 it under the terms of the GNU General Public License as published by
322 the Free Software Foundation, either version 3 of the License, or
323 (at your option) any later version.
324
325 This program is distributed in the hope that it will be useful,
326 but WITHOUT ANY WARRANTY; without even the implied warranty of
327 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
328 GNU General Public License for more details.
329
330 You should have received a copy of the GNU General Public License
331 along with this program.  If not, see <http://www.gnu.org/licenses/>.
332
333 =cut