Completed
Push — master ( 494bdc...a919b1 )
by Sébastien
13:15
created

ScanCommand::getMedias()   B

Complexity

Conditions 7
Paths 36

Size

Total Lines 53
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 37
CRAP Score 7

Importance

Changes 0
Metric Value
eloc 39
dl 0
loc 53
ccs 37
cts 37
cp 1
rs 8.3626
c 0
b 0
f 0
cc 7
nc 36
nop 3
crap 7

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
/**
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 ScriptFUSION\Byte\ByteFormatter;
15
use Soluble\MediaTools\Cli\Exception\InvalidArgumentException;
16
use Soluble\MediaTools\Cli\Exception\MissingFFProbeBinaryException;
17
use Soluble\MediaTools\Cli\FileSystem\DirectoryScanner;
18
use Soluble\MediaTools\Cli\Media\FileExtensions;
19
use Soluble\MediaTools\Video\Exception as VideoException;
20
use Soluble\MediaTools\Video\SeekTime;
21
use Soluble\MediaTools\Video\VideoInfoReaderInterface;
22
use SplFileInfo;
23
use Symfony\Component\Console\Command\Command;
24
use Symfony\Component\Console\Helper\ProgressBar;
25
use Symfony\Component\Console\Helper\Table;
26
use Symfony\Component\Console\Helper\TableCell;
27
use Symfony\Component\Console\Helper\TableSeparator;
28
use Symfony\Component\Console\Helper\TableStyle;
29
use Symfony\Component\Console\Input\InputDefinition;
30
use Symfony\Component\Console\Input\InputInterface;
31
use Symfony\Component\Console\Input\InputOption;
32
use Symfony\Component\Console\Output\OutputInterface;
33
34
class ScanCommand extends Command
35
{
36
    /** @var VideoInfoReaderInterface */
37
    private $reader;
38
39
    /** @var string[] */
40
    private $supportedVideoExtensions;
41
42 4
    public function __construct(VideoInfoReaderInterface $videoInfoReader)
43
    {
44 4
        $this->reader                   = $videoInfoReader;
45 4
        $this->supportedVideoExtensions = (new FileExtensions())->getMediaExtensions();
46 4
        parent::__construct();
47 4
    }
48
49
    /**
50
     * Configures the command.
51
     */
52 4
    protected function configure(): void
53
    {
54
        $this
55 4
            ->setName('scan:videos')
56 4
            ->setDescription('Scan for video')
57 4
            ->setDefinition(
58 4
                new InputDefinition([
59 4
                    new InputOption('dir', 'd', InputOption::VALUE_REQUIRED, 'Directory to scan'),
60 4
                    new InputOption('recursive', 'r', InputOption::VALUE_NONE, 'Recursive mode'),
61
                ])
62
            );
63 4
    }
64
65
    /**
66
     * @throws InvalidArgumentException
67
     */
68 4
    protected function execute(InputInterface $input, OutputInterface $output): int
69
    {
70 4
        if (!$input->hasOption('dir')) {
71
            throw new InvalidArgumentException('Missing dir argument, use <command> <dir>');
72
        }
73
74 4
        $videoPath = $input->getOption('dir');
75 4
        if (!is_string($videoPath) || !is_dir($videoPath)) {
76 2
            throw new InvalidArgumentException(sprintf(
77 2
                'Video dir %s does not exists',
78 2
                is_string($videoPath) ? $videoPath : json_encode($videoPath)
79
            ));
80
        }
81
82 2
        $recursive = $input->getOption('recursive') === true;
83
84 2
        $output->writeln(sprintf('* Scanning %s for files...', $videoPath));
85
86
        // Get the videos in path
87 2
        $videos = (new DirectoryScanner())->findFiles($videoPath, $this->supportedVideoExtensions, $recursive);
88
89 2
        $output->writeln('* Reading metadata...');
90
91 2
        $progressBar = new ProgressBar($output, count($videos));
92 2
        $progressBar->start();
93
94
        $medias = $this->getMedias($videos, $this->reader, function () use ($progressBar) {
95 1
            $progressBar->advance();
96 2
        });
97
98 1
        $progressBar->finish();
99
100 1
        $output->writeln('');
101 1
        $output->writeln('* Available media files:');
102
103 1
        $this->renderResultsTable($output, $medias['rows'], $medias['totalSize']);
104
105
        // display warnings
106
107 1
        if (count($medias['warnings']) > 0) {
108 1
            $output->writeln('* The following files were not detected as valid medias:');
109 1
            $table = new Table($output);
110 1
            $table->setHeaders([
111 1
                'Unsupported files',
112
            ]);
113 1
            $table->setStyle('box-double');
114 1
            $table->setRows($medias['warnings']);
115 1
            $table->render();
116
        }
117
118 1
        $output->writeln('');
119
120 1
        return 0;
121
    }
122
123
    /**
124
     * @param SplFileInfo[] $files
125
     */
126 2
    private function getMedias(array $files, VideoInfoReaderInterface $reader, ?callable $callback): array
