Passed
Push — master ( eb1d3e...b38e7e )
by Sébastien
02:22
created

VideoInfoReader::getInfo()   B

Complexity

Conditions 6
Paths 17

Size

Total Lines 32
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 6.0029

Importance

Changes 0
Metric Value
cc 6
eloc 24
nc 17
nop 1
dl 0
loc 32
ccs 22
cts 23
cp 0.9565
crap 6.0029
rs 8.9137
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @see       https://github.com/soluble-io/soluble-mediatools for the canonical repository
7
 *
8
 * @copyright Copyright (c) 2018-2019 Sébastien Vanvelthem. (https://github.com/belgattitude)
9
 * @license   https://github.com/soluble-io/soluble-mediatools/blob/master/LICENSE.md MIT
10
 */
11
12
namespace Soluble\MediaTools\Video;
13
14
use Psr\Log\LoggerInterface;
15
use Psr\Log\LogLevel;
16
use Psr\Log\NullLogger;
17
use Soluble\MediaTools\Common\Assert\PathAssertionsTrait;
18
use Soluble\MediaTools\Common\Exception\FileNotFoundException;
19
use Soluble\MediaTools\Common\Exception\FileNotReadableException;
20
use Soluble\MediaTools\Common\Process\ProcessFactory;
21
use Soluble\MediaTools\Common\Process\ProcessParamsInterface;
22
use Soluble\MediaTools\Video\Config\FFProbeConfigInterface;
23
use Soluble\MediaTools\Video\Exception\InfoProcessReaderExceptionInterface;
24
use Soluble\MediaTools\Video\Exception\InfoReaderExceptionInterface;
25
use Soluble\MediaTools\Video\Exception\MissingInputFileException;
26
use Soluble\MediaTools\Video\Exception\ProcessFailedException;
27
use Soluble\MediaTools\Video\Exception\RuntimeReaderException;
28
use Symfony\Component\Process\Exception as SPException;
29
use Symfony\Component\Process\Process;
30
31
class VideoInfoReader implements VideoInfoReaderInterface
32
{
33
    use PathAssertionsTrait;
34
35
    /** @var FFProbeConfigInterface */
36
    protected $ffprobeConfig;
37
38
    /** @var LoggerInterface */
39
    protected $logger;
40
41 12
    public function __construct(FFProbeConfigInterface $ffProbeConfig, ?LoggerInterface $logger = null)
42
    {
43 12
        $this->ffprobeConfig = $ffProbeConfig;
44 12
        $this->logger        = $logger ?? new NullLogger();
45 12
    }
46
47
    /**
48
     * Return ready-to-run symfony process object that you can use
49
     * to `run()` or `start()` programmatically. Useful if you want to make
50
     * things your way...
51
     *
52
     * @see https://symfony.com/doc/current/components/process.html
53
     */
54 4
    public function getSymfonyProcess(string $inputFile, ?ProcessParamsInterface $processParams = null): Process
55
    {
56
        $ffprobeCmd = [
57 4
            $this->ffprobeConfig->getBinary(),
58 4
            '-v',
59 4
            'quiet',
60 4
            '-print_format',
61 4
            'json',
62 4
            '-show_format',
63 4
            '-show_streams',
64 4
            '-i',
65 4
            $inputFile,
66
        ];
67
68 4
        $pp = $processParams ?? $this->ffprobeConfig->getProcessParams();
69
70 4
        return (new ProcessFactory($ffprobeCmd, $pp))();
71
    }
72
73
    /**
74
     * @throws InfoReaderExceptionInterface
75
     * @throws InfoProcessReaderExceptionInterface
76
     * @throws ProcessFailedException
77
     * @throws MissingInputFileException
78
     * @throws RuntimeReaderException
79
     */
80 6
    public function getInfo(string $file): VideoInfo
81
    {
82
        try {
83
            try {
84 6
                $this->ensureFileReadable($file);
85 4
                $process = $this->getSymfonyProcess($file);
86
87 4
                $process->mustRun();
88 3
                $output = $process->getOutput();
89 3
            } catch (FileNotFoundException | FileNotReadableException $e) {
90 2
                throw new MissingInputFileException($e->getMessage());
91 1
            } catch (SPException\ProcessFailedException | SPException\ProcessTimedOutException | SPException\ProcessSignaledException $e) {
92 1
                throw new ProcessFailedException($e->getProcess(), $e);
93
            } catch (SPException\RuntimeException $e) {
94 3
                throw new RuntimeReaderException($e->getMessage());
95
            }
96 3
        } catch (\Throwable $e) {
97 3
            $exceptionNs = explode('\\', get_class($e));
98 3
            $this->logger->log(
99 3
                ($e instanceof MissingInputFileException) ? LogLevel::WARNING : LogLevel::ERROR,
100 3
                sprintf(
101 3
                    'Video info retrieval failed \'%s\' with \'%s\'. "%s(%s)"',
102 3
                    $exceptionNs[count($exceptionNs) - 1],
103 3
                    __METHOD__,
104 3
                    $e->getMessage(),
105 3
                    $file
106
                )
107
            );
108 3
            throw $e;
109
        }
110
111 3
        return VideoInfo::createFromFFProbeJson($file, $output);
112
    }
113
}
114