prepare($sql); $result = $stmt->execute(); while ($row = $result->fetchArray(SQLITE3_ASSOC)) { self::$resourceCache[$row["name"]] = $row; } } public static function hasResourceCache(string $name) { if (count(self::$resourceCache) == 0) { self::fillResourceCache(); } return isset(self::$resourceCache[$name]); } public static function setResourceCache(string $name, $data) { self::$resourceCache[$name] = $data; } public static function getResourceCache(string $name) { if (count(self::$resourceCache) == 0) { self::fillResourceCache(); } if (isset(self::$resourceCache[$name])) { return self::$resourceCache[$name]; } return null; } public static function getResourceCacheByThumbnailUrl(string $url) { if (count(self::$resourceCache) == 0) { self::fillResourceCache(); } foreach (self::$resourceCache as $resource) { if ($resource["url"] == $url) { return $resource; } } return null; } public static function getCacheFile(string $name, $maxAge = 3600) { $cacheFile = CACHE_DIR . "/" . $name; if (file_exists($cacheFile) && filemtime($cacheFile) > time() - $maxAge) { Logger::log("Cache hit for " . $name, Logger::IOREAD, "Cache"); return COMPRESS_LOCAL_CACHE ? gzuncompress(file_get_contents($cacheFile)) : file_get_contents($cacheFile); } return null; } public static function saveCacheFile(string $name, string $data) { $cacheFile = CACHE_DIR . "/" . $name; file_put_contents($cacheFile, COMPRESS_LOCAL_CACHE ? gzcompress($data,9) : $data ); Logger::log("Saved cache file " . $name, Logger::IOWRITE, "Cache"); } public static function publicCacheExists(string $name, bool $skipExtensionCheck = false) { return self::hasResourceCache($name); } public static function savePublicCacheFile(string $name, string $data, int $cacheLevel = 70, bool $skipExtensionCheck = false) { $originalName = $name; $name = strtolower($name); $ext = substr($name, strrpos($name, ".") + 1); if ($skipExtensionCheck || strtolower($ext) == "jpg" || strtolower($ext) == "jpeg" || strtolower($ext) == "png") { $size = strlen($data); $img = new Imagick(); $img->readImageBlob($data); $img->setImageFormat('webp'); $img->setImageCompressionQuality($cacheLevel); $img->stripImage(); $data = $img->getImageBlob(); $name = str_replace("." . $ext, ".webp", strtolower($name)); $cacheFile = PUBLIC_CACHE_DIR . "/" . $name; $blurhash = self::generateBlurHash($cacheFile, $img, $skipExtensionCheck); $resourceSql = "INSERT INTO `resources` (`name`, `type`, `blurhash`, `url`, `width`, `height`, `hasBeenProcessed`, `modified`) VALUES (:name,:type,:blurhash,:url,:width,:height,1,strftime('%Y-%m-%d %H:%M:%S','now'))"; $db = new Database(); $resourceStmt = $db::$handle->prepare($resourceSql); $resourceStmt->bindValue(":name", $originalName); $resourceStmt->bindValue(":type", "image/webp"); $resourceStmt->bindValue(":blurhash", $blurhash); $resourceStmt->bindValue(":url", PUBLIC_CACHE_URL . "/" . $name); $resourceStmt->bindValue(":width", $img->getImageWidth()); $resourceStmt->bindValue(":height", $img->getImageHeight()); $resourceStmt->execute(); // Append to the resource cache self::setResourceCache($originalName, [ "name" => $originalName, "type" => "image/webp", "blurhash" => $blurhash, "url" => PUBLIC_CACHE_URL . "/" . $name, "width" => $img->getImageWidth(), "height" => $img->getImageHeight(), "hasBeenProcessed" => 1, "modified" => date("Y-m-d H:i:s") ]); // For now don't transfer file if its generated name exists if (file_exists($cacheFile)) { Logger::log("Public cache file " . $name . " already exists", Logger::INFO, "Cache"); return PUBLIC_CACHE_URL . "/" . $name; } Logger::log("Compressed image from " . $size . " to " . strlen($data) . " bytes", Logger::METRICS, "Cache"); } $context = stream_context_create([ 's3' => [ 'ACL' => 'public-read' ] ]); file_put_contents($cacheFile, $data, 0, $context); Logger::log("Saved public cache file " . $name, Logger::IOWRITE, "Cache"); return PUBLIC_CACHE_URL . "/" . $name; } public static function generateBlurHash(string $cacheFile, ?IMagick $img = null, bool $skipExtensionCheck = false):?string { $ext = substr($cacheFile, strrpos($cacheFile, ".") + 1); if ($skipExtensionCheck || strtolower($ext) == "jpg" || strtolower($ext) == "jpeg" || strtolower($ext) == "png" || strtolower($ext) == "webp") { $hashFile = strtolower($cacheFile); $hashFileName = substr($hashFile, strrpos($hashFile, "/") + 1); if (file_exists($hashFile)) { return null; } $pixels = []; if ($img === null) { $img = new Imagick(); $img->readImage($cacheFile); } $width = $img->getImageWidth(); $height = $img->getImageHeight(); for ($y = 0; $y < $height; $y++) { $row = []; for ($x = 0; $x < $width; $x++) { $pixel = $img->getImagePixelColor($x, $y); $color = $pixel->getColor(); $row[] = [$color["r"], $color["g"], $color["b"]]; } $pixels[] = $row; } $hash = \kornrunner\Blurhash\Blurhash::encode($pixels, BLURHASH_X, BLURHASH_Y); return $hash; } } public static function getBlurHashImage($blurhash,$width = 269, $height = 173) { $hash = \kornrunner\Blurhash\Blurhash::decode($blurhash, $width, $height); $img = new Imagick(); $img->newImage($width, $height, new ImagickPixel("white")); $img->setImageFormat("webp"); $img->setImageCompressionQuality(60); $pixels = []; for ($y = 0; $y < $height; ++$y) { for ($x = 0; $x < $width; ++$x) { [$r, $g, $b] = $hash[$y][$x]; $pixels[] = $r; $pixels[] = $g; $pixels[] = $b; } } $img->importImagePixels(0, 0, $width, $height, "RGB", Imagick::PIXEL_CHAR, $pixels); $img->stripImage(); $data = $img->getImageBlob(); return "data:image/webp;base64," . base64_encode($data); } }