116 lines
3.7 KiB
PHP
116 lines
3.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Media;
|
|
|
|
use App\Enums\SocialAccount\Platform;
|
|
use Intervention\Image\Drivers\Gd\Driver;
|
|
use Intervention\Image\ImageManager;
|
|
|
|
class MediaOptimizer
|
|
{
|
|
private ImageManager $manager;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->manager = new ImageManager(Driver::class);
|
|
}
|
|
|
|
/**
|
|
* Optimize an image for a specific platform.
|
|
* Returns path to optimized temp file (caller must clean up).
|
|
*/
|
|
public function optimizeImage(string $filePath, Platform $platform): string
|
|
{
|
|
$config = $this->getImageConfig($platform);
|
|
$image = $this->manager->decodePath($filePath);
|
|
|
|
$maxWidth = data_get($config, 'max_width');
|
|
$maxSize = data_get($config, 'max_size');
|
|
$format = data_get($config, 'format');
|
|
$quality = data_get($config, 'quality');
|
|
|
|
// Resize if needed (maintain aspect ratio, never upscale)
|
|
if ($maxWidth && $image->width() > $maxWidth) {
|
|
$image->scaleDown(width: $maxWidth);
|
|
}
|
|
|
|
// Encode to target format
|
|
$tempFile = tempnam(sys_get_temp_dir(), 'media_opt_');
|
|
$encoded = $image->encodeUsingMediaType($format, quality: $quality);
|
|
file_put_contents($tempFile, (string) $encoded);
|
|
|
|
// Reduce quality iteratively if file still too large
|
|
while (filesize($tempFile) > $maxSize && $quality > 30) {
|
|
$quality -= 10;
|
|
$encoded = $image->encodeUsingMediaType($format, quality: $quality);
|
|
file_put_contents($tempFile, (string) $encoded);
|
|
}
|
|
|
|
return $tempFile;
|
|
}
|
|
|
|
/**
|
|
* @return array{max_width: int, max_size: int, format: string, quality: int}
|
|
*/
|
|
private function getImageConfig(Platform $platform): array
|
|
{
|
|
return match ($platform) {
|
|
Platform::Instagram, Platform::Threads => [
|
|
'max_width' => 1440,
|
|
'max_size' => 8 * 1024 * 1024,
|
|
'format' => 'image/jpeg',
|
|
'quality' => 90,
|
|
],
|
|
Platform::Facebook => [
|
|
'max_width' => 2048,
|
|
'max_size' => 4 * 1024 * 1024,
|
|
'format' => 'image/jpeg',
|
|
'quality' => 90,
|
|
],
|
|
Platform::X => [
|
|
'max_width' => 2048,
|
|
'max_size' => 5 * 1024 * 1024,
|
|
'format' => 'image/jpeg',
|
|
'quality' => 90,
|
|
],
|
|
Platform::TikTok => [
|
|
'max_width' => 1080,
|
|
'max_size' => 20 * 1024 * 1024,
|
|
'format' => 'image/jpeg',
|
|
'quality' => 95,
|
|
],
|
|
Platform::LinkedIn, Platform::LinkedInPage => [
|
|
'max_width' => 2048,
|
|
'max_size' => 10 * 1024 * 1024,
|
|
'format' => 'image/jpeg',
|
|
'quality' => 90,
|
|
],
|
|
Platform::Pinterest => [
|
|
'max_width' => 1000,
|
|
'max_size' => 20 * 1024 * 1024,
|
|
'format' => 'image/jpeg',
|
|
'quality' => 90,
|
|
],
|
|
Platform::Bluesky => [
|
|
'max_width' => 2048,
|
|
'max_size' => 976 * 1024,
|
|
'format' => 'image/jpeg',
|
|
'quality' => 85,
|
|
],
|
|
Platform::Mastodon => [
|
|
'max_width' => 2048,
|
|
'max_size' => 10 * 1024 * 1024,
|
|
'format' => 'image/jpeg',
|
|
'quality' => 90,
|
|
],
|
|
Platform::YouTube => [
|
|
'max_width' => 1920,
|
|
'max_size' => 2 * 1024 * 1024,
|
|
'format' => 'image/jpeg',
|
|
'quality' => 90,
|
|
],
|
|
};
|
|
}
|
|
}
|