tweaked error messages
[id3fs.git] / bin / id3fs-index
1 #!/usr/bin/perl -w
2 # Ian Beckwith <ianb@erislabs.net>
3 #
4
5 use lib '/home/ianb/projects/id3fs/id3fs/lib'; # FIXME: remove
6 use strict;
7 use Getopt::Long qw(Configure);
8 use ID3FS::DB;
9 use File::Find;
10 use vars qw($me);
11 $me=($0=~/(?:.*\/)?(.*)/)[0];
12
13 my $verbose=0;
14 my $help=0;
15 my $basedir=undef;
16 my $dbpath=undef;
17 my $list=0;
18 my $init=0;
19 my @extensions=qw(mp3 flac ogg);
20 my $files_pruned;
21
22 Configure(qw(bundling no_ignore_case));
23 my $optret=GetOptions(
24     "verbose|v"      => \$verbose,
25     "quiet|q"        => sub { $verbose=0; },
26     "help|h"         => \$help,
27     "dir|d=s"        => \$basedir,
28     "database|f=s"   => \$dbpath,
29     "extensions|e=s" => sub { @extensions=split(/\s+|\s*,\s*/, $_[1]); },
30     "list|l"         => \$list,
31     );
32
33 usage() if(!@ARGV || !$optret || $help);
34 $init=1 unless($list);
35
36 if(@ARGV > 1 && !defined($basedir))
37 {
38     die("$me: --basedir must be specified if multiple paths are supplied\n");
39 }
40
41 my $db=ID3FS::DB->new($me, $verbose, $init, $dbpath, $basedir, $ARGV[0]);
42 exit unless($db);
43
44 if($list)
45 {
46     list_tags($db);
47 }
48 else
49 {
50     $db->last_update(time());
51     my $base=$db->base_dir();
52     my $abs_base=Cwd::abs_path($base);
53     while(my $path=shift)
54     {
55         if(Cwd::abs_path($path) !~ /^$abs_base/)
56         {
57             print "$me: $path is outside $base, skipping\n";
58         }
59         File::Find::find( {wanted => \&wanted, follow => 1, no_chdir => 1}, $path);
60     }
61     my $directories_pruned=$db->prune_directories();
62     if($files_pruned || $directories_pruned)
63     {
64         print "$me: removing data from pruned files\n" if $verbose;
65         $db->remove_unused();
66     }
67 }
68
69 sub wanted
70 {
71     my $ext='';
72     if(/.*\.(.*)/) { $ext=lc($1); }
73     if(-d)
74     {
75         print("$_\n") if $verbose;
76         prune($_);
77     }
78     elsif(-f && scalar(grep({ $ext eq lc($_);} @extensions)))
79     {
80         s/^\.\///;
81         $db->add($_);
82     }
83 }
84
85
86 sub prune
87 {
88     my $dir=shift;
89     return unless(opendir(DIR, $dir));
90     my @oldfiles=$db->files_in($dir);
91     my @newfiles=grep { !/^\.\.?$/; } readdir(DIR);
92     closedir(DIR);
93     @oldfiles=sort @oldfiles;
94     @newfiles=sort @newfiles;
95     my %hash;
96     @hash{@newfiles}=();
97     for my $file (@oldfiles)
98     {
99         unless(exists($hash{$file}))
100         {
101             # FIXME: add path, rebasify
102             $files_pruned=1;
103             $db->unindex($file);
104         }
105     }
106 }
107
108 sub list_tags
109 {
110     my($db)=@_;
111     my @baretags=$db->bare_tags();
112     my $valtags=$db->tags_with_values();
113     if(@baretags)
114     {
115         print "BARE TAGS\n";
116         print join(', ', sort @baretags), "\n\n";
117     }
118     if(keys(%$valtags))
119     {
120         print "TAGS WITH VALUES\n";
121         for my $key (sort keys %$valtags)
122         {
123             print "$key: ", join(', ', sort(@{$valtags->{$key}})), "\n";
124         }
125     }
126 }
127
128 sub usage
129 {
130     die("Usage: $me [-vqh] [-d basedir] [-f dbpath] [-e mp3,ogg,flac] [--] DIR...\n".
131         " -v|--verbose\t\t\tVerbose\n".
132         " -q|--quiet\t\t\tQuiet (default)\n".
133         " -d|--dir=PATH\t\t\tBase directory of source files (default: ARGV[0])\n".
134         " -f|--database=FILE\t\tPath to database file (default: basedir/.id3fs)\n".
135         " -e|--extensions=EXT1,EXT2\tFile extensions to index (default: mp3, ogg, flac)\n".
136         " -h|--help\t\t\tThis help\n".
137         " --\t\t\t\tEnd of options\n");
138 }
139
140 __END__
141
142 =head1 NAME
143
144 id3fs-index - Add files to id3fs index
145
146 =head1 SYNOPSIS
147
148 B<id3fs-index> [B<-vqh>] S<[B<-d >I<basedir>]> S<[B<-f >I<dbpath>]> S<[B<-e >I<mp3,ogg,flac>]> [B<-->] [I<DIR>...]
149
150 =head1 DESCRIPTION
151
152 Extracts id3 tags from mp3 files (and comment tags from ogg and flac
153 files) and adds them to a sqlite database, ready for mounting
154 with L<id3fsd(8)>.
155
156 =head1 OPTIONS
157
158 =over 4
159
160 =item B<-v>
161
162 Enable verbose operation.
163
164 =item B<-q>
165
166 Quiet (no output). This is the default.
167
168 =item S<B<-d >I<PATH>> | S<B<--dir=>I<PATH>>
169
170 Specify base directory of source files. All files will be indexed
171 relative to this point.
172
173 If not specified, defaults to the first non-option argument on the
174 command line. Note that to avoid ambiguities, if more than one
175 directory is specified on the command line, the base directory must
176 be specified explicitly.
177
178 All files indexed must be under the base directory.
179
180 =item S<B<-f >I<FILE>> | S<B<--database=>I<FILE>>
181
182 Database file to use. If not specified, defaults to
183 a hidden file called B<".id3fs"> under the base directory.
184
185 =item S<B<-e >I<EXT1,EXT2>> | S<B<--extensions=>I<EXT1,EXT2>>
186
187 File extensions to consider when indexing.
188 Defaults to B<.mp3>, B<.ogg> and B<.flac>.
189
190 =item B<-h>
191
192 Show a short help message.
193
194 =item B<-->
195
196 End of options.
197
198 =back
199
200 =head1 EXAMPLES
201
202 Index all files in the current directory:
203
204     id3fs-index .
205
206 Index current directory, printing each subdirectory as it recurses
207 into it:
208
209     id3fs-index -v .
210
211 Just index some sub-directories:
212
213     id3fs-index -d . dir1 dir2
214
215 Store the database in a custom location:
216
217     id3fs-index -f ~/.id3fs/index.sqlite .
218
219 Only index .mp3 and .flac files:
220
221     id3fs-index -e mp3,flac .
222
223 =head1 BUGS
224
225 Please report any found to ianb@erislabs.net
226
227 =head1 SEE ALSO
228
229 L<id3fsd(8)>
230
231 =head1 AUTHOR
232
233 Ian Beckwith <ianb@erislabs.net>
234
235 =head1 AVAILABILITY
236
237 The latest version can be found at:
238
239 B<http://erislabs.net/ianb/projects/id3fs/>
240
241 =head1 COPYRIGHT
242
243 Copyright 2010 Ian Beckwith <ianb@erislabs.net>
244
245 This program is free software: you can redistribute it and/or modify
246 it under the terms of the GNU General Public License as published by
247 the Free Software Foundation; either version 3 of the License, or
248 (at your option) any later version.
249
250 This program is distributed in the hope that it will be useful,
251 but WITHOUT ANY WARRANTY; without even the implied warranty of
252 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
253 GNU General Public License for more details.
254
255 You should have received a copy of the GNU General Public License
256 along with this program.  If not, see <http://www.gnu.org/licenses/>.
257
258 =cut