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