Passed
Pull Request — v1 (#20)
by
unknown
04:43
created

Transcode::getAudioUrl()   F

Complexity

Conditions 14
Paths 436

Size

Total Lines 100
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 55
nc 436
nop 2
dl 0
loc 100
rs 2.8833
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
 * Transcoder plugin for Craft CMS 3.x
4
 *
5
 * Transcode videos to various formats, and provide thumbnails of the video
6
 *
7
 * @link      https://nystudio107.com
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @copyright tag
Loading history...
8
 * @copyright Copyright (c) 2017 nystudio107
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
9
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
10
11
namespace nystudio107\transcoder\services;
12
13
use craft\records\VolumeFolder;
14
use craft\services\Assets;
15
use nystudio107\transcoder\Transcoder;
16
17
use Craft;
18
use craft\base\Component;
19
use craft\elements\Asset;
20
use craft\events\AssetThumbEvent;
21
use craft\helpers\FileHelper;
22
use craft\helpers\Json as JsonHelper;
23
use craft\volumes\Local;
24
25
use yii\base\Exception;
26
use yii\validators\UrlValidator;
27
28
use mikehaertl\shellcommand\Command as ShellCommand;
29
use yii\base\InvalidConfigException;
30
31
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
32
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
33
 * @package   Transcode
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
34
 * @since     1.0.0
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
35
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
36
class Transcode extends Component
37
{
38
    // Constants
39
    // =========================================================================
40
41
    // Suffixes to add to the generated filename params
42
    const SUFFIX_MAP = [
43
        'videoFrameRate' => 'fps',
44
        'videoBitRate' => 'bps',
45
        'audioBitRate' => 'bps',
46
        'audioChannels' => 'c',
47
        'height' => 'h',
48
        'width' => 'w',
49
        'timeInSecs' => 's',
50
    ];
51
52
    // Params that should be excluded from being part of the generated filename
53
    const EXCLUDE_PARAMS = [
54
        'videoEncoder',
55
        'audioEncoder',
56
        'fileSuffix',
57
        'sharpen',
58
    ];
59
60
    // Mappings for getFileInfo() summary values
61
    const INFO_SUMMARY = [
62
        'format' => [
63
            'filename' => 'filename',
64
            'duration' => 'duration',
65
            'size' => 'size',
66
        ],
67
        'audio' => [
68
            'codec_name' => 'audioEncoder',
69
            'bit_rate' => 'audioBitRate',
70
            'sample_rate' => 'audioSampleRate',
71
            'channels' => 'audioChannels',
72
        ],
73
        'video' => [
74
            'codec_name' => 'videoEncoder',
75
            'bit_rate' => 'videoBitRate',
76
            'avg_frame_rate' => 'videoFrameRate',
77
            'height' => 'height',
78
            'width' => 'width',
79
        ],
80
    ];
81
82
    // Public Methods
83
    // =========================================================================
84
85
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $filePath should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $videoOptions should have a doc-comment as per coding-style.
Loading history...
86
     * Returns a URL to the transcoded video or "" if it doesn't exist (at which
87
     * time it will create it).
88
     *
89
     * @param $filePath     string  path to the original video -OR- an Asset
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
90
     * @param $videoOptions array   of options for the video
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
91
     * @param bool   $generate         whether the video should be encoded
0 ignored issues
show
Coding Style introduced by
Expected 58 spaces after parameter type; 3 found
Loading history...
Coding Style introduced by
Expected 1 spaces after parameter name; 9 found
Loading history...
92
     *
93
     * @return string       URL of the transcoded video or ""
94
     */
95
    public function getVideoUrl($filePath, $videoOptions, $generate = true): string
96
    {
97
98
        $result = '';
99
        $settings = Transcoder::$plugin->getSettings();
100
		$subfolder = '';
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 2
Loading history...
101
102
		// sub folder check
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 2
Loading history...
103
		if(\is_object($filePath) && ($filePath instanceof Asset) && $settings['createSubfolders']) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 2
Loading history...
Coding Style introduced by
Expected "if (...) {\n"; found "if(...) {\n"
Loading history...
104
			$subfolder = $filePath->folderPath;
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 12 spaces, found 3
Loading history...
105
		}
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 2
Loading history...
106
107
		// file path
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 2
Loading history...
108
        $filePath = $this->getAssetPath($filePath);
109
110
        if (!empty($filePath)) {
111
            $destVideoPath = $this->localDestPath('video', $subfolder);
0 ignored issues
show
Bug introduced by
It seems like $subfolder can also be of type null; however, parameter $subfolder of nystudio107\transcoder\s...nscode::localDestPath() does only seem to accept string, 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

111
            $destVideoPath = $this->localDestPath('video', /** @scrutinizer ignore-type */ $subfolder);
Loading history...
112
            $videoOptions = $this->coalesceOptions('defaultVideoOptions', $videoOptions);
113
114
            // Get the video encoder presets to use
115
            $videoEncoders = $settings['videoEncoders'];
116
            $thisEncoder = $videoEncoders[$videoOptions['videoEncoder']];
117
118
            $videoOptions['fileSuffix'] = $thisEncoder['fileSuffix'];
119
120
            // Build the basic command for ffmpeg
121
            $ffmpegCmd = $settings['ffmpegPath']
122
                .' -i '.escapeshellarg($filePath)
123
                .' -vcodec '.$thisEncoder['videoCodec']
124
                .' '.$thisEncoder['videoCodecOptions']
125
                .' -bufsize 1000k'
126
                .' -threads '.$thisEncoder['threads'];
127
128
            // Set the framerate if desired
129
            if (!empty($videoOptions['videoFrameRate'])) {
130
                $ffmpegCmd .= ' -r '.$videoOptions['videoFrameRate'];
131
            }
132
133
            // Set the bitrate if desired
134
            if (!empty($videoOptions['videoBitRate'])) {
135
                $ffmpegCmd .= ' -b:v '.$videoOptions['videoBitRate'].' -maxrate '.$videoOptions['videoBitRate'];
136
            }
137
138
            // Adjust the scaling if desired
139
            $ffmpegCmd = $this->addScalingFfmpegArgs(
140
                $videoOptions,
141
                $ffmpegCmd
142
            );
143
144
            // Handle any audio transcoding
145
            if (empty($videoOptions['audioBitRate'])
146
                && empty($videoOptions['audioSampleRate'])
147
                && empty($videoOptions['audioChannels'])
148
            ) {
149
                // Just copy the audio if no options are provided
150
                $ffmpegCmd .= ' -c:a copy';
151
            } else {
152
                // Do audio transcoding based on the settings
153
                $ffmpegCmd .= ' -acodec '.$thisEncoder['audioCodec'];
154
                if (!empty($videoOptions['audioBitRate'])) {
155
                    $ffmpegCmd .= ' -b:a '.$videoOptions['audioBitRate'];
156
                }
157
                if (!empty($videoOptions['audioSampleRate'])) {
158
                    $ffmpegCmd .= ' -ar '.$videoOptions['audioSampleRate'];
159
                }
160
                if (!empty($videoOptions['audioChannels'])) {
161
                    $ffmpegCmd .= ' -ac '.$videoOptions['audioChannels'];
162
                }
163
                $ffmpegCmd .= ' '.$thisEncoder['audioCodecOptions'];
164
            }
165
166
            // Create the directory if it isn't there already
167
            if (!is_dir($destVideoPath)) {
168
                try {
169
                    FileHelper::createDirectory($destVideoPath);
170
                } catch (Exception $e) {
171
                    Craft::error($e->getMessage(), __METHOD__);
172
                }
173
            }
174
175
            $destVideoFile = $this->getFilename($filePath, $videoOptions);
176
177
            // File to store the video encoding progress in
178
            $progressFile = sys_get_temp_dir().DIRECTORY_SEPARATOR.$destVideoFile.'.progress';
179
180
            // Assemble the destination path and final ffmpeg command
181
            $destVideoPath .= $destVideoFile;
182
            $ffmpegCmd .= ' -f '
183
                .$thisEncoder['fileFormat']
184
                .' -y '.escapeshellarg($destVideoPath)
185
                .' 1> '.$progressFile.' 2>&1 & echo $!';
186
187
            // Make sure there isn't a lockfile for this video already
188
            $lockFile = sys_get_temp_dir().DIRECTORY_SEPARATOR.$destVideoFile.'.lock';
189
            $oldPid = @file_get_contents($lockFile);
190
            if ($oldPid !== false) {
191
                exec("ps $oldPid", $ProcessState);
192
                if (\count($ProcessState) >= 2) {
193
                    return $result;
194
                }
195
                // It's finished transcoding, so delete the lockfile and progress file
196
                @unlink($lockFile);
197
                @unlink($progressFile);
198
            }
199
200
            // If the video file already exists and hasn't been modified, return it.  Otherwise, start it transcoding
201
            if (!$this->shouldGenerateAsset('video', $destVideoPath, $filePath)) {
202
                $url = $settings['transcoderUrls']['video'] ?? $settings['transcoderUrls']['default'];
203
                $result = Craft::getAlias($url).$destVideoFile;
204
205
            // skip encoding
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 16 spaces, found 12
Loading history...
206
            } elseif (!$generate) {
207
	            $result = "";
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 16 spaces, found 13
Loading history...
208
            } else {
209
                // Kick off the transcoding
210
                $pid = $this->executeShellCommand($ffmpegCmd);
211
                Craft::info($ffmpegCmd."\nffmpeg PID: ".$pid, __METHOD__);
212
213
                // Create a lockfile in tmp
214
                file_put_contents($lockFile, $pid);
215
            }
216
217
            if ($remoteUrl = $this->moveAssetToVolume('video', $destVideoPath)) {
218
                $result = $remoteUrl;
219
            }
220
            return $result;
221
        }
222
223
        return $result;
224
    }
