Changeset 10 in mp3-find for trunk/lib/MP3/Find/DB.pm


Ignore:
Timestamp:
02/02/06 01:51:00 (18 years ago)
Author:
peter
Message:
  • added test suite for the DB backend
  • doc corrections and updates to Find.pm and Base.pm
  • moved create and update database functions from mp3db to DB.pm
  • added "destroy_db" function to DB.pm
  • documented mp3db
  • set version to 0.02
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/lib/MP3/Find/DB.pm

    r3 r10  
    55 
    66use base qw(MP3::Find::Base); 
     7use Carp; 
    78 
    89use DBI; 
     
    1112my $sql = SQL::Abstract->new; 
    1213 
     14my @COLUMNS = ( 
     15    [ mtime        => 'INTEGER' ],  # the filesystem mtime, so we can do incremental updates 
     16    [ FILENAME     => 'TEXT' ],  
     17    [ TITLE        => 'TEXT' ],  
     18    [ ARTIST       => 'TEXT' ],  
     19    [ ALBUM        => 'TEXT' ], 
     20    [ YEAR         => 'INTEGER' ],  
     21    [ COMMENT      => 'TEXT' ],  
     22    [ GENRE        => 'TEXT' ],  
     23    [ TRACKNUM     => 'INTEGER' ],  
     24    [ VERSION      => 'NUMERIC' ], 
     25    [ LAYER        => 'INTEGER' ],  
     26    [ STEREO       => 'TEXT' ], 
     27    [ VBR          => 'TEXT' ], 
     28    [ BITRATE      => 'INTEGER' ],  
     29    [ FREQUENCY    => 'INTEGER' ],  
     30    [ SIZE         => 'INTEGER' ],  
     31    [ OFFSET       => 'INTEGER' ],  
     32    [ SECS         => 'INTEGER' ],  
     33    [ MM           => 'INTEGER' ], 
     34    [ SS           => 'INTEGER' ], 
     35    [ MS           => 'INTEGER' ],  
     36    [ TIME         => 'TEXT' ], 
     37    [ COPYRIGHT    => 'TEXT' ],  
     38    [ PADDING      => 'INTEGER' ],  
     39    [ MODE         => 'INTEGER' ], 
     40    [ FRAMES       => 'INTEGER' ],  
     41    [ FRAME_LENGTH => 'INTEGER' ],  
     42    [ VBR_SCALE    => 'INTEGER' ], 
     43); 
     44 
     45 
    1346sub search { 
    1447    my $self = shift; 
    1548    my ($query, $dirs, $sort, $options) = @_; 
    1649     
    17     my $dbh = DBI->connect("dbi:SQLite:dbname=$$options{db_file}", '', ''); 
     50    croak 'Need a database name to search (set "db_file" in the call to find_mp3s)' unless $$options{db_file}; 
     51     
     52    my $dbh = DBI->connect("dbi:SQLite:dbname=$$options{db_file}", '', '', {RaiseError => 1}); 
    1853     
    1954    # use the 'LIKE' operator to ignore case 
     
    4378     
    4479    return @results; 
     80} 
     81 
     82sub create_db { 
     83    my $self = shift; 
     84    my $db_file = shift or croak "Need a name for the database I'm about to create"; 
     85    my $dbh = DBI->connect("dbi:SQLite:dbname=$db_file", '', '', {RaiseError => 1}); 
     86    $dbh->do('CREATE TABLE mp3 (' . join(',', map { "$$_[0] $$_[1]" } @COLUMNS) . ')'); 
     87} 
     88 
     89sub update_db { 
     90    my $self = shift; 
     91    my $db_file = shift or croak "Need the name of the databse to update"; 
     92    my $dirs = shift; 
     93     
     94    my @dirs = ref $dirs eq 'ARRAY' ? @$dirs : ($dirs); 
     95     
     96    my $dbh = DBI->connect("dbi:SQLite:dbname=$db_file", '', '', {RaiseError => 1}); 
     97    my $mtime_sth = $dbh->prepare('SELECT mtime FROM mp3 WHERE FILENAME = ?'); 
     98    my $insert_sth = $dbh->prepare( 
     99        'INSERT INTO mp3 (' .  
     100            join(',', map { $$_[0] } @COLUMNS) . 
     101        ') VALUES (' . 
     102            join(',', map { '?' } @COLUMNS) . 
     103        ')' 
     104    ); 
     105    my $update_sth = $dbh->prepare( 
     106        'UPDATE mp3 SET ' .  
     107            join(',', map { "$$_[0] = ?" } @COLUMNS) .  
     108        ' WHERE FILENAME = ?' 
     109    ); 
     110     
     111    # the number of records added or updated 
     112    my $count = 0; 
     113     
     114    # look for mp3s using the filesystem backend 
     115    require MP3::Find::Filesystem; 
     116    my $finder = MP3::Find::Filesystem->new; 
     117    for my $mp3 ($finder->find_mp3s(dir => \@dirs, no_format => 1)) { 
     118        # see if the file has been modified since it was first put into the db 
     119        $mp3->{mtime} = (stat($mp3->{FILENAME}))[9]; 
     120        $mtime_sth->execute($mp3->{FILENAME}); 
     121        my $records = $mtime_sth->fetchall_arrayref; 
     122         
     123        warn "Multiple records for $$mp3{FILENAME}\n" if @$records > 1; 
     124         
     125        if (@$records == 0) { 
     126            $insert_sth->execute(map { $mp3->{$$_[0]} } @COLUMNS); 
     127            print STDERR "A $$mp3{FILENAME}\n"; 
     128            $count++; 
     129        } elsif ($mp3->{mtime} > $$records[0][0]) { 
     130            # the mp3 file is newer than its record 
     131            $update_sth->execute((map { $mp3->{$$_[0]} } @COLUMNS), $mp3->{FILENAME}); 
     132            print STDERR "U $$mp3{FILENAME}\n"; 
     133            $count++; 
     134        } 
     135    } 
     136     
     137    # as a workaround for the 'closing dbh with active staement handles warning 
     138    # (see http://rt.cpan.org/Ticket/Display.html?id=9643#txn-120724) 
     139    foreach ($mtime_sth, $insert_sth, $update_sth) { 
     140        $_->{Active} = 1; 
     141        $_->finish; 
     142    } 
     143     
     144    return $count; 
     145} 
     146 
     147sub destroy_db { 
     148    my $self = shift; 
     149    my $db_file = shift or croak "Need the name of a database to destory"; 
     150    unlink $db_file; 
    45151} 
    46152 
     
    64170        }, 
    65171        ignore_case => 1, 
    66     ); 
     172        db_file => 'mp3.db', 
     173    ); 
     174     
     175    # you can do things besides just searching the database 
     176     
     177    # create another database 
     178    $finder->create_db('my_mp3s.db'); 
     179     
     180    # update the database from the filesystem 
     181    $finder->update_db('my_mp3s.db', ['/home/peter/mp3', '/home/peter/cds']); 
     182     
     183    # and then blow it away 
     184    $finder->destroy_db('my_mp3s.db'); 
    67185 
    68186=head1 REQUIRES 
     
    121239=back 
    122240 
     241=head1 METHODS 
     242 
     243=head2 create_db 
     244 
     245    $finder->create_db($db_filename); 
     246 
     247Creates a SQLite database in the file named c<$db_filename>. 
     248 
     249=head2 update_db 
     250 
     251    my $count = $finder->update_db($db_filename, \@dirs); 
     252 
     253Searches for all mp3 files in the directories named by C<@dirs> 
     254using L<MP3::Find::Filesystem>, and adds or updates the ID3 info 
     255from those files to the database. If a file already has a record 
     256in the database, then it will only be updated if it has been modified 
     257sinc ethe last time C<update_db> was run. 
     258 
     259=head2 destroy_db 
     260 
     261    $finder->destroy_db($db_filename); 
     262 
     263Permanantly removes the database. 
     264 
    123265=head1 TODO 
    124266 
    125 Move the database/table creation code from F<mp3db> into this 
    126 module. 
    127  
    128267Database maintanence routines (e.g. clear out old entries) 
    129268 
     269Allow the passing of a DSN or an already created C<$dbh> instead 
     270of a SQLite database filename. 
     271 
    130272=head1 SEE ALSO 
    131273 
    132 L<MP3::Find>, L<MP3::Find::DB> 
     274L<MP3::Find>, L<MP3::Find::Filesystem>, L<mp3db> 
    133275 
    134276=head1 AUTHOR 
Note: See TracChangeset for help on using the changeset viewer.