127
    {
128 2
        $bitRateFormatter = new ByteFormatter();
129 2
        $sizeFormatter    = new ByteFormatter();
130
131 2
        $rows      = [];
132 2
        $warnings  = [];
133 2
        $totalSize = 0;
134
135
        /** @var SplFileInfo $file */
136 2
        foreach ($files as $file) {
137 2
            $videoFile = $file->getPathname();
138
            try {
139 2
                $info     = $reader->getInfo($videoFile);
140 1
                $vStream  = $info->getVideoStreams()->getFirst();
141 1
                $aStream  = $info->getAudioStreams()->getFirst();
142 1
                $pixFmt   = $vStream->getPixFmt();
143 1
                $bitRate  = $vStream->getBitRate();
144 1
                $fileSize = $file->getSize();
145
146 1
                $fps = (string) ($vStream->getFps(0) ?? '');
147
148
                $row = [
149 1
                    'video'      => $file,
150 1
                    'duration'   => preg_replace('/\.([0-9])+$/', '', SeekTime::convertSecondsToHMSs(round($info->getDuration(), 1))),
151 1
                    'codec'      => sprintf('%s/%s', $vStream->getCodecName(), $aStream->getCodecName()),
152 1
                    'resolution' => sprintf(
153 1
                        '%sx%s%s',
154 1
                        $vStream->getWidth(),
155 1
                        $vStream->getHeight(),
156 1
                        $fps !== '' ? " <fg=yellow>${fps}fps</>" : ''
157
                    ),
158 1
                    'bitrate' => ($bitRate > 0 ? $bitRateFormatter->format((int) $bitRate) . '/s' : ''),
159 1
                    'size'    => $sizeFormatter->format($fileSize),
160 1
                    'pixFmt'  => $pixFmt,
161
                ];
162 1
                $rows[] = $row;
163 1
                $totalSize += $fileSize;
164 2
            } catch (VideoException\MissingFFProbeBinaryException $e) {
165 1
                throw new MissingFFProbeBinaryException('Unable to run ffprobe binary, check your config and ensure it\'s properly installed');
166 1
            } catch (VideoException\InfoReaderExceptionInterface $e) {
167 1
                $warnings[] = [$videoFile];
168
            }
169
170 1
            if ($callback !== null) {
171 1
                $callback();
172
            }
173
        }
174
175
        return [
176 1
            'rows'      => $rows,
177 1
            'totalSize' => $totalSize,
178 1
            'warnings'  => $warnings
179
        ];
180
    }
181
182 1
    private function renderResultsTable(OutputInterface $output, array $rows, int $totalSize, array $columns = []): void
0 ignored issues
show
Unused Code introduced by
The parameter $columns 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

182
    private function renderResultsTable(OutputInterface $output, array $rows, int $totalSize, /** @scrutinizer ignore-unused */ array $columns = []): void

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...
183
    {
184 1
        $sizeFormatter = new ByteFormatter();
185
186 1
        $table = new Table($output);
187
188 1
        $rightAlignstyle = new TableStyle();
189 1
        $rightAlignstyle->setPadType(STR_PAD_LEFT);
190
191
        //$table->setStyle($tableStyle);
192 1
        $table->setStyle('box');
193 1
        $table->setHeaders([
194 1
            'file',
195
            'duration',
196
            'codec',
197
            'size',
198
            'bitrate',
199
            'filesize',
200
            'pix_fmt',
201
        ]);
202
203 1
        foreach ($colIndexes = [1, 2, 3, 4, 5] as $idx) {
0 ignored issues
show
Unused Code introduced by
The assignment to $colIndexes is dead and can be removed.
Loading history...
204 1
            $table->setColumnStyle($idx, $rightAlignstyle);
205
        }
206
207 1
        $previousPath = null;
208 1
        $first        = true;
209
210 1
        foreach ($rows as $idx => $row) {
211
            /** @var \SplFileInfo $file */
212 1
            $file         = $row['video'];
213 1
            $fileName     = $file->getBasename();
214 1
            $fileName     = mb_strlen($fileName) > 30 ? mb_substr($file->getBasename(), 0, 30) . '[...].' . $file->getExtension() : $fileName;
215 1
            $row['video'] = $fileName;
216 1
            if ($previousPath !== $file->getPath()) {
217 1
                if (!$first) {
218
                    $table->addRow(new TableSeparator(['colspan' => count($row)]));
219
                }
220 1
                $table->addRow([new TableCell(sprintf('<fg=yellow>%s</>', $file->getPath()), ['colspan' => count($row)])]);
221 1
                $table->addRow(new TableSeparator(['colspan' => count($row)]));
222 1
                $previousPath = $file->getPath();
223
            }
224 1
            $table->addRow($row);
225 1
            $first = false;
226
        }
227
228 1
        $table->addRow(new TableSeparator());
229 1
        $table->addRow(['<fg=cyan>Total</>', '', '', '', '', sprintf('<fg=cyan>%s</>', $sizeFormatter->format($totalSize))]);
230
231 1
        $table->render();
232 1
    }
233
}
234