225
226
    /**
227
     * Returns a URL to a video thumbnail
228
     *
229
     * @param string $filePath         path to the original video or an Asset
230
     * @param array  $thumbnailOptions of options for the thumbnail
231
     * @param bool   $generate         whether the thumbnail should be
232
     *                                 generated if it doesn't exists
233
     * @param bool   $asPath           Whether we should return a path or not
234
     *
235
     * @return string|false|null URL or path of the video thumbnail
236
     */
237
    public function getVideoThumbnailUrl($filePath, $thumbnailOptions, $generate = true, $asPath = false)
238
    {
239
        $result = null;
240
        $settings = Transcoder::$plugin->getSettings();
241
		$subfolder = '';
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 2
Loading history...
242
243
		// sub folder check
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 2
Loading history...
244
		if(\is_object($filePath) && ($filePath instanceof Asset) && $settings['createSubfolders']) {
0 ignored issues
show
introduced by
The condition is_object($filePath) is always false.
Loading history...
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 2
Loading history...
Coding Style introduced by
Expected "if (...) {\n"; found "if(...) {\n"
Loading history...
245
			$subfolder = $filePath->folderPath;
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 12 spaces, found 3
Loading history...
246
		}
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 2
Loading history...
247
248
        $filePath = $this->getAssetPath($filePath);
249
250
        if (!empty($filePath)) {
251
            $destThumbnailPath = $this->localDestPath('thumbnail', $subfolder);
252
253
            $thumbnailOptions = $this->coalesceOptions('defaultThumbnailOptions', $thumbnailOptions);
254
255
            // Build the basic command for ffmpeg
256
            $ffmpegCmd = $settings['ffmpegPath']
257
                .' -i '.escapeshellarg($filePath)
258
                .' -vcodec mjpeg'
259
                .' -vframes 1';
260
261
            // Adjust the scaling if desired
262
            $ffmpegCmd = $this->addScalingFfmpegArgs(
263
                $thumbnailOptions,
264
                $ffmpegCmd
265
            );
266
267
            // Set the timecode to get the thumbnail from if desired
268
            if (!empty($thumbnailOptions['timeInSecs'])) {
269
                $timeCode = gmdate('H:i:s', $thumbnailOptions['timeInSecs']);
270
                $ffmpegCmd .= ' -ss '.$timeCode.'.00';
271
            }
272
273
            // Create the directory if it isn't there already
274
            if (!is_dir($destThumbnailPath)) {
275
                try {
276
                    FileHelper::createDirectory($destThumbnailPath);
277
                } catch (Exception $e) {
278
                    Craft::error($e->getMessage(), __METHOD__);
279
                }
280
            }
281
282
            $destThumbnailFile = $this->getFilename($filePath, $thumbnailOptions);
283
284
            // Assemble the destination path and final ffmpeg command
285
            $destThumbnailPath .= $destThumbnailFile;
286
            $ffmpegCmd .= ' -f image2 -y '.escapeshellarg($destThumbnailPath).' >/dev/null 2>/dev/null &';
287
288
            // If the thumbnail file already exists, return it.  Otherwise, generate it and return it
289
            if ($this->shouldGenerateAsset('thumbnail', $destThumbnailPath)) {
290
                if ($generate) {
291
                    /** @noinspection PhpUnusedLocalVariableInspection */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
292
                    $shellOutput = $this->executeShellCommand($ffmpegCmd);
293
                    Craft::info($ffmpegCmd, __METHOD__);
294
295
                    // if ffmpeg fails which we can't check because the process is ran in the background
296
                    // dont return the future path of the image or else we can't check this in the front end
297
298
                    return false;
299
300
                } else {
301
                    Craft::info('Thumbnail does not exist, but not asked to generate it: '.$filePath, __METHOD__);
302
303
                    // The file doesn't exist, and we weren't asked to generate it
304
                    return false;
305
                }
306
            }
307
308
            if ($remoteUrl = $this->moveAssetToVolume('thumbnail', $destThumbnailPath)) {
309
                return $remoteUrl;
310
            }
311
312
            // Return either a path or a URL
313
            if ($asPath) {
314
                $result = $destThumbnailPath;
315
            } else {
316
                $url = $settings['transcoderUrls']['thumbnail'] . $subfolder ?? $settings['transcoderUrls']['default'];
317
                $result = Craft::getAlias($url).$destThumbnailFile;
318
            }
319
        }
320
321
        return $result;
322
    }
