YeelightImageService::storeImageFromPath()   F
last analyzed

Complexity

Conditions 20
Paths 330

Size

Total Lines 90
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
eloc 58
nc 330
nop 4
dl 0
loc 90
rs 1.7083
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Yeelight\Services\Image;
4
5
use Carbon\Carbon;
6
use File;
7
use Image;
8
use Intervention\Image\ImageManager;
9
use Symfony\Component\HttpFoundation\File\UploadedFile;
10
use Validator;
11
use Yeelight\Models\Image\YeelightImage;
12
use Yeelight\Services\Image\Exception\StoreImageException;
13
use Yeelight\Services\Image\Exception\YeelightImageException;
14
use Yeelight\Services\Image\Models\YeelightImageHash;
15
16
/**
17
 * Class YeelightImageService
18
 *
19
 * @category Yeelight
20
 *
21
 * @package Yeelight\Services\Image
22
 *
23
 * @author Sheldon Lee <[email protected]>
24
 *
25
 * @license https://opensource.org/licenses/MIT MIT
26
 *
27
 * @link https://www.yeelight.com
28
 */
29
class YeelightImageService
30
{
31
    /**
32
     * @param $key
33
     * @param null $default
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $default is correct as it would always require null to be passed?
Loading history...
34
     *
35
     * @return mixed
36
     */
37
    public function getConfig($key, $default = null)
38
    {
39
        return config("yeelight-image.$key", $default);
40
    }
41
42
    /**
43
     * @return string
44
     */
45
    private function storagePath()
46
    {
47
        return $this->getConfig('storage_path');
48
    }
49
50
    /**
51
     * @return int
52
     */
53
    private function defaultQuality()
54
    {
55
        return (int) $this->getConfig('default_quality', 75);
56
    }
57
58
    /**
59
     * @return int
60
     */
61
    private function largeImageQuality()
62
    {
63
        return (int) $this->getConfig('large_image_quality', 65);
64
    }
65
66
    /**
67
     * @param UploadedFile $file
68
     * @param string       $additionValidatorRule
69
     * @param bool         $isAllowGIF
70
     * @param int          $maxSizeAllowed
71
     * @param bool         $isStorePNG
72
     *
73
     * @return YeelightImage
74
     */
75
    public function handleUploadedFile(UploadedFile $file, $additionValidatorRule = '', $isAllowGIF = false, $maxSizeAllowed = 12829, $isStorePNG = false)
76
    {
77
        // check valid
78
        if (!$file->isValid()) {
79
            throw new StoreImageException($file->getErrorMessage());
80
        }
81
82
        // validate image
83
        $additionValidatorRule = !empty($additionValidatorRule) ? '|'.$additionValidatorRule : '';
84
        $mimes = $isAllowGIF ? ',gif' : '';
85
        /** @var Validator $validator */
86
        $validator = Validator::make(['image' => $file], [
87
            'image' => 'required|mimes:jpg,jpeg,bmp,png'.$mimes.'|max:'.$maxSizeAllowed.$additionValidatorRule,
88
        ]);
89
        if ($validator->fails()) {
0 ignored issues
show
Bug introduced by
The method fails() does not exist on Illuminate\Support\Facades\Validator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

89
        if ($validator->/** @scrutinizer ignore-call */ fails()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
90
            throw new StoreImageException($validator->errors()->first());
0 ignored issues
show
Bug introduced by
The method errors() does not exist on Illuminate\Support\Facades\Validator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

90
            throw new StoreImageException($validator->/** @scrutinizer ignore-call */ errors()->first());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
91
        }
92
93
        // file info
94
        $path_with_name = $file->getPathname();
95
        $file_extension = $file->getClientOriginalExtension();
96
97
        return $this->storeImageFromPath($path_with_name, $file_extension, $isAllowGIF, $isStorePNG);
98
    }
99
100
    /**
101
     * @param $path_with_name
102
     * @param null $file_extension
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $file_extension is correct as it would always require null to be passed?
Loading history...
103
     * @param bool $isAllowGIF
104
     * @param bool $isStorePNG
105
     *
106
     * @return null|YeelightImage
107
     */
108
    public function storeImageFromPath($path_with_name, $file_extension = null, $isAllowGIF = false, $isStorePNG = false)
109
    {
110
        $file_extension = $file_extension ?: pathinfo($path_with_name, PATHINFO_EXTENSION);
111
        $is_file_gif = $file_extension == 'gif';
112
        $is_file_png = $file_extension == 'png';
113
        $file_origin_size = filesize($path_with_name);
114
        $is_animated_gif = $is_file_gif && $this->isAnimatedGif($path_with_name);
115
116
        if (!File::exists($path_with_name)) {
117
            throw new StoreImageException('File not found: '.$path_with_name);
118
        }
119
120
        // read exif info
121
        $exif = read_exif_data_safe($path_with_name);
122
123
        // passed validation and make image
124
        $image = Image::make($path_with_name);
125
        $originWidth = $image->getWidth();
0 ignored issues
show
Unused Code introduced by
The assignment to $originWidth is dead and can be removed.
Loading history...
126
        $originHeight = $image->getHeight();
0 ignored issues
show
Unused Code introduced by
The assignment to $originHeight is dead and can be removed.
Loading history...
127
        $image_file_size_kb = $file_origin_size / 1024;
0 ignored issues
show
Unused Code introduced by
The assignment to $image_file_size_kb is dead and can be removed.
Loading history...
128
        $is_allowed_animated_gif = $is_animated_gif;
129
130
        // save as jpg
131
        $default_file_extension = 'jpg';
132
        // save as gif if is allowed
133
        if ($isAllowGIF && $is_allowed_animated_gif) {
134
            $default_file_extension = 'gif';
135
        }
136
        if ($isStorePNG && $is_file_png) {
137
            $default_file_extension = 'png';
138
        }
139
        $storage_path = $this->storagePath();
140
        $file_sha1 = sha1_file($path_with_name);
141
        $final_file_sha1 = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $final_file_sha1 is dead and can be removed.
Loading history...
142
        if ($file_sha1 === false) {
143
            throw new StoreImageException('Failed to create SHA1 for file: '.$path_with_name);
144
        }
145
146
        // final
147
        $final_file_name = strtolower($file_sha1.'.'.$default_file_extension);
148
        $final_path_with_name = $storage_path.$final_file_name;
149
150
        // check directory
151
        if (!self::autoCreateDirectory($final_path_with_name)) {
152
            throw new StoreImageException('Failed to make dir: '.dirname($final_path_with_name));
153
        }
154
155
        // save
156
        $isExists = File::exists($final_path_with_name);
157
        $isSimilarExists = false;
158
        $yeelight_image_hash = null;
159
        $final_file_sha1 = null;
160
        $final_file_size = null;
161
        if ($isExists) {
162
            $final_file_sha1 = $file_sha1;
163
            $final_file_size = filesize($final_path_with_name);
164
        } else {
165
            $yeelight_image_hash = YeelightImageHash::where(['file_sha1' => $file_sha1])->first();
166
            $isExists = $isSimilarExists = $yeelight_image_hash ? true : false;
167
        }
168
169
        if (!$isExists) {
170
            if (($isAllowGIF && $is_allowed_animated_gif) || ($isStorePNG && $is_file_png)) {
171
                if (File::copy($path_with_name, $final_path_with_name)) {
172
                    @chmod($final_path_with_name, 0666 & ~umask());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

172
                    /** @scrutinizer ignore-unhandled */ @chmod($final_path_with_name, 0666 & ~umask());

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
173
                } else {
174
                    throw new StoreImageException('Failed to move file to path: '.$final_path_with_name);
175
                }
176
                $final_file_sha1 = sha1_file($final_path_with_name);
177
                $final_file_size = filesize($final_path_with_name);
178
                $isExists = File::exists($final_path_with_name);
179
            } else {
180
                $isExists = $this->saveImage($image, $exif,
181
                    $final_path_with_name, $final_file_sha1, $final_file_size);
0 ignored issues
show
Bug introduced by
It seems like $final_file_size can also be of type integer; however, parameter $final_file_size of Yeelight\Services\Image\...ageService::saveImage() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

181
                    $final_path_with_name, $final_file_sha1, /** @scrutinizer ignore-type */ $final_file_size);
Loading history...
Bug introduced by
It seems like $final_file_sha1 can also be of type string; however, parameter $final_file_sha1 of Yeelight\Services\Image\...ageService::saveImage() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

181
                    $final_path_with_name, /** @scrutinizer ignore-type */ $final_file_sha1, $final_file_size);
Loading history...
182
            }
183
        }
184
185
        // stored and save to database
186
        $yeelight_image = null;
187
        if ($isExists) {
188
            $yeelight_image = $this->saveImageInfo($isSimilarExists, $yeelight_image_hash,
189
                $final_file_name, $is_allowed_animated_gif, $final_file_size, $image, $exif,
190
                $file_sha1, $final_file_sha1);
191
        }
192
193
        if (!$yeelight_image) {
194
            throw new StoreImageException('Failed to store image.');
195
        }
196
197
        return $yeelight_image;
198
    }
