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