323
324
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $filePath should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $audioOptions should have a doc-comment as per coding-style.
Loading history...
325
     * Returns a URL to the transcoded audio file or "" if it doesn't exist
326
     * (at which time it will create it).
327
     *
328
     * @param $filePath     string path to the original audio file -OR- an Asset
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
329
     * @param $audioOptions array of options for the audio file
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
330
     *
331
     * @return string       URL of the transcoded audio file or ""
332
     */
333
    public function getAudioUrl($filePath, $audioOptions): string
334
    {
335
        $result = '';
336
        $settings = Transcoder::$plugin->getSettings();
337
		$subfolder = '';
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 2
Loading history...
338
339
		// sub folder check
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 2
Loading history...
340
		if(\is_object($filePath) && ($filePath instanceof Asset) && $settings['createSubfolders']) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 2
Loading history...
Coding Style introduced by
Expected "if (...) {\n"; found "if(...) {\n"
Loading history...
341
			$subfolder = $filePath->folderPath;
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 12 spaces, found 3
Loading history...
342
		}
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 2
Loading history...
343
344
        $filePath = $this->getAssetPath($filePath);
345
346
        if (!empty($filePath)) {
347
            $destAudioPath = $this->localDestPath('audio', $subfolder);
0 ignored issues
show
Bug introduced by
It seems like $subfolder can also be of type null; however, parameter $subfolder of nystudio107\transcoder\s...nscode::localDestPath() does only seem to accept string, 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

347
            $destAudioPath = $this->localDestPath('audio', /** @scrutinizer ignore-type */ $subfolder);
Loading history...
348
349
            $audioOptions = $this->coalesceOptions('defaultAudioOptions', $audioOptions);
350
351
            // Get the audio encoder presets to use
352
            $audioEncoders = $settings['audioEncoders'];
353
            $thisEncoder = $audioEncoders[$audioOptions['audioEncoder']];
354
355
            $audioOptions['fileSuffix'] = $thisEncoder['fileSuffix'];
356
357
            // Build the basic command for ffmpeg
358
            $ffmpegCmd = $settings['ffmpegPath']
359
                .' -i '.escapeshellarg($filePath)
360
                .' -acodec '.$thisEncoder['audioCodec']
361
                .' '.$thisEncoder['audioCodecOptions']
362
                .' -bufsize 1000k'
363
                .' -threads '.$thisEncoder['threads'];
364
365
            // Set the bitrate if desired
366
            if (!empty($audioOptions['audioBitRate'])) {
367
                $ffmpegCmd .= ' -b:a '.$audioOptions['audioBitRate'];
368
            }
369
            // Set the sample rate if desired
370
            if (!empty($audioOptions['audioSampleRate'])) {
371
                $ffmpegCmd .= ' -ar '.$audioOptions['audioSampleRate'];
372
            }
373
            // Set the audio channels if desired
374
            if (!empty($audioOptions['audioChannels'])) {
375
                $ffmpegCmd .= ' -ac '.$audioOptions['audioChannels'];
376
            }
377
            $ffmpegCmd .= ' '.$thisEncoder['audioCodecOptions'];
378
379
380
            // Create the directory if it isn't there already
381
            if (!is_dir($destAudioPath)) {
382
                try {
383
                    FileHelper::createDirectory($destAudioPath);
384
                } catch (Exception $e) {
385
                    Craft::error($e->getMessage(), __METHOD__);
386
                }
387
            }
388
389
            $destAudioFile = $this->getFilename($filePath, $audioOptions);
390
391
            // File to store the audio encoding progress in
392
            $progressFile = sys_get_temp_dir().DIRECTORY_SEPARATOR.$destAudioFile.'.progress';
393
394
            // Assemble the destination path and final ffmpeg command
395
            $destAudioPath .= $destAudioFile;
396
            $ffmpegCmd .= ' -f '
397
                .$thisEncoder['fileFormat']
398
                .' -y '.escapeshellarg($destAudioPath)
399
                .' 1> '.$progressFile.' 2>&1 & echo $!';
400
401
            // Make sure there isn't a lockfile for this audio file already
402
            $lockFile = sys_get_temp_dir().DIRECTORY_SEPARATOR.$destAudioFile.'.lock';
403
            $oldPid = @file_get_contents($lockFile);
404
            if ($oldPid !== false) {
405
                exec("ps $oldPid", $ProcessState);
406
                if (\count($ProcessState) >= 2) {
407
                    return $result;
408
                }
409
                // It's finished transcoding, so delete the lockfile and progress file
410
                @unlink($lockFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). 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

410
                /** @scrutinizer ignore-unhandled */ @unlink($lockFile);

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...
411
                @unlink($progressFile);
412
            }
413
414
            // If the audio file already exists and hasn't been modified, return it.  Otherwise, start it transcoding
415
            if ($this->shouldGenerateAsset('audio', $destAudioPath, $filePath)) {
416
                $url = $settings['transcoderUrls']['audio'] . $subfolder ?? $settings['transcoderUrls']['default'];
417
                $result = Craft::getAlias($url).$destAudioFile;
418
            } else {
419
                // Kick off the transcoding
420
                $pid = $this->executeShellCommand($ffmpegCmd);
421
                Craft::info($ffmpegCmd."\nffmpeg PID: ".$pid, __METHOD__);
422
423
                // Create a lockfile in tmp
424
                file_put_contents($lockFile, $pid);
425
            }
426
        }
