Changed random and limit functionality to be fucking awesome
authorChip Black <bytex64@bytex64.net>
Mon, 9 Feb 2009 19:47:38 +0000 (13:47 -0600)
committerChip Black <bytex64@bytex64.net>
Mon, 9 Feb 2009 19:47:38 +0000 (13:47 -0600)
Created a db_select subroutine that returns a closure that returns
filenames until there are no more, then returns undef.  Random
functionality has been moved into this routine, so 'random' and
'limit==foo' are now query keywords instead of a special case.  Also,
random is now done in the DB layer for even greater speed.

udb.pl

diff --git a/udb.pl b/udb.pl
index 262cb5f..3f0fbbd 100755 (executable)
--- a/udb.pl
+++ b/udb.pl
@@ -194,6 +194,84 @@ sub db_info {
        print "\tsize\t\t", -s $file, "\n";
 }
 
+# db_select returns an anonymous function that returns a filename on
+# every call, then undef when there are no more filenames
+sub db_select {
+       my (@where, $order, $ltype, $limit);
+
+       $order = 'ORDER BY artist,album,year,track';
+       foreach (@_) {
+               if (/^(artist|title|album|genre|comment|filename)(==|=)(.*?)$/) {
+                       my ($k, $op, $v) = ($1, $2, $3);
+                       $v =~ s/"/""/g;
+                       if ($op eq '==') {
+                               push @where, qq{$k LIKE "$v"};
+                       } else {
+                               push @where, qq{$k LIKE "\%$v\%"};
+                       }
+               } elsif (/^(track|year|length|bitrate|samplerate|channels)(>|<|==|=)(.*)$/) {
+                       my ($k, $op, $v) = ($1, $2, int($3));
+                       if ($op eq '=' or $op eq '==') {
+                               push @where, qq{$k=$v};
+                       } elsif ($op eq '>' or $op eq '<') {
+                               push @where, qq{$k$op$v};
+                       }
+               } elsif ($_ eq 'random') {
+                       $order = 'ORDER BY random()';
+               } elsif (/^limit==?(.*)$/) {
+                       my $v = $1;
+                       if ($v =~ /^\d+$/) {
+                               $order .= " LIMIT $v";
+                       } elsif ($v =~ /:/) {
+                               $ltype = 'time';
+                               $limit = time_expand($v);
+                       } elsif ($v =~ /[bkmgt]$/) {
+                               $ltype = 'size';
+                               $limit = size_expand($v);
+                       } else {
+                               print "Unknown type of limit: $v\n";
+                               exit 1;
+                       }
+               } else {
+                       s/"/""/g;
+                       push @where, qq{(artist LIKE "\%$_\%" OR title LIKE "\%$_\%" OR album LIKE "\%$_\%" OR comment LIKE "\%$_\%" OR filename LIKE "\%$_\%")};
+               }
+       }
+       my $query = "SELECT filename,length FROM tunes ";
+       if (@where) {
+               $query .= "WHERE " . join(' AND ', @where) . ' ';
+       }
+       $query .= $order;
+       #print $query,"\n";
+       my $sth = $dbh->prepare($query) or die $dbh->errstr;
+       $sth->execute;
+
+       # /me kicks lisp in the teeth
+       if (not defined $ltype) {
+               return sub {
+                       return ($sth->fetchrow_array)[0];
+               }
+       } elsif ($ltype eq 'time') {
+               my $length = 0;
+               return sub {
+                       if ($length < $limit) {
+                               my $fi = $sth->fetchrow_hashref;
+                               $length += $fi->{length};
+                               return $fi->{filename};
+                       }
+               }
+       } elsif ($ltype eq 'size') {
+               my $size = 0;
+               return sub {
+                       if ($size < $limit) {
+                               my $file = ($sth->fetchrow_array)[0];
+                               $size += -s $file;
+                               return $file;
+                       }
+               }
+       }
+}
+
 if ($ARGV[0] eq 'add') {
        shift;
        $dbh->begin_work;
@@ -229,7 +307,7 @@ if ($ARGV[0] eq 'add') {
        while ($r = $sth->fetchrow_hashref()) {
                if (blacklisted($r->{filename}) ||
                    ! (-e $r->{filename} || -e decode('utf8', $r->{filename}))) {
-                       print "Purging |$r->{filename}|\n";
+                       print "Purging $r->{filename}\n";
                        my $hash = $r->{hash};
                        $dbh->do(qq{DELETE FROM tunes WHERE hash="$hash"});
                }
@@ -246,65 +324,6 @@ if ($ARGV[0] eq 'add') {
                        db_info($_);
                }
        }
-} elsif ($ARGV[0] eq 'random') {
-       shift;
-       my $method = shift;
-       my $quantity = shift;
-       if ($method eq 'size') {
-               $quantity = size_expand($quantity);
-       } elsif ($method eq 'length') {
-               $quantity = time_expand($quantity);
-       } elsif ($method eq 'all') {
-               ($quantity) = $dbh->selectrow_array(qq{SELECT count(*) FROM tunes});
-               $method = 'count';
-       } elsif ($method eq 'count') {
-               # Do nothing
-       } elsif ($method =~ /^\d+$/) {
-               $quantity = int($method);
-               $method = 'count';
-       } else {
-               print "Unknown kind of random: $method $quantity\n";
-               exit 1;
-       }
-       my $n_files;
-
-       if ($method eq 'count') {
-               # This could take a very large amount of memory...
-               my @files = @{$dbh->selectcol_arrayref(qq{SELECT filename FROM tunes})};
-
-               for my $i (1..$quantity) {
-                       my $n = int(rand(@files));
-                       print scalar splice(@files, $n, 1), "\n";
-                       last unless @files;
-               }
-       } elsif ($method eq 'size') {
-               my @files = @{$dbh->selectcol_arrayref(qq{SELECT filename FROM tunes})};
-
-               my $size = 0;
-               while ($size < $quantity) {
-                       my $n = int(rand(@files));
-                       my $file = splice(@files, $n, 1);
-                       $size += -s $file;
-                       print "$file\n";
-                       last unless @files;
-               }
-       } elsif ($method eq 'length') {
-               my @files;
-               my $sth = $dbh->prepare(qq{SELECT filename,length FROM tunes});
-               $sth->execute;
-               while (my $row = $sth->fetchrow_arrayref) {
-                       push @files, [@$row];
-               }
-
-               my $length = 0;
-               while ($length < $quantity) {
-                       my $n = int(rand(@files));
-                       my $file = splice(@files, $n, 1);
-                       $length += $file->[1];
-                       print "$file->[0]\n";
-                       last unless @files;
-               }
-       }
 } elsif ($ARGV[0] eq 'scan') {
        shift;
        unless (@ARGV) {
@@ -349,35 +368,9 @@ if ($ARGV[0] eq 'add') {
        $import_dbh->disconnect;
        $dbh->commit;
 } else {
-       my @where;
-       foreach (@ARGV) {
-               if (/^(artist|title|album|genre|comment|filename)(==|=)(.*?)$/) {
-                       my ($k, $op, $v) = ($1, $2, $3);
-                       $v =~ s/"/""/g;
-                       if ($op eq '==') {
-                               push @where, qq{$k LIKE "$v"};
-                       } else {
-                               push @where, qq{$k LIKE "\%$v\%"};
-                       }
-               } elsif (/^(track|year|length|bitrate|samplerate|channels)(>|<|==|=)(.*)$/) {
-                       my ($k, $op, $v) = ($1, $2, int($3));
-                       if ($op eq '=' or $op eq '==') {
-                               push @where, qq{$k=$v};
-                       } elsif ($op eq '>' or $op eq '<') {
-                               push @where, qq{$k$op$v};
-                       }
-               } else {
-                       s/"/""/g;
-                       push @where, qq{(artist LIKE "\%$_\%" OR title LIKE "\%$_\%" OR album LIKE "\%$_\%" OR comment LIKE "\%$_\%" OR filename LIKE "\%$_\%")};
-               }
-       }
-       my $query = "SELECT filename FROM tunes WHERE " . join(' AND ', @where);
-       #print $query,"\n";
-       my $sth = $dbh->prepare($query);
-       $sth->execute;
-       while (my $file = ($sth->fetchrow_array)[0]) {
-               print "$file\n";
-       }
+       my $results = db_select(@ARGV);
+       my $file;
+       print "$file\n" while $file = $results->();
 }
 
 $dbh->disconnect();