remove "use lib"
[id3fs.git] / bin / id3fs-index
index 8fa80b3..631bd63 100755 (executable)
@@ -1,8 +1,21 @@
 #!/usr/bin/perl -w
 #!/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;
 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 $help=0;
 my $basedir=undef;
 my $dbpath=undef;
+my $list=0;
+my $init=0;
 my @extensions=qw(mp3 flac ogg);
 my @extensions=qw(mp3 flac ogg);
+my $files_pruned;
 
 Configure(qw(bundling no_ignore_case));
 my $optret=GetOptions(
     "verbose|v"      => \$verbose,
 
 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]); },
     "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);
 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
 }
 
 sub wanted
@@ -48,6 +99,7 @@ sub wanted
     if(-d)
     {
        print("$_\n") if $verbose;
     if(-d)
     {
        print("$_\n") if $verbose;
+       prune($_);
     }
     elsif(-f && scalar(grep({ $ext eq lc($_);} @extensions)))
     {
     }
     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
 {
 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__
 }
 
 __END__
@@ -76,7 +173,7 @@ id3fs-index - Add files to id3fs index
 
 =head1 SYNOPSIS
 
 
 =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
 
 
 =head1 DESCRIPTION
 
@@ -88,13 +185,9 @@ with L<id3fsd(8)>.
 
 =over 4
 
 
 =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>>
 
 
 =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>.
 
 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.
 =item B<-h>
 
 Show a short help message.
@@ -157,25 +254,27 @@ Please report any found to ianb@erislabs.net
 
 =head1 SEE ALSO
 
 
 =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>
 
 
 =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:
 
 =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
 
 
 =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
 
 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,
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,