427
428
        if ($remoteUrl = $this->moveAssetToVolume('audio', $destAudioPath)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $destAudioPath does not seem to be defined for all execution paths leading up to this point.
Loading history...
429
            $result = $remoteUrl;
430
        }
431
432
        return $result;
433
    }
434
435
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $filePath should have a doc-comment as per coding-style.
Loading history...
436
     * Extract information from a video/audio file
437
     *
438
     * @param      $filePath
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 1 spaces but found 6
Loading history...
439
     * @param bool $summary
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
440
     *
441
     * @return null|array
442
     */
443
    public function getFileInfo($filePath, $summary = false)
444
    {
445
        $result = null;
446
        $settings = Transcoder::$plugin->getSettings();
447
        $filePath = $this->getAssetPath($filePath);
448
449
        if (!empty($filePath)) {
450
            // Build the basic command for ffprobe
451
            $ffprobeOptions = $settings['ffprobeOptions'];
452
            $ffprobeCmd = $settings['ffprobePath']
453
                .' '.$ffprobeOptions
454
                .' '.escapeshellarg($filePath);
455
456
            $shellOutput = $this->executeShellCommand($ffprobeCmd);
457
            Craft::info($ffprobeCmd, __METHOD__);
458
            $result = JsonHelper::decodeIfJson($shellOutput, true);
459
            Craft::info(print_r($result, true), __METHOD__);
460
461
            // Trim down the arrays to just a summary
462
            if ($summary && !empty($result)) {
463
                $summaryResult = [];
464
                foreach ($result as $topLevelKey => $topLevelValue) {
465
                    switch ($topLevelKey) {
466
                        // Format info
467
                        case 'format':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
468
                            foreach (self::INFO_SUMMARY['format'] as $settingKey => $settingValue) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
469
                                if (!empty($topLevelValue[$settingKey])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 28 spaces, found 32
Loading history...
470
                                    $summaryResult[$settingValue] = $topLevelValue[$settingKey];
471
                                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 28 spaces, found 32
Loading history...
472
                            }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
473
                            break;
474
                        // Stream info
475
                        case 'streams':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
476
                            foreach ($topLevelValue as $stream) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
477
                                $infoSummaryType = $stream['codec_type'];
478
                                if (in_array($infoSummaryType, self::INFO_SUMMARY, false)) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 28 spaces, found 32
Loading history...
479
                                    foreach (self::INFO_SUMMARY[$infoSummaryType] as $settingKey => $settingValue) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 32 spaces, found 36
Loading history...
480
                                        if (!empty($stream[$settingKey])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 36 spaces, found 40
Loading history...
481
                                            $summaryResult[$settingValue] = $stream[$settingKey];
482
                                        }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 36 spaces, found 40
Loading history...
483
                                    }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 32 spaces, found 36
Loading history...
484
                                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 28 spaces, found 32
Loading history...
485
                            }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
486
                            break;
487
                        // Unknown info
488
                        default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
489
                            break;
490
                    }
491
                }
492
                // Handle cases where the framerate is returned as XX/YY
493
                if (!empty($summaryResult['videoFrameRate'])
494
                    && (strpos($summaryResult['videoFrameRate'], '/') !== false)
495
                ) {
496
                    $parts = explode('/', $summaryResult['videoFrameRate']);
497
                    $summaryResult['videoFrameRate'] = (float)$parts[0] / (float)$parts[1];
498
                }
499
                $result = $summaryResult;
500
            }
