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