package MP3::Find::Base; use strict; use warnings; use Carp; #TODO: allow ID3v2 tags in printf format my %format_codes = ( a => 'ARTIST', t => 'TITLE', b => 'ALBUM', n => 'TRACKNUM', y => 'YEAR', g => 'GENRE', ); sub new { my $invocant = shift; my $class = ref $invocant || $invocant; my %options = @_; my $self = \%options; bless $self, $class; } sub find_mp3s { my $self = shift; my %opt = @_; my $dir = $opt{dir} || $ENV{HOME}; my @DIRS = ref $dir eq 'ARRAY' ? @$dir : ($dir); my %QUERY = %{ $opt{query} || {} }; # array ref for multiple sort fields, but allow # a simple scalar for single values my @SORT = $opt{sort} ? (ref $opt{sort} eq 'ARRAY' ? @{ $opt{sort} } : ($opt{sort})) : (); foreach (keys %QUERY) { if (defined $QUERY{$_}) { # package everything uniformly, so subclasses don't need to unpack it $QUERY{$_} = [ $QUERY{$_} ] unless ref $QUERY{$_} eq 'ARRAY'; } else { # so we don't have spurious warnings when trying to match against undef delete $QUERY{$_}; } } # do the search my @results = $self->search(\%QUERY, \@DIRS, \@SORT, \%opt); # maybe they want the unformatted data return @results if $opt{no_format}; if ($opt{printf}) { # printf style output format foreach (@results) { my $output = $opt{printf}; for my $code (keys %format_codes) { while ($output =~ m/%((-\d)?\d*)$code/g) { # field size modifier my $modifier = $1 || ''; # figure out the size of the formating code my $code_size = 2 + length($modifier); my $value = sprintf("%${modifier}s", $_->{$format_codes{$code}} || ''); substr($output, pos($output) - $code_size, $code_size, $value); } } # to allow literal '%' $output =~ s/%%/%/g; $_ = $output; } } else { # just the filenames, please @results = map { $_->{FILENAME} } @results; } return @results; } sub search { croak "Method 'search' not implemented in " . __PACKAGE__; } # module return 1; =head1 NAME MP3::Find::Base - Base class for MP3::Find backends =head1 SYNOPSIS package MyFinder; use base 'MP3::Find::Base'; sub search { my $self = shift; my ($query, $dirs, $sort, $options) = @_; # do something to find and sort the mp3s... my @results = do_something(...); return @results; } package main; my $finder = MyFinder->new; # see MP3::Find for details about %options print "$_\n" foreach $finder->find_mp3s(\%options); =head1 DESCRIPTION This is the base class for the classes that actually do the searching and sorting for L. =head1 METHODS =head2 new Really simple constructor. If you pass it a hash of options, it will hang on to them for you. =head2 search This is the one you should override in your subclass. If you don't, the base class C method will croak. The C method is called by the C method with the following arguments: the finder object, a hashref of query parameters, an arrayref of directories to search, and a hashref of miscellaneous options. The search method should return a list of hashrefs representing the results of the search. Each hashref should have the following keys (all except C are derived from the keys returned by the C and C functions from L): FILENAME TITLE ARTIST ALBUM YEAR COMMENT GENRE TRACKNUM VERSION -- MPEG audio version (1, 2, 2.5) LAYER -- MPEG layer description (1, 2, 3) STEREO -- boolean for audio is in stereo VBR -- boolean for variable bitrate BITRATE -- bitrate in kbps (average for VBR files) FREQUENCY -- frequency in kHz SIZE -- bytes in audio stream OFFSET -- bytes offset that stream begins SECS -- total seconds MM -- minutes SS -- leftover seconds MS -- leftover milliseconds TIME -- time in MM:SS COPYRIGHT -- boolean for audio is copyrighted PADDING -- boolean for MP3 frames are padded MODE -- channel mode (0 = stereo, 1 = joint stereo, -- 2 = dual channel, 3 = single channel) FRAMES -- approximate number of frames FRAME_LENGTH -- approximate length of a frame VBR_SCALE -- VBR scale from VBR header =head2 find_mp3s The method that should be called by the program doing the searching. See L for an explanation of the options that can be passed to C. =head1 TODO More format codes? Possibly look into using L =head1 SEE ALSO L, L, L See L for more information about the fields you can search and sort on. =head1 AUTHOR Peter Eichman =head1 COPYRIGHT AND LICENSE 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. =cut