501
        }
502
503
        return $result;
504
    }
505
506
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $filePath should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $videoOptions should have a doc-comment as per coding-style.
Loading history...
507
     * Get the name of a video file from a path and options
508
     *
509
     * @param $filePath
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
510
     * @param $videoOptions
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
511
     *
512
     * @return string
513
     */
514
    public function getVideoFilename($filePath, $videoOptions): string
515
    {
516
        $settings = Transcoder::$plugin->getSettings();
517
        $videoOptions = $this->coalesceOptions('defaultVideoOptions', $videoOptions);
518
519
        // Get the video encoder presets to use
520
        $videoEncoders = $settings['videoEncoders'];
521
        $thisEncoder = $videoEncoders[$videoOptions['videoEncoder']];
522
523
        $videoOptions['fileSuffix'] = $thisEncoder['fileSuffix'];
524
525
        return $this->getFilename($filePath, $videoOptions);
526
    }
527
528
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $filePath should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $audioOptions should have a doc-comment as per coding-style.
Loading history...
529
     * Get the name of an audio file from a path and options
530
     *
531
     * @param $filePath
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
532
     * @param $audioOptions
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
533
     *
534
     * @return string
535
     */
536
    public function getAudioFilename($filePath, $audioOptions): string
537
    {
538
        $settings = Transcoder::$plugin->getSettings();
539
        $audioOptions = $this->coalesceOptions('defaultAudioOptions', $audioOptions);
540
541
        // Get the video encoder presets to use
542
        $audioEncoders = $settings['audioEncoders'];
543
        $thisEncoder = $audioEncoders[$audioOptions['audioEncoder']];
544
545
        $audioOptions['fileSuffix'] = $thisEncoder['fileSuffix'];
546
547
        return $this->getFilename($filePath, $audioOptions);
548
    }
549
550
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $filePath should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $gifOptions should have a doc-comment as per coding-style.
Loading history...
551
     * Get the name of a gif video file from a path and options
552
     *
553
     * @param $filePath
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
554
     * @param $gifOptions
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
555
     *
556
     * @return string
557
     */
558
    public function getGifFilename($filePath, $gifOptions): string
559
    {
560
        $settings = Transcoder::$plugin->getSettings();
561
        $gifOptions = $this->coalesceOptions('defaultGifOptions', $gifOptions);
562
563
        // Get the video encoder presets to use
564
        $videoEncoders = $settings['videoEncoders'];
565
        $thisEncoder = $videoEncoders[$gifOptions['videoEncoder']];
566
567
        $gifOptions['fileSuffix'] = $thisEncoder['fileSuffix'];
568
569
        return $this->getFilename($filePath, $gifOptions);
570
    }
571
572
    /**
573
     * Handle generated a thumbnail for the Control Panel
574
     *
575
     * @param AssetThumbEvent $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
576
     *
577
     * @return null|false|string
578
     */
579
    public function handleGetAssetThumbPath(AssetThumbEvent $event)
580
    {
581
        $options = [
582
            'width' => $event->width,
583
            'height' => $event->height,
584
        ];
585
        $thumbPath = $this->getVideoThumbnailUrl($event->asset, $options, $event->generate, true);
586
587
        return $thumbPath;
588
    }
589
590
    // Protected Methods
591
    // =========================================================================
592
593
    /**
594
     * Returns a URL to a encoded GIF file (mp4)
595
     *
596
     * @param string $filePath         path to the original video or an Asset
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 9 found
Loading history...
597
     * @param array  $gifOptions       of options for the GIF file
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 7 found
Loading history...
598
     *
599
     * @return string|false|null URL or path of the GIF file
600
     */
