Failed Conditions
Push — master ( 1f1533...4e2f19 )
by Sébastien
02:05
created

VideoThumbGenerator::getSymfonyProcess()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 51
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 5.0909

Importance

Changes 0
Metric Value
eloc 26
dl 0
loc 51
ccs 22
cts 26
cp 0.8462
rs 9.1928
c 0
b 0
f 0
cc 5
nc 9
nop 4
crap 5.0909

How to fix   Long Method   

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
declare(strict_types=1);
4
5
namespace Soluble\MediaTools\Video;
6
7
use Psr\Log\LoggerInterface;
8
use Psr\Log\LogLevel;
9
use Psr\Log\NullLogger;
10
use Soluble\MediaTools\Common\Assert\PathAssertionsTrait;
11
use Soluble\MediaTools\Common\Exception\FileNotFoundException;
12
use Soluble\MediaTools\Common\Exception\FileNotReadableException;
13
use Soluble\MediaTools\Common\Exception\UnsupportedParamException;
14
use Soluble\MediaTools\Common\Exception\UnsupportedParamValueException;
15
use Soluble\MediaTools\Common\Process\ProcessFactory;
16
use Soluble\MediaTools\Common\Process\ProcessParamsInterface;
17
use Soluble\MediaTools\Video\Config\FFMpegConfigInterface;
18
use Soluble\MediaTools\Video\Exception\ConverterExceptionInterface;
19
use Soluble\MediaTools\Video\Exception\ConverterProcessExceptionInterface;
20
use Soluble\MediaTools\Video\Exception\InvalidParamReaderException;
21
use Soluble\MediaTools\Video\Exception\MissingInputFileException;
22
use Soluble\MediaTools\Video\Exception\MissingTimeException;
23
use Soluble\MediaTools\Video\Exception\ProcessFailedException;
24
use Soluble\MediaTools\Video\Exception\ProcessSignaledException;
25
use Soluble\MediaTools\Video\Exception\ProcessTimedOutException;
26
use Soluble\MediaTools\Video\Exception\RuntimeReaderException;
27
use Symfony\Component\Process\Exception as SPException;
28
use Symfony\Component\Process\Process;
29
30
class VideoThumbGenerator implements VideoThumbGeneratorInterface
31
{
32
    public const DEFAULT_QUALITY_SCALE = 2;
33
34
    use PathAssertionsTrait;
35
36
    /** @var FFMpegConfigInterface */
37
    protected $ffmpegConfig;
38
39
    /** @var int */
40
    protected $defaultQualityScale;
41
42
    /** @var LoggerInterface|NullLogger */
43
    protected $logger;
44
45 7
    public function __construct(FFMpegConfigInterface $ffmpegConfig, ?LoggerInterface $logger = null, int $defaultQualityScale = self::DEFAULT_QUALITY_SCALE)
46
    {
47 7
        $this->ffmpegConfig        = $ffmpegConfig;
48 7
        $this->defaultQualityScale = $defaultQualityScale;
49 7
        $this->logger              = $logger ?? new NullLogger();
50 7
    }
51
52
    /**
53
     * Return ready-to-run symfony process object that you can use
54
     * to `run()` or `start()` programmatically. Useful if you want
55
     * handle the process your way...
56
     *
57
     * @see https://symfony.com/doc/current/components/process.html
58
     *
59
     * @throws UnsupportedParamException
60
     * @throws UnsupportedParamValueException
61
     * @throws MissingTimeException
62
     */
63 4
    public function getSymfonyProcess(string $videoFile, string $thumbnailFile, VideoThumbParamsInterface $thumbParams, ?ProcessParamsInterface $processParams = null): Process
64
    {
65 4
        $adapter = $this->ffmpegConfig->getAdapter();
66
67 4
        if (!$thumbParams->hasParam(VideoThumbParamsInterface::PARAM_SEEK_TIME)) {
68
            throw new MissingTimeException('Missing seekTime parameter');
69
        }
70
71
        // TIME params are separated from the rest, so we can inject them
72
        // before input file
73 4
        $timeParams = (new VideoConvertParams())->withSeekStart(
74 4
            $thumbParams->getParam(VideoThumbParamsInterface::PARAM_SEEK_TIME)
75
        );
76
77 4
        $conversionParams = (new VideoConvertParams());
78
79 4
        if ($adapter->getDefaultThreads() !== null) {
80
            $conversionParams->withThreads($adapter->getDefaultThreads());
81
        }
82
83
        // Only one frame
84 4
        $conversionParams = $conversionParams->withVideoFrames(1);
85
86 4
        if ($thumbParams->hasParam(VideoThumbParamsInterface::PARAM_VIDEO_FILTER)) {
87 2
            $conversionParams = $conversionParams->withVideoFilter(
88 2
                $thumbParams->getParam(VideoThumbParamsInterface::PARAM_VIDEO_FILTER)
89
            );
90
        }
91
92 4
        if ($thumbParams->hasParam(VideoThumbParamsInterface::PARAM_QUALITY_SCALE)) {
93
            $conversionParams = $conversionParams->withVideoQualityScale(
94
                $thumbParams->getParam(VideoThumbParamsInterface::PARAM_QUALITY_SCALE)
95
            );
96
        } else {
97 4
            $conversionParams = $conversionParams->withVideoQualityScale(
98 4
                $this->defaultQualityScale
99
            );
100
        }
101
102 4
        $arguments = $adapter->getMappedConversionParams($conversionParams);
103
104 4
        $ffmpegCmd = $adapter->getCliCommand(
105 4
            $arguments,
106 4
            $videoFile,
107 4
            $thumbnailFile,
108 4
            $adapter->getMappedConversionParams($timeParams)
109
        );
110
111 4
        $pp = $processParams ?? $this->ffmpegConfig->getProcessParams();
112
113 4
        return (new ProcessFactory($ffmpegCmd, $pp))();
114
    }
115
116
    /**
117
     * @throws ConverterExceptionInterface        Base exception class for conversion exceptions
118
     * @throws ConverterProcessExceptionInterface Base exception class for process conversion exceptions
119
     * @throws MissingInputFileException
120
     * @throws MissingTimeException
121
     * @throws ProcessTimedOutException
122
     * @throws ProcessFailedException
123
     * @throws ProcessSignaledException
124
     * @throws RuntimeReaderException
125
     * @throws InvalidParamReaderException
126
     */
127 7
    public function makeThumbnail(string $videoFile, string $thumbnailFile, VideoThumbParamsInterface $thumbParams, ?callable $callback = null, ?ProcessParamsInterface $processParams = null): void
128
    {
129
        try {
130
            try {
131 7
                $this->ensureFileReadable($videoFile);
132
133 4
                $process = $this->getSymfonyProcess($videoFile, $thumbnailFile, $thumbParams, $processParams);
134 4
                $process->mustRun($callback);
135 5
            } catch (FileNotFoundException | FileNotReadableException $e) {
136 3
                throw new MissingInputFileException($e->getMessage());
137 2
            } catch (UnsupportedParamValueException | UnsupportedParamException $e) {
138
                throw new InvalidParamReaderException($e->getMessage());
139 2
            } catch (SPException\ProcessTimedOutException $e) {
140 1
                throw new ProcessTimedOutException($e->getProcess(), $e);
141 1
            } catch (SPException\ProcessSignaledException $e) {
142
                throw new ProcessSignaledException($e->getProcess(), $e);
143 1
            } catch (SPException\ProcessFailedException $e) {
144 1
                throw new ProcessFailedException($e->getProcess(), $e);
145
            } catch (SPException\RuntimeException $e) {
146 2
                throw new RuntimeReaderException($e->getMessage());
147
            }
148 5
        } catch (\Throwable $e) {
149 5
            $exceptionNs = explode('\\', get_class($e));
150 5
            $this->logger->log(
151 5
                ($e instanceof MissingInputFileException) ? LogLevel::WARNING : LogLevel::ERROR,
152 5
                sprintf(
153 5
                    'Video thumbnailing failed \'%s\' with \'%s\'. "%s(%s, %s,...)"',
154 5
                    $exceptionNs[count($exceptionNs) - 1],
155 5
                    __METHOD__,
156 5
                    $e->getMessage(),
157 5
                    $videoFile,
158 5
                    $thumbnailFile
159
                )
160
            );
161 5
            throw $e;
162
        }
163 2
    }
164
}
165