remove "use lib"
[id3fs.git] / bin / id3fs-index
index 1c479f3..631bd63 100755 (executable)
@@ -1,8 +1,21 @@
 #!/usr/bin/perl -w
-# Ian Beckwith <ianb@erislabs.net>
 #
+# id3fs - a FUSE-based filesystem for browsing audio metadata
+# Copyright (C) 2010  Ian Beckwith <ianb@erislabs.net>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-use lib '/home/ianb/projects/id3fs/id3fs/lib'; # FIXME: remove
 use strict;
 use Getopt::Long qw(Configure);
 use ID3FS::DB;
@@ -14,31 +27,69 @@ my $verbose=0;
 my $help=0;
 my $basedir=undef;
 my $dbpath=undef;
-my @extensions=qw(mp3); # ogg flac); # FIXME
+my $list=0;
+my $init=0;
+my @extensions=qw(mp3 flac ogg);
+my $files_pruned;
 
 Configure(qw(bundling no_ignore_case));
 my $optret=GetOptions(
     "verbose|v"      => \$verbose,
-    "quiet|q"        => sub { $verbose=0; },
     "help|h"         => \$help,
     "dir|d=s"        => \$basedir,
     "database|f=s"   => \$dbpath,
     "extensions|e=s" => sub { @extensions=split(/\s+|\s*,\s*/, $_[1]); },
+    "list|l"         => \$list,
     );
 
+if($list && !@ARGV)
+{
+    push(@ARGV, ".");
+}
 usage() if(!@ARGV || !$optret || $help);
+$init=1 unless($list);
 
-if(@ARGV > 1 && !defined($basedir))
+unless(defined($basedir) && defined($dbpath))
 {
-    die("$me: --basedir must be specified if multiple paths are supplied\n");
+    $basedir=ID3FS::DB::find_db($me, $init, @ARGV);
+    exit unless($basedir);
+    my $absbase=Cwd::abs_path($basedir);
+    for my $dir (@ARGV)
+    {
+       if(Cwd::abs_path($dir) !~ /^\Q$absbase\E/)
+       {
+           die("$me: $dir: must be under basedir $absbase - use --basedir to specify\n");
+       }
+    }
 }
+my $db=ID3FS::DB->new($me, $verbose, $init, $basedir, $dbpath);
+exit unless($db);
 
-my $db=ID3FS::DB->new($me, $dbpath, $basedir, $ARGV[0]);
-$db->last_update(time());
-
-while(my $path=shift)
+if($list)
+{
+    list_tags($db);
+}
+else
 {
-    File::Find::find( {wanted => \&wanted, follow => 1, no_chdir => 1}, $path);
+    $db->last_update(time());
+    my $base=$db->base_dir();
+    my $abs_base=Cwd::abs_path($base);
+    while(my $path=shift)
+    {
+       if(Cwd::abs_path($path) !~ /^$abs_base/)
+       {
+           print "$me: $path is outside $base, skipping\n";
+       }
+       File::Find::find( {wanted => \&wanted, follow => 1, no_chdir => 1}, $path);
+    }
+    my $directories_pruned=$db->prune_directories();
+    if($files_pruned || $directories_pruned)
+    {
+       print "$me: pruning removed files from db\n" if $verbose;
+       $db->remove_unused();
+    }
+    print "$me: analyzing db\n" if $verbose;
+    $db->analyze();
 }
 
 sub wanted
@@ -48,6 +99,7 @@ sub wanted
     if(-d)
     {
        print("$_\n") if $verbose;
+       prune($_);
     }
     elsif(-f && scalar(grep({ $ext eq lc($_);} @extensions)))
     {
@@ -56,16 +108,61 @@ sub wanted
     }
 }
 