0 ignored issues
show
Coding Style introduced by
There must be no blank lines after the function comment
Loading history...
601
602
    public function getGifUrl($filePath, $gifOptions): string
603
    {
604
        $result = '';
605
        $settings = Transcoder::$plugin->getSettings();
606
		$subfolder = '';
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 2
Loading history...
607
608
		// sub folder check
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 8 spaces, found 2
Loading history...
609
		if(\is_object($filePath) && ($filePath instanceof Asset) && $settings['createSubfolders']) {
0 ignored issues
show
introduced by
The condition is_object($filePath) is always false.
Loading history...
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 2
Loading history...
Coding Style introduced by
Expected "if (...) {\n"; found "if(...) {\n"
Loading history...
610
			$subfolder = $filePath->folderPath;
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 12 spaces, found 3
Loading history...
611
		}
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 2
Loading history...
612
613
        $filePath = $this->getAssetPath($filePath);
614
615
        if (!empty($filePath)) {
616
            // Dest path
617
            $destVideoPath = $this->localDestPath('gif', $subfolder);
618
619
            // Options
620
            $gifOptions = $this->coalesceOptions('defaultGifOptions', $gifOptions);
621
622
            // Get the video encoder presets to use
623
            $videoEncoders = $settings['videoEncoders'];
624
            $thisEncoder = $videoEncoders[$gifOptions['videoEncoder']];
625
            $gifOptions['fileSuffix'] = $thisEncoder['fileSuffix'];
626
627
            // Build the basic command for ffmpeg
628
            $ffmpegCmd = $settings['ffmpegPath']
629
                .' -f gif'
630
                .' -i '.escapeshellarg($filePath)
631
                .' -vcodec '.$thisEncoder['videoCodec']
632
                .' '.$thisEncoder['videoCodecOptions'];
633
634
635
            // Create the directory if it isn't there already
636
            if (!is_dir($destVideoPath)) {
637
                try {
638
                    FileHelper::createDirectory($destVideoPath);
639
                } catch (Exception $e) {
640
                    Craft::error($e->getMessage(), __METHOD__);
641
                }
642
            }
643
644
            $destVideoFile = $this->getFilename($filePath, $gifOptions);
645
646
            // File to store the video encoding progress in
647
            $progressFile = sys_get_temp_dir().DIRECTORY_SEPARATOR.$destVideoFile.'.progress';
648
649
            // Assemble the destination path and final ffmpeg command
650
            $destVideoPath .= $destVideoFile;
651
            $ffmpegCmd .= ' '
652
                .' -y '.escapeshellarg($destVideoPath)
653
                .' 1> '.$progressFile.' 2>&1 & echo $!';
654
655
            // Make sure there isn't a lockfile for this video already
656
            $lockFile = sys_get_temp_dir().DIRECTORY_SEPARATOR.$destVideoFile.'.lock';
657
            $oldPid = @file_get_contents($lockFile);
658
            if ($oldPid !== false) {
659
                exec("ps $oldPid", $ProcessState);
660
                if (\count($ProcessState) >= 2) {
661
                    return $result;
662
                }
663
                // It's finished transcoding, so delete the lockfile and progress file
664
                @unlink($lockFile);
665
                @unlink($progressFile);
666
            }
667
668
            // If the video file already exists and hasn't been modified, return it.  Otherwise, start it transcoding
669
            if ($this->shouldGenerateAsset('gif', $destVideoPath, $filePath)) {
670
                $url = $settings['transcoderUrls']['gif'] . $subfolder ?? $settings['transcoderUrls']['default'];
671
                $result = Craft::getAlias($url).$destVideoFile;
672
            } else {
673
                // Kick off the transcoding
674
                $pid = $this->executeShellCommand($ffmpegCmd);
675
                Craft::info($ffmpegCmd."\nffmpeg PID: ".$pid, __METHOD__);
676
677
                // Create a lockfile in tmp
678
                file_put_contents($lockFile, $pid);
679
            }
680
681
            if ($remoteUrl = $this->moveAssetToVolume('gif', $destVideoPath)) {
682
                $result = $remoteUrl;
683
            }
684
        }
685
686
        return $result;
687
    }
688
689
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $filePath should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $options should have a doc-comment as per coding-style.
Loading history...
690
     * Get the name of a file from a path and options
691
     *
692
     * @param $filePath
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
693
     * @param $options
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
694
     *
695
     * @return string
696
     */
697
    protected function getFilename($filePath, $options): string
698
    {
699
        $settings = Transcoder::$plugin->getSettings();
700
        $filePath = $this->getAssetPath($filePath);
701
702
        $validator = new UrlValidator();
703
        $error = '';
704
        if ($validator->validate($filePath, $error)) {
705
            $urlParts = parse_url($filePath);
706
            $pathParts = pathinfo($urlParts['path']);
707
        } else {
708
            $pathParts = pathinfo($filePath);
709
        }
710
        $fileName = $pathParts['filename'];
711
712
        // Add our options to the file name
713
        foreach ($options as $key => $value) {
714
            if (!empty($value)) {
715
                $suffix = '';
716
                if (!empty(self::SUFFIX_MAP[$key])) {
717
                    $suffix = self::SUFFIX_MAP[$key];
718
                }
719
                if (\is_bool($value)) {
720
                    $value = $value ? $key : 'no'.$key;
721
                }
722
                if (!\in_array($key, self::EXCLUDE_PARAMS, true)) {
723
                    $fileName .= '_'.$value.$suffix;
724
                }
725
            }
726
        }
727
        // See if we should use a hash instead
728
        if ($settings['useHashedNames']) {
729
            $fileName = $pathParts['filename'].md5($fileName);
730
        }
731
        $fileName .= $options['fileSuffix'];
732
733
        return $fileName;
734
    }
