Completed
Push — master ( 5ac1ba...840ad0 )
by Freek
01:48
created

ResponsiveImageGenerator::cleanResponsiveImages()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
1
<?php
2
3
namespace Spatie\MediaLibrary\ResponsiveImages;
4
5
use Spatie\MediaLibrary\Helpers\File;
6
use Spatie\MediaLibrary\Models\Media;
7
use Illuminate\Support\Facades\Storage;
8
use Spatie\MediaLibrary\Helpers\ImageFactory;
9
use Spatie\MediaLibrary\Conversion\Conversion;
10
use Spatie\MediaLibrary\Filesystem\Filesystem;
11
use Spatie\MediaLibrary\Helpers\TemporaryDirectory;
12
use Spatie\MediaLibrary\Events\ResponsiveImagesGenerated;
13
use Spatie\MediaLibrary\ResponsiveImages\Exceptions\InvalidTinyJpg;
14
use Spatie\MediaLibrary\ResponsiveImages\WidthCalculator\WidthCalculator;
15
use Spatie\TemporaryDirectory\TemporaryDirectory as BaseTemporaryDirectory;
16
use Spatie\MediaLibrary\ResponsiveImages\TinyPlaceholderGenerator\TinyPlaceholderGenerator;
17
18
class ResponsiveImageGenerator
19
{
20
    /** \Spatie\MediaLibrary\Filesystem\Filesystem */
21
    protected $filesystem;
22
23
    /** \Spatie\MediaLibrary\ResponsiveImages\WidthCalculator\WidthCalculator */
24
    protected $widthCalculator;
25
26
    /** \Spatie\MediaLibrary\ResponsiveImages\TinyPlaceHolderGenerator\TinyPlaceHolderGenerator */
27
    protected $tinyPlaceholderGenerator;
28
29
    public function __construct(
30
        Filesystem $filesystem,
31
        WidthCalculator $widthCalculator,
32
        TinyPlaceholderGenerator $tinyPlaceholderGenerator
33
    ) {
34
        $this->filesystem = $filesystem;
35
36
        $this->widthCalculator = $widthCalculator;
37
38
        $this->tinyPlaceholderGenerator = $tinyPlaceholderGenerator;
39
    }
40
41
    public function generateResponsiveImages(Media $media)
42
    {
43
        $temporaryDirectory = TemporaryDirectory::create();
44
45
        $baseImage = app(Filesystem::class)->copyFromMediaLibrary(
46
            $media,
47
            $temporaryDirectory->path(str_random(16).'.'.$media->extension)
48
        );
49
50
        $media = $this->cleanResponsiveImages($media);
51
52
        foreach ($this->widthCalculator->calculateWidthsFromFile($baseImage) as $width) {
53
            $this->generateResponsiveImage($media, $baseImage, 'medialibrary_original', $width, $temporaryDirectory);
54
        }
55
56
        event(new ResponsiveImagesGenerated($media));
57
58
        $this->generateTinyJpg($media, $baseImage, 'medialibrary_original', $temporaryDirectory);
59
60
        $temporaryDirectory->delete();
61
    }
62
63
    public function generateResponsiveImagesForConversion(Media $media, Conversion $conversion, string $baseImage)
64
    {
65
        $temporaryDirectory = TemporaryDirectory::create();
66
67
        $media = $this->cleanResponsiveImages($media, $conversion->getName());
68
69
        foreach ($this->widthCalculator->calculateWidthsFromFile($baseImage) as $width) {
70
            $this->generateResponsiveImage($media, $baseImage, $conversion->getName(), $width, $temporaryDirectory);
71
        }
72
73
        $this->generateTinyJpg($media, $baseImage, $conversion->getName(), $temporaryDirectory);
74
75
        $temporaryDirectory->delete();
76
    }
77
78
    public function generateResponsiveImage(
79
        Media $media,
80
        string $baseImage,
81
        string $conversionName,
82
        int $targetWidth,
83
        BaseTemporaryDirectory $temporaryDirectory
84
    ) {
85
        $responsiveImagePath = $this->appendToFileName($media->file_name, "___{$conversionName}_{$targetWidth}");
86
87
        $tempDestination = $temporaryDirectory->path($responsiveImagePath);
88
89
        ImageFactory::load($baseImage)
0 ignored issues
show
Bug introduced by
The method optimize() does not exist on Spatie\Image\Image. Did you maybe mean shouldOptimize()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
90
            ->optimize()
91
            ->width($targetWidth)
92
            ->save($tempDestination);
93
94
        $responsiveImageHeight = ImageFactory::load($tempDestination)->getHeight();
95
96
        $finalImageFileName = $this->appendToFileName($responsiveImagePath, "_{$responsiveImageHeight}");
97
98
        $finalResponsiveImagePath = $temporaryDirectory->path($finalImageFileName);
99
100
        rename($tempDestination, $finalResponsiveImagePath);
101
102
        $this->filesystem->copyToMediaLibrary($finalResponsiveImagePath, $media, 'responsiveImages');
103
104
        ResponsiveImage::register($media, $finalImageFileName, $conversionName);
105
    }
106
107
    public function generateTinyJpg(Media $media, string $originalImagePath, string $conversionName, BaseTemporaryDirectory $temporaryDirectory)
108
    {
109
        $tempDestination = $temporaryDirectory->path('tiny.jpg');
110
111
        $this->tinyPlaceholderGenerator->generateTinyPlaceholder($originalImagePath, $tempDestination);
112
113
        $this->guardAgainstInvalidTinyPlaceHolder($tempDestination);
114
115
        $tinyImageDataBase64 = base64_encode(file_get_contents($tempDestination));
116
117
        $tinyImageBase64 = 'data:image/jpeg;base64,'.$tinyImageDataBase64;
118
119
        $originalImage = ImageFactory::load($originalImagePath);
120
121
        $originalImageWidth = $originalImage->getWidth();
122
123
        $originalImageHeight = $originalImage->getHeight();
124
125
        $svg = view('medialibrary::placeholderSvg', compact(
126
            'originalImageWidth',
127
            'originalImageHeight',
128
            'tinyImageBase64'
129
        ));
130
131
        $base64Svg = 'data:image/svg+xml;base64,'.base64_encode($svg);
132
133
        ResponsiveImage::registerTinySvg($media, $base64Svg, $conversionName);
134
    }
135
136
    protected function appendToFileName(string $filePath, string $suffix): string
137
    {
138
        $baseName = pathinfo($filePath, PATHINFO_FILENAME);
139
140
        $extension = pathinfo($filePath, PATHINFO_EXTENSION);
141
142
        return $baseName.$suffix.'.'.$extension;
143
    }
144
145
    protected function guardAgainstInvalidTinyPlaceHolder(string $tinyPlaceholderPath)
146
    {
147
        if (! file_exists($tinyPlaceholderPath)) {
148
            throw InvalidTinyJpg::doesNotExist($tinyPlaceholderPath);
149
        }
150
151
        $mimeType = File::getMimetype($tinyPlaceholderPath);
152
153
        if ($mimeType !== 'image/jpeg') {
154
            throw InvalidTinyJpg::hasWrongMimeType($tinyPlaceholderPath);
155
        }
156
    }
157
158
    protected function cleanResponsiveImages(Media $media, string $conversionName = 'medialibrary_original') : Media
159
    {
160
        $responsiveImages = $media->responsive_images;
161
        $responsiveImages[$conversionName]['urls'] = [];
162
        $media->responsive_images = $responsiveImages;
163
164
        $responsiveImagesDirectory = $this->filesystem->getResponsiveImagesDirectory($media);
165
        $storage = Storage::disk($media->disk);
166
167
        $files = array_filter(
168
            $storage->allFiles($responsiveImagesDirectory),
169
            function ($path) use ($conversionName) {
170
                return str_contains($path, $conversionName);
171
            }
172
        );
173
174
        $storage->delete($files);
175
176
        return $media;
177
    }
178
}
179