199
200
    /**
201
     * @param $originImageEncodedData
202
     * @param bool $isAllowGIF
203
     *
204
     * @return null|YeelightImage
205
     */
206
    public function storeImageFromURLEncodedImageData($originImageEncodedData, $isAllowGIF = false)
207
    {
208
        $exif = read_exif_data_safe($originImageEncodedData);
209
        $is_gif = self::isDataURLEncodedImageGif($originImageEncodedData);
210
        $origin_image_data = self::convertDataURLEncodedToImageData($originImageEncodedData);
211
        $origin_image_sha1 = sha1($origin_image_data);
212
213
        return $this->storeImageFromMake($origin_image_data, $origin_image_sha1, $exif, $isAllowGIF, $is_gif);
214
    }
215
216
    /**
217
     * @param $source
218
     * @param $file_sha1
219
     * @param $exif
220
     * @param bool $isAllowGIF
221
     * @param bool $isGIF
222
     *
223
     * @return YeelightImage
224
     */
225
    public function storeImageFromMake($source, $file_sha1, $exif, $isAllowGIF = false, $isGIF = false)
226
    {
227
        try {
228
            $image = Image::make($source);
229
        } catch (StoreImageException $e) {
230
            throw new StoreImageException('YeelightImageService: Unable to make image [' + (string) $e + ']');
231
        }
232
233
        $is_allowed_animated_gif = $isAllowGIF && $isGIF;
234
235
        // save as jpg
236
        $default_file_extension = 'jpg';
237
        // save as gif if is allowed
238
        if ($isAllowGIF && $is_allowed_animated_gif) {
239
            $default_file_extension = 'gif';
240
        }
241
        $storage_path = $this->storagePath();
242
        $final_file_sha1 = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $final_file_sha1 is dead and can be removed.
Loading history...
243
        if ($file_sha1 === false || strlen($file_sha1) != 40) {
244
            throw new StoreImageException('Invalid SHA1 for file.');
245
        }
246
247
        // final
248
        $final_file_name = strtolower($file_sha1.'.'.$default_file_extension);
249
        $final_path_with_name = $storage_path.$final_file_name;
250
251
        // check directory
252
        if (!self::autoCreateDirectory($final_path_with_name)) {
253
            throw new StoreImageException('Failed to make dir: '.dirname($final_path_with_name));
254
        }
255
256
        // save
257
        $isExists = File::exists($final_path_with_name);
258
        $isSimilarExists = false;
259
        $yeelight_image_hash = null;
260
        $final_file_sha1 = null;
261
        $final_file_size = null;
262
        if ($isExists) {
263
            $final_file_sha1 = $file_sha1;
264
            $final_file_size = filesize($final_path_with_name);
265
        } else {
266
            $yeelight_image_hash = YeelightImageHash::where(['file_sha1' => $file_sha1])->first();
267
            $isExists = $isSimilarExists = $yeelight_image_hash ? true : false;
268
        }
269
270
        if (!$isExists) {
271
            if ($isAllowGIF && $is_allowed_animated_gif) {
272
                if (file_put_contents($final_path_with_name, $source)) {
273
                    @chmod($final_path_with_name, 0666 & ~umask());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

273
                    /** @scrutinizer ignore-unhandled */ @chmod($final_path_with_name, 0666 & ~umask());

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
274
                } else {
275
                    throw new StoreImageException('Failed to move file to path: '.$final_path_with_name);
276
                }
277
                $final_file_sha1 = sha1_file($final_path_with_name);
278
                $final_file_size = filesize($final_path_with_name);
279
                $isExists = File::exists($final_path_with_name);
280
            } else {
281
                $isExists = $this->saveImage($image, $exif,
282
                    $final_path_with_name, $final_file_sha1, $final_file_size);
0 ignored issues
show
Bug introduced by
It seems like $final_file_size can also be of type integer; however, parameter $final_file_size of Yeelight\Services\Image\...ageService::saveImage() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

282
                    $final_path_with_name, $final_file_sha1, /** @scrutinizer ignore-type */ $final_file_size);
Loading history...
283
            }
284
        }
