mirror of
https://github.com/Yetangitu/ampache
synced 2025-10-04 10:19:25 +02:00

Reduce the frequency of some expensive database operations when processing multiple catalogs. More debugging output. Remove unnecessary or unused parameters from function calls.
1206 lines
32 KiB
PHP
1206 lines
32 KiB
PHP
<?php
|
|
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
|
|
/**
|
|
* Art Class
|
|
*
|
|
* PHP version 5
|
|
*
|
|
* LICENSE: GNU General Public License, version 2 (GPLv2)
|
|
* Copyright (c) 2001 - 2011 Ampache.org All Rights Reserved
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License v2
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* 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.
|
|
*
|
|
* @category Art
|
|
* @package Ampache
|
|
* @author Karl Vollmer <vollmer@ampache.org>
|
|
* @copyright 2001 - 2011 Ampache.org
|
|
* @license http://opensource.org/licenses/gpl-2.0 GPLv2
|
|
* @version PHP 5.2
|
|
* @link http://www.ampache.org/
|
|
* @since File available since Release 1.0
|
|
*/
|
|
|
|
/**
|
|
* Art Class
|
|
*
|
|
* This class handles the images / artwork in ampache
|
|
* This was initially in the album class, but was pulled out
|
|
* to be more general, and apply to albums, artists, movies etc
|
|
*
|
|
* @category Art
|
|
* @package Ampache
|
|
* @author Karl Vollmer <vollmer@ampache.org>
|
|
* @copyright 2001 - 2011 Ampache.org
|
|
* @license http://opensource.org/licenses/gpl-2.0 GPLv2
|
|
* @version Release: 3.6
|
|
* @link http://www.ampache.org/
|
|
* @since Class available since Release 1.0
|
|
*/
|
|
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
|
|
|
|
/**
|
|
* _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 function get_from_source($data) {
|
|
|
|
// 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($this->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 incase 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);
|
|
|
|
$type = Dba::escape($type);
|
|
$uid = Dba::escape($uid);
|
|
|
|
$sql = "SELECT `mime`,`size` FROM `image` WHERE `object_type`='$type' AND `object_id`='$uid'";
|
|
$db_results = Dba::read($sql);
|
|
|
|
while ($row = Dba::fetch_assoc($db_results)) {
|
|
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
|
|
|
|
|
|
/**
|
|
* clean
|
|
* This cleans up art that no longer has a corresponding object
|
|
*/
|
|
public static function clean() {
|
|
// 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
|
|
} // clean
|
|
|
|
public function checkOrderDB($method, $gatherAll) {
|
|
|
|
if( !($gatherAll) ) {
|
|
return true;
|
|
}
|
|
|
|
$config = Config::get('art_order');
|
|
$config = array($config);
|
|
$art_order = array();
|
|
|
|
|
|
for( $i = 0; $i < sizeof($config[0]); $i++) {
|
|
$art_order[$config[0][$i]] = $i;
|
|
}
|
|
|
|
$sql = 'SELECT object_id FROM image WHERE object_id = ' . $this->uid;
|
|
debug_event('Art', $sql, 6);
|
|
$db_results = Dba::read($sql);
|
|
|
|
if( $art_order['db'] < $art_order[$method] && (Dba::num_rows($db_results) != 0) ) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
/**
|
|
* gather
|
|
* This tries to get the art in question
|
|
*/
|
|
public function gather($options = array(), $limit = false, $gatherAll = false) {
|
|
|
|
// Define vars
|
|
$results = array();
|
|
|
|
switch ($this->type) {
|
|
case 'album':
|
|
$allowed_methods = array('lastfm','folder','amazon','google','musicbrainz','tag');
|
|
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!
|
|
return array();
|
|
}
|
|
elseif (!is_array($config)) {
|
|
$config = array($config);
|
|
}
|
|
|
|
debug_event('Art','Searching using:' . print_r($config, true),3);
|
|
|
|
foreach ($config as $method) {
|
|
|
|
$data = array();
|
|
|
|
debug_event('Art',"method used " .$method_name,3);
|
|
|
|
$method_name = "gather_" . $method;
|
|
if (in_array($method_name, $methods)) {
|
|
// Some of these take options!
|
|
switch ($method_name) {
|
|
case 'gather_amazon':
|
|
if($this->checkOrderDB($method,$gatherAll)) {
|
|
$data = $this->{$method_name}($limit, $options['keyword']);
|
|
}
|
|
break;
|
|
case 'gather_lastfm':
|
|
if($this->checkOrderDB($method,$gatherAll)) {
|
|
$data = $this->{$method_name}($limit, $options);
|
|
}
|
|
break;
|
|
default:
|
|
if($this->checkOrderDB($method,$gatherAll)) {
|
|
$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
|
|
|
|
} // end foreach
|
|
|
|
return $results;
|
|
|
|
} // gather
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Art Methods
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* 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',_('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: ' . print_r($results, true), 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 incase 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", "set Proxy", "5");
|
|
$lastfm->setProxy($proxyhost, $proxyport, $proxyuser, $proxypass);
|
|
}
|
|
$raw_data = $lastfm->album_search($artist,$album);
|
|
|
|
if (!count($raw_data)) { return array(); }
|
|
|
|
$coverart = ksort($raw_data['coverart']);
|
|
$i = 0;
|
|
|
|
foreach ($coverart as $key => $value) {
|
|
$i++;
|
|
$url = $coverart[$key];
|
|
|
|
// 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;
|
|
}
|
|
|
|
$results = pathinfo($url);
|
|
$mime = 'image/' . $results['extension'];
|
|
$data[] = array('url'=>$url,'mime'=>$mime);
|
|
if ($i >= $limit) { return $data; }
|
|
} // end foreach
|
|
|
|
return $data;
|
|
|
|
} // gather_lastfm
|
|
|
|
} // Art
|