package Net::FreeDB;
use 5.006;
use strict;
use warnings;
use IO::Socket;
use Net::Cmd;
use CDDB::File;
use Carp;
use Data::Dumper;
require Exporter;
require DynaLoader;
use AutoLoader;
our @ISA = qw(Exporter DynaLoader Net::Cmd IO::Socket::INET);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use Net::FreeDB ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw() ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw();
our $VERSION = '0.06';
our $ERROR;
sub AUTOLOAD {
# This AUTOLOAD is used to 'autoload' constants from the constant()
# XS function. If a constant is not found then control is passed
# to the AUTOLOAD in AutoLoader.
my $constname;
our $AUTOLOAD;
($constname = $AUTOLOAD) =~ s/.*:://;
croak "& not defined" if $constname eq 'constant';
my $val = constant($constname, @_ ? $_[0] : 0);
if ($! != 0) {
if ($! =~ /Invalid/ || $!{EINVAL}) {
$AutoLoader::AUTOLOAD = $AUTOLOAD;
goto &AutoLoader::AUTOLOAD;
}
else {
croak "Your vendor has not defined Net::FreeDB macro $constname";
}
}
{
no strict 'refs';
# Fixed between 5.005_53 and 5.005_61
if ($] >= 5.00561) {
*$AUTOLOAD = sub () { $val };
}
else {
*$AUTOLOAD = sub { $val };
}
}
goto &$AUTOLOAD;
}
bootstrap Net::FreeDB $VERSION;
# Preloaded methods go here.
sub new {
my $class = shift;
my $self = {};
$self = {@_};
bless($self, $class);
$self->{HOST} = 'freedb.freedb.org' unless defined($self->{HOST});
$self->{PORT} = '8880' unless defined($self->{PORT});
if (!defined($self->{USER})) {
$self->{USER} = defined($ENV{USER}) ? $ENV{USER} : 'unknown';
}
if (!defined($self->{HOSTNAME})) {
$self->{HOSTNAME} = defined($ENV{HOSTNAME}) ? $ENV{HOSTNAME} : 'unknown';
}
my $obj = $self->SUPER::new(PeerAddr => $self->{HOST},
PeerPort => $self->{PORT},
Proto => 'tcp',
Timeout =>
defined($self->{TIMEOUT}) ? $self->{TIMEOUT} : 120
);
return undef
unless defined $obj;
$obj->autoflush(1);
$obj->debug(exists $self->{DEBUG} ? $self->{DEBUG} : undef);
unless ($obj->response() == CMD_OK) {
$obj->close;
return undef;
}
$obj->command(
"cddb hello",
$self->{USER},
$self->{HOSTNAME},
ref($self),
$VERSION
);
unless ($obj->response() == CMD_OK) {
$obj->close;
return undef;
}
$obj;
}
sub read {
my $self = shift;
my ($cat, $id);
if (scalar(@_) == 2) {
($cat, $id) = @_;
} else {
if ((scalar(@_) % 2) == 0) {
if ($_[0] =~ /^CATEGORY$/i || $_[0] =~ /^ID$/i) {
my %input = @_;
($cat, $id) = ($input{CATEGORY}, $input{ID});
} else {
print "Error: Unknown input!\n";
return undef;
}
} else {
print "Error: Unknown input!\n";
return undef;
}
}
my $cddb_file = new CDDB::File;
$cddb_file->{_data} =
$self->_READ($cat, $id) ? $self->_read : undef;
return $cddb_file;
}
sub query {
my $self = shift;
$self->_QUERY(@_) ? $self->_query : undef;
}
sub sites {
my $self = shift;
$self->_SITES ? $self->_sites : undef;
}
sub getdiscid {
my $self = shift;
my ($driveNo, $id);
if (ref($self) ne 'Net::FreeDB') {
$driveNo = $self;
} else {
$driveNo = shift;
}
$id = discid($driveNo);
if ($id eq "UNDEF" || $id eq '') {
$ERROR = "Drive Error: no disc found\n";
return undef;
}
return $id;
}
sub getdiscdata {
my $self = shift;
my ($driveNo, $data);
if (ref($self) ne 'Net::FreeDB') {
$driveNo = $self;
} else {
$driveNo = shift;
}
$data = discinfo($driveNo);
if (!$data) {
$ERROR = "Drive Error: no disc found\n";
return undef;
}
return $data;
}
sub lscat {
my $self = shift;
$self->_LSCAT();
}
sub quit {
my $self = shift;
$self->_QUIT();
}
sub DESTROY {
my $self = shift;
$self = {};
}
sub _read {
my $self = shift;
my $data = $self->read_until_dot or
return undef;
return $data;
}
sub _query {
my $self = shift;
my $data = $self->message();
my $code = $self->code();
my @returns;
if ($code == 210 || $code == 211) {
my $data = $self->read_until_dot
or return undef;
foreach my $i (@{$data}) {
next if $i =~ /^\.$/;
$i =~
/([^\s]+)\s([^\s]+)\s([^\/|\:|\-]+)\s[\/|\|:|\-]\s?(.*)\s?/;
push @returns, {GENRE =>$1,DISCID =>$2,ARTIST=>$3,ALBUM=>$4};
}
} else {
#we got a single; parse it, hash it and return it
$data =~ /([^\s]+)\s([^\s]+)\s([^\/|\:|\-]+)\s[\/|\:|\-]\s?(.*)\s?/;
push @returns, {GENRE=>$1,DISCID=>$2,ARTIST=>$3,ALBUM=>$4};
}
return @returns;
}
sub _sites {
my $self = shift;
my $data = $self->read_until_dot
or return undef;
my @sites;
foreach (@$data) {
s/([^\s]+)\s([^\s]+).*/$1 $2/;
push(@sites, $_);
}
return \@sites;
}
sub _READ { shift->command('CDDB READ',@_)->response == CMD_OK }
sub _SITES { shift->command('SITES',@_)->response == CMD_OK }
sub _LSCAT { shift->command('CDDB LSCAT')->response == CMD_OK }
sub _QUERY { shift->command('CDDB QUERY',@_)->response == CMD_OK }
sub _QUIT { shift->command('QUIT')->response == CMD_OK }
sub _WRITE { shift->command('CDDB WRITE',@_)->response == CMD_OK }
sub _WHOM { shift->command('CDDB WHOM')->response == CMD_OK }
sub _UPDATE { shift->command('CDDB UPDATE')->response == CMD_OK }
sub _VER { shift->command('CDDB VER')->response == CMD_OK }
sub _STAT { shift->command('CDDB STAT')->response == CMD_OK }
sub _PROTO { shift->command('CDDB PROTO')->response == CMD_OK }
sub _MOTD { shift->command('CDDB MOTD')->response == CMD_OK }
sub _LOG { shift->command('CDDB LOG',@_)->response == CMD_OK }
sub _HELP { shift->command('CDDB HELP')->response == CMD_OK }
sub _DISCID { shift->command('DISCID',@_)->response == CMD_OK }
1;
__END__
=head1 NAME
Net::FreeDB - Perl interface to freedb server(s)
=head1 SYNOPSIS
use Net::FreeDB;
$freedb = Net::FreeDB->new();
$discdata = $freedb->getdiscdata('/dev/cdrom');
my $cddb_file_object = $freedb->read('rock', $discdata->{ID});
print $cddb_file_object->id;
=head1 DESCRIPTION
Net::FreeDB was inspired by Net::CDDB. And in-fact
was designed as a replacement in-part by Net::CDDB's
author Jeremy D. Zawodny. Net::FreeDB allows an
oop interface to the freedb server(s) as well as
some basic cdrom functionality like determining
disc ids, track offsets, etc.
=head2 METHODS
=item new(HOST => $h, PORT => $p, USER => $u, HOSTNAME => $hn, TIMEOUT => $to)
Constructor:
Creates a new Net::FreeDB object.
Parameters:
Set to username or user-string you'd like to be logged as.
HOSTNAME: (optional)
Set to the hostname you'd like to be known as.
TIMEOUT: (optional)
Set to the number of seconds to timeout on freedb server.
new() creates and returns a new Net::FreeDB object that is connected
to either the given host or freedb.freedb.org as default.
=item read($cat, $id)
Parameters:
read($$) takes 2 parameters, the first being a category name.
This can be any string either that you make up yourself or
that you believe the disc to be. The second is the disc id. This
may be generated for the current cd in your drive by calling getdiscid()
NOTE:
Using an incorrect category will result in either no return or an
incorrect return. Please check the CDDB::File documentation for
information on this module.
read() requests a freedb record for the given information and returns a
CDDB::File object.
=item query($id, $num_trks, $trk_offset1, $trk_offset2, $trk_offset3...)
Parameters:
query($$$...) takes:
1: a discid
2: the number of tracks
3: first track offset
4: second track offset... etc.
Query expects $num_trks number of extra params after the first two.
query() returns an array of hashes. The hashes looks like:
{
GENRE => 'newage',
DISCID => 'discid',
ARTIST => 'artist',
ALBUM => 'title'
}
NOTE: query() can return 'inexact' matches and/or 'multiple exact'
matches. The returned array is the given returned match(es).
=item sites()
Parameters:
None
sites() returns an array reference of urls that can be used as
a new HOST.
=item getdiscid($device)
Parameters:
getdiscid($) takes the device you want to use.
Basically this means '/dev/cdrom' or whatever on linux machines
but it's an array index in the number of cdrom drives on windows
machines starting at 0. (Sorry, I may change this at a later time).
So, if you have only 1 cdrom drive then getdiscid(0) would work fine.
getdiscid() returns the discid of the current disc in the given drive.
NOTE: See BUGS
=item getdiscdata($device)
Parameters:
getdiscdata($) takes the device you want to use. See getdiscid()
for full description.
getdiscdata() returns a hash of the given disc data as you would
require for a call to query. The returns hash look like:
{
ID => 'd00b3d10',
NUM_TRKS => '3',
TRACKS => [
'150',
'18082',
'29172'
],
SECONDS => '2879'
}
NOTE: A different return type/design may be developed.
=head1 BUGS
The current version of getdiscid() and getdiscdata()
on the Windows platform takes ANY string in a single
cdrom configuration and works fine. That is if you
only have 1 cdrom drive; you can pass in ANY string
and it will still scan that cdrom drive and return
the correct data. If you have more then 1 cdrom drive
giving the correct drive number will return in an
accurate return.
=head1 AUTHOR
David Shultz E<lt>dshultz@cpan.orgE<gt>
=head1 CREDITS
Jeremy D. Zawodny E<lt>jzawodn@users.sourceforge.netE<gt>
Pete Jordon E<lt>ramtops@users.sourceforge.netE<gt>
=head1 COPYRIGHT
Copywright (c) 2002 David Shultz. All rights reserved.
This program is free software; you can redistribute it
and/or modify if under the same terms as Perl itself.
=cut