285
286
        // stored and save to database
287
        $yeelight_image = null;
288
        if ($isExists) {
289
            $yeelight_image = $this->saveImageInfo($isSimilarExists, $yeelight_image_hash,
290
                $final_file_name, $is_allowed_animated_gif, $final_file_size, $image, $exif,
291
                $file_sha1, $final_file_sha1);
292
        }
293
294
        if (!$yeelight_image) {
295
            throw new StoreImageException('Failed to store image.');
296
        }
297
298
        return $yeelight_image;
299
    }
300
301
    /**
302
     * @param $filename
303
     *
304
     * @return bool
305
     */
306
    public function isAnimatedGif($filename)
307
    {
308
        $file_contents = file_get_contents($filename);
309
310
        $str_loc = 0;
311
        $count = 0;
312
313
        // There is no point in continuing after we find a 2nd frame
314
        while ($count < 2) {
315
            $where1 = strpos($file_contents, "\x00\x21\xF9\x04", $str_loc);
316
            if ($where1 === false) {
317
                break;
318
            }
319
320
            $str_loc = $where1 + 1;
321
            $where2 = strpos($file_contents, "\x00\x2C", $str_loc);
322
            if ($where2 === false) {
323
                break;
324
            } else {
325
                if ($where1 + 8 == $where2) {
326
                    $count++;
327
                }
328
                $str_loc = $where2 + 1;
329
            }
330
        }
331
332
        // gif is animated when it has two or more frames
333
        return $count >= 2;
334
    }
