########################################################################### # # MP3Plug.pm -- Plugin for MP3 files (MPEG audio layer 3). # # A component of the Greenstone digital library software from the New # Zealand Digital Library Project at the University of Waikato, New # Zealand. # # Copyright (C) 2001 New Zealand Digital Library Project # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # ########################################################################### package MP3Plug; use UnknownPlug; use strict; no strict 'refs'; # allow filehandles to be variables and viceversa use MP3::Info; require giget; sub BEGIN { @MP3Plug::ISA = ('UnknownPlug'); } my $arguments = [ { 'name' => "process_exp", 'desc' => "{BasPlug.process_exp}", 'type' => "string", 'deft' => &get_default_process_exp(), 'reqd' => "no" }, { 'name' => "assoc_images", 'desc' => "{MP3Plug.assoc_images}", 'type' => "flag", 'deft' => "", 'reqd' => "no" }, { 'name' => "applet_metadata", 'desc' => "{MP3Plug.applet_metadata}", 'type' => "flag", 'deft' => "" }, { 'name' => "metadata_fields", 'desc' => "{MP3Plug.metadata_fields}", 'type' => "string", 'deft' => "Title,Artist,Genre" } ]; my $options = { 'name' => "MP3Plug", 'desc' => "{MP3Plug.desc}", 'abstract' => "no", 'inherits' => "yes", 'args' => $arguments }; sub new { my ($class) = shift (@_); my ($pluginlist,$inputargs,$hashArgOptLists) = @_; push(@$pluginlist, $class); if(defined $arguments){ push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});} if(defined $options) { push(@{$hashArgOptLists->{"OptList"}},$options)}; my $self = new UnknownPlug($pluginlist, $inputargs, $hashArgOptLists); return bless $self, $class; } sub get_default_process_exp { return q^(?i)\.mp3$^; } sub gen_mp3applet { my ($mp3_filename) = @_; my $applet_html = ' </COMMENT> '; $applet_html =~ s/MP3FILENAME/$mp3_filename/g; return $applet_html; } # Associate the mp3 file with the new document sub associate_mp3_file { my $self = shift (@_); my $filename = shift (@_); # filename with full path my $file = shift (@_); # filename without path my $doc_obj = shift (@_); my $verbosity = $self->{'verbosity'}; my $outhandle = $self->{'outhandle'}; # check the filename is okay return 0 if ($file eq "" || $filename eq ""); # Add the file metadata my $assoc_url = $file; # $assoc_url =~ s/ /%20/g; # probably need to do more escaping than this!! $assoc_url =~ s/ /_/g; # workaround for now my $dst_file = $file; $dst_file =~ s/ /_/g; # Add the file as an associated file ... my $section = $doc_obj->get_top_section(); my $mime_type = $self->{'mime_type'} || "audio/mp3"; my $assoc_field = $self->{'assoc_field'} || "mp3"; my $assoc_name = $file; $assoc_name =~ s/\.mp3$//; $doc_obj->associate_file($filename, $dst_file, $mime_type, $section); $doc_obj->add_metadata ($section, "Source", $file); $doc_obj->add_metadata ($section, $assoc_field, $assoc_name); $doc_obj->add_metadata ($section, "srcurl", $assoc_url); my $mp3_info = get_mp3info($filename); my $mp3_tags = get_mp3tag($filename); my $metadata_fields = $self->{'metadata_fields'}; if ($metadata_fields eq "*") { # Locate all info and tag metadata foreach my $ki ( keys %$mp3_info ) { my $mp3_metavalue = $mp3_info->{$ki}; if ($mp3_metavalue !~ m/^\s*$/s) { my $mp3_metaname = "mp3:".lc($ki); $doc_obj->add_metadata ($section, $mp3_metaname, $mp3_metavalue); } } foreach my $kt ( keys %$mp3_tags ) { my $mp3_metavalue = $mp3_tags->{$kt}; if ($mp3_metavalue !~ m/^\s*$/s) { my $kt_len = length($kt); my $kt_initial_cap = uc(substr($kt,0,1)).lc(substr($kt,1,$kt_len-1)); my $mp3_metaname = "mp3:".$kt_initial_cap; $doc_obj->add_metadata ($section, $mp3_metaname, $mp3_metavalue); } } } else { # Restrict metadata to that specifically given foreach my $field (split /,/, $metadata_fields) { # check info if (defined $mp3_info->{$field}) { my $mp3i_metavalue = $mp3_info->{$field}; if ($mp3i_metavalue !~ m/^\s*$/s) { my $mp3i_metaname = "mp3:".lc($field); $doc_obj->add_metadata ($section, $mp3i_metaname, $mp3i_metavalue); } } # check tags if (defined $mp3_tags->{uc($field)}) { my $mp3t_metavalue = $mp3_tags->{uc($field)}; if ($mp3t_metavalue !~ m/^\s*$/s) { my $mp3t_metaname = "mp3:".$field; $doc_obj->add_metadata ($section, $mp3t_metaname, $mp3t_metavalue); } } } } $doc_obj->add_metadata ($section, "FileFormat", "MP3"); $doc_obj->add_metadata ($section, "srclink", ""); $doc_obj->add_metadata ($section, "srcicon", "_iconmp3_"); $doc_obj->add_metadata ($section, "/srclink", ""); my $applet_metadata = $self->{'applet_metadata'}; if (defined $applet_metadata && $applet_metadata ) { my $applet_html = gen_mp3applet("_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[srcurl]"); $doc_obj->add_metadata ($section, "mp3applet", $applet_html); } my $assoc_images = $self->{'assoc_images'}; if (defined $assoc_images && $assoc_images) { my @search_terms = (); my $title = $mp3_tags->{'TITLE'}; my $artist = $mp3_tags->{'ARTIST'}; if (defined $title && $title ne "") { push(@search_terms,$title); if (defined $artist && $artist ne "") { push(@search_terms,$artist); } } else { push(@search_terms,$assoc_name); } push(@search_terms,"song"); my $output_dir = $filename; $output_dir =~ s/\.\w+$//; my ($imgref_urls) = giget(\@search_terms,$output_dir); my $gi_base = gi_url_base(); my $gi_query_url = gi_query_url(\@search_terms); $doc_obj->add_metadata ($section, "giquery", ""); $doc_obj->add_metadata ($section, "/giquery", ""); for (my $i=1; $i<=2; $i++) { my $img_filename = "$output_dir/img_$i.jpg"; my $dst_file = "img_$i.jpg"; if (-e $img_filename) { $doc_obj->associate_file($img_filename, $dst_file, "image/jpeg", $section); my $srcurl = "src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/$dst_file\""; $doc_obj->add_metadata ($section, "img$i", ""); $doc_obj->add_metadata ($section, "smallimg$i", ""); my $imgref_url = $imgref_urls->[$i-1]; $doc_obj->add_metadata ($section, "imgref$i", ""); $doc_obj->add_metadata ($section, "/imgref$i", ""); } } } return 1; } # The MP3Plug read() function is based on UnknownPlug read(). This # function does all the right things to make general options work for # a given plugin. my $mp3_doc_count = 0; ## is this used anywhere now !!??? sub read { my $self = shift (@_); my ($pluginfo, $base_dir, $file, $metadata, $processor, $maxdocs, $total_count, $gli) = @_; my $outhandle = $self->{'outhandle'}; #check for associate_ext, blocking etc my ($block_status,$filename) = $self->read_block(@_); return $block_status if ((!defined $block_status) || ($block_status==0)); print STDERR "\n" if ($gli); print $outhandle "MP3Plug processing \"$filename\"\n" if $self->{'verbosity'} > 1; #if there's a leading directory name, eat it... $file =~ s/^.*[\/\\]//; # create a new document my $doc_obj = new doc ($filename, "indexed_doc"); $mp3_doc_count++; ## $doc_obj->set_OIDtype ($processor->{'OIDtype'}); if ($processor->{'OIDtype'} =~ /^(assigned|dirname)$/) { $doc_obj->set_OIDtype ($processor->{'OIDtype'}, $processor->{'OIDmetadata'}); } else { $doc_obj->set_OIDtype ("incremental"); # this is done to avoid hashing content of file } $doc_obj->add_utf8_metadata($doc_obj->get_top_section(), "Plugin", "$self->{'plugin_type'}"); $doc_obj->add_utf8_metadata($doc_obj->get_top_section(), "FileSize", (-s $filename)); # associate the file with the document if (associate_mp3_file($self, $filename, $file, $doc_obj) != 1) { print "MP3Plug: couldn't process \"$filename\"\n"; return 0; } #create an empty text string so we don't break downstream plugins my $text = &gsprintf::lookup_string("{BasPlug.dummy_text}"); if ($self->{'assoc_images'}) { $text .= "[img1]
"; $text .= "[img2]
"; } # include any metadata passed in from previous plugins my $section = $doc_obj->get_top_section(); $self->extra_metadata ($doc_obj, $section, $metadata); $self->title_fallback($doc_obj,$section,$file); # do plugin specific processing of doc_obj return undef unless defined ($self->process (\$text, $pluginfo, $base_dir, $file, $metadata, $doc_obj)); # do any automatic metadata extraction $self->auto_extract_metadata ($doc_obj); # add an OID $doc_obj->set_OID(); $doc_obj->add_text($section, $text); # process the document $processor->process($doc_obj); $self->{'num_processed'} ++; return 1; } sub title_fallback { my $self = shift (@_); my ($doc_obj,$section,$file) = @_; if (!defined $doc_obj->get_metadata_element ($section, "Title")) { my $mp3_title = $doc_obj->get_metadata_element ($section, "mp3:Title"); if (defined $mp3_title) { $doc_obj->add_metadata ($section, "Title", $mp3_title); } else { &BasPlug::title_fallback($self, $doc_obj, $section, $file); } } } 1;