| MP3-Find documentation | Contained in the MP3-Find distribution. |
MP3::Find::Filesystem - File::Find-based backend to MP3::Find
use MP3::Find::Filesystem;
my $finder = MP3::Find::Filesystem->new;
my @mp3s = $finder->find_mp3s(
dir => '/home/peter/music',
query => {
artist => 'ilyaimy',
album => 'myxomatosis',
},
ignore_case => 1,
);
File::Find, MP3::Info, Scalar::Util
MP3::Tag is also needed if you want to search using ID3v2 tags.
This module implements the search method from MP3::Find::Base
using a File::Find based search of the local filesystem.
exclude_pathScalar or arrayref; any file whose name matches any of these paths will be skipped.
use_id3v2Boolean, defaults to false. If set to true, MP3::Find::Filesystem will
use MP3::Tag to get the ID3v2 tag for each file. You can then search
for files by their ID3v2 data, using the four-character frame names.
This isn't very useful if you are just search by artist or title, but if,
for example, you have made use of the TOPE ("Orignal Performer") frame,
you could search for all the cover songs in your collection:
$finder->find_mp3s(query => { tope => '.' });
As with the basic query keys, ID3v2 query keys are converted to uppercase internally.
Peter Eichman <peichman@cpan.org>
Copyright (c) 2006 by Peter Eichman. All rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
| MP3-Find documentation | Contained in the MP3-Find distribution. |
package MP3::Find::Filesystem; use strict; use warnings; use base 'MP3::Find::Base'; use File::Find; use MP3::Info; use Scalar::Util qw(looks_like_number); use MP3::Find::Util qw(get_mp3_metadata); eval { require Sort::Key; Sort::Key->import(qw(multikeysorter)); require Sort::Key::Natural; }; my $USE_SORT_KEY = $@ ? 0 : 1; eval { require MP3::Tag }; my $CAN_USE_ID3V2 = $@ ? 0 : 1; use_winamp_genres(); sub search { my $self = shift; my ($query, $dirs, $sort, $options) = @_; # prep the search patterns as regexes foreach (keys(%$query)) { my $ref = ref $$query{$_}; # make arrays into 'OR' searches if ($ref eq 'ARRAY') { $$query{$_} = '(' . join('|', @{ $$query{$_} }) . ')'; } # convert to a regex unless it already IS a regex unless ($ref eq 'Regexp') { $$query{$_} = "^$$query{$_}\$" if $$options{exact_match}; $$query{$_} = $$options{ignore_case} ? qr[$$query{$_}]i : qr[$$query{$_}]; } } if ($$options{exclude_path}) { my $ref = ref $$options{exclude_path}; if ($ref eq 'ARRAY') { $$options{exclude_path} = '(' . join('|', @{ $$options{exclude_path} }) . ')'; } unless ($ref eq 'Regexp') { $$options{exclude_path} = qr[$$options{exclude_path}]; } } if ($$options{use_id3v2} and not $CAN_USE_ID3V2) { # they want to use ID3v2, but don't have MP3::Tag warn "MP3::Tag is required to search ID3v2 tags\n"; } # run the actual find my @results; find(sub { match_mp3($File::Find::name, $query, \@results, $options) }, $_) foreach @$dirs; # sort the results if (@$sort) { if ($USE_SORT_KEY) { # use Sort::Key to do a (hopefully!) faster sort #TODO: profile this; at first glance, it doesn't actually seem to be any faster #warn "Using Sort::Key"; my $sorter = multikeysorter( sub { my $info = $_; map { $info->{uc $_} } @$sort }, map { 'natural' } @$sort ); @results = $sorter->(@results); } else { @results = sort { my $compare; foreach (map { uc } @$sort) { # use Scalar::Util to do the right sort of comparison $compare = (looks_like_number($a->{$_}) && looks_like_number($b->{$_})) ? $a->{$_} <=> $b->{$_} : $a->{$_} cmp $b->{$_}; # we found a field they differ on last if $compare; } return $compare; } @results; } } return @results } sub match_mp3 { my ($filename, $query, $results, $options) = @_; return unless $filename =~ m{[^/]\.mp3$}; if ($$options{exclude_path}) { return if $filename =~ $$options{exclude_path}; } my $mp3 = get_mp3_metadata({ filename => $filename, use_id3v2 => $options->{use_id3v2}, }); for my $field (keys(%{ $query })) { my $value = $mp3->{uc($field)}; return unless defined $value; return unless $value =~ $query->{$field}; } push @{ $results }, $mp3; } # module return 1;