335
336
    /**
337
     * @param $image_name
338
     *
339
     * @return mixed
340
     */
341
    public function getImageOrFail($image_name)
342
    {
343
        return YeelightImage::where('image_name', $image_name)->firstOrFail();
344
    }
345
346
    /**
347
     * @param $template_name The template to be used for showing
348
     * @param $image_name The image name for showing
0 ignored issues
show
Bug introduced by
The type Yeelight\Services\Image\The was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
349
     * @param $image_templates array Additional image templates, if same template exists, will be replaced
350
     * @param null $storage_path
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $storage_path is correct as it would always require null to be passed?
Loading history...
351
     *
352
     * @return mixed|\Symfony\Component\HttpFoundation\BinaryFileResponse
353
     */
354
    public function showImage($template_name, $image_name, array $image_templates = [], $storage_path = null)
355
    {
356
        $cache_minutes = 60 * 24 * 15; // 15 days
357
        $storage_path = $storage_path ?: $this->storagePath();
358
        $image_templates = array_merge($this->getConfig('image_templates', []), $image_templates);
359
        $file_path = $storage_path.$image_name;
360
361
        // check exists
362
        $isExists = File::exists($file_path);
363
        if (!$isExists) {
364
            abort(404);
365
        }
366
367
        // if is invalid type
368
        if (!isset($image_templates[$template_name])) {
369
            throw new YeelightImageException('Invalid template name supplied.');
370
        }
371
372
        /** @var ImageTemplate $imageTemplate */
373
        $imageTemplate = $image_templates[$template_name];
374
375
        // if download
376
        if ($imageTemplate->isDownload()) {
377
            return response()->download($file_path);
0 ignored issues
show
Bug introduced by
The method download() does not exist on Symfony\Component\HttpFoundation\Response. It seems like you code against a sub-type of Symfony\Component\HttpFoundation\Response such as Illuminate\Http\Response or Illuminate\Http\JsonResponse or Illuminate\Http\RedirectResponse. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

377
            return response()->/** @scrutinizer ignore-call */ download($file_path);
Loading history...
378
        }
379
380
        // if gif
381
        if (str_contains($image_name, 'gif')) {
382
            return response(file_get_contents($file_path), 200)
383
                ->header('Content-Type', 'image/gif')
0 ignored issues
show
Bug introduced by
The method header() does not exist on Symfony\Component\HttpFoundation\Response. It seems like you code against a sub-type of Symfony\Component\HttpFoundation\Response such as Illuminate\Http\Response or Illuminate\Http\JsonResponse or Illuminate\Http\RedirectResponse. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

383
                ->/** @scrutinizer ignore-call */ header('Content-Type', 'image/gif')
Loading history...
384
                ->setPublic()
385
                ->setMaxAge(604800)
386
                ->setExpires(Carbon::now()->addDay(7));
387
        }
388
389
        // convert to image
390
        $img = Image::cache(function ($image) use ($file_path, $imageTemplate) {
391
            /* @var \Intervention\Image\Image $image */
392
            $image->make($file_path);
393
394
            // resize
395
            if (!$imageTemplate->isOriginal()) {
396
                if ($imageTemplate->isHeighten()) {
397
                    $image->heighten($imageTemplate->getHeight(), function ($constraint) use ($imageTemplate) {
398
                        if ($imageTemplate->isRatio()) {
399
                            $constraint->aspectRatio();
400
                        }
401
                        $constraint->upsize();
402
                    });
403
                } elseif ($imageTemplate->isWiden()) {
404
                    $image->widen($imageTemplate->getWidth(), function ($constraint) use ($imageTemplate) {
405
                        if ($imageTemplate->isRatio()) {
406
                            $constraint->aspectRatio();
407
                        }
408
                        $constraint->upsize();
409
                    });
410
                } elseif ($imageTemplate->isResize()) {
411
                    // remain aspect ratio and to its largest possible
412
                    if ($imageTemplate->isFit()) {
413
                        $image->fit($imageTemplate->getWidth(), $imageTemplate->getHeight());
414
                    } else {
415
                        $image->resize($imageTemplate->getWidth(), $imageTemplate->getHeight(),
416
                            function ($constraint) use ($imageTemplate) {
417
                                if ($imageTemplate->isRatio()) {
418
                                    $constraint->aspectRatio();
419
                                }
420
                                $constraint->upsize();
421
                            });
422
                    }
423
                }
424
            }
425
426
            // blur
427
            $blur_option = $imageTemplate->getOption('blur');
428
            if (!empty($blur_option)) {
429
                $amount = $blur_option['amount'];
430
                $image->blur($amount);
431
            }
432
        }, $cache_minutes, true);
433
434
        return $img->response()->setPublic()
435
            ->setMaxAge(604800)
436
            ->setExpires(Carbon::now()->addDay(7));
437
    }