735
736
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $filePath should have a doc-comment as per coding-style.
Loading history...
737
     * Extract a file system path if $filePath is an Asset object
738
     *
739
     * @param $filePath
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
740
     *
741
     * @return string
742
     */
743
    protected function getAssetPath($filePath): string
744
    {
745
        // If we're passed an Asset, extract the path from it
746
        if (\is_object($filePath) && ($filePath instanceof Asset)) {
747
            /** @var Asset $asset */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
748
            $asset = $filePath;
749
            $assetVolume = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $assetVolume is dead and can be removed.
Loading history...
750
            try {
751
                $assetVolume = $asset->getVolume();
752
            } catch (InvalidConfigException $e) {
753
                Craft::error($e->getMessage(), __METHOD__);
754
            }
755
756
            if ($assetVolume) {
0 ignored issues
show
introduced by
$assetVolume is of type craft\base\VolumeInterface, thus it always evaluated to true.
Loading history...
757
                // If it's local, get a path to the file
758
                if ($assetVolume instanceof Local) {
759
                    $sourcePath = rtrim($assetVolume->path, DIRECTORY_SEPARATOR);
760
                    $sourcePath .= '' === $sourcePath ? '': DIRECTORY_SEPARATOR;
761
                    $folderPath = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $folderPath is dead and can be removed.
Loading history...
762
                    try {
763
                        $folderPath = rtrim($asset->getFolder()->path, DIRECTORY_SEPARATOR);
764
                    } catch (InvalidConfigException $e) {
765
                        Craft::error($e->getMessage(), __METHOD__);
766
                    }
767
                    $folderPath .= '' === $folderPath ? '': DIRECTORY_SEPARATOR;
768
769
                    $filePath = $sourcePath.$folderPath.$asset->filename;
770
                } else {
771
                    // Otherwise, get a URL
772
                    $filePath = $asset->getUrl();
773
                }
774
            }
775
        }
776
777
        $filePath = Craft::getAlias($filePath);
778
779
        // Make sure that $filePath is either an existing file, or a valid URL
780
        if (!file_exists($filePath)) {
781
            $validator = new UrlValidator();
782
            $error = '';
783
            if (!$validator->validate($filePath, $error)) {
784
                Craft::error($error, __METHOD__);
785
                $filePath = '';
786
            }
787
        }
788
789
        return $filePath;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $filePath could return the type boolean which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
790
    }
791
792
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $options should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $ffmpegCmd should have a doc-comment as per coding-style.
Loading history...
793
     * Set the width & height if desired
794
     *
795
     * @param $options
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
796
     * @param $ffmpegCmd
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
797
     *
798
     * @return string
799
     */
800
    protected function addScalingFfmpegArgs($options, $ffmpegCmd): string
801
    {
802
        if (!empty($options['width']) && !empty($options['height'])) {
803
            // Handle "none", "crop", and "letterbox" aspectRatios
804
            $aspectRatio = '';
805
            if (!empty($options['aspectRatio'])) {
806
                switch ($options['aspectRatio']) {
807
                    // Scale to the appropriate aspect ratio, padding
808
                    case 'letterbox':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
809
                        $letterboxColor = '';
810
                        if (!empty($options['letterboxColor'])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
811
                            $letterboxColor = ':color='.$options['letterboxColor'];
812
                        }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
813
                        $aspectRatio = ':force_original_aspect_ratio=decrease'
814
                            .',pad='.$options['width'].':'.$options['height'].':(ow-iw)/2:(oh-ih)/2'
815
                            .$letterboxColor;
816
                        break;
817
                    // Scale to the appropriate aspect ratio, cropping
818
                    case 'crop':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
819
                        $aspectRatio = ':force_original_aspect_ratio=increase'
820
                            .',crop='.$options['width'].':'.$options['height'];
821
                        break;
822
                    // No aspect ratio scaling at all
823
                    default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
824
                        $aspectRatio = ':force_original_aspect_ratio=disable';
825
                        $options['aspectRatio'] = 'none';
826
                        break;
827
                }
828
            }
829
            $sharpen = '';
830
            if (!empty($options['sharpen']) && ($options['sharpen'] !== false)) {
831
                $sharpen = ',unsharp=5:5:1.0:5:5:0.0';
832
            }
833
            $ffmpegCmd .= ' -vf "scale='
834
                .$options['width'].':'.$options['height']
835
                .$aspectRatio
836
                .$sharpen
837
                .'"';
838
        }
839
840
        return $ffmpegCmd;
841
    }
842
843
    // Protected Methods
844
    // =========================================================================
845
846
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $defaultName should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $options should have a doc-comment as per coding-style.
Loading history...
847
     * Combine the options arrays
848
     *
849
     * @param $defaultName
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
850
     * @param $options
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
851
     *
852
     * @return array
853
     */
