Refactored thumb generator; added imagick support
This commit is contained in:
parent
fc486190c2
commit
dcfe6a00ea
10 changed files with 314 additions and 169 deletions
|
@ -1,165 +0,0 @@
|
|||
<?php
|
||||
class ThumbnailHelper
|
||||
{
|
||||
public static function cropOutside($srcImage, $dstWidth, $dstHeight)
|
||||
{
|
||||
$srcWidth = imagesx($srcImage);
|
||||
$srcHeight = imagesy($srcImage);
|
||||
|
||||
if (($dstHeight / $dstWidth) > ($srcHeight / $srcWidth))
|
||||
{
|
||||
$h = $srcHeight;
|
||||
$w = $h * $dstWidth / $dstHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
$w = $srcWidth;
|
||||
$h = $w * $dstHeight / $dstWidth;
|
||||
}
|
||||
$x = ($srcWidth - $w) / 2;
|
||||
$y = ($srcHeight - $h) / 2;
|
||||
|
||||
$dstImage = imagecreatetruecolor($dstWidth, $dstHeight);
|
||||
imagecopyresampled($dstImage, $srcImage, 0, 0, $x, $y, $dstWidth, $dstHeight, $w, $h);
|
||||
return $dstImage;
|
||||
}
|
||||
|
||||
public static function cropInside($srcImage, $dstWidth, $dstHeight)
|
||||
{
|
||||
$srcWidth = imagesx($srcImage);
|
||||
$srcHeight = imagesy($srcImage);
|
||||
|
||||
if (($dstHeight / $dstWidth) < ($srcHeight / $srcWidth))
|
||||
{
|
||||
$h = $dstHeight;
|
||||
$w = $h * $srcWidth / $srcHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
$w = $dstWidth;
|
||||
$h = $w * $srcHeight / $srcWidth;
|
||||
}
|
||||
|
||||
$dstImage = imagecreatetruecolor($w, $h);
|
||||
imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $w, $h, $srcWidth, $srcHeight);
|
||||
return $dstImage;
|
||||
}
|
||||
|
||||
public static function generateFromUrl($url, $dstPath, $width, $height)
|
||||
{
|
||||
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.jpg';
|
||||
|
||||
TransferHelper::download(
|
||||
$url,
|
||||
$tmpPath,
|
||||
null);
|
||||
|
||||
$ret = self::generateFromPath($tmpPath, $dstPath, $width, $height);
|
||||
|
||||
unlink($tmpPath);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function generateFromPath($srcPath, $dstPath, $width, $height)
|
||||
{
|
||||
$mime = mime_content_type($srcPath);
|
||||
|
||||
switch ($mime)
|
||||
{
|
||||
case 'application/x-shockwave-flash':
|
||||
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.png';
|
||||
|
||||
$cmd = sprintf(
|
||||
'dump-gnash --screenshot last --screenshot-file "%s" -1 -r1 --max-advances 15 "%s"',
|
||||
$tmpPath,
|
||||
$srcPath);
|
||||
exec($cmd);
|
||||
|
||||
if (file_exists($tmpPath))
|
||||
{
|
||||
$ret = self::generateFromPath($tmpPath, $dstPath, $width, $height);
|
||||
unlink($tmpPath);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
exec('swfrender ' . $srcPath . ' -o ' . $tmpPath);
|
||||
|
||||
if (file_exists($tmpPath))
|
||||
{
|
||||
$ret = self::generateFromPath($tmpPath, $dstPath, $width, $height);
|
||||
unlink($tmpPath);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case 'video/mp4':
|
||||
case 'video/webm':
|
||||
case 'video/ogg':
|
||||
case 'application/ogg':
|
||||
case 'video/x-flv':
|
||||
case 'video/3gpp':
|
||||
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.jpg';
|
||||
|
||||
$cmd = sprintf(
|
||||
'ffmpegthumbnailer -i"%s" -o"%s" -s0 -t"12%%"',
|
||||
$srcPath,
|
||||
$tmpPath);
|
||||
exec($cmd);
|
||||
|
||||
if (file_exists($tmpPath))
|
||||
{
|
||||
$ret = self::generateFromPath($tmpPath, $dstPath, $width, $height);
|
||||
unlink($tmpPath);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$cmd = sprintf(
|
||||
'ffmpeg -i "%s" -vframes 1 "%s"',
|
||||
$srcPath,
|
||||
$tmpPath);
|
||||
exec($cmd);
|
||||
|
||||
if (file_exists($tmpPath))
|
||||
{
|
||||
$ret = self::generateFromPath($tmpPath, $dstPath, $width, $height);
|
||||
unlink($tmpPath);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case 'image/jpeg':
|
||||
$srcImage = imagecreatefromjpeg($srcPath);
|
||||
break;
|
||||
|
||||
case 'image/png':
|
||||
$srcImage = imagecreatefrompng($srcPath);
|
||||
break;
|
||||
|
||||
case 'image/gif':
|
||||
$srcImage = imagecreatefromgif($srcPath);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new SimpleException('Invalid thumbnail file type');
|
||||
}
|
||||
|
||||
$config = Core::getConfig();
|
||||
switch ($config->browsing->thumbStyle)
|
||||
{
|
||||
case 'outside':
|
||||
$dstImage = ThumbnailHelper::cropOutside($srcImage, $width, $height);
|
||||
break;
|
||||
case 'inside':
|
||||
$dstImage = ThumbnailHelper::cropInside($srcImage, $width, $height);
|
||||
break;
|
||||
default:
|
||||
throw new SimpleException('Unknown thumbnail crop style');
|
||||
}
|
||||
|
||||
imagejpeg($dstImage, $dstPath);
|
||||
imagedestroy($srcImage);
|
||||
imagedestroy($dstImage);
|
||||
}
|
||||
}
|
|
@ -393,12 +393,20 @@ final class PostEntity extends AbstractEntity implements IValidatable, ISerializ
|
|||
$height = Core::getConfig()->browsing->thumbHeight;
|
||||
$dstPath = $this->getThumbPath($width, $height);
|
||||
|
||||
$thumbnailGenerator = new SmartThumbnailGenerator();
|
||||
|
||||
if (file_exists($this->getThumbCustomSourcePath()))
|
||||
return ThumbnailHelper::generateFromPath($this->getThumbCustomSourcePath(), $dstPath, $width, $height);
|
||||
{
|
||||
return $thumbnailGenerator->generateFromFile(
|
||||
$this->getThumbCustomSourcePath(),
|
||||
$dstPath,
|
||||
$width,
|
||||
$height);
|
||||
}
|
||||
|
||||
if ($this->getType()->toInteger() == PostType::Youtube)
|
||||
{
|
||||
return ThumbnailHelper::generateFromUrl(
|
||||
return $thumbnailGenerator->generateFromUrl(
|
||||
'http://img.youtube.com/vi/' . $this->getFileHash() . '/mqdefault.jpg',
|
||||
$dstPath,
|
||||
$width,
|
||||
|
@ -406,7 +414,11 @@ final class PostEntity extends AbstractEntity implements IValidatable, ISerializ
|
|||
}
|
||||
else
|
||||
{
|
||||
return ThumbnailHelper::generateFromPath($this->getFullPath(), $dstPath, $width, $height);
|
||||
return $thumbnailGenerator->generateFromFile(
|
||||
$this->getFullPath(),
|
||||
$dstPath,
|
||||
$width,
|
||||
$height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
34
src/ThumbnailGenerators/FlashThumbnailGenerator.php
Normal file
34
src/ThumbnailGenerators/FlashThumbnailGenerator.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
class FlashThumbnailGenerator implements IThumbnailGenerator
|
||||
{
|
||||
public function generateFromFile($srcPath, $dstPath, $width, $height)
|
||||
{
|
||||
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.png';
|
||||
|
||||
$cmd = sprintf(
|
||||
'dump-gnash --screenshot last --screenshot-file "%s" -1 -r1 --max-advances 15 "%s"',
|
||||
$tmpPath,
|
||||
$srcPath);
|
||||
exec($cmd);
|
||||
|
||||
if (file_exists($tmpPath))
|
||||
{
|
||||
$thumbnailGenerator = new ImageThumbnailGenerator();
|
||||
$ret = $thumbnailGenerator->generateFromFile($tmpPath, $dstPath, $width, $height);
|
||||
unlink($tmpPath);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
exec('swfrender ' . $srcPath . ' -o ' . $tmpPath);
|
||||
|
||||
if (file_exists($tmpPath))
|
||||
{
|
||||
$thumbnailGenerator = new ImageThumbnailGenerator();
|
||||
$ret = $thumbnailGenerator->generateFromFile($tmpPath, $dstPath, $width, $height);
|
||||
unlink($tmpPath);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
5
src/ThumbnailGenerators/IThumbnailGenerator.php
Normal file
5
src/ThumbnailGenerators/IThumbnailGenerator.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
interface IThumbnailGenerator
|
||||
{
|
||||
public function generateFromFile($srcPath, $dstPath, $width, $height);
|
||||
}
|
89
src/ThumbnailGenerators/ImageGdThumbnailGenerator.php
Normal file
89
src/ThumbnailGenerators/ImageGdThumbnailGenerator.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
class ImageGdThumbnailGenerator implements IThumbnailGenerator
|
||||
{
|
||||
public function generateFromFile($srcPath, $dstPath, $width, $height)
|
||||
{
|
||||
$mime = mime_content_type($srcPath);
|
||||
|
||||
switch ($mime)
|
||||
{
|
||||
case 'image/jpeg':
|
||||
$srcImage = imagecreatefromjpeg($srcPath);
|
||||
break;
|
||||
|
||||
case 'image/png':
|
||||
$srcImage = imagecreatefrompng($srcPath);
|
||||
break;
|
||||
|
||||
case 'image/gif':
|
||||
$srcImage = imagecreatefromgif($srcPath);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new SimpleException('Invalid thumbnail image type');
|
||||
}
|
||||
|
||||
$config = Core::getConfig();
|
||||
switch ($config->browsing->thumbStyle)
|
||||
{
|
||||
case 'outside':
|
||||
$dstImage = $this->cropOutside($srcImage, $width, $height);
|
||||
break;
|
||||
case 'inside':
|
||||
$dstImage = $this->cropInside($srcImage, $width, $height);
|
||||
break;
|
||||
default:
|
||||
throw new SimpleException('Unknown thumbnail crop style');
|
||||
}
|
||||
|
||||
imagejpeg($dstImage, $dstPath);
|
||||
imagedestroy($srcImage);
|
||||
imagedestroy($dstImage);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function cropOutside($srcImage, $dstWidth, $dstHeight)
|
||||
{
|
||||
$srcWidth = imagesx($srcImage);
|
||||
$srcHeight = imagesy($srcImage);
|
||||
|
||||
if (($dstHeight / $dstWidth) > ($srcHeight / $srcWidth))
|
||||
{
|
||||
$h = $srcHeight;
|
||||
$w = $h * $dstWidth / $dstHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
$w = $srcWidth;
|
||||
$h = $w * $dstHeight / $dstWidth;
|
||||
}
|
||||
$x = ($srcWidth - $w) / 2;
|
||||
$y = ($srcHeight - $h) / 2;
|
||||
|
||||
$dstImage = imagecreatetruecolor($dstWidth, $dstHeight);
|
||||
imagecopyresampled($dstImage, $srcImage, 0, 0, $x, $y, $dstWidth, $dstHeight, $w, $h);
|
||||
return $dstImage;
|
||||
}
|
||||
|
||||
private function cropInside($srcImage, $dstWidth, $dstHeight)
|
||||
{
|
||||
$srcWidth = imagesx($srcImage);
|
||||
$srcHeight = imagesy($srcImage);
|
||||
|
||||
if (($dstHeight / $dstWidth) < ($srcHeight / $srcWidth))
|
||||
{
|
||||
$h = $dstHeight;
|
||||
$w = $h * $srcWidth / $srcHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
$w = $dstWidth;
|
||||
$h = $w * $srcHeight / $srcWidth;
|
||||
}
|
||||
|
||||
$dstImage = imagecreatetruecolor($w, $h);
|
||||
imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $w, $h, $srcWidth, $srcHeight);
|
||||
return $dstImage;
|
||||
}
|
||||
}
|
69
src/ThumbnailGenerators/ImageImagickThumbnailGenerator.php
Normal file
69
src/ThumbnailGenerators/ImageImagickThumbnailGenerator.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
class ImageImagickThumbnailGenerator implements IThumbnailGenerator
|
||||
{
|
||||
public function generateFromFile($srcPath, $dstPath, $width, $height)
|
||||
{
|
||||
$image = new Imagick($srcPath);
|
||||
$image = $image->coalesceImages();
|
||||
|
||||
$config = Core::getConfig();
|
||||
switch ($config->browsing->thumbStyle)
|
||||
{
|
||||
case 'outside':
|
||||
$this->cropOutside($image, $width, $height);
|
||||
break;
|
||||
case 'inside':
|
||||
$this->cropInside($image, $width, $height);
|
||||
break;
|
||||
default:
|
||||
throw new SimpleException('Unknown thumbnail crop style');
|
||||
}
|
||||
|
||||
$image->writeImage($dstPath);
|
||||
$image->destroy();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function cropOutside($srcImage, $dstWidth, $dstHeight)
|
||||
{
|
||||
$srcWidth = $srcImage->getImageWidth();
|
||||
$srcHeight = $srcImage->getImageHeight();
|
||||
|
||||
if (($dstHeight / $dstWidth) > ($srcHeight / $srcWidth))
|
||||
{
|
||||
$h = $dstHeight;
|
||||
$w = $h * $srcWidth / $srcHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
$w = $dstWidth;
|
||||
$h = $w * $srcHeight / $srcWidth;
|
||||
}
|
||||
$x = ($srcWidth - $w) / 2;
|
||||
$y = ($srcHeight - $h) / 2;
|
||||
|
||||
$srcImage->resizeImage($w, $h, imagick::FILTER_LANCZOS, 0.9);
|
||||
$srcImage->cropImage($dstWidth, $dstHeight, ($w - $dstWidth) >> 1, ($h - $dstHeight) >> 1);
|
||||
$srcImage->setImagePage(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private function cropInside($srcImage, $dstWidth, $dstHeight)
|
||||
{
|
||||
$srcWidth = $srcImage->getImageWidth();
|
||||
$srcHeight = $srcImage->getImageHeight();
|
||||
|
||||
if (($dstHeight / $dstWidth) < ($srcHeight / $srcWidth))
|
||||
{
|
||||
$h = $dstHeight;
|
||||
$w = $h * $srcWidth / $srcHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
$w = $dstWidth;
|
||||
$h = $w * $srcHeight / $srcWidth;
|
||||
}
|
||||
|
||||
$srcImage->resizeImage($w, $h, imagick::FILTER_LANCZOS, 0.9);
|
||||
}
|
||||
}
|
15
src/ThumbnailGenerators/ImageThumbnailGenerator.php
Normal file
15
src/ThumbnailGenerators/ImageThumbnailGenerator.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
class ImageThumbnailGenerator implements IThumbnailGenerator
|
||||
{
|
||||
public function generateFromFile($srcPath, $dstPath, $width, $height)
|
||||
{
|
||||
if (extension_loaded('imagick'))
|
||||
$realImageGenerator = new ImageImagickThumbnailGenerator();
|
||||
elseif (extension_loaded('gd'))
|
||||
$realImageGenerator = new ImageGdThumbnailGenerator();
|
||||
else
|
||||
return false;
|
||||
|
||||
return $realImageGenerator->generateFromFile($srcPath, $dstPath, $width, $height);
|
||||
}
|
||||
}
|
48
src/ThumbnailGenerators/SmartThumbnailGenerator.php
Normal file
48
src/ThumbnailGenerators/SmartThumbnailGenerator.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
class SmartThumbnailGenerator implements IThumbnailGenerator
|
||||
{
|
||||
public function generateFromUrl($url, $dstPath, $width, $height)
|
||||
{
|
||||
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.jpg';
|
||||
|
||||
TransferHelper::download(
|
||||
$url,
|
||||
$tmpPath,
|
||||
null);
|
||||
|
||||
$ret = self::generateFromFile($tmpPath, $dstPath, $width, $height);
|
||||
|
||||
unlink($tmpPath);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function generateFromFile($srcPath, $dstPath, $width, $height)
|
||||
{
|
||||
$mime = mime_content_type($srcPath);
|
||||
|
||||
switch ($mime)
|
||||
{
|
||||
case 'application/x-shockwave-flash':
|
||||
$thumbnailGenerator = new FlashThumbnailGenerator();
|
||||
return $thumbnailGenerator->generateFromFile($srcPath, $dstPath, $width, $height);
|
||||
|
||||
case 'video/mp4':
|
||||
case 'video/webm':
|
||||
case 'video/ogg':
|
||||
case 'application/ogg':
|
||||
case 'video/x-flv':
|
||||
case 'video/3gpp':
|
||||
$thumbnailGenerator = new VideoThumbnailGenerator();
|
||||
return $thumbnailGenerator->generateFromFile($srcPath, $dstPath, $width, $height);
|
||||
|
||||
case 'image/jpeg':
|
||||
case 'image/png':
|
||||
case 'image/gif':
|
||||
$thumbnailGenerator = new ImageThumbnailGenerator();
|
||||
return $thumbnailGenerator->generateFromFile($srcPath, $dstPath, $width, $height);
|
||||
|
||||
default:
|
||||
throw new SimpleException('Invalid thumbnail file type');
|
||||
}
|
||||
}
|
||||
}
|
38
src/ThumbnailGenerators/VideoThumbnailGenerator.php
Normal file
38
src/ThumbnailGenerators/VideoThumbnailGenerator.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
class VideoThumbnailGenerator implements IThumbnailGenerator
|
||||
{
|
||||
public function generateFromFile($srcPath, $dstPath, $width, $height)
|
||||
{
|
||||
$tmpPath = tempnam(sys_get_temp_dir(), 'thumb') . '.jpg';
|
||||
|
||||
$cmd = sprintf(
|
||||
'ffmpegthumbnailer -i"%s" -o"%s" -s0 -t"12%%"',
|
||||
$srcPath,
|
||||
$tmpPath);
|
||||
exec($cmd);
|
||||
|
||||
if (file_exists($tmpPath))
|
||||
{
|
||||
$thumbnailGenerator = new ImageThumbnailGenerator();
|
||||
$ret = $thumbnailGenerator->generateFromFile($tmpPath, $dstPath, $width, $height);
|
||||
unlink($tmpPath);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$cmd = sprintf(
|
||||
'ffmpeg -i "%s" -vframes 1 "%s"',
|
||||
$srcPath,
|
||||
$tmpPath);
|
||||
exec($cmd);
|
||||
|
||||
if (file_exists($tmpPath))
|
||||
{
|
||||
$thumbnailGenerator = new ImageThumbnailGenerator();
|
||||
$ret = $thumbnailGenerator->generateFromFile($tmpPath, $dstPath, $width, $height);
|
||||
unlink($tmpPath);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -70,7 +70,7 @@ final class Core
|
|||
TransferHelper::createDirectory($config->main->thumbsPath);
|
||||
|
||||
//extension sanity checks
|
||||
$requiredExtensions = ['pdo', 'pdo_' . $config->main->dbDriver, 'gd', 'openssl', 'fileinfo'];
|
||||
$requiredExtensions = ['pdo', 'pdo_' . $config->main->dbDriver, 'openssl', 'fileinfo'];
|
||||
foreach ($requiredExtensions as $ext)
|
||||
if (!extension_loaded($ext))
|
||||
die('PHP extension "' . $ext . '" must be enabled to continue.' . PHP_EOL);
|
||||
|
|
Loading…
Reference in a new issue