Passed
Push — master ( ff4791...e25d55 )
by Sébastien
07:48
created

ConvertDirCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @see       https://github.com/soluble-io/soluble-mediatools-cli 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-cli/blob/master/LICENSE.md MIT
10
 */
11
12
namespace Soluble\MediaTools\Cli\Command;
13
14
use Soluble\MediaTools\Cli\FileSystem\DirectoryScanner;
15
use Soluble\MediaTools\Cli\Media\FileExtensions;
16
use Soluble\MediaTools\Cli\Media\MediaScanner;
17
use Soluble\MediaTools\Cli\Service\MediaToolsServiceInterface;
18
use Soluble\MediaTools\Common\Exception\ProcessException;
19
use Soluble\MediaTools\Preset\PresetInterface;
20
use Soluble\MediaTools\Preset\PresetLoader;
21
use Symfony\Component\Console\Command\Command;
22
use Symfony\Component\Console\Helper\ProgressBar;
23
use Symfony\Component\Console\Input\InputDefinition;
24
use Symfony\Component\Console\Input\InputInterface;
25
use Symfony\Component\Console\Input\InputOption;
26
use Symfony\Component\Console\Output\OutputInterface;
27
use Symfony\Component\Console\Question\ConfirmationQuestion;
28
use Webmozart\Assert\Assert;
29
30
class ConvertDirCommand extends Command
31
{
32
    /** @var MediaToolsServiceInterface */
33
    private $mediaTools;
34
35
    /** @var PresetLoader */
36
    private $presetLoader;
37
38
    /** @var string[] */
39
    private $supportedVideoExtensions;
40
41 2
    public function __construct(MediaToolsServiceInterface $mediaTools, PresetLoader $presetLoader)
42
    {
43 2
        $this->mediaTools               = $mediaTools;
44 2
        $this->presetLoader             = $presetLoader;
45 2
        $this->supportedVideoExtensions = (new FileExtensions())->getMediaExtensions();
46 2
        parent::__construct();
47 2
    }
48
49 2
    protected function configure(): void
50
    {
51
        $this
52 2
            ->setName('convert:directory')
53 2
            ->setDescription('Convert all media files in a directory using a preset')
54 2
            ->setDefinition(
55 2
                new InputDefinition([
56 2
                    new InputOption('dir', ['d'], InputOption::VALUE_REQUIRED, 'Input directory to scan for medias'),
57 2
                    new InputOption('preset', ['p'], InputOption::VALUE_REQUIRED, 'Conversion preset to use'),
58 2
                    new InputOption('exts', ['e', 'extensions'], InputOption::VALUE_OPTIONAL, 'File extensions to process (ie. m4v,mp4,mov)'),
59 2
                    new InputOption('output', ['o', 'out'], InputOption::VALUE_REQUIRED, 'Output directory'),
60 2
                    new InputOption('recursive', 'r', InputOption::VALUE_NONE, 'Recursive mode'),
61
                ])
62
            );
63 2
    }
64
65 2
    protected function execute(InputInterface $input, OutputInterface $output): int
66
    {
67 2
        $helper = $this->getHelper('question');
68
69
        // ########################
70
        // Step 1: Check directory
71
        // ########################
72
73 2
        $directory = $input->getOption('dir');
74 2
        Assert::stringNotEmpty($directory);
75 1
        Assert::directory($directory);
76
77
        // ########################
78
        // Step 2: Init preset
79
        // ########################
80
81
        Assert::stringNotEmpty($input->getOption('preset'));
82
        $preset = $this->getPreset($input->getOption('preset'));
83
84
        // ########################
85
        // Step 3: Output dir
86
        // ########################
87
88
        if ($input->getOption('output') !== null) {
89
            $outputDir = $input->getOption('output');
90
            Assert::directory($outputDir);
91
            Assert::writable($outputDir);
92
        } else {
93
            $outputDir = $directory;
94
        }
95
        Assert::stringNotEmpty($outputDir);
96
97
        // ########################
98
        // Step 4: Extensions
99
        // ########################
100
101
        if ($input->getOption('exts') !== null) {
102
            $tmp = $input->getOption('exts');
103
            Assert::stringNotEmpty($tmp);
104
            $exts = array_filter(
105
                array_map(
106
                    'trim',
107
                    explode(',', $tmp)
0 ignored issues
show
Bug introduced by
It seems like $tmp can also be of type string[]; however, parameter $string of explode() 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

107
                    explode(',', /** @scrutinizer ignore-type */ $tmp)
Loading history...
108
                )
109
            );
110
            Assert::minCount($exts, 1);
111
        } else {
112
            $exts = FileExtensions::BUILTIN_EXTENSIONS;
113
        }
114
115
        $recursive = $input->getOption('recursive') === true;
116
117
        // ########################
118
        // Step 5: Scanning dir
119
        // ########################
120
121
        $output->writeln(sprintf('* Scanning %s for media files...', $directory));
0 ignored issues
show
Bug introduced by
It seems like $directory can also be of type string[]; however, parameter $args of sprintf() 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

121
        $output->writeln(sprintf('* Scanning %s for media files...', /** @scrutinizer ignore-type */ $directory));
Loading history...
122
123
        // Get the videos in path
124
        $files = (new DirectoryScanner())->findFiles($directory, $exts, $recursive);
125
126
        $output->writeln('* Reading metadata...');
127
128
        $progressBar = new ProgressBar($output, count($files));
129
        $progressBar->start();
130
131
        $medias = (new MediaScanner($this->mediaTools->getReader()))->getMedias($files, function () use ($progressBar) {
132
            $progressBar->advance();
133
        });
134
135
        $progressBar->finish();
136
137
        $err = 'frame=  991 fps= 46 q=1.0 size=  588032kB time=00:00:33.04 bitrate=145774.2kbits/s speed=1.53x';
0 ignored issues
show
Unused Code introduced by
The assignment to $err is dead and can be removed.
Loading history...
138
139
        // Ask confirmation
140
        ScanCommand::renderMediaInTable($output, $medias['rows'], $medias['totalSize']);
141
142
        $question = new ConfirmationQuestion('Convert files ?', false);
143
144
        if (!$helper->ask($input, $output, $question)) {
145
            return 0;
146
        }
147
148
        $converter = $this->mediaTools->getConverter();
149
150
        /** @var \SplFileInfo $file */
151
        foreach ($files as $file) {
152
            try {
153
                $params = $preset->getParams($file->getPathname());
154
155
                $outputFile = sprintf(
156
                    '%s/%s%s',
157
                    $outputDir,
158
                    $file->getBasename($file->getExtension()),
159
                    $preset->getFileExtension()
160
                );
161
162
                $tmpFile = sprintf('%s.tmp', $outputFile);
163
164
                if (realpath($outputFile) === realpath((string) $file)) {
165
                    throw new \RuntimeException(sprintf('Conversion error, input and output files are the same: %s', $outputFile));
166
                }
167
168
                if (!file_exists($outputFile)) {
169
                    $converter->convert((string) $file, $tmpFile, $params, function ($stdOut, $stdErr) use ($output): void {
170
                        // frame=  991 fps= 46 q=1.0 size=  588032kB time=00:00:33.04 bitrate=145774.2kbits/s speed=1.53x
171
                        $output->write($stdErr);
172
                    });
173
                    $success = rename($tmpFile, $outputFile);
174
                    if (!$success) {
175
                        throw new \RuntimeException(sprintf(
176
                            'Cannot rename temp file %s to %s',
177
                            basename($tmpFile),
178
                            $outputFile
179
                        ));
180
                    }
181
182
                    $output->writeln(sprintf('<fg=green>- Converted:</> %s.', $file));
183
                } else {
184
                    $output->writeln(sprintf('<fg=yellow>- Skipped:</> %s : Output file already exists.', $file));
185
                }
186
            } catch (ProcessException $e) {
187
                $output->writeln(sprintf('<fg=red>- Skipped:</> %s : Not a valid media file.', $file));
188
            }
189
190
            //$progressBar->advance();
191
        }
192
193
        $progressBar->finish();
194
        $output->writeln('');
195
196
        return 0;
197
    }
198
199
    private function grepFrame(string $line): int
0 ignored issues
show
Unused Code introduced by
The method grepFrame() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
Unused Code introduced by
The parameter $line is not used and could be removed. ( Ignorable by Annotation )

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

199
    private function grepFrame(/** @scrutinizer ignore-unused */ string $line): int

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
200
    {
201
        return 1;
202
    }
203
204
    private function getPreset(string $presetName): PresetInterface
205
    {
206
        return $this->presetLoader->getPreset($presetName);
207
    }
208
}
209