+sub prune
+{
+    my $dir=shift;
+    $dir=Cwd::abs_path($dir);
+    return unless(opendir(DIR, $dir));
+    my $base=Cwd::abs_path($db->base_dir());
+    $dir=~s/^$base\/?//;
+    my @oldfiles=$db->files_in($dir);
+    my @newfiles=grep { !/^\.\.?$/; } readdir(DIR);
+    closedir(DIR);
+    @oldfiles=sort @oldfiles;
+    @newfiles=sort @newfiles;
+    my %hash;
+    # hash slice!
+    @hash{@newfiles}=();
+    for my $file (@oldfiles)
+    {
+       unless(exists($hash{$file}))
+       {
+           $files_pruned=1;
+           $db->unindex($dir, $file);
+       }
+    }
+}
+
+sub list_tags
+{
+    my($db)=@_;
+    my @baretags=$db->bare_tags();
+    my $valtags=$db->tags_with_values();
+    if(@baretags)
+    {
+       print "BARE TAGS\n";
+       print join(', ', sort @baretags), "\n\n";
+    }
+    if(keys(%$valtags))
+    {
+       print "TAGS WITH VALUES\n";
+       for my $key (sort keys %$valtags)
+       {
+           print "$key: ", join(', ', sort(@{$valtags->{$key}})), "\n";
+       }
+    }
+}
+
 sub usage
 {
-    die("Usage: $me [-vqh] [-d basedir] [-f dbpath] [-e mp3,ogg,flac] [--] DIR...\n".
-       " -v|--verbose\t\t\tVerbose\n".
-       " -q|--quiet\t\t\tQuiet (default)\n".
-       " -d|--dir=PATH\t\t\tBase directory of source files (default: ARGV[0])\n".
-       " -f|--database=FILE\t\tPath to database file (default: basedir/.id3fs)\n".
-       " -e|--extensions=EXT1,EXT2\tFile extensions to index (default: mp3, ogg, flac)\n".
-       " -h|--help\t\t\tThis help\n".
-       " --\t\t\t\tEnd of options\n");
+    die("Usage: $me [-lvh] [-d basedir] [-f dbpath] [-e mp3,ogg,flac] [--] DIR...\n".
+       " -d|--dir=PATH              Base directory of source files (default: ARGV[0])\n".
+       " -f|--database=FILE         Path to database file (default: basedir/.id3fs)\n".
+       " -e|--extensions=EXT1,EXT2  File extensions to index (default: mp3, ogg, flac)\n".
+       " -l|list                    List tags in use\n" .
+       " -v|--verbose               Verbose\n".
+       " -h|--help                  This help\n".
+       " --                         End of options\n");
 }
 
 __END__
@@ -76,7 +173,7 @@ id3fs-index - Add files to id3fs index
 
 =head1 SYNOPSIS
 
-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>...]
+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>...]
 
 =head1 DESCRIPTION
 
@@ -88,13 +185,9 @@ with L<id3fsd(8)>.
 
 =over 4
 
-=item B<-v>
+=item B<-l> | B<--list>
 
-Enable verbose operation.
-
-=item B<-q>
-
-Quiet (no output). This is the default.
+List tags in use in specified database.
 
 =item S<B<-d >I<PATH>> | S<B<--dir=>I<PATH>>
 
@@ -118,6 +211,10 @@ a hidden file called B<".id3fs"> under the base directory.
 File extensions to consider when indexing.
 Defaults to B<.mp3>, B<.ogg> and B<.flac>.
 
+=item B<-v>
+
+Enable verbose operation.
+
 =item B<-h>
 
 Show a short help message.
@@ -157,25 +254,27 @@ Please report any found to ianb@erislabs.net
 
 =head1 SEE ALSO
 
-L<id3fsd(8)>
+L<id3fsd(8)>, L<MP3::Tag>, L<Audio::Flac::Header>, L<Ogg::Vorbis::Header>
 
 =head1 AUTHOR
 
 Ian Beckwith <ianb@erislabs.net>
 
+Many thanks to Aubrey Stark-Toller for help wrangling SQL.
+
 =head1 AVAILABILITY
 
 The latest version can be found at:
 
-B<http://erislabs.net/ianb/projects/id3fs/>
+L<http://erislabs.net/ianb/projects/id3fs/>
 
 =head1 COPYRIGHT
 
-Copyright 2010 Ian Beckwith <ianb@erislabs.net>
+Copyright (C) 2010  Ian Beckwith <ianb@erislabs.net>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3 of the License, or
+the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,