854
    protected function coalesceOptions($defaultName, $options): array
855
    {
856
        // Default options
857
        $settings = Transcoder::$plugin->getSettings();
858
        $defaultOptions = $settings[$defaultName];
859
860
        // Coalesce the passed in $options with the $defaultOptions
861
        $options = array_merge($defaultOptions, $options);
862
863
        return $options;
864
    }
865
866
    /**
867
     * Execute a shell command
868
     *
869
     * @param string $command
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
870
     *
871
     * @return string
872
     */
873
    protected function executeShellCommand(string $command): string
874
    {
875
        // Create the shell command
876
        $shellCommand = new ShellCommand();
877
        $shellCommand->setCommand($command);
878
879
        // If we don't have proc_open, maybe we've got exec
880
        if (!\function_exists('proc_open') && \function_exists('exec')) {
881
            $shellCommand->useExec = true;
882
        }
883
884
        // Return the result of the command's output or error
885
        if ($shellCommand->execute()) {
886
            $result = $shellCommand->getOutput();
887
        } else {
888
            $result = $shellCommand->getError();
889
        }
890
891
        return $result;
892
    }
893
894
    protected function shouldGenerateAsset(string $type, string $destVideoFilePath, string $originalFilePath = null)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function shouldGenerateAsset()
Loading history...
895
    {
896
        $settings = Transcoder::$plugin->getSettings();
897
        $destSettings = $settings['transcoderPaths'][$type] ?? $settings['transcoderPaths']['default'];
898
899
        if (file_exists($destVideoFilePath) && (@filemtime($destVideoFilePath) >= @filemtime($originalFilePath))) {
900
            return false;
901
        }
902
903
        if (!is_array($destSettings)) {
904
            return true;
905
        }
906
907
        $filename = basename($destVideoFilePath);
908
909
        $volume = Craft::$app->getVolumes()->getVolumeByHandle($destSettings['volume']);
910
        /** @var Asset $asset */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
911
        $asset = Asset::findOne(['volumeId' => $volume->id, 'filename' => $filename]);
0 ignored issues
show
Bug introduced by
Accessing id on the interface craft\base\VolumeInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
912
913
        if ($asset && $asset->dateModified->getTimestamp() >= @filemtime($originalFilePath)) {
0 ignored issues
show
Bug introduced by
The method getTimestamp() does not exist on null. ( Ignorable by Annotation )

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

913
        if ($asset && $asset->dateModified->/** @scrutinizer ignore-call */ getTimestamp() >= @filemtime($originalFilePath)) {

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...
914
            return false;
915
        }
916
        return true;
917
    }
918
919
    protected function localDestPath(string $type, string $subfolder) {
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function localDestPath()
Loading history...
Coding Style introduced by
Opening brace should be on a new line
Loading history...
920
        $settings = Transcoder::$plugin->getSettings();
921
        $destSettings = $settings['transcoderPaths'][$type]  ?? $settings['transcoderPaths']['default'];
922
        if (is_array($destSettings)) {
923
            return sys_get_temp_dir();
924
        }
925
        if (isset($settings['transcoderPaths'][$type])) {
926
            $destSettings .= $subfolder;
927
        }
928
        return Craft::getAlias($destSettings);
929
    }
930
931
    protected function moveAssetToVolume(string $type, string $filePath)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function moveAssetToVolume()
Loading history...
932
    {
933
        $settings = Transcoder::$plugin->getSettings();
934
        $destSettings = $settings['transcoderPaths'][$type] ?? $settings['transcoderPaths']['default'];
935
936
        // check if the setting is a volume
937
        if (!is_array($destSettings) || empty($filePath)) {
938
            return;
939
        }
940
941
        $volume = Craft::$app->getVolumes()->getVolumeByHandle($destSettings['volume']);
942
        if (!$volume) {
943
            $message = sprintf('Volume (handle: "%s") does not exist.', $destSettings['volume']);
944
            throw new \RuntimeException($message);
945
        }
946
947
        $filename = basename($filePath);
948
949
        /** @var Assets $assetService */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
950
        $assetElement = Asset::findOne(['volumeId' => $volume->id, 'filename' => $filename]);
0 ignored issues
show
Bug introduced by
Accessing id on the interface craft\base\VolumeInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
951
        if ($assetElement) {
0 ignored issues
show
introduced by
$assetElement is of type craft\elements\Asset, thus it always evaluated to true.
Loading history...
952
            return $assetElement->getUrl();
953
        }
954
955
        // check if the given folder exists
956
        $volumeFolder = VolumeFolder::findOne(['path' => $destSettings['folder'], 'volumeId' => $volume->id]);
957
        if (!$volumeFolder) {
958
            $message = sprintf('Folder (path: "%s") in volume (handle: "%s") does not exist.', $destSettings['folder'], $destSettings['volume']);
959
            throw new \RuntimeException($message);
960
        }
961
962
        $asset = new Asset();
963
        $asset->volumeId = $volume->id;
964
        $asset->tempFilePath = $filePath;
965
        $asset->filename = $filename;
966
        $asset->folderId = $volumeFolder->id;
967
968
        Craft::$app->getElements()->saveElement($asset);
969
        @unlink($filePath);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). 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

969
        /** @scrutinizer ignore-unhandled */ @unlink($filePath);

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...
970
971
        return $asset->getUrl();
972
    }
973
}
974