Changeset 29 in mp3-find


Ignore:
Timestamp:
05/21/06 05:17:23 (18 years ago)
Author:
peter
Message:
  • factored out 'get_mp3_metadata' function from Filesystem.pm to Util.pm
  • added 'update' function to DB.pm that combines 'update_db' and 'update_file' (for updating just specific files); idea courtesy of Matt Dietrich
  • modified mp3db to let you mix and match files and directories on the command line; now also uses the 'update' function in DB.pm
Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/bin/mp3db

    r23 r29  
    1313$DB_FILE ||= catfile($ENV{HOME}, 'mp3.db'); 
    1414 
    15 my @DIRS = @ARGV; 
     15my @NAMES = @ARGV; 
    1616 
    1717my $f = MP3::Find::DB->new; 
    1818$f->create_db($DB_FILE) if $CREATE; 
    19 $f->update_db($DB_FILE, \@DIRS) if @DIRS; 
     19 
     20if (@NAMES) { 
     21    my @files = grep { -f } @NAMES; 
     22    my @dirs  = grep { -d } @NAMES; 
     23 
     24    $f->update({ 
     25        dsn   => "dbi:SQLite:dbname=$DB_FILE", 
     26        dirs  => \@dirs, 
     27        files => \@files, 
     28    }); 
     29} 
    2030 
    2131=head1 NAME 
  • trunk/lib/MP3/Find/DB.pm

    r20 r29  
    99use DBI; 
    1010use SQL::Abstract; 
     11 
     12use MP3::Find::Util qw(get_mp3_metadata); 
    1113 
    1214my $sql = SQL::Abstract->new; 
     
    4850}; 
    4951 
     52# TODO: use DSNs instead of SQLite db names 
    5053sub search { 
    5154    my $self = shift; 
     
    8487} 
    8588 
     89# TODO: convert to using DSNs instead of hardcoded SQLite connections 
     90# TODO: extended table for ID3v2 data 
    8691sub create_db { 
    8792    my $self = shift; 
     
    9095    $dbh->do('CREATE TABLE mp3 (' . join(',', map { "$$_[0] $$_[1]" } @COLUMNS) . ')'); 
    9196} 
     97 
     98# this is update_db and update_files (from Matt Dietrich) rolled into one 
     99=head2 update 
     100 
     101    my $count = $finder->update({ 
     102        dsn   => 'dbi:SQLite:dbname=mp3.db', 
     103        files => \@filenames, 
     104        dirs  => [qw(music downloads/mp3)], 
     105    }); 
     106 
     107Compares the files in the C<files> list plus any MP3s found by searching 
     108in C<dirs> to their records in the database pointed to by C<dsn>. If the 
     109files found have been updated since they have been recorded in the database 
     110(or if they are not in the database), they are updated (or added). 
     111 
     112=cut 
     113 
     114sub update { 
     115    my $self = shift; 
     116    my $args = shift; 
     117 
     118    my $dsn   = $args->{dsn} or croak "Need a DSN to connect to"; 
     119    my @dirs  = $args->{dirs} 
     120                    ? ref $args->{dirs} eq 'ARRAY' 
     121                        ? @{ $args->{dirs} } 
     122                        : ($args->{dirs}) 
     123                    : (); 
     124 
     125    my @files  = $args->{files} 
     126                    ? ref $args->{files} eq 'ARRAY'  
     127                        ? @{ $args->{files} } 
     128                        : ($args->{files}) 
     129                    : (); 
     130     
     131    my $status_callback = $self->{status_callback} || $DEFAULT_STATUS_CALLBACK; 
     132 
     133    my $dbh = DBI->connect($dsn, '', '', {RaiseError => 1}); 
     134    my $mtime_sth = $dbh->prepare('SELECT mtime FROM mp3 WHERE FILENAME = ?'); 
     135    my $insert_sth = $dbh->prepare( 
     136        'INSERT INTO mp3 (' .  
     137            join(',', map { $$_[0] } @COLUMNS) . 
     138        ') VALUES (' . 
     139            join(',', map { '?' } @COLUMNS) . 
     140        ')' 
     141    ); 
     142    my $update_sth = $dbh->prepare( 
     143        'UPDATE mp3 SET ' .  
     144            join(',', map { "$$_[0] = ?" } @COLUMNS) .  
     145        ' WHERE FILENAME = ?' 
     146    ); 
     147     
     148    my $count = 0;  # the number of records added or updated 
     149    my @mp3s;       # metadata for mp3s found 
     150 
     151    # look for mp3s using the filesystem backend if we have dirs to search in 
     152    if (@dirs) { 
     153        require MP3::Find::Filesystem; 
     154        my $finder = MP3::Find::Filesystem->new; 
     155        unshift @mp3s, $finder->find_mp3s(dir => \@dirs, no_format => 1); 
     156    } 
     157 
     158    # get the metadata on specific files 
     159    unshift @mp3s, map { get_mp3_metadata({ filename => $_ }) } @files; 
     160 
     161    # check each file against its record in the database 
     162    for my $mp3 (@mp3s) {        
     163        # see if the file has been modified since it was first put into the db 
     164        $mp3->{mtime} = (stat($mp3->{FILENAME}))[9]; 
     165        $mtime_sth->execute($mp3->{FILENAME}); 
     166        my $records = $mtime_sth->fetchall_arrayref; 
     167         
     168        warn "Multiple records for $$mp3{FILENAME}\n" if @$records > 1; 
     169         
     170        if (@$records == 0) { 
     171            # we are adding a record 
     172            $insert_sth->execute(map { $mp3->{$$_[0]} } @COLUMNS); 
     173            $status_callback->(A => $$mp3{FILENAME}); 
     174            $count++; 
     175        } elsif ($mp3->{mtime} > $$records[0][0]) { 
     176            # the mp3 file is newer than its record 
     177            $update_sth->execute((map { $mp3->{$$_[0]} } @COLUMNS), $mp3->{FILENAME}); 
     178            $status_callback->(U => $$mp3{FILENAME}); 
     179            $count++; 
     180        } 
     181    } 
     182     
     183    # SQLite specific code: 
     184    # as a workaround for the 'closing dbh with active staement handles warning 
     185    # (see http://rt.cpan.org/Ticket/Display.html?id=9643#txn-120724) 
     186    foreach ($mtime_sth, $insert_sth, $update_sth) { 
     187        $_->{RaiseError} = 0;  # don't die on error 
     188        $_->{PrintError} = 0;  # ...and don't even say anything 
     189        $_->{Active} = 1; 
     190        $_->finish; 
     191    } 
     192     
     193    return $count; 
     194} 
     195 
     196 
    92197 
    93198sub update_db { 
     
    129234        warn "Multiple records for $$mp3{FILENAME}\n" if @$records > 1; 
    130235         
    131         #TODO: maybe print status updates somewhere else? 
    132236        if (@$records == 0) { 
    133237            $insert_sth->execute(map { $mp3->{$$_[0]} } @COLUMNS); 
     
    154258} 
    155259 
     260# TODO: use DSNs instead of SQLite db names 
    156261sub sync_db { 
    157262    my $self = shift; 
     
    179284} 
    180285 
     286# TODO: use DSNs instead of SQLite db names (this might get funky) 
    181287sub destroy_db { 
    182288    my $self = shift; 
     
    342448Peter Eichman <peichman@cpan.org> 
    343449 
     450=head1 THANKS 
     451 
     452Thanks to Matt Dietrich for suggesting having an option to just  
     453update specific files instead of doing a (longer) full search. 
     454 
    344455=head1 COPYRIGHT AND LICENSE 
    345456 
  • trunk/lib/MP3/Find/Filesystem.pm

    r28 r29  
    99use MP3::Info; 
    1010use Scalar::Util qw(looks_like_number); 
     11 
     12use MP3::Find::Util qw(get_mp3_metadata); 
    1113 
    1214eval { 
     
    9799        return if $filename =~ $$options{exclude_path}; 
    98100    } 
    99      
    100     my $mp3 = { 
    101         FILENAME => $filename, 
    102         %{ get_mp3tag($filename)  || {} }, 
    103         %{ get_mp3info($filename) || {} }, 
    104     }; 
    105      
    106     if ($CAN_USE_ID3V2 and $$options{use_id3v2}) { 
    107         # add ID3v2 tag info, if present 
    108         my $mp3_tags = MP3::Tag->new($filename); 
    109         unless (defined $mp3_tags) { 
    110             warn "Can't get MP3::Tag object for $filename\n"; 
    111         } else { 
    112             $mp3_tags->get_tags; 
    113             if (my $id3v2 = $mp3_tags->{ID3v2}) { 
    114                 for my $frame_id (keys %{ $id3v2->get_frame_ids }) { 
    115                     my ($info) = $id3v2->get_frame($frame_id); 
    116                     if (ref $info eq 'HASH') { 
    117                         # use the "Text" value as the value for this frame, if present 
    118                         $mp3->{$frame_id} = $info->{Text} if exists $info->{Text}; 
    119                     } else { 
    120                         $mp3->{$frame_id} = $info; 
    121                     } 
    122                 } 
    123             } 
    124         } 
    125     } 
     101 
     102    my $mp3 = get_mp3_metadata({ 
     103        filename  => $filename, 
     104        use_id3v2 => $options->{use_id3v2}, 
     105    }); 
    126106 
    127107    for my $field (keys(%{ $query })) { 
  • trunk/lib/MP3/Find/Util.pm

    r1 r29  
    77use vars qw(@EXPORT_OK); 
    88 
    9 @EXPORT_OK = qw(build_query); 
     9@EXPORT_OK = qw(build_query get_mp3_metadata); 
     10 
     11use Carp; 
     12use MP3::Info; 
     13 
     14eval { require MP3::Tag }; 
     15my $CAN_USE_ID3V2 = $@ ? 0 : 1; 
    1016 
    1117sub build_query { 
     
    3844} 
    3945 
     46sub get_mp3_metadata { 
     47    my $args = shift; 
     48 
     49    my $filename = $args->{filename} or croak "get_mp3_metadata needs a 'filename' argument"; 
     50     
     51    my $mp3 = { 
     52        FILENAME => $filename, 
     53        %{ get_mp3tag($filename)  || {} }, 
     54        %{ get_mp3info($filename) || {} }, 
     55    }; 
     56     
     57    if ($CAN_USE_ID3V2 and $args->{use_id3v2}) { 
     58        # add ID3v2 tag info, if present 
     59        my $mp3_tags = MP3::Tag->new($filename); 
     60        unless (defined $mp3_tags) { 
     61            warn "Can't get MP3::Tag object for $filename\n"; 
     62        } else { 
     63            $mp3_tags->get_tags; 
     64            if (my $id3v2 = $mp3_tags->{ID3v2}) { 
     65                for my $frame_id (keys %{ $id3v2->get_frame_ids }) { 
     66                    my ($info) = $id3v2->get_frame($frame_id); 
     67                    if (ref $info eq 'HASH') { 
     68                        # use the "Text" value as the value for this frame, if present 
     69                        $mp3->{$frame_id} = $info->{Text} if exists $info->{Text}; 
     70                    } else { 
     71                        $mp3->{$frame_id} = $info; 
     72                    } 
     73                } 
     74            } 
     75        } 
     76    } 
     77 
     78    return $mp3; 
     79} 
     80 
    4081# module return 
    41821; 
Note: See TracChangeset for help on using the changeset viewer.