Changeset 29 in mp3-find
- Timestamp:
- 05/21/06 05:17:23 (19 years ago)
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/bin/mp3db
r23 r29 13 13 $DB_FILE ||= catfile($ENV{HOME}, 'mp3.db'); 14 14 15 my @ DIRS = @ARGV;15 my @NAMES = @ARGV; 16 16 17 17 my $f = MP3::Find::DB->new; 18 18 $f->create_db($DB_FILE) if $CREATE; 19 $f->update_db($DB_FILE, \@DIRS) if @DIRS; 19 20 if (@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 } 20 30 21 31 =head1 NAME -
trunk/lib/MP3/Find/DB.pm
r20 r29 9 9 use DBI; 10 10 use SQL::Abstract; 11 12 use MP3::Find::Util qw(get_mp3_metadata); 11 13 12 14 my $sql = SQL::Abstract->new; … … 48 50 }; 49 51 52 # TODO: use DSNs instead of SQLite db names 50 53 sub search { 51 54 my $self = shift; … … 84 87 } 85 88 89 # TODO: convert to using DSNs instead of hardcoded SQLite connections 90 # TODO: extended table for ID3v2 data 86 91 sub create_db { 87 92 my $self = shift; … … 90 95 $dbh->do('CREATE TABLE mp3 (' . join(',', map { "$$_[0] $$_[1]" } @COLUMNS) . ')'); 91 96 } 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 107 Compares the files in the C<files> list plus any MP3s found by searching 108 in C<dirs> to their records in the database pointed to by C<dsn>. If the 109 files 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 114 sub 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 92 197 93 198 sub update_db { … … 129 234 warn "Multiple records for $$mp3{FILENAME}\n" if @$records > 1; 130 235 131 #TODO: maybe print status updates somewhere else?132 236 if (@$records == 0) { 133 237 $insert_sth->execute(map { $mp3->{$$_[0]} } @COLUMNS); … … 154 258 } 155 259 260 # TODO: use DSNs instead of SQLite db names 156 261 sub sync_db { 157 262 my $self = shift; … … 179 284 } 180 285 286 # TODO: use DSNs instead of SQLite db names (this might get funky) 181 287 sub destroy_db { 182 288 my $self = shift; … … 342 448 Peter Eichman <peichman@cpan.org> 343 449 450 =head1 THANKS 451 452 Thanks to Matt Dietrich for suggesting having an option to just 453 update specific files instead of doing a (longer) full search. 454 344 455 =head1 COPYRIGHT AND LICENSE 345 456 -
trunk/lib/MP3/Find/Filesystem.pm
r28 r29 9 9 use MP3::Info; 10 10 use Scalar::Util qw(looks_like_number); 11 12 use MP3::Find::Util qw(get_mp3_metadata); 11 13 12 14 eval { … … 97 99 return if $filename =~ $$options{exclude_path}; 98 100 } 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 }); 126 106 127 107 for my $field (keys(%{ $query })) { -
trunk/lib/MP3/Find/Util.pm
r1 r29 7 7 use vars qw(@EXPORT_OK); 8 8 9 @EXPORT_OK = qw(build_query); 9 @EXPORT_OK = qw(build_query get_mp3_metadata); 10 11 use Carp; 12 use MP3::Info; 13 14 eval { require MP3::Tag }; 15 my $CAN_USE_ID3V2 = $@ ? 0 : 1; 10 16 11 17 sub build_query { … … 38 44 } 39 45 46 sub 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 40 81 # module return 41 82 1;
Note: See TracChangeset
for help on using the changeset viewer.