summaryrefslogtreecommitdiffstats
path: root/lib/class/art.class.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/class/art.class.php')
-rw-r--r--lib/class/art.class.php2360
1 files changed, 1180 insertions, 1180 deletions
diff --git a/lib/class/art.class.php b/lib/class/art.class.php
index 0e23463f..4b896e7f 100644
--- a/lib/class/art.class.php
+++ b/lib/class/art.class.php
@@ -1,5 +1,5 @@
<?php
-/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
+/* vim:set softtabstop=4 shiftwidth=4 expandtab: */
/**
*
* LICENSE: GNU General Public License, version 2 (GPLv2)
@@ -29,1184 +29,1184 @@
*/
class Art extends database_object {
- public $type;
- public $uid; // UID of the object not ID because it's not the ART.ID
- public $raw; // Raw art data
- public $raw_mime;
-
- public $thumb;
- public $thumb_mime;
-
- private static $enabled;
-
- /**
- * Constructor
- * Art constructor, takes the UID of the object and the
- * object type.
- */
- public function __construct($uid, $type = 'album') {
-
- $this->type = Art::validate_type($type);
- $this->uid = $uid;
-
- } // constructor
-
- /**
- * build_cache
- * This attempts to reduce # of queries by asking for everything in the
- * browse all at once and storing it in the cache, this can help if the
- * db connection is the slow point
- */
- public static function build_cache($object_ids) {
-
- if (!is_array($object_ids) || !count($object_ids)) { return false; }
- $uidlist = '(' . implode(',', $object_ids) . ')';
- $sql = "SELECT `object_type`, `object_id`, `mime`, `size` FROM `image` WHERE `object_id` IN $uidlist";
- $db_results = Dba::read($sql);
-
- while ($row = Dba::fetch_assoc($db_results)) {
- parent::add_to_cache('art', $row['object_type'] .
- $row['object_id'] . $row['size'], $row);
- }
-
- return true;
- } // build_cache
-
-
- /**
- * _auto_init
- * Called on creation of the class
- */
- public static function _auto_init() {
- if (!isset($_SESSION['art_enabled'])) {
- $_SESSION['art_enabled'] = (Config::get('bandwidth') > 25);
- }
- self::$enabled = make_bool($_SESSION['art_enabled']);
- }
-
- /**
- * is_enabled
- * Checks whether the user currently wants art
- */
- public static function is_enabled() {
- if (self::$enabled) {
- return true;
- }
-
- return false;
- }
-
- /**
- * set_enabled
- * Changes the value of enabled
- */
- public static function set_enabled($value = null) {
- if (is_null($value)) {
- self::$enabled = self::$enabled ? false : true;
- }
- else {
- self::$enabled = make_bool($value);
- }
-
- $_SESSION['art_enabled'] = self::$enabled;
- }
-
- /**
- * validate_type
- * This validates the type
- */
- public static function validate_type($type) {
-
- switch ($type) {
- case 'album':
- case 'artist':
- case 'video':
- return $type;
- break;
- default:
- return 'album';
- break;
- }
-
- } // validate_type
-
- /**
- * extension
- * This returns the file extension for the currently loaded art
- */
- public static function extension($mime) {
-
- $data = explode("/", $mime);
- $extension = $data['1'];
-
- if ($extension == 'jpeg') { $extension = 'jpg'; }
-
- return $extension;
-
- } // extension
-
- /**
- * test_image
- * Runs some sanity checks on the putative image
- */
- public static function test_image($source) {
- if (strlen($source) < 10) {
- debug_event('Art', 'Invalid image passed', 1);
- return false;
- }
-
- // Check to make sure PHP:GD exists. If so, we can sanity check
- // the image.
- if (function_exists('ImageCreateFromString')) {
- $image = ImageCreateFromString($source);
- if (!$image || imagesx($image) < 5 || imagesy($image) < 5) {
- debug_event('Art', 'Image failed PHP-GD test',1);
- return false;
- }
- }
-
- return true;
- } //test_image
-
- /**
- * get
- * This returns the art for our current object, this can
- * look in the database and will return the thumb if it
- * exists, if it doesn't depending on settings it will try
- * to create it.
- */
- public function get($raw=false) {
-
- // Get the data either way
- if (!$this->get_db()) {
- return false;
- }
-
- if ($raw || !$this->thumb) {
- return $this->raw;
- }
- else {
- return $this->thumb;
- }
-
- } // get
-
-
- /**
- * get_db
- * This pulls the information out from the database, depending
- * on if we want to resize and if there is not a thumbnail go
- * ahead and try to resize
- */
- public function get_db() {
-
- $type = Dba::escape($this->type);
- $id = Dba::escape($this->uid);
-
- $sql = "SELECT `image`, `mime`, `size` FROM `image` WHERE `object_type`='$type' AND `object_id`='$id'";
- $db_results = Dba::read($sql);
-
- while ($results = Dba::fetch_assoc($db_results)) {
- if ($results['size'] == 'original') {
- $this->raw = $results['image'];
- $this->raw_mime = $results['mime'];
- }
- else if (Config::get('resize_images') &&
- $results['size'] == '275x275') {
- $this->thumb = $results['image'];
- $this->raw_mime = $results['mime'];
- }
- }
- // If we get nothing return false
- if (!$this->raw) { return false; }
-
- // If there is no thumb and we want thumbs
- if (!$this->thumb && Config::get('resize_images')) {
- $data = $this->generate_thumb($this->raw, array('width' => 275, 'height' => 275), $this->raw_mime);
- // If it works save it!
- if ($data) {
- $this->save_thumb($data['thumb'], $data['thumb_mime'], '275x275');
- $this->thumb = $data['thumb'];
- $this->thumb_mime = $data['thumb_mime'];
- }
- else {
- debug_event('Art','Unable to retrieve or generate thumbnail for ' . $type . '::' . $id,1);
- }
- } // if no thumb, but art and we want to resize
-
- return true;
-
- } // get_db
-
- /**
- * insert
- * This takes the string representation of an image and inserts it into
- * the database. You must also pass the mime type.
- */
- public function insert($source, $mime) {
-
- // Disabled in demo mode cause people suck and upload porn
- if (Config::get('demo_mode')) { return false; }
-
- // Check to make sure we like this image
- if (!self::test_image($source)) {
- debug_event('Art', 'Not inserting image, invalid data passed', 1);
- return false;
- }
-
- // Default to image/jpeg if they don't pass anything
- $mime = $mime ? $mime : 'image/jpeg';
-
- $image = Dba::escape($source);
- $mime = Dba::escape($mime);
- $uid = Dba::escape($this->uid);
- $type = Dba::escape($this->type);
-
- // Blow it away!
- $this->reset();
-
- // Insert it!
- $sql = "INSERT INTO `image` (`image`, `mime`, `size`, `object_type`, `object_id`) VALUES('$image', '$mime', 'original', '$type', '$uid')";
- $db_results = Dba::write($sql);
-
- return true;
-
- } // insert
-
- /**
- * reset
- * This resets the art in the database
- */
- public function reset() {
-
- $type = Dba::escape($this->type);
- $uid = Dba::escape($this->uid);
-
- $sql = "DELETE FROM `image` WHERE `object_id`='$uid' AND `object_type`='$type'";
- $db_results = Dba::write($sql);
-
- } // reset
-
- /**
- * save_thumb
- * This saves the thumbnail that we're passed
- */
- public function save_thumb($source, $mime, $size) {
-
- // Quick sanity check
- if (!self::test_image($source)) {
- debug_event('Art', 'Not inserting thumbnail, invalid data passed', 1);
- return false;
- }
-
- $source = Dba::escape($source);
- $mime = Dba::escape($mime);
- $size = Dba::escape($size);
- $uid = Dba::escape($this->uid);
- $type = Dba::escape($this->type);
-
- $sql = "DELETE FROM `image` WHERE `object_id`='$uid' AND `object_type`='$type' AND `size`='$size'";
- $db_results = Dba::write($sql);
-
- $sql = "INSERT INTO `image` (`image`, `mime`, `size`, `object_type`, `object_id`) VALUES('$source', '$mime', '$size', '$type', '$uid')";
- $db_results = Dba::write($sql);
-
- } // save_thumb
-
- /**
- * get_thumb
- * Returns the specified resized image. If the requested size doesn't
- * already exist, create and cache it.
- */
- public function get_thumb($size) {
- $sizetext = $size['width'] . 'x' . $size['height'];
- $sizetext = Dba::escape($sizetext);
- $type = Dba::escape($this->type);
- $uid = Dba::escape($this->uid);
-
- $sql = "SELECT `image`, `mime` FROM `image` WHERE `size`='$sizetext' AND `object_type`='$type' AND `object_id`='$uid'";
- $db_results = Dba::read($sql);
-
- $results = Dba::fetch_assoc($db_results);
- if (count($results)) {
- return array('thumb' => $results['image'],
- 'thumb_mime' => $results['mime']);
- }
-
- // If we didn't get a result
- $results = $this->generate_thumb($this->raw, $size, $this->raw_mime);
- if ($results) {
- $this->save_thumb($results['thumb'], $results['thumb_mime'], $sizetext);
- }
-
- return $results;
- } // get_thumb
-
- /**
- * generate_thumb
- * Automatically resizes the image for thumbnail viewing.
- * Only works on gif/jpg/png/bmp. Fails if PHP-GD isn't available
- * or lacks support for the requested image type.
- */
- public function generate_thumb($image,$size,$mime) {
-
- $data = explode("/",$mime);
- $type = strtolower($data['1']);
-
- if (!self::test_image($image)) {
- debug_event('Art', 'Not trying to generate thumbnail, invalid data passed', 1);
- return false;
- }
-
- if (!function_exists('gd_info')) {
- debug_event('Art','PHP-GD Not found - unable to resize art',1);
- return false;
- }
-
- // Check and make sure we can resize what you've asked us to
- if (($type == 'jpg' OR $type == 'jpeg') AND !(imagetypes() & IMG_JPG)) {
- debug_event('Art','PHP-GD Does not support JPGs - unable to resize',1);
- return false;
- }
- if ($type == 'png' AND !imagetypes() & IMG_PNG) {
- debug_event('Art','PHP-GD Does not support PNGs - unable to resize',1);
- return false;
- }
- if ($type == 'gif' AND !imagetypes() & IMG_GIF) {
- debug_event('Art','PHP-GD Does not support GIFs - unable to resize',1);
- return false;
- }
- if ($type == 'bmp' AND !imagetypes() & IMG_WBMP) {
- debug_event('Art','PHP-GD Does not support BMPs - unable to resize',1);
- return false;
- }
-
- $source = imagecreatefromstring($image);
-
- if (!$source) {
- debug_event('Art','Failed to create Image from string - Source Image is damaged / malformed',1);
- return false;
- }
-
- $source_size = array('height' => imagesy($source), 'width' => imagesx($source));
-
- // Create a new blank image of the correct size
- $thumbnail = imagecreatetruecolor($size['width'], $size['height']);
-
- if (!imagecopyresampled($thumbnail, $source, 0, 0, 0, 0, $size['width'], $size['height'], $source_size['width'], $source_size['height'])) {
- debug_event('Art','Unable to create resized image',1);
- return false;
- }
-
- // Start output buffer
- ob_start();
-
- // Generate the image to our OB
- switch ($type) {
- case 'jpg':
- case 'jpeg':
- imagejpeg($thumbnail, null, 75);
- $mime_type = image_type_to_mime_type(IMAGETYPE_JPEG);
- break;
- case 'gif':
- imagegif($thumbnail);
- $mime_type = image_type_to_mime_type(IMAGETYPE_GIF);
- break;
- // Turn bmps into pngs
- case 'bmp':
- $type = 'png';
- case 'png':
- imagepng($thumbnail);
- $mime_type = image_type_to_mime_type(IMAGETYPE_PNG);
- break;
- } // resized
-
- $data = ob_get_contents();
- ob_end_clean();
-
- if (!strlen($data)) {
- debug_event('Art', 'Unknown Error resizing art', 1);
- return false;
- }
-
- return array('thumb' => $data, 'thumb_mime' => $mime_type);
-
- } // generate_thumb
-
- /**
- * get_from_source
- * This gets an image for the album art from a source as
- * defined in the passed array. Because we don't know where
- * it's coming from we are a passed an array that can look like
- * ['url'] = URL *** OPTIONAL ***
- * ['file'] = FILENAME *** OPTIONAL ***
- * ['raw'] = Actual Image data, already captured
- */
- public static function get_from_source($data, $type = 'album') {
-
- // Already have the data, this often comes from id3tags
- if (isset($data['raw'])) {
- return $data['raw'];
- }
-
- // If it came from the database
- if (isset($data['db'])) {
- // Repull it
- $uid = Dba::escape($data['db']);
- $type = Dba::escape($type);
-
- $sql = "SELECT * FROM `image` WHERE `object_type`='$type' AND `object_id`='$uid' AND `size`='original'";
- $db_results = Dba::read($sql);
- $row = Dba::fetch_assoc($db_results);
- return $row['art'];
- } // came from the db
-
- // Check to see if it's a URL
- if (isset($data['url'])) {
- $snoopy = new Snoopy();
- if(Config::get('proxy_host') AND Config::get('proxy_port')) {
- $snoopy->proxy_user = Config::get('proxy_host');
- $snoopy->proxy_port = Config::get('proxy_port');
- $snoopy->proxy_user = Config::get('proxy_user');
- $snoopy->proxy_pass = Config::get('proxy_pass');
- }
- $snoopy->fetch($data['url']);
- return $snoopy->results;
- }
-
- // Check to see if it's a FILE
- if (isset($data['file'])) {
- $handle = fopen($data['file'],'rb');
- $image_data = fread($handle,filesize($data['file']));
- fclose($handle);
- return $image_data;
- }
-
- // Check to see if it is embedded in id3 of a song
- if (isset($data['song'])) {
- // If we find a good one, stop looking
- $getID3 = new getID3();
- $id3 = $getID3->analyze($data['song']);
-
- if ($id3['format_name'] == "WMA") {
- return $id3['asf']['extended_content_description_object']['content_descriptors']['13']['data'];
- }
- elseif (isset($id3['id3v2']['APIC'])) {
- // Foreach in case they have more then one
- foreach ($id3['id3v2']['APIC'] as $image) {
- return $image['data'];
- }
- }
- } // if data song
-
- return false;
-
- } // get_from_source
-
- /**
- * url
- * This returns the constructed URL for the art in question
- */
- public static function url($uid,$type,$sid=false) {
-
- $sid = $sid ? scrub_out($sid) : scrub_out(session_id());
- $type = self::validate_type($type);
-
- $key = $type . $uid;
- if (parent::is_cached('art', $key . '275x275') && Config::get('resize_images')) {
- $row = parent::get_from_cache('art', $key . '275x275');
- $mime = $row['mime'];
- }
- if (parent::is_cached('art', $key . 'original')) {
- $row = parent::get_from_cache('art', $key . 'original');
- $thumb_mime = $row['mime'];
- }
- if (!$mime && !$thumb_mime) {
-
- $type = Dba::escape($type);
- $uid = Dba::escape($uid);
-
- $sql = "SELECT `object_type`, `object_id`, `mime`, `size` FROM `image` WHERE `object_type`='$type' AND `object_id`='$uid'";
- $db_results = Dba::read($sql);
-
- while ($row = Dba::fetch_assoc($db_results)) {
- parent::add_to_cache('art', $key . $row['size'], $row);
- if ($row['size'] == 'original') {
- $mime = $row['mime'];
- }
- else if ($row['size'] == '275x275' && Config::get('resize_images')) {
- $thumb_mime = $row['mime'];
- }
- }
- }
-
- $mime = $thumb_mime ? $thumb_mime : $mime;
- $extension = self::extension($mime);
-
- $name = 'art.' . $extension;
- $url = Config::get('web_path') . '/image.php?id=' . scrub_out($uid) . '&object_type=' . scrub_out($type) . '&auth=' . $sid . '&name=' . $name;
-
- return $url;
-
- } // url
-
-
- /**
- * gc
- * This cleans up art that no longer has a corresponding object
- */
- public static function gc() {
- // iterate over our types and delete the images
- foreach (array('album', 'artist') as $type) {
- $sql = "DELETE FROM `image` USING `image` LEFT JOIN `" .
- $type . "` ON `" . $type . "`.`id`=" .
- "`image`.`object_id` WHERE `object_type`='" .
- $type . "' AND `" . $type . "`.`id` IS NULL";
- $db_results = Dba::write($sql);
- } // foreach
- }
-
- /**
- * gather
- * This tries to get the art in question
- */
- public function gather($options = array(), $limit = false) {
-
- // Define vars
- $results = array();
-
- switch ($this->type) {
- case 'album':
- $allowed_methods = array('db','lastfm','folder','amazon','google','musicbrainz','tags');
- break;
- case 'artist':
- $allowed_methods = array();
- break;
- case 'video':
- $allowed_methods = array();
- break;
- }
-
- $config = Config::get('art_order');
- $methods = get_class_methods('Art');
-
- /* If it's not set */
- if (empty($config)) {
- // They don't want art!
- debug_event('Art', 'art_order is empty, skipping art gathering', 3);
- return array();
- }
- elseif (!is_array($config)) {
- $config = array($config);
- }
-
- debug_event('Art','Searching using:' . json_encode($config), 3);
-
- foreach ($config as $method) {
-
- $data = array();
-
- if (!in_array($method, $allowed_methods)) {
- debug_event('Art', "$method not in allowed_methods, skipping", 3);
- continue;
- }
-
- $method_name = "gather_" . $method;
-
- if (in_array($method_name, $methods)) {
- debug_event('Art', "Method used: $method_name", 3);
- // Some of these take options!
- switch ($method_name) {
- case 'gather_amazon':
- $data = $this->{$method_name}($limit, $options['keyword']);
- break;
- case 'gather_lastfm':
- $data = $this->{$method_name}($limit, $options);
- break;
- default:
- $data = $this->{$method_name}($limit);
- break;
- }
-
- // Add the results we got to the current set
- $results = array_merge($results, (array)$data);
-
- if ($limit && count($results) >= $limit) {
- return array_slice($results, 0, $limit);
- }
-
- } // if the method exists
- else {
- debug_event("Art", "$method_name not defined", 1);
- }
-
- } // end foreach
-
- return $results;
-
- } // gather
-
-
- ///////////////////////////////////////////////////////////////////////
- // Art Methods
- ///////////////////////////////////////////////////////////////////////
-
- /**
- * gather_db
- * This function retrieves art that's already in the database
- */
- public function gather_db($limit = null) {
- if ($this->get_db()) {
- return array('db' => true);
- }
- return array();
- }
-
- /**
- * gather_musicbrainz
- * This function retrieves art based on MusicBrainz' Advanced
- * Relationships
- */
- public function gather_musicbrainz($limit = 5) {
- $images = array();
- $num_found = 0;
-
- if ($this->type == 'album') {
- $album = new Album($this->uid);
- }
- else {
- return $images;
- }
-
- if ($album->mbid) {
- debug_event('mbz-gatherart', "Album MBID: " . $album->mbid, '5');
- }
- else {
- return $images;
- }
-
- $mbquery = new MusicBrainzQuery();
- $includes = new mbReleaseIncludes();
- try {
- $release = $mbquery->getReleaseByID($album->mbid, $includes->urlRelations());
- } catch (Exception $e) {
- return $images;
- }
-
- $asin = $release->getAsin();
-
- if ($asin) {
- debug_event('mbz-gatherart', "Found ASIN: " . $asin, '5');
- $base_urls = array(
- "01" => "ec1.images-amazon.com",
- "02" => "ec1.images-amazon.com",
- "03" => "ec2.images-amazon.com",
- "08" => "ec1.images-amazon.com",
- "09" => "ec1.images-amazon.com",
- );
- foreach ($base_urls as $server_num => $base_url) {
- // to avoid complicating things even further, we only look for large cover art
- $url = 'http://' . $base_url . '/images/P/' . $asin . '.' . $server_num . '.LZZZZZZZ.jpg';
- debug_event('mbz-gatherart', "Evaluating Amazon URL: " . $url, '5');
- $snoopy = new Snoopy();
- if(Config::get('proxy_host') AND Config::get('proxy_port')) {
- $snoopy->proxy_user = Config::get('proxy_host');
- $snoopy->proxy_port = Config::get('proxy_port');
- $snoopy->proxy_user = Config::get('proxy_user');
- $snoopy->proxy_pass = Config::get('proxy_pass');
- }
- if ($snoopy->fetch($url)) {
- $num_found++;
- debug_event('mbz-gatherart', "Amazon URL added: " . $url, '5');
- $images[] = array(
- 'url' => $url,
- 'mime' => 'image/jpeg',
- );
- if ($num_found >= $limit) {
- return $images;
- }
- }
- }
- }
- // The next bit is based directly on the MusicBrainz server code
- // that displays cover art.
- // I'm leaving in the releaseuri info for the moment, though
- // it's not going to be used.
- $coverartsites[] = array(
- 'name' => "CD Baby",
- 'domain' => "cdbaby.com",
- 'regexp' => '@http://cdbaby\.com/cd/(\w)(\w)(\w*)@',
- 'imguri' => 'http://cdbaby.name/$matches[1]/$matches[2]/$matches[1]$matches[2]$matches[3].jpg',
- 'releaseuri' => 'http://cdbaby.com/cd/$matches[1]$matches[2]$matches[3]/from/musicbrainz',
- );
- $coverartsites[] = array(
- 'name' => "CD Baby",
- 'domain' => "cdbaby.name",
- 'regexp' => "@http://cdbaby\.name/([a-z0-9])/([a-z0-9])/([A-Za-z0-9]*).jpg@",
- 'imguri' => 'http://cdbaby.name/$matches[1]/$matches[2]/$matches[3].jpg',
- 'releaseuri' => 'http://cdbaby.com/cd/$matches[3]/from/musicbrainz',
- );
- $coverartsites[] = array(
- 'name' => 'archive.org',
- 'domain' => 'archive.org',
- 'regexp' => '/^(.*\.(jpg|jpeg|png|gif))$/',
- 'imguri' => '$matches[1]',
- 'releaseuri' => '',
- );
- $coverartsites[] = array(
- 'name' => "Jamendo",
- 'domain' => "www.jamendo.com",
- 'regexp' => '/http://www\.jamendo\.com/(\w\w/)?album/(\d+)/',
- 'imguri' => 'http://img.jamendo.com/albums/$matches[2]/covers/1.200.jpg',
- 'releaseuri' => 'http://www.jamendo.com/album/$matches[2]',
- );
- $coverartsites[] = array(
- 'name' => '8bitpeoples.com',
- 'domain' => '8bitpeoples.com',
- 'regexp' => '/^(.*)$/',
- 'imguri' => '$matches[1]',
- 'releaseuri' => '',
- );
- $coverartsites[] = array(
- 'name' => 'Encyclopédisque',
- 'domain' => 'encyclopedisque.fr',
- 'regexp' => '/http://www.encyclopedisque.fr/images/imgdb/(thumb250|main)/(\d+).jpg/',
- 'imguri' => 'http://www.encyclopedisque.fr/images/imgdb/thumb250/$matches[2].jpg',
- 'releaseuri' => 'http://www.encyclopedisque.fr/',
- );
- $coverartsites[] = array(
- 'name' => 'Thastrom',
- 'domain' => 'www.thastrom.se',
- 'regexp' => '/^(.*)$/',
- 'imguri' => '$matches[1]',
- 'releaseuri' => '',
- );
- $coverartsites[] = array(
- 'name' => 'Universal Poplab',
- 'domain' => 'www.universalpoplab.com',
- 'regexp' => '/^(.*)$/',
- 'imguri' => '$matches[1]',
- 'releaseuri' => '',
- );
- foreach ($release->getRelations(mbRelation::TO_URL) as $ar) {
- $arurl = $ar->getTargetId();
- debug_event('mbz-gatherart', "Found URL AR: " . $arurl , '5');
- foreach ($coverartsites as $casite) {
- if (strpos($arurl, $casite['domain']) !== false) {
- debug_event('mbz-gatherart', "Matched coverart site: " . $casite['name'], '5');
- if (preg_match($casite['regexp'], $arurl, $matches)) {
- $num_found++;
- eval("\$url = \"$casite[imguri]\";");
- debug_event('mbz-gatherart', "Generated URL added: " . $url, '5');
- $images[] = array(
- 'url' => $url,
- 'mime' => 'image/jpeg',
- );
- if ($num_found >= $limit) {
- return $images;
- }
- }
- }
- } // end foreach coverart sites
- } // end foreach
-
- return $images;
-
- } // gather_musicbrainz
-
- /**
- * gather_amazon
- * This takes keywords and performs a search of the Amazon website
- * for the art. It returns an array of found objects with mime/url keys
- */
- public function gather_amazon($limit = 5, $keywords = '') {
-
- $images = array();
- $final_results = array();
- $possible_keys = array(
- 'LargeImage',
- 'MediumImage',
- 'SmallImage'
- );
-
- // Prevent the script from timing out
- set_time_limit(0);
-
- if (empty($keywords)) {
- $keywords = $this->full_name;
- /* If this isn't a various album combine with artist name */
- if ($this->artist_count == '1') { $keywords .= ' ' . $this->artist_name; }
- }
-
- /* Attempt to retrieve the album art order */
- $amazon_base_urls = Config::get('amazon_base_urls');
-
- /* If it's not set */
- if (!count($amazon_base_urls)) {
- $amazon_base_urls = array('http://webservices.amazon.com');
- }
-
- /* Foreach through the base urls that we should check */
- foreach ($amazon_base_urls as $amazon_base) {
-
- // Create the Search Object
- $amazon = new AmazonSearch(Config::get('amazon_developer_public_key'), Config::get('amazon_developer_private_key'), $amazon_base);
- if(Config::get('proxy_host') AND Config::get('proxy_port')) {
- $proxyhost = Config::get('proxy_host');
- $proxyport = Config::get('proxy_port');
- $proxyuser = Config::get('proxy_user');
- $proxypass = Config::get('proxy_pass');
- debug_event('amazon', 'setProxy', 5);
- $amazon->setProxy($proxyhost, $proxyport, $proxyuser, $proxypass);
- }
-
- $search_results = array();
-
- /* Set up the needed variables */
- $max_pages_to_search = max(Config::get('max_amazon_results_pages'),$amazon->_default_results_pages);
- $pages_to_search = $max_pages_to_search; //init to max until we know better.
- // while we have pages to search
- do {
- $raw_results = $amazon->search(array('artist'=>$artist,'album'=>$albumname,'keywords'=>$keywords));
-
- $total = count($raw_results) + count($search_results);
-
- // If we've gotten more then we wanted
- if ($limit && $total > $limit) {
- $raw_results = array_slice($raw_results, 0, -($total - $limit), true);
-
- debug_event('amazon-xml', "Found $total, limit $limit; reducing and breaking from loop", 5);
- // Merge the results and BREAK!
- $search_results = array_merge($search_results,$raw_results);
- break;
- } // if limit defined
-
- $search_results = array_merge($search_results,$raw_results);
- $pages_to_search = min($max_pages_to_search, $amazon->_maxPage);
- debug_event('amazon-xml', "Searched results page " . ($amazon->_currentPage+1) . "/" . $pages_to_search,'5');
- $amazon->_currentPage++;
-
- } while ($amazon->_currentPage < $pages_to_search);
-
-
- // Only do the second search if the first actually returns something
- if (count($search_results)) {
- $final_results = $amazon->lookup($search_results);
- }
-
- /* Log this if we're doin debug */
- debug_event('amazon-xml',"Searched using $keywords with " . Config::get('amazon_developer_key') . " as key, results: " . count($final_results), 5);
-
- // If we've hit our limit
- if (!empty($limit) && count($final_results) >= $limit) {
- break;
- }
-
- } // end foreach
-
- /* Foreach through what we've found */
- foreach ($final_results as $result) {
-
- /* Recurse through the images found */
- foreach ($possible_keys as $key) {
- if (strlen($result[$key])) {
- break;
- }
- } // foreach
-
- // Rudimentary image type detection, only JPG and GIF allowed.
- if (substr($result[$key], -4 == '.jpg')) {
- $mime = "image/jpeg";
- }
- elseif (substr($result[$key], -4 == '.gif')) {
- $mime = "image/gif";
- }
- elseif (substr($result[$key], -4 == '.png')) {
- $mime = "image/png";
- }
- else {
- /* Just go to the next result */
- continue;
- }
-
- $data['url'] = $result[$key];
- $data['mime'] = $mime;
-
- $images[] = $data;
-
- if (!empty($limit)) {
- if (count($images) >= $limit) {
- return $images;
- }
- }
-
- } // if we've got something
-
- return $images;
-
- } // gather_amazon
-
- /**
- * gather_folder
- * This returns the art from the folder of the files
- * If a limit is passed or the preferred filename is found the current
- * results set is returned
- */
- public function gather_folder($limit = 5) {
-
- $media = new Album($this->uid);
- $songs = $media->get_songs();
- $results = array();
- $preferred = false;
- // For storing which directories we've already done
- $processed = array();
-
- /* See if we are looking for a specific filename */
- $preferred_filename = Config::get('album_art_preferred_filename');
-
- // Array of valid extensions
- $image_extensions = array(
- 'bmp',
- 'gif',
- 'jp2',
- 'jpeg',
- 'jpg',
- 'png'
- );
-
- foreach ($songs as $song_id) {
- $data = array();
- $song = new Song($song_id);
- $dir = dirname($song->file);
-
- if (isset($processed[$dir])) {
- continue;
- }
-
- debug_event('folder_art', "Opening $dir and checking for Album Art", 3);
-
- /* Open up the directory */
- $handle = opendir($dir);
-
- if (!$handle) {
- Error::add('general', T_('Error: Unable to open') . ' ' . $dir);
- debug_event('folder_art', "Error: Unable to open $dir for album art read", 2);
- continue;
- }
-
- $processed[$dir] = true;
-
- // Recurse through this dir and create the files array
- while ($file = readdir($handle)) {
- $extension = pathinfo($file);
- $extension = $extension['extension'];
-
- // Make sure it looks like an image file
- if (!in_array($extension, $image_extensions)) {
- continue;
- }
-
- $full_filename = $dir . '/' . $file;
-
- // Make sure it's got something in it
- if (!filesize($full_filename)) {
- debug_event('folder_art', "Empty file, rejecting $file", 5);
- continue;
- }
-
- // Regularise for mime type
- if ($extension == 'jpg') {
- $extension = 'jpeg';
- }
-
- // Take an md5sum so we don't show duplicate
- // files.
- $index = md5($full_filename);
-
- if ($file == $preferred_filename) {
- // We found the preferred filename and
- // so we're done.
- debug_event('folder_art', "Found preferred image file: $file", 5);
- $preferred[$index] = array(
- 'file' => $full_filename,
- 'mime' => 'image/' . $extension
- );
- break;
- }
-
- debug_event('folder_art', "Found image file: $file", 5);
- $results[$index] = array(
- 'file' => $full_filename,
- 'mime' => 'image/' . $extension
- );
-
- } // end while reading dir
- closedir($handle);
-
- } // end foreach songs
-
- if (is_array($preferred)) {
- // We found our favourite filename somewhere, so we need
- // to dump the other, less sexy ones.
- $results = $preferred;
- }
-
- debug_event('folder_art', 'Results: ' . json_encode($results), 5);
- if ($limit && count($results) > $limit) {
- $results = array_slice($results, 0, $limit);
- }
-
- return array_values($results);
-
- } // gather_folder
-
- /**
- * gather_tags
- * This looks for the art in the meta-tags of the file
- * itself
- */
- public function gather_tags($limit = 5) {
-
- // We need the filenames
- $album = new Album($this->uid);
-
- // grab the songs and define our results
- $songs = $album->get_songs();
- $data = array();
-
- // Foreach songs in this album
- foreach ($songs as $song_id) {
- $song = new Song($song_id);
- // If we find a good one, stop looking
- $getID3 = new getID3();
- try { $id3 = $getID3->analyze($song->file); }
- catch (Exception $error) {
- debug_event('getid3', $error->message, 1);
- }
-
- if (isset($id3['asf']['extended_content_description_object']['content_descriptors']['13'])) {
- $image = $id3['asf']['extended_content_description_object']['content_descriptors']['13'];
- $data[] = array(
- 'song' => $song->file,
- 'raw' => $image['data'],
- 'mime' => $image['mime']);
- }
-
- if (isset($id3['id3v2']['APIC'])) {
- // Foreach in case they have more then one
- foreach ($id3['id3v2']['APIC'] as $image) {
- $data[] = array(
- 'song' => $song->file,
- 'raw' => $image['data'],
- 'mime' => $image['mime']);
- }
- }
-
- if ($limit && count($data) >= $limit) {
- return array_slice($data, 0, $limit);
- }
-
- } // end foreach
-
- return $data;
-
- } // gather_tags
-
- /**
- * gather_google
- * Raw google search to retrieve the art, not very reliable
- */
- public function gather_google($limit = 5) {
-
- $images = array();
- $media = new $this->type($this->uid);
- $media->format();
-
- $search = $media->full_name;
-
- if ($media->artist_count == '1')
- $search = $media->artist_name . ', ' . $search;
-
- $search = rawurlencode($search);
-
- $size = '&imgsz=m'; // Medium
- //$size = '&imgsz=l'; // Large
-
- $html = file_get_contents("http://images.google.com/images?source=hp&q=$search&oq=&um=1&ie=UTF-8&sa=N&tab=wi&start=0&tbo=1$size");
-
- if(preg_match_all("|\ssrc\=\"(http.+?)\"|", $html, $matches, PREG_PATTERN_ORDER))
- foreach ($matches[1] as $match) {
- $extension = "image/jpeg";
-
- if (strrpos($extension, '.') !== false) $extension = substr($extension, strrpos($extension, '.') + 1);
-
- $images[] = array('url' => $match, 'mime' => $extension);
- }
-
- return $images;
-
- } // gather_google
-
- /**
- * gather_lastfm
- * This returns the art from lastfm. It doesn't currently require an
- * account but may in the future.
- */
- public function gather_lastfm($limit, $options = false) {
-
- // Create the parser object
- $lastfm = new LastFMSearch();
-
- switch ($this->type) {
- case 'album':
- if (is_array($options)) {
- $artist = $options['artist'];
- $album = $options['album_name'];
- }
- else {
- $media = new Album($this->uid);
- $media->format();
- $artist = $media->artist_name;
- $album = $media->full_name;
- }
- break;
- }
-
- if(Config::get('proxy_host') AND Config::get('proxy_port')) {
- $proxyhost = Config::get('proxy_host');
- $proxyport = Config::get('proxy_port');
- $proxyuser = Config::get('proxy_user');
- $proxypass = Config::get('proxy_pass');
- debug_event('LastFM', 'proxy set', 5);
- $lastfm->setProxy($proxyhost, $proxyport, $proxyuser, $proxypass);
- }
-
- $raw_data = $lastfm->album_search($artist, $album);
-
- if (!count($raw_data)) { return array(); }
-
- $coverart = $raw_data['coverart'];
- if (!is_array($coverart)) { return array(); }
-
- ksort($coverart);
- foreach ($coverart as $url) {
- // We need to check the URL for the /noimage/ stuff
- if (strpos($url, '/noimage/') !== false) {
- debug_event('LastFM', 'Detected as noimage, skipped ' . $url, 3);
- continue;
- }
-
- // HACK: we shouldn't rely on the extension to determine file type
- $results = pathinfo($url);
- $mime = 'image/' . $results['extension'];
- $data[] = array('url' => $url, 'mime' => $mime);
- if ($limit && count($data) >= $limit) {
- return $data;
- }
- } // end foreach
-
- return $data;
-
- } // gather_lastfm
+ public $type;
+ public $uid; // UID of the object not ID because it's not the ART.ID
+ public $raw; // Raw art data
+ public $raw_mime;
+
+ public $thumb;
+ public $thumb_mime;
+
+ private static $enabled;
+
+ /**
+ * Constructor
+ * Art constructor, takes the UID of the object and the
+ * object type.
+ */
+ public function __construct($uid, $type = 'album') {
+
+ $this->type = Art::validate_type($type);
+ $this->uid = $uid;
+
+ } // constructor
+
+ /**
+ * build_cache
+ * This attempts to reduce # of queries by asking for everything in the
+ * browse all at once and storing it in the cache, this can help if the
+ * db connection is the slow point
+ */
+ public static function build_cache($object_ids) {
+
+ if (!is_array($object_ids) || !count($object_ids)) { return false; }
+ $uidlist = '(' . implode(',', $object_ids) . ')';
+ $sql = "SELECT `object_type`, `object_id`, `mime`, `size` FROM `image` WHERE `object_id` IN $uidlist";
+ $db_results = Dba::read($sql);
+
+ while ($row = Dba::fetch_assoc($db_results)) {
+ parent::add_to_cache('art', $row['object_type'] .
+ $row['object_id'] . $row['size'], $row);
+ }
+
+ return true;
+ } // build_cache
+
+
+ /**
+ * _auto_init
+ * Called on creation of the class
+ */
+ public static function _auto_init() {
+ if (!isset($_SESSION['art_enabled'])) {
+ $_SESSION['art_enabled'] = (Config::get('bandwidth') > 25);
+ }
+ self::$enabled = make_bool($_SESSION['art_enabled']);
+ }
+
+ /**
+ * is_enabled
+ * Checks whether the user currently wants art
+ */
+ public static function is_enabled() {
+ if (self::$enabled) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * set_enabled
+ * Changes the value of enabled
+ */
+ public static function set_enabled($value = null) {
+ if (is_null($value)) {
+ self::$enabled = self::$enabled ? false : true;
+ }
+ else {
+ self::$enabled = make_bool($value);
+ }
+
+ $_SESSION['art_enabled'] = self::$enabled;
+ }
+
+ /**
+ * validate_type
+ * This validates the type
+ */
+ public static function validate_type($type) {
+
+ switch ($type) {
+ case 'album':
+ case 'artist':
+ case 'video':
+ return $type;
+ break;
+ default:
+ return 'album';
+ break;
+ }
+
+ } // validate_type
+
+ /**
+ * extension
+ * This returns the file extension for the currently loaded art
+ */
+ public static function extension($mime) {
+
+ $data = explode("/", $mime);
+ $extension = $data['1'];
+
+ if ($extension == 'jpeg') { $extension = 'jpg'; }
+
+ return $extension;
+
+ } // extension
+
+ /**
+ * test_image
+ * Runs some sanity checks on the putative image
+ */
+ public static function test_image($source) {
+ if (strlen($source) < 10) {
+ debug_event('Art', 'Invalid image passed', 1);
+ return false;
+ }
+
+ // Check to make sure PHP:GD exists. If so, we can sanity check
+ // the image.
+ if (function_exists('ImageCreateFromString')) {
+ $image = ImageCreateFromString($source);
+ if (!$image || imagesx($image) < 5 || imagesy($image) < 5) {
+ debug_event('Art', 'Image failed PHP-GD test',1);
+ return false;
+ }
+ }
+
+ return true;
+ } //test_image
+
+ /**
+ * get
+ * This returns the art for our current object, this can
+ * look in the database and will return the thumb if it
+ * exists, if it doesn't depending on settings it will try
+ * to create it.
+ */
+ public function get($raw=false) {
+
+ // Get the data either way
+ if (!$this->get_db()) {
+ return false;
+ }
+
+ if ($raw || !$this->thumb) {
+ return $this->raw;
+ }
+ else {
+ return $this->thumb;
+ }
+
+ } // get
+
+
+ /**
+ * get_db
+ * This pulls the information out from the database, depending
+ * on if we want to resize and if there is not a thumbnail go
+ * ahead and try to resize
+ */
+ public function get_db() {
+
+ $type = Dba::escape($this->type);
+ $id = Dba::escape($this->uid);
+
+ $sql = "SELECT `image`, `mime`, `size` FROM `image` WHERE `object_type`='$type' AND `object_id`='$id'";
+ $db_results = Dba::read($sql);
+
+ while ($results = Dba::fetch_assoc($db_results)) {
+ if ($results['size'] == 'original') {
+ $this->raw = $results['image'];
+ $this->raw_mime = $results['mime'];
+ }
+ else if (Config::get('resize_images') &&
+ $results['size'] == '275x275') {
+ $this->thumb = $results['image'];
+ $this->raw_mime = $results['mime'];
+ }
+ }
+ // If we get nothing return false
+ if (!$this->raw) { return false; }
+
+ // If there is no thumb and we want thumbs
+ if (!$this->thumb && Config::get('resize_images')) {
+ $data = $this->generate_thumb($this->raw, array('width' => 275, 'height' => 275), $this->raw_mime);
+ // If it works save it!
+ if ($data) {
+ $this->save_thumb($data['thumb'], $data['thumb_mime'], '275x275');
+ $this->thumb = $data['thumb'];
+ $this->thumb_mime = $data['thumb_mime'];
+ }
+ else {
+ debug_event('Art','Unable to retrieve or generate thumbnail for ' . $type . '::' . $id,1);
+ }
+ } // if no thumb, but art and we want to resize
+
+ return true;
+
+ } // get_db
+
+ /**
+ * insert
+ * This takes the string representation of an image and inserts it into
+ * the database. You must also pass the mime type.
+ */
+ public function insert($source, $mime) {
+
+ // Disabled in demo mode cause people suck and upload porn
+ if (Config::get('demo_mode')) { return false; }
+
+ // Check to make sure we like this image
+ if (!self::test_image($source)) {
+ debug_event('Art', 'Not inserting image, invalid data passed', 1);
+ return false;
+ }
+
+ // Default to image/jpeg if they don't pass anything
+ $mime = $mime ? $mime : 'image/jpeg';
+
+ $image = Dba::escape($source);
+ $mime = Dba::escape($mime);
+ $uid = Dba::escape($this->uid);
+ $type = Dba::escape($this->type);
+
+ // Blow it away!
+ $this->reset();
+
+ // Insert it!
+ $sql = "INSERT INTO `image` (`image`, `mime`, `size`, `object_type`, `object_id`) VALUES('$image', '$mime', 'original', '$type', '$uid')";
+ $db_results = Dba::write($sql);
+
+ return true;
+
+ } // insert
+
+ /**
+ * reset
+ * This resets the art in the database
+ */
+ public function reset() {
+
+ $type = Dba::escape($this->type);
+ $uid = Dba::escape($this->uid);
+
+ $sql = "DELETE FROM `image` WHERE `object_id`='$uid' AND `object_type`='$type'";
+ $db_results = Dba::write($sql);
+
+ } // reset
+
+ /**
+ * save_thumb
+ * This saves the thumbnail that we're passed
+ */
+ public function save_thumb($source, $mime, $size) {
+
+ // Quick sanity check
+ if (!self::test_image($source)) {
+ debug_event('Art', 'Not inserting thumbnail, invalid data passed', 1);
+ return false;
+ }
+
+ $source = Dba::escape($source);
+ $mime = Dba::escape($mime);
+ $size = Dba::escape($size);
+ $uid = Dba::escape($this->uid);
+ $type = Dba::escape($this->type);
+
+ $sql = "DELETE FROM `image` WHERE `object_id`='$uid' AND `object_type`='$type' AND `size`='$size'";
+ $db_results = Dba::write($sql);
+
+ $sql = "INSERT INTO `image` (`image`, `mime`, `size`, `object_type`, `object_id`) VALUES('$source', '$mime', '$size', '$type', '$uid')";
+ $db_results = Dba::write($sql);
+
+ } // save_thumb
+
+ /**
+ * get_thumb
+ * Returns the specified resized image. If the requested size doesn't
+ * already exist, create and cache it.
+ */
+ public function get_thumb($size) {
+ $sizetext = $size['width'] . 'x' . $size['height'];
+ $sizetext = Dba::escape($sizetext);
+ $type = Dba::escape($this->type);
+ $uid = Dba::escape($this->uid);
+
+ $sql = "SELECT `image`, `mime` FROM `image` WHERE `size`='$sizetext' AND `object_type`='$type' AND `object_id`='$uid'";
+ $db_results = Dba::read($sql);
+
+ $results = Dba::fetch_assoc($db_results);
+ if (count($results)) {
+ return array('thumb' => $results['image'],
+ 'thumb_mime' => $results['mime']);
+ }
+
+ // If we didn't get a result
+ $results = $this->generate_thumb($this->raw, $size, $this->raw_mime);
+ if ($results) {
+ $this->save_thumb($results['thumb'], $results['thumb_mime'], $sizetext);
+ }
+
+ return $results;
+ } // get_thumb
+
+ /**
+ * generate_thumb
+ * Automatically resizes the image for thumbnail viewing.
+ * Only works on gif/jpg/png/bmp. Fails if PHP-GD isn't available
+ * or lacks support for the requested image type.
+ */
+ public function generate_thumb($image,$size,$mime) {
+
+ $data = explode("/",$mime);
+ $type = strtolower($data['1']);
+
+ if (!self::test_image($image)) {
+ debug_event('Art', 'Not trying to generate thumbnail, invalid data passed', 1);
+ return false;
+ }
+
+ if (!function_exists('gd_info')) {
+ debug_event('Art','PHP-GD Not found - unable to resize art',1);
+ return false;
+ }
+
+ // Check and make sure we can resize what you've asked us to
+ if (($type == 'jpg' OR $type == 'jpeg') AND !(imagetypes() & IMG_JPG)) {
+ debug_event('Art','PHP-GD Does not support JPGs - unable to resize',1);
+ return false;
+ }
+ if ($type == 'png' AND !imagetypes() & IMG_PNG) {
+ debug_event('Art','PHP-GD Does not support PNGs - unable to resize',1);
+ return false;
+ }
+ if ($type == 'gif' AND !imagetypes() & IMG_GIF) {
+ debug_event('Art','PHP-GD Does not support GIFs - unable to resize',1);
+ return false;
+ }
+ if ($type == 'bmp' AND !imagetypes() & IMG_WBMP) {
+ debug_event('Art','PHP-GD Does not support BMPs - unable to resize',1);
+ return false;
+ }
+
+ $source = imagecreatefromstring($image);
+
+ if (!$source) {
+ debug_event('Art','Failed to create Image from string - Source Image is damaged / malformed',1);
+ return false;
+ }
+
+ $source_size = array('height' => imagesy($source), 'width' => imagesx($source));
+
+ // Create a new blank image of the correct size
+ $thumbnail = imagecreatetruecolor($size['width'], $size['height']);
+
+ if (!imagecopyresampled($thumbnail, $source, 0, 0, 0, 0, $size['width'], $size['height'], $source_size['width'], $source_size['height'])) {
+ debug_event('Art','Unable to create resized image',1);
+ return false;
+ }
+
+ // Start output buffer
+ ob_start();
+
+ // Generate the image to our OB
+ switch ($type) {
+ case 'jpg':
+ case 'jpeg':
+ imagejpeg($thumbnail, null, 75);
+ $mime_type = image_type_to_mime_type(IMAGETYPE_JPEG);
+ break;
+ case 'gif':
+ imagegif($thumbnail);
+ $mime_type = image_type_to_mime_type(IMAGETYPE_GIF);
+ break;
+ // Turn bmps into pngs
+ case 'bmp':
+ $type = 'png';
+ case 'png':
+ imagepng($thumbnail);
+ $mime_type = image_type_to_mime_type(IMAGETYPE_PNG);
+ break;
+ } // resized
+
+ $data = ob_get_contents();
+ ob_end_clean();
+
+ if (!strlen($data)) {
+ debug_event('Art', 'Unknown Error resizing art', 1);
+ return false;
+ }
+
+ return array('thumb' => $data, 'thumb_mime' => $mime_type);
+
+ } // generate_thumb
+
+ /**
+ * get_from_source
+ * This gets an image for the album art from a source as
+ * defined in the passed array. Because we don't know where
+ * it's coming from we are a passed an array that can look like
+ * ['url'] = URL *** OPTIONAL ***
+ * ['file'] = FILENAME *** OPTIONAL ***
+ * ['raw'] = Actual Image data, already captured
+ */
+ public static function get_from_source($data, $type = 'album') {
+
+ // Already have the data, this often comes from id3tags
+ if (isset($data['raw'])) {
+ return $data['raw'];
+ }
+
+ // If it came from the database
+ if (isset($data['db'])) {
+ // Repull it
+ $uid = Dba::escape($data['db']);
+ $type = Dba::escape($type);
+
+ $sql = "SELECT * FROM `image` WHERE `object_type`='$type' AND `object_id`='$uid' AND `size`='original'";
+ $db_results = Dba::read($sql);
+ $row = Dba::fetch_assoc($db_results);
+ return $row['art'];
+ } // came from the db
+
+ // Check to see if it's a URL
+ if (isset($data['url'])) {
+ $snoopy = new Snoopy();
+ if(Config::get('proxy_host') AND Config::get('proxy_port')) {
+ $snoopy->proxy_user = Config::get('proxy_host');
+ $snoopy->proxy_port = Config::get('proxy_port');
+ $snoopy->proxy_user = Config::get('proxy_user');
+ $snoopy->proxy_pass = Config::get('proxy_pass');
+ }
+ $snoopy->fetch($data['url']);
+ return $snoopy->results;
+ }
+
+ // Check to see if it's a FILE
+ if (isset($data['file'])) {
+ $handle = fopen($data['file'],'rb');
+ $image_data = fread($handle,filesize($data['file']));
+ fclose($handle);
+ return $image_data;
+ }
+
+ // Check to see if it is embedded in id3 of a song
+ if (isset($data['song'])) {
+ // If we find a good one, stop looking
+ $getID3 = new getID3();
+ $id3 = $getID3->analyze($data['song']);
+
+ if ($id3['format_name'] == "WMA") {
+ return $id3['asf']['extended_content_description_object']['content_descriptors']['13']['data'];
+ }
+ elseif (isset($id3['id3v2']['APIC'])) {
+ // Foreach in case they have more then one
+ foreach ($id3['id3v2']['APIC'] as $image) {
+ return $image['data'];
+ }
+ }
+ } // if data song
+
+ return false;
+
+ } // get_from_source
+
+ /**
+ * url
+ * This returns the constructed URL for the art in question
+ */
+ public static function url($uid,$type,$sid=false) {
+
+ $sid = $sid ? scrub_out($sid) : scrub_out(session_id());
+ $type = self::validate_type($type);
+
+ $key = $type . $uid;
+ if (parent::is_cached('art', $key . '275x275') && Config::get('resize_images')) {
+ $row = parent::get_from_cache('art', $key . '275x275');
+ $mime = $row['mime'];
+ }
+ if (parent::is_cached('art', $key . 'original')) {
+ $row = parent::get_from_cache('art', $key . 'original');
+ $thumb_mime = $row['mime'];
+ }
+ if (!$mime && !$thumb_mime) {
+
+ $type = Dba::escape($type);
+ $uid = Dba::escape($uid);
+
+ $sql = "SELECT `object_type`, `object_id`, `mime`, `size` FROM `image` WHERE `object_type`='$type' AND `object_id`='$uid'";
+ $db_results = Dba::read($sql);
+
+ while ($row = Dba::fetch_assoc($db_results)) {
+ parent::add_to_cache('art', $key . $row['size'], $row);
+ if ($row['size'] == 'original') {
+ $mime = $row['mime'];
+ }
+ else if ($row['size'] == '275x275' && Config::get('resize_images')) {
+ $thumb_mime = $row['mime'];
+ }
+ }
+ }
+
+ $mime = $thumb_mime ? $thumb_mime : $mime;
+ $extension = self::extension($mime);
+
+ $name = 'art.' . $extension;
+ $url = Config::get('web_path') . '/image.php?id=' . scrub_out($uid) . '&object_type=' . scrub_out($type) . '&auth=' . $sid . '&name=' . $name;
+
+ return $url;
+
+ } // url
+
+
+ /**
+ * gc
+ * This cleans up art that no longer has a corresponding object
+ */
+ public static function gc() {
+ // iterate over our types and delete the images
+ foreach (array('album', 'artist') as $type) {
+ $sql = "DELETE FROM `image` USING `image` LEFT JOIN `" .
+ $type . "` ON `" . $type . "`.`id`=" .
+ "`image`.`object_id` WHERE `object_type`='" .
+ $type . "' AND `" . $type . "`.`id` IS NULL";
+ $db_results = Dba::write($sql);
+ } // foreach
+ }
+
+ /**
+ * gather
+ * This tries to get the art in question
+ */
+ public function gather($options = array(), $limit = false) {
+
+ // Define vars
+ $results = array();
+
+ switch ($this->type) {
+ case 'album':
+ $allowed_methods = array('db','lastfm','folder','amazon','google','musicbrainz','tags');
+ break;
+ case 'artist':
+ $allowed_methods = array();
+ break;
+ case 'video':
+ $allowed_methods = array();
+ break;
+ }
+
+ $config = Config::get('art_order');
+ $methods = get_class_methods('Art');
+
+ /* If it's not set */
+ if (empty($config)) {
+ // They don't want art!
+ debug_event('Art', 'art_order is empty, skipping art gathering', 3);
+ return array();
+ }
+ elseif (!is_array($config)) {
+ $config = array($config);
+ }
+
+ debug_event('Art','Searching using:' . json_encode($config), 3);
+
+ foreach ($config as $method) {
+
+ $data = array();
+
+ if (!in_array($method, $allowed_methods)) {
+ debug_event('Art', "$method not in allowed_methods, skipping", 3);
+ continue;
+ }
+
+ $method_name = "gather_" . $method;
+
+ if (in_array($method_name, $methods)) {
+ debug_event('Art', "Method used: $method_name", 3);
+ // Some of these take options!
+ switch ($method_name) {
+ case 'gather_amazon':
+ $data = $this->{$method_name}($limit, $options['keyword']);
+ break;
+ case 'gather_lastfm':
+ $data = $this->{$method_name}($limit, $options);
+ break;
+ default:
+ $data = $this->{$method_name}($limit);
+ break;
+ }
+
+ // Add the results we got to the current set
+ $results = array_merge($results, (array)$data);
+
+ if ($limit && count($results) >= $limit) {
+ return array_slice($results, 0, $limit);
+ }
+
+ } // if the method exists
+ else {
+ debug_event("Art", "$method_name not defined", 1);
+ }
+
+ } // end foreach
+
+ return $results;
+
+ } // gather
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // Art Methods
+ ///////////////////////////////////////////////////////////////////////
+
+ /**
+ * gather_db
+ * This function retrieves art that's already in the database
+ */
+ public function gather_db($limit = null) {
+ if ($this->get_db()) {
+ return array('db' => true);
+ }
+ return array();
+ }
+
+ /**
+ * gather_musicbrainz
+ * This function retrieves art based on MusicBrainz' Advanced
+ * Relationships
+ */
+ public function gather_musicbrainz($limit = 5) {
+ $images = array();
+ $num_found = 0;
+
+ if ($this->type == 'album') {
+ $album = new Album($this->uid);
+ }
+ else {
+ return $images;
+ }
+
+ if ($album->mbid) {
+ debug_event('mbz-gatherart', "Album MBID: " . $album->mbid, '5');
+ }
+ else {
+ return $images;
+ }
+
+ $mbquery = new MusicBrainzQuery();
+ $includes = new mbReleaseIncludes();
+ try {
+ $release = $mbquery->getReleaseByID($album->mbid, $includes->urlRelations());
+ } catch (Exception $e) {
+ return $images;
+ }
+
+ $asin = $release->getAsin();
+
+ if ($asin) {
+ debug_event('mbz-gatherart', "Found ASIN: " . $asin, '5');
+ $base_urls = array(
+ "01" => "ec1.images-amazon.com",
+ "02" => "ec1.images-amazon.com",
+ "03" => "ec2.images-amazon.com",
+ "08" => "ec1.images-amazon.com",
+ "09" => "ec1.images-amazon.com",
+ );
+ foreach ($base_urls as $server_num => $base_url) {
+ // to avoid complicating things even further, we only look for large cover art
+ $url = 'http://' . $base_url . '/images/P/' . $asin . '.' . $server_num . '.LZZZZZZZ.jpg';
+ debug_event('mbz-gatherart', "Evaluating Amazon URL: " . $url, '5');
+ $snoopy = new Snoopy();
+ if(Config::get('proxy_host') AND Config::get('proxy_port')) {
+ $snoopy->proxy_user = Config::get('proxy_host');
+ $snoopy->proxy_port = Config::get('proxy_port');
+ $snoopy->proxy_user = Config::get('proxy_user');
+ $snoopy->proxy_pass = Config::get('proxy_pass');
+ }
+ if ($snoopy->fetch($url)) {
+ $num_found++;
+ debug_event('mbz-gatherart', "Amazon URL added: " . $url, '5');
+ $images[] = array(
+ 'url' => $url,
+ 'mime' => 'image/jpeg',
+ );
+ if ($num_found >= $limit) {
+ return $images;
+ }
+ }
+ }
+ }
+ // The next bit is based directly on the MusicBrainz server code
+ // that displays cover art.
+ // I'm leaving in the releaseuri info for the moment, though
+ // it's not going to be used.
+ $coverartsites[] = array(
+ 'name' => "CD Baby",
+ 'domain' => "cdbaby.com",
+ 'regexp' => '@http://cdbaby\.com/cd/(\w)(\w)(\w*)@',
+ 'imguri' => 'http://cdbaby.name/$matches[1]/$matches[2]/$matches[1]$matches[2]$matches[3].jpg',
+ 'releaseuri' => 'http://cdbaby.com/cd/$matches[1]$matches[2]$matches[3]/from/musicbrainz',
+ );
+ $coverartsites[] = array(
+ 'name' => "CD Baby",
+ 'domain' => "cdbaby.name",
+ 'regexp' => "@http://cdbaby\.name/([a-z0-9])/([a-z0-9])/([A-Za-z0-9]*).jpg@",
+ 'imguri' => 'http://cdbaby.name/$matches[1]/$matches[2]/$matches[3].jpg',
+ 'releaseuri' => 'http://cdbaby.com/cd/$matches[3]/from/musicbrainz',
+ );
+ $coverartsites[] = array(
+ 'name' => 'archive.org',
+ 'domain' => 'archive.org',
+ 'regexp' => '/^(.*\.(jpg|jpeg|png|gif))$/',
+ 'imguri' => '$matches[1]',
+ 'releaseuri' => '',
+ );
+ $coverartsites[] = array(
+ 'name' => "Jamendo",
+ 'domain' => "www.jamendo.com",
+ 'regexp' => '/http://www\.jamendo\.com/(\w\w/)?album/(\d+)/',
+ 'imguri' => 'http://img.jamendo.com/albums/$matches[2]/covers/1.200.jpg',
+ 'releaseuri' => 'http://www.jamendo.com/album/$matches[2]',
+ );
+ $coverartsites[] = array(
+ 'name' => '8bitpeoples.com',
+ 'domain' => '8bitpeoples.com',
+ 'regexp' => '/^(.*)$/',
+ 'imguri' => '$matches[1]',
+ 'releaseuri' => '',
+ );
+ $coverartsites[] = array(
+ 'name' => 'Encyclopédisque',
+ 'domain' => 'encyclopedisque.fr',
+ 'regexp' => '/http://www.encyclopedisque.fr/images/imgdb/(thumb250|main)/(\d+).jpg/',
+ 'imguri' => 'http://www.encyclopedisque.fr/images/imgdb/thumb250/$matches[2].jpg',
+ 'releaseuri' => 'http://www.encyclopedisque.fr/',
+ );
+ $coverartsites[] = array(
+ 'name' => 'Thastrom',
+ 'domain' => 'www.thastrom.se',
+ 'regexp' => '/^(.*)$/',
+ 'imguri' => '$matches[1]',
+ 'releaseuri' => '',
+ );
+ $coverartsites[] = array(
+ 'name' => 'Universal Poplab',
+ 'domain' => 'www.universalpoplab.com',
+ 'regexp' => '/^(.*)$/',
+ 'imguri' => '$matches[1]',
+ 'releaseuri' => '',
+ );
+ foreach ($release->getRelations(mbRelation::TO_URL) as $ar) {
+ $arurl = $ar->getTargetId();
+ debug_event('mbz-gatherart', "Found URL AR: " . $arurl , '5');
+ foreach ($coverartsites as $casite) {
+ if (strpos($arurl, $casite['domain']) !== false) {
+ debug_event('mbz-gatherart', "Matched coverart site: " . $casite['name'], '5');
+ if (preg_match($casite['regexp'], $arurl, $matches)) {
+ $num_found++;
+ eval("\$url = \"$casite[imguri]\";");
+ debug_event('mbz-gatherart', "Generated URL added: " . $url, '5');
+ $images[] = array(
+ 'url' => $url,
+ 'mime' => 'image/jpeg',
+ );
+ if ($num_found >= $limit) {
+ return $images;
+ }
+ }
+ }
+ } // end foreach coverart sites
+ } // end foreach
+
+ return $images;
+
+ } // gather_musicbrainz
+
+ /**
+ * gather_amazon
+ * This takes keywords and performs a search of the Amazon website
+ * for the art. It returns an array of found objects with mime/url keys
+ */
+ public function gather_amazon($limit = 5, $keywords = '') {
+
+ $images = array();
+ $final_results = array();
+ $possible_keys = array(
+ 'LargeImage',
+ 'MediumImage',
+ 'SmallImage'
+ );
+
+ // Prevent the script from timing out
+ set_time_limit(0);
+
+ if (empty($keywords)) {
+ $keywords = $this->full_name;
+ /* If this isn't a various album combine with artist name */
+ if ($this->artist_count == '1') { $keywords .= ' ' . $this->artist_name; }
+ }
+
+ /* Attempt to retrieve the album art order */
+ $amazon_base_urls = Config::get('amazon_base_urls');
+
+ /* If it's not set */
+ if (!count($amazon_base_urls)) {
+ $amazon_base_urls = array('http://webservices.amazon.com');
+ }
+
+ /* Foreach through the base urls that we should check */
+ foreach ($amazon_base_urls as $amazon_base) {
+
+ // Create the Search Object
+ $amazon = new AmazonSearch(Config::get('amazon_developer_public_key'), Config::get('amazon_developer_private_key'), $amazon_base);
+ if(Config::get('proxy_host') AND Config::get('proxy_port')) {
+ $proxyhost = Config::get('proxy_host');
+ $proxyport = Config::get('proxy_port');
+ $proxyuser = Config::get('proxy_user');
+ $proxypass = Config::get('proxy_pass');
+ debug_event('amazon', 'setProxy', 5);
+ $amazon->setProxy($proxyhost, $proxyport, $proxyuser, $proxypass);
+ }
+
+ $search_results = array();
+
+ /* Set up the needed variables */
+ $max_pages_to_search = max(Config::get('max_amazon_results_pages'),$amazon->_default_results_pages);
+ $pages_to_search = $max_pages_to_search; //init to max until we know better.
+ // while we have pages to search
+ do {
+ $raw_results = $amazon->search(array('artist'=>$artist,'album'=>$albumname,'keywords'=>$keywords));
+
+ $total = count($raw_results) + count($search_results);
+
+ // If we've gotten more then we wanted
+ if ($limit && $total > $limit) {
+ $raw_results = array_slice($raw_results, 0, -($total - $limit), true);
+
+ debug_event('amazon-xml', "Found $total, limit $limit; reducing and breaking from loop", 5);
+ // Merge the results and BREAK!
+ $search_results = array_merge($search_results,$raw_results);
+ break;
+ } // if limit defined
+
+ $search_results = array_merge($search_results,$raw_results);
+ $pages_to_search = min($max_pages_to_search, $amazon->_maxPage);
+ debug_event('amazon-xml', "Searched results page " . ($amazon->_currentPage+1) . "/" . $pages_to_search,'5');
+ $amazon->_currentPage++;
+
+ } while ($amazon->_currentPage < $pages_to_search);
+
+
+ // Only do the second search if the first actually returns something
+ if (count($search_results)) {
+ $final_results = $amazon->lookup($search_results);
+ }
+
+ /* Log this if we're doin debug */
+ debug_event('amazon-xml',"Searched using $keywords with " . Config::get('amazon_developer_key') . " as key, results: " . count($final_results), 5);
+
+ // If we've hit our limit
+ if (!empty($limit) && count($final_results) >= $limit) {
+ break;
+ }
+
+ } // end foreach
+
+ /* Foreach through what we've found */
+ foreach ($final_results as $result) {
+
+ /* Recurse through the images found */
+ foreach ($possible_keys as $key) {
+ if (strlen($result[$key])) {
+ break;
+ }
+ } // foreach
+
+ // Rudimentary image type detection, only JPG and GIF allowed.
+ if (substr($result[$key], -4 == '.jpg')) {
+ $mime = "image/jpeg";
+ }
+ elseif (substr($result[$key], -4 == '.gif')) {
+ $mime = "image/gif";
+ }
+ elseif (substr($result[$key], -4 == '.png')) {
+ $mime = "image/png";
+ }
+ else {
+ /* Just go to the next result */
+ continue;
+ }
+
+ $data['url'] = $result[$key];
+ $data['mime'] = $mime;
+
+ $images[] = $data;
+
+ if (!empty($limit)) {
+ if (count($images) >= $limit) {
+ return $images;
+ }
+ }
+
+ } // if we've got something
+
+ return $images;
+
+ } // gather_amazon
+
+ /**
+ * gather_folder
+ * This returns the art from the folder of the files
+ * If a limit is passed or the preferred filename is found the current
+ * results set is returned
+ */
+ public function gather_folder($limit = 5) {
+
+ $media = new Album($this->uid);
+ $songs = $media->get_songs();
+ $results = array();
+ $preferred = false;
+ // For storing which directories we've already done
+ $processed = array();
+
+ /* See if we are looking for a specific filename */
+ $preferred_filename = Config::get('album_art_preferred_filename');
+
+ // Array of valid extensions
+ $image_extensions = array(
+ 'bmp',
+ 'gif',
+ 'jp2',
+ 'jpeg',
+ 'jpg',
+ 'png'
+ );
+
+ foreach ($songs as $song_id) {
+ $data = array();
+ $song = new Song($song_id);
+ $dir = dirname($song->file);
+
+ if (isset($processed[$dir])) {
+ continue;
+ }
+
+ debug_event('folder_art', "Opening $dir and checking for Album Art", 3);
+
+ /* Open up the directory */
+ $handle = opendir($dir);
+
+ if (!$handle) {
+ Error::add('general', T_('Error: Unable to open') . ' ' . $dir);
+ debug_event('folder_art', "Error: Unable to open $dir for album art read", 2);
+ continue;
+ }
+
+ $processed[$dir] = true;
+
+ // Recurse through this dir and create the files array
+ while ($file = readdir($handle)) {
+ $extension = pathinfo($file);
+ $extension = $extension['extension'];
+
+ // Make sure it looks like an image file
+ if (!in_array($extension, $image_extensions)) {
+ continue;
+ }
+
+ $full_filename = $dir . '/' . $file;
+
+ // Make sure it's got something in it
+ if (!filesize($full_filename)) {
+ debug_event('folder_art', "Empty file, rejecting $file", 5);
+ continue;
+ }
+
+ // Regularise for mime type
+ if ($extension == 'jpg') {
+ $extension = 'jpeg';
+ }
+
+ // Take an md5sum so we don't show duplicate
+ // files.
+ $index = md5($full_filename);
+
+ if ($file == $preferred_filename) {
+ // We found the preferred filename and
+ // so we're done.
+ debug_event('folder_art', "Found preferred image file: $file", 5);
+ $preferred[$index] = array(
+ 'file' => $full_filename,
+ 'mime' => 'image/' . $extension
+ );
+ break;
+ }
+
+ debug_event('folder_art', "Found image file: $file", 5);
+ $results[$index] = array(
+ 'file' => $full_filename,
+ 'mime' => 'image/' . $extension
+ );
+
+ } // end while reading dir
+ closedir($handle);
+
+ } // end foreach songs
+
+ if (is_array($preferred)) {
+ // We found our favourite filename somewhere, so we need
+ // to dump the other, less sexy ones.
+ $results = $preferred;
+ }
+
+ debug_event('folder_art', 'Results: ' . json_encode($results), 5);
+ if ($limit && count($results) > $limit) {
+ $results = array_slice($results, 0, $limit);
+ }
+
+ return array_values($results);
+
+ } // gather_folder
+
+ /**
+ * gather_tags
+ * This looks for the art in the meta-tags of the file
+ * itself
+ */
+ public function gather_tags($limit = 5) {
+
+ // We need the filenames
+ $album = new Album($this->uid);
+
+ // grab the songs and define our results
+ $songs = $album->get_songs();
+ $data = array();
+
+ // Foreach songs in this album
+ foreach ($songs as $song_id) {
+ $song = new Song($song_id);
+ // If we find a good one, stop looking
+ $getID3 = new getID3();
+ try { $id3 = $getID3->analyze($song->file); }
+ catch (Exception $error) {
+ debug_event('getid3', $error->message, 1);
+ }
+
+ if (isset($id3['asf']['extended_content_description_object']['content_descriptors']['13'])) {
+ $image = $id3['asf']['extended_content_description_object']['content_descriptors']['13'];
+ $data[] = array(
+ 'song' => $song->file,
+ 'raw' => $image['data'],
+ 'mime' => $image['mime']);
+ }
+
+ if (isset($id3['id3v2']['APIC'])) {
+ // Foreach in case they have more then one
+ foreach ($id3['id3v2']['APIC'] as $image) {
+ $data[] = array(
+ 'song' => $song->file,
+ 'raw' => $image['data'],
+ 'mime' => $image['mime']);
+ }
+ }
+
+ if ($limit && count($data) >= $limit) {
+ return array_slice($data, 0, $limit);
+ }
+
+ } // end foreach
+
+ return $data;
+
+ } // gather_tags
+
+ /**
+ * gather_google
+ * Raw google search to retrieve the art, not very reliable
+ */
+ public function gather_google($limit = 5) {
+
+ $images = array();
+ $media = new $this->type($this->uid);
+ $media->format();
+
+ $search = $media->full_name;
+
+ if ($media->artist_count == '1')
+ $search = $media->artist_name . ', ' . $search;
+
+ $search = rawurlencode($search);
+
+ $size = '&imgsz=m'; // Medium
+ //$size = '&imgsz=l'; // Large
+
+ $html = file_get_contents("http://images.google.com/images?source=hp&q=$search&oq=&um=1&ie=UTF-8&sa=N&tab=wi&start=0&tbo=1$size");
+
+ if(preg_match_all("|\ssrc\=\"(http.+?)\"|", $html, $matches, PREG_PATTERN_ORDER))
+ foreach ($matches[1] as $match) {
+ $extension = "image/jpeg";
+
+ if (strrpos($extension, '.') !== false) $extension = substr($extension, strrpos($extension, '.') + 1);
+
+ $images[] = array('url' => $match, 'mime' => $extension);
+ }
+
+ return $images;
+
+ } // gather_google
+
+ /**
+ * gather_lastfm
+ * This returns the art from lastfm. It doesn't currently require an
+ * account but may in the future.
+ */
+ public function gather_lastfm($limit, $options = false) {
+
+ // Create the parser object
+ $lastfm = new LastFMSearch();
+
+ switch ($this->type) {
+ case 'album':
+ if (is_array($options)) {
+ $artist = $options['artist'];
+ $album = $options['album_name'];
+ }
+ else {
+ $media = new Album($this->uid);
+ $media->format();
+ $artist = $media->artist_name;
+ $album = $media->full_name;
+ }
+ break;
+ }
+
+ if(Config::get('proxy_host') AND Config::get('proxy_port')) {
+ $proxyhost = Config::get('proxy_host');
+ $proxyport = Config::get('proxy_port');
+ $proxyuser = Config::get('proxy_user');
+ $proxypass = Config::get('proxy_pass');
+ debug_event('LastFM', 'proxy set', 5);
+ $lastfm->setProxy($proxyhost, $proxyport, $proxyuser, $proxypass);
+ }
+
+ $raw_data = $lastfm->album_search($artist, $album);
+
+ if (!count($raw_data)) { return array(); }
+
+ $coverart = $raw_data['coverart'];
+ if (!is_array($coverart)) { return array(); }
+
+ ksort($coverart);
+ foreach ($coverart as $url) {
+ // We need to check the URL for the /noimage/ stuff
+ if (strpos($url, '/noimage/') !== false) {
+ debug_event('LastFM', 'Detected as noimage, skipped ' . $url, 3);
+ continue;
+ }
+
+ // HACK: we shouldn't rely on the extension to determine file type
+ $results = pathinfo($url);
+ $mime = 'image/' . $results['extension'];
+ $data[] = array('url' => $url, 'mime' => $mime);
+ if ($limit && count($data) >= $limit) {
+ return $data;
+ }
+ } // end foreach
+
+ return $data;
+
+ } // gather_lastfm
} // Art