438
439
    /**
440
     * @param $image
441
     * @param $exif
442
     * @param $final_path_with_name
443
     * @param null $final_file_sha1
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $final_file_sha1 is correct as it would always require null to be passed?
Loading history...
444
     * @param null $final_file_size
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $final_file_size is correct as it would always require null to be passed?
Loading history...
445
     *
446
     * @return bool
447
     */
448
    public function saveImage($image, $exif, $final_path_with_name,
449
                              &$final_file_sha1 = null, &$final_file_size = null)
450
    {
451
        // set correct orientation
452
        if (!empty($exif) && is_array($exif)) {
453
            if (isset($exif['Orientation']) && $exif['Orientation'] != 1) {
454
                $orientation = $exif['Orientation'];
455
                $deg = 0;
456
                switch ($orientation) {
457
                    case 3:
458
                        $deg = 180;
459
                        break;
460
                    case 6:
461
                        $deg = 270;
462
                        break;
463
                    case 8:
464
                        $deg = 90;
465
                        break;
466
                }
467
                // rotate image
468
                $image->rotate($deg);
469
            }
470
        }
471
472
        // basic info
473
        $originWidth = $image->getWidth();
474
        $originHeight = $image->getHeight();
475
        $file_origin_size = $image->filesize();
476
        $image_file_size_kb = $file_origin_size / 1024;
477
478
        // default quality 75
479
        $image_quality = $this->defaultQuality();
480
481
        // higher quality for small images
482
        if ($image_file_size_kb < 500) {
483
            $image_quality = 100;
484
        }
485
486
        // higher compression for large size images, 2000kb
487
        if ($image_file_size_kb > 2000) {
488
            // quality 65
489
            $image_quality = $this->largeImageQuality();
490
        }
491
492
        // reduce dimension to max 5000px
493
        $maxDimensionAllow = 5000;
494
        if ($originWidth > $maxDimensionAllow || $originHeight > $maxDimensionAllow) {
495
            $newWidth = $newHeight = null;
496
            if ($originWidth >= $originHeight) {
497
                $newWidth = $maxDimensionAllow;
498
            } else {
499
                $newHeight = $maxDimensionAllow;
500
            }
501
502
            // resize
503
            $image->resize($newWidth, $newHeight, function ($constraint) {
504
                $constraint->aspectRatio();
505
                $constraint->upsize();
506
            });
507
        }
508
509
        // save as JPG
510
        $image->save($final_path_with_name, $image_quality);
511
        $final_file_sha1 = sha1_file($final_path_with_name);
512
        $final_file_size = filesize($final_path_with_name);
513
        $isSaved = File::exists($final_path_with_name);
514
515
        return $isSaved;
516
    }
