Version: 1.0
Type: Class
Category: Other
License: GNU General Public License
Description: A small and quick class to get CDDB information using the Linux program cd-discid.
<?php
#####################################################################################
#####################################################################################
#####################################################################################
#####################################################################################
################### ###################
################### Aces XMMS-PHP MP3 Jukebox v0.1.0 ###################
################### -------------------------------- ###################
################### ###################
################### Based on XMMS-Control by Joe Thielen ###################
################### ###################
################### Designed by Ace (ace_jedi_2k1@hotmail.com) ###################
################### ###################
#####################################################################################
#####################################################################################
#####################################################################################
#####################################################################################
### You may use or modify this program for any non-profit use ###
### as long as there is a the copyright notice displayed in a ###
### comment inside the output html ###
### (i.e. <!-- Generated by XMMS-PHP. ###
### http://bassetts.port5.com (C) Ace 2003 -->) ###
#####################################################################################
#####################################################################################
#####################################################################################
#####################################################################################
### ###
### PHPlibcddb 0.01 - PHP-based implementation of the CDDB protocol ###
### Copyright (C) 2003 David Jonathan Grant ###
### ###
### 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ###
### ###
### David Jonathan Grant <phplibcddb@davidjonathangrant.info> ###
### ###
#####################################################################################
#####################################################################################
#####################################################################################
#####################################################################################
############### ###############
############### CDDB Connectivity Class ###############
############### ###############
############### Designed by: Ace. ###############
############### <ace_jedi_2k1@hotmail.com> ###############
############### ###############
#####################################################################################
#####################################################################################
#####################################################################################
#####################################################################################
#### ####
#### Usage Example: ####
#### -------------- ####
#### ####
#### echo "<PRE>"; ####
#### ####
#### include("classes/class.cddb.php"); ####
#### ####
#### $cddb = new cddb(); ####
#### $cddb->connect(); ####
#### $cddb->protocol(5); ####
#### ####
#### print_r($cddb->genres()); ####
#### print_r($cddb->help("discid")); ####
#### print_r($cddb->log()); ####
#### print_r($cddb->status()); ####
#### $info = $cddb->import(); ####
#### $data = $cddb->query($info['discid'], $info['tracks'], ####
#### $info['timing'], $info['length']); ####
#### print_r($data[0]); ####
#### print_r($cddb->read($data[0]['category'], $info['discid'])); ####
#### print_r($cddb->motd()); ####
#### print_r($cddb->version()); ####
#### $cddb->disconnect(); ####
#### print_r($cddb->messages); ####
#### ####
#### echo "</PRE>"; ####
#### ####
#####################################################################################
#####################################################################################
class cddb
{
var $connection = NULL;
var $messages = NULL;
function code($response)
{
return (substr($response, 0, 3));
}
function connect($servernumber = 0)
{
$serverport = 8880;
$server[0] = "freedb.freedb.org"; // Random freedb server
$server[1] = "freedb.freedb.de"; // Dortmund, Germany
$server[2] = "at.freedb.org"; // Vienna, Austria
$server[3] = "au.freedb.org"; // Sydney, Australia
$server[4] = "bg.freedb.org"; // Sofia, Bulgaria
$server[5] = "ca.freedb.org"; // Winnipeg, MB Canada
$server[6] = "de.freedb.org"; // Berlin, Germany
$server[7] = "es.freedb.org"; // Madrid, Spain
$server[8] = "lu.freedb.org"; // Betzdorf, Luxemburg
$server[9] = "uk.freedb.org"; // London, UK
$server[10] = "us.freedb.org"; // San Jose, CA USA
if (!isset($this->serverport))
{
$this->serverport = 8880;
}
if (!isset($servernumber))
{
$servernumber = 0;
}
// Connect to the central FreeDB server.
$this->connection = fsockopen($this->server[$this->servernumber], $this->serverport);
// Checck to see if the socket connected properly.
if (is_resource($this->connection) == FALSE)
{
return (FALSE);
}
else
{
//$code = $this->code($this->parse(1));
switch ($code = $this->code($this->parse(1)))
{
case 200:
$this->messages[] = "200: OK, Read/Write Allowed.";
return ($this->hello());
break;
case 201:
$this->messages[] = "200: OK, Read Only.";
return ($this->hello());
break;
case 432:
$this->messages[] = "432: No Connections Allowed: Permission Denied.";
return (FALSE);
case 433:
$this->messages[] = "433: No Connections Allowed: X Users Allowed, Y Currently Active.";
return (FALSE);
case 434:
$this->messages[] = "434: No Connections Allowed: System Load Too High.";
return (FALSE);
default:
return ($this->errors($code));
}
}
}
function discid($tracks, $offsets, $time)
{
$this->send("discid " . $tracks . " " . implode(" ", $offsets) . " " . $time);
$header = $this->parse(1);
switch ($code = $this->code($header))
{
case 200:
$this->messages[] = "200: Calculated Disc ID Properly.";
preg_match("/.+ (\S+)^$/", $header, $matches);
return ($matches[1]);
default:
return ($this->errors($code));
}
}
function disconnect()
{
// Disconnect from server.
$this->send("quit");
switch ($code = $this->code($this->parse(1)))
{
case 230:
$this->messages[] = "230: OK, Goodbye.";
fclose($this->connection);
return (TRUE);
default:
return ($this->errors($code));
}
}
function errors($code)
{
// Handle general error status codes from other functions.
switch ($code)
{
case 402:
$this->messages[] = "402: Server Error.";
return (FALSE);
case 408:
$this->messages[] = "408: CGI Environment Error.";
return (FALSE);
case 500:
$this->messages[] = "500: Command Syntax Error.";
return (FALSE);
case 530:
$this->messages[] = "503: Server Error, Server Timeout.";
return (FALSE);
default:
$this->messages[] = "Received Unrecognised Code (" . $code . ")";
return (FALSE);
}
}
function genres()
{
// Return a list of muscial genres.
$this->send("cddb lscat");;
switch ($code = $this->code($this->parse(1)))
{
case 210:
$this->messages[] = "210: OK, Category List Follows.";
return ($this->parse(0));
default:
return ($this->errors($code));
}
}
function hello()
{
// Handshake
$sys = posix_uname();
$usr = posix_getpwuid(posix_geteuid());
// Send 'hello' command to the conneccted server.
$this->send("cddb hello " . $usr['name'] . " " . $sys['nodename'] . " PHPlibcddb 0.01");
switch ($code = $this->code($this->parse(1)))
{
case 200:
$this->messages[] = "200: OK, Handshake Successful.";
// Handshake was completed successfully.
return (TRUE);
case 402:
// Not *really* a problem, but it is important to know if this is occurring frequently.
$this->messages[] = "402: Already Shook Hands.";
return (TRUE);
case 431:
//
$this->messages[] = "431: Handshake Not Successful. Closing Connection.";
return (FALSE);
default:
return ($this->errors($code));
}
}
function help($topic)
{
// Request help from server
$this->send("help " . $topic);
switch ($code = $this->code($this->parse(1)))
{
case 210:
$this->messages[] = "210: OK, Help Information Follows.";
return ($this->parse(0));
case 401:
$this->messages[] = "401: No Help Information Available.";
return (FALSE);
default:
return ($this->errors($code));
}
}
function import()
{
// Grab the CD information from the local machine.
// This will only work on Unix-based machines with cd-discid.
$data = explode(" ", `cd-discid /dev/cdrom`);
$info['discid'] = array_shift($data);
$info['tracks'] = array_shift($data);
$info['length'] = array_pop($data);
$info['timing'] = $data;
return ($info);
}
function log()
{
// Request usagee log from the server.
$this->send("log");
switch ($code = $this->code($this->parse(1)))
{
case 210:
$this->messages[] = "210: OK, Log Summary Follows.";
return ($this->parse(0));
case 211:
$this->messages[] = "211: OK Log Follows.";
return ($this->parse(0));
case 401:
$this->messages[] = "401: Permission Denied.";
return (FALSE);
case 402:
$this->messages[] = "402: No Log Information Available.";
return (FALSE);
case 501:
$this->messages[] = "501: Invalid Start/End Date.";
return (FALSE);
default:
return ($this->errors($code));
}
}
function motd()
{
// Request the Message of the Day
$this->send("motd");
switch ($code = $this->code($this->parse(1)))
{
case 210:
$this->messages[] = "210: Last Modified MM/DD/YYYY HH:MM:SS. MOTD Follows.";
return ($this->parse(0));
case 401:
$this->messages[] = "401: No Message of the Day Available.";
return (FALSE);
default:
return ($this->errors($code));
}
}
function parse($lines)
{
// Check if read is for multiple (0) or single (1) lines.
if ($lines == 1)
{
$response = "";
// Loop through the response
while (feof($this->connection) == FALSE)
{
// Read a character from the response.
$character = fgetc($this->connection);
// Continue until we get a carriage return.
if ($character == chr(13))
{
break;
}
// Add current character to response string.
if (empty($response))
{
$response = $character;
}
else
{
$response = $response . $character;
}
}
return (trim($response));
}
else
{
$offset = 0;
$response = array();
while (feof($this->connection) == FALSE)
{
// Prevent PHP throwing an error.
if (array_key_exists($offset, $response) == FALSE)
{
// Populate current array offset
$response[$offset] = "";
// Remove the NL character from the response.
fgetc($this->connection);
}
// Read a character from the response.
$character = fgetc($this->connection);
// Check for an ASCII CR character.
if ($character == chr(13))
{
// Add a new line to the array
$response[$offset] = trim($response[$offset]);
$offset++;
continue;
}
// If the character is a fullstop (.) and is at the beginning of the line, delete the row, and exit.
// Reasoning: a fullstop (.) is the terminating character for multiple line responses.
if ($character == chr(46) && strlen($response[$offset]) == 0)
{
unset($response[$offset]);
// Grab what *SHOULD* be the last character (CR) from the response.
fgetc($this->connection);
break;
}
else
{
// Add the character to the current row in the reponse array.
if (empty($response[$offset]))
{
$response[$offset] = $character;
}
else
{
$response[$offset] = $response[$offset] . $character;
}
}
}
return ($response);
}
}
function protocol($protocol)
{
// Change the current CDCDP level.
$this->send("proto " . $protocol);
switch ($code = $this->code($this->parse(1)))
{
case 200:
$this->messages[] = "200: CDDB Protocol Level, Current Level, Current Supported Level.";
return (TRUE);
case 201:
$this->messages[] = "201: OK, Protocol Level Now Current Level.";
return (TRUE);
case 501:
$this->messages[] = "501: Illegal Protocol Level.";
return (FALSE);
case 502:
$this->messages[] = "502: Protocol Level Already Current Level.";
return (FALSE);
default:
return ($this->errors($code));
}
}
function query($discid, $tracks, $offset, $length)
{
// Find matches for CD data.
$this->send("cddb query " . $discid . " " . $tracks . " " . implode(" ", $offset) . " " . $length);
$header = $this->parse(1);
switch($code = $this->code($header))
{
case 200:
$this->messages[] = "200: Found Exact Match.";
preg_match("/^\d{3} (\S+) (\S+) (.+\/.+)$/", $header, $matches);
$results[0]['category'] = $matches[1];
$results[0]['discid'] = $matches[2];
$results[0]['dtitle'] = $matches[3];
return ($results);
case 202:
$this->messages[] = "202: No Match Found.";
return (FALSE);
case 210:
$this->messages[] = "210: Found Exact Matches. List Follows.";
foreach ($this->parse(0) as $offset => $data)
{
// Extract CD information from response.
preg_match("/^(\S+) (\S+) (.+\/.+)$/", $data, $matches);
$results[$offset]['category'] = $matches[1];
$results[$offset]['discid'] = $matches[2];
$results[$offset]['dtitle'] = $matches[3];
}
return ($results);
case 211:
$this->messages[] = "211: Found Inexact Matches. List Follows.";
foreach ($this->parse(0) as $offset => $data)
{
// Extract CD information from response.
preg_match("/^(\S+) (\S+) (.+\/.+)$/", $data, $matches);
$results[$offset]['category'] = $matches[1];
$results[$offset]['discid'] = $matches[2];
$results[$offset]['dtitle'] = $matches[3];
}
return ($results);
case 403:
$this->messages[] = "403: Database Entry is Corrupt.";
return (FALSE);
case 409:
$this->messages[] = "409: No Handshake.";
return (FALSE);
default:
return ($this->errors($code));
}
}
function read($category, $discid)
{
// Fetch the track data corresponding to the passed CD data.
$this->send("cddb read " . $category . " " . $discid);
switch ($code = $this->code($this->parse(1)))
{
case 210:
$this->messages[] = "200: OK, CDDB Database Entry Follows.";
return ($this->parse(0));
case 401:
$this->messages[] = "401: Specified CDDB Entry Not Found.";
return (FALSE);
case 402:
$this->messages[] = "402: Server Error.";
return (FALSE);
case 403:
$this->messages[] = "403: Database Entry is Corrupt.";
return (FALSE);
case 409:
$this->messages[] = "409: No Handshake.";
return (FALSE);
default:
return ($this->errors($code));
}
}
function send($request)
{
// Wrapper for sending request.
fputs($this->connection, $request . "\n");
}
function sites()
{
// List all the alternative servers.
$this->send("sites");
// Check the status code.
switch ($code = $this->code($this->parse(1)))
{
case 210:
$this->messages[] = "210: OK, Site Information Follows.";
// Read in all sites.
$sites = $this->parse(0);
// Loop through server list and extract different parts.
foreach ($sites as $offset => $data)
{
preg_match("/^(.+) (.+) (\d+) (.+) ([N|S]\d{3}\.\d{2}) ([E|W]\d{3}\.\d{2}) (.+)/", $data, $matches);
$servers[$offset]['hostname'] = $matches[1];
$servers[$offset]['protocol'] = $matches[2];
$servers[$offset]['portno'] = $matches[3];
$servers[$offset]['url'] = $matches[4];
$servers[$offset]['latitude'] = $matches[5];
$servers[$offset]['longitude'] = $matches[6];
$servers[$offset]['name'] = $matches[7];
}
return ($servers);
case 401:
$this->messages[] = "401: No Site Information Available.";
return (FALSE);
default:
return ($this->errors($code));
}
}
function status()
{
// Extract server status information
$this->send("stat");
switch ($code = $this->code($this->parse(1)))
{
case 210:
$this->messages[] = "210: OK, Status Information Follows.";
return ($this->parse(0));
default:
return ($this->errors($code));
}
}
function update()
{
// Ask server to update the database.
$this->send("update");
switch ($code = $this->code($this->parse(1)))
{
case 200:
$this->messages[] = "200: Updating the Database.";
return (TRUE);
case 401:
$this->messages[] = "401: Permission Denied.";
return (FALSE);
case 402:
$this->messages[] = "402: Unable to Update the Database.";
return (FALSE);
default:
return ($this->errors($code));
}
}
function users()
{
// List all the current users attached to the server.
$this->send("whom");
switch ($code = $this->code($this->parse(1)))
{
case 210:
$this->messages[] = "210: OK, User List Follows.";
return ($this->parse(0));
case 401:
$this->messages[] = "401: No User Information Available.";
return (FALSE);
default:
return ($this->errors($code));
}
}
function version()
{
// Request server software version information.
$this->send("ver");
$header = $this->parse(1);
switch ($code = $this->code($header))
{
case 200:
$this->messages[] = "200: Version Information.";
$version[0] = $header;
return ($version);
case 201:
$this->messages[] = "201: Version Information Follows.";
$version = $this->parse(0);
return ($version);
default:
return ($this->errors($code));
}
}
}
?>