Implement creator studio and upload updates
This commit is contained in:
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Uploads;
|
||||
|
||||
use App\Services\Images\SquareThumbnailService;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Intervention\Image\ImageManager as ImageManager;
|
||||
use Intervention\Image\Interfaces\ImageInterface as InterventionImageInterface;
|
||||
@@ -14,7 +15,10 @@ final class UploadDerivativesService
|
||||
private bool $imageAvailable = false;
|
||||
private ?ImageManager $manager = null;
|
||||
|
||||
public function __construct(private readonly UploadStorageService $storage)
|
||||
public function __construct(
|
||||
private readonly UploadStorageService $storage,
|
||||
private readonly SquareThumbnailService $squareThumbnails,
|
||||
)
|
||||
{
|
||||
// Intervention Image v3 uses ImageManager; instantiate appropriate driver
|
||||
try {
|
||||
@@ -27,33 +31,32 @@ final class UploadDerivativesService
|
||||
}
|
||||
}
|
||||
|
||||
public function storeOriginal(string $sourcePath, string $hash, ?string $originalFileName = null): string
|
||||
/**
|
||||
* @return array{local_path: string, object_path: string, filename: string, mime: string, size: int, ext: string}
|
||||
*/
|
||||
public function storeOriginal(string $sourcePath, string $hash, ?string $originalFileName = null): array
|
||||
{
|
||||
$this->assertImageAvailable();
|
||||
// Preserve original file extension and store with filename = <hash>.<ext>
|
||||
$dir = $this->storage->ensureHashDirectory('original', $hash);
|
||||
|
||||
$origExt = $this->resolveOriginalExtension($sourcePath, $originalFileName);
|
||||
$target = $dir . DIRECTORY_SEPARATOR . $hash . ($origExt !== '' ? '.' . $origExt : '');
|
||||
$filename = $hash . ($origExt !== '' ? '.' . $origExt : '');
|
||||
$target = $this->storage->localOriginalPath($hash, $filename);
|
||||
|
||||
// Try a direct copy first (works for images and archives). If that fails,
|
||||
// fall back to re-encoding image to webp as a last resort.
|
||||
try {
|
||||
if (! File::copy($sourcePath, $target)) {
|
||||
throw new \RuntimeException('Copy failed');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Fallback: encode to webp
|
||||
$quality = (int) config('uploads.quality', 85);
|
||||
/** @var InterventionImageInterface $img */
|
||||
$img = $this->manager->read($sourcePath);
|
||||
$encoder = new \Intervention\Image\Encoders\WebpEncoder($quality);
|
||||
$encoded = (string) $img->encode($encoder);
|
||||
$target = $dir . DIRECTORY_SEPARATOR . $hash . '.webp';
|
||||
File::put($target, $encoded);
|
||||
if (! File::copy($sourcePath, $target)) {
|
||||
throw new RuntimeException('Unable to copy original file into local artwork originals storage.');
|
||||
}
|
||||
|
||||
return $target;
|
||||
$mime = (string) (File::mimeType($target) ?: 'application/octet-stream');
|
||||
$size = (int) filesize($target);
|
||||
$objectPath = $this->storage->objectPathForVariant('original', $hash, $filename);
|
||||
$this->storage->putObjectFromPath($target, $objectPath, $mime);
|
||||
|
||||
return [
|
||||
'local_path' => $target,
|
||||
'object_path' => $objectPath,
|
||||
'filename' => $filename,
|
||||
'mime' => $mime,
|
||||
'size' => $size,
|
||||
'ext' => $origExt,
|
||||
];
|
||||
}
|
||||
|
||||
private function resolveOriginalExtension(string $sourcePath, ?string $originalFileName): string
|
||||
@@ -90,6 +93,9 @@ final class UploadDerivativesService
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{path: string, size: int, mime: string}>
|
||||
*/
|
||||
public function generatePublicDerivatives(string $sourcePath, string $hash): array
|
||||
{
|
||||
$this->assertImageAvailable();
|
||||
@@ -99,9 +105,14 @@ final class UploadDerivativesService
|
||||
|
||||
foreach ($variants as $variant => $options) {
|
||||
$variant = (string) $variant;
|
||||
$dir = $this->storage->ensureHashDirectory($variant, $hash);
|
||||
// store derivative filename as <hash>.webp per variant directory
|
||||
$path = $dir . DIRECTORY_SEPARATOR . $hash . '.webp';
|
||||
|
||||
if ($variant === 'sq') {
|
||||
$written[$variant] = $this->generateSquareDerivative($sourcePath, $hash);
|
||||
continue;
|
||||
}
|
||||
|
||||
$filename = $hash . '.webp';
|
||||
$objectPath = $this->storage->objectPathForVariant($variant, $hash, $filename);
|
||||
|
||||
/** @var InterventionImageInterface $img */
|
||||
$img = $this->manager->read($sourcePath);
|
||||
@@ -120,13 +131,51 @@ final class UploadDerivativesService
|
||||
|
||||
$encoder = new \Intervention\Image\Encoders\WebpEncoder($quality);
|
||||
$encoded = (string) $out->encode($encoder);
|
||||
File::put($path, $encoded);
|
||||
$written[$variant] = $path;
|
||||
$this->storage->putObjectContents($objectPath, $encoded, 'image/webp');
|
||||
$written[$variant] = [
|
||||
'path' => $objectPath,
|
||||
'size' => strlen($encoded),
|
||||
'mime' => 'image/webp',
|
||||
];
|
||||
}
|
||||
|
||||
return $written;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
* @return array{path: string, size: int, mime: string, result: SquareThumbnailResultData}
|
||||
*/
|
||||
public function generateSquareDerivative(string $sourcePath, string $hash, array $options = []): array
|
||||
{
|
||||
$filename = $hash . '.webp';
|
||||
$objectPath = $this->storage->objectPathForVariant('sq', $hash, $filename);
|
||||
$temporaryPath = tempnam(sys_get_temp_dir(), 'sq-derivative-');
|
||||
|
||||
if ($temporaryPath === false) {
|
||||
throw new RuntimeException('Unable to allocate a temporary file for square derivative generation.');
|
||||
}
|
||||
|
||||
$temporaryWebp = $temporaryPath . '.webp';
|
||||
rename($temporaryPath, $temporaryWebp);
|
||||
|
||||
try {
|
||||
$result = $this->squareThumbnails->generateFromPath($sourcePath, $temporaryWebp, $options);
|
||||
$mime = 'image/webp';
|
||||
$size = (int) filesize($temporaryWebp);
|
||||
$this->storage->putObjectFromPath($temporaryWebp, $objectPath, $mime);
|
||||
|
||||
return [
|
||||
'path' => $objectPath,
|
||||
'size' => $size,
|
||||
'mime' => $mime,
|
||||
'result' => $result,
|
||||
];
|
||||
} finally {
|
||||
File::delete($temporaryWebp);
|
||||
}
|
||||
}
|
||||
|
||||
private function assertImageAvailable(): void
|
||||
{
|
||||
if (! $this->imageAvailable) {
|
||||
|
||||
Reference in New Issue
Block a user