517
518
    /**
519
     * @param $isSimilarExists
520
     * @param $yeelight_image_hash
521
     * @param $final_file_name
522
     * @param $is_allowed_animated_gif
523
     * @param $final_file_size
524
     * @param $image
525
     * @param $exif
526
     * @param $file_sha1
527
     * @param $final_file_sha1
528
     *
529
     * @return YeelightImage
530
     */
531
    private function saveImageInfo($isSimilarExists, $yeelight_image_hash, $final_file_name, $is_allowed_animated_gif, $final_file_size, $image, $exif, $file_sha1, $final_file_sha1)
532
    {
533
        // check has Yeelight image
534
        if ($isSimilarExists) {
535
            $yeelight_image = $yeelight_image_hash->image;
536
        } else {
537
            $yeelight_image = YeelightImage::where('image_name', $final_file_name)->first();
538
        }
539
        // if no record, then save
540
        if (!$yeelight_image) {
541
            // read all existing data into an array
542
            $exif_data = $exif && is_array($exif) ? json_encode_safe($exif) : null;
543
544
            // create
545
            $yeelight_image = YeelightImage::firstOrCreate([
546
                'image_name' => $final_file_name,
547
                'is_gif'     => $is_allowed_animated_gif,
548
                'exif'       => $exif_data,
549
                'file_size'  => $final_file_size,
550
                'width'      => $image->getWidth(),
551
                'height'     => $image->getHeight(),
552
            ]);
553
554
            // add to image hash
555
            YeelightImageHash::firstOrCreate([
556
                'yeelight_image_id' => $yeelight_image->yeelight_image_id,
557
                'file_sha1'         => $file_sha1,
558
            ]);
559
            if (!empty($final_file_sha1) && $final_file_sha1 != $file_sha1) {
560
                YeelightImageHash::firstOrCreate([
561
                    'yeelight_image_id' => $yeelight_image->yeelight_image_id,
562
                    'file_sha1'         => $final_file_sha1,
563
                ]);
564
            }
565
        }
566
567
        return $yeelight_image;
568
    }
569
570
    public static function convertDataURLEncodedToImageData($encodedData)
571
    {
572
        try {
573
            $filteredData = substr($encodedData, strpos($encodedData, ',') + 1);
574
            $image_data = base64_decode($filteredData);
575
576
            return $image_data;
577
        } catch (StoreImageException $e) {
578
            \Log::error('YeelightImageService: Unable to convert to image [' + (string) $e + ']');
579
580
            return;
581
        }
582
    }
583
584
    public static function isDataURLEncodedImageGif($encodedData)
585
    {
586
        try {
587
            $image_type = substr($encodedData, 0, strpos($encodedData, ','));
588
            $is_gif = str_contains($image_type, 'gif');
589
590
            return $is_gif;
591
        } catch (StoreImageException $e) {
592
            \Log::error('YeelightImageService: Unable to convert to image [' + (string) $e + ']');
593
594
            return false;
595
        }
596
    }
597
598
    /**
599
     * auto create directory if not exists.
600
     *
601
     * @param $path_name
602
     *
603
     * @return bool
604
     */
605
    public static function autoCreateDirectory($path_name)
606
    {
607
        // check directory
608
        if (!File::exists(dirname($path_name))) {
609
            $isMadeDir = mkdir(dirname($path_name), 0777, true);
610
            if (!$isMadeDir) {
611
                return false;
612
            }
613
        }
614
615
        return true;
616
    }
617
618
    /**
619
     * @param int $yeelight_image_id
620
     *
621
     * @return \Intervention\Image\Image
622
     */
623
    public static function getImageFromYeelightImageId($yeelight_image_id)
624
    {
625
        $yeelight_image = YeelightImage::find($yeelight_image_id);
626
        if (is_null($yeelight_image)) {
627
            throw new YeelightImageException('Yeelight image record not found!');
628
        }
629
        $imageManager = new ImageManager(config('image'));
630
        $background_image = $imageManager->make($yeelight_image->getImagePath());
631
632
        return $background_image;
633
    }
634
}
635