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