Passed
Push — master ( 8c546c...90a0e3 )
by Sébastien
02:01
created

ScanCommand::renderResultsTable()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 48
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 5.0014

Importance

Changes 0
Metric Value
cc 5
eloc 31
nc 8
nop 4
dl 0
loc 48
ccs 25
cts 26
cp 0.9615
crap 5.0014
rs 9.1128
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-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\MissingFFProbeBinaryException;
16
use Soluble\MediaTools\Video\Exception as VideoException;
17
use Soluble\MediaTools\Video\SeekTime;
18
use Soluble\MediaTools\Video\VideoInfoReaderInterface;
19
use Symfony\Component\Console\Command\Command;
20
use Symfony\Component\Console\Helper\ProgressBar;
21
use Symfony\Component\Console\Helper\Table;
22
use Symfony\Component\Console\Helper\TableCell;
23
use Symfony\Component\Console\Helper\TableSeparator;
24
use Symfony\Component\Console\Helper\TableStyle;
25
use Symfony\Component\Console\Input\InputDefinition;
26
use Symfony\Component\Console\Input\InputInterface;
27
use Symfony\Component\Console\Input\InputOption;
28
use Symfony\Component\Console\Output\OutputInterface;
29
use Symfony\Component\Finder\Finder;
30
31
class ScanCommand extends Command
32
{
33
    /** @var VideoInfoReaderInterface */
34
    private $reader;
35
36
    /** @var string[] */
37
    private $supportedVideoExtensions = [
38
        'mov',
39
        'mp4',
40
        'm4v',
41
        'mkv',
42
        'flv',
43
        'webm',
44
    ];
45
46 3
    public function __construct(VideoInfoReaderInterface $videoInfoReader)
47
    {
48 3
        $this->reader = $videoInfoReader;
49 3
        parent::__construct();
50 3
    }
51
52
    /**
53
     * Configures the command.
54
     */
55 3
    protected function configure(): void
56
    {
57
        $this
58 3
            ->setName('scan:videos')
59 3
            ->setDescription('Scan for video')
60 3
            ->setDefinition(
61 3
                new InputDefinition([
62 3
                    new InputOption('dir', 'd', InputOption::VALUE_REQUIRED),
63
                ])
64
            );
65 3
    }
66
67 3
    protected function execute(InputInterface $input, OutputInterface $output): int
68
    {
69 3
        if (!$input->hasOption('dir')) {
70
            throw new \InvalidArgumentException('Missing dir argument, use <command> <dir>');
71
        }
72 3
        $videoPath = $input->hasOption('dir') ? $input->getOption('dir') : null;
73 3
        if (!is_string($videoPath) || !is_dir($videoPath)) {
74 1
            throw new \InvalidArgumentException(sprintf(
75 1
                'Video dir %s does not exists',
76 1
                is_string($videoPath) ? $videoPath : json_encode($videoPath)
77
            ));
78
        }
79
80 2
        $output->writeln(sprintf('* Scanning %s for files...', $videoPath));
81
82
        // Get the videos in path
83 2
        $videos = $this->getVideoFiles($videoPath);
84
85 2
        $output->writeln('* Reading metadata...');
86
87 2
        $progressBar = new ProgressBar($output, count($videos));
88 2
        $progressBar->start();
89
90 2
        $bitRateFormatter = new ByteFormatter();
91 2
        $sizeFormatter = new ByteFormatter();
92
93 2
        $rows      = [];
94 2
        $warnings  = [];
95 2
        $totalSize = 0;
96
97
        /** @var \SplFileInfo $video */
98 2
        foreach ($videos as $video) {
99 2
            $videoFile = $video->getPathname();
100
            try {
101 2
                $info     = $this->reader->getInfo($videoFile);
102 1
                $vStream  = $info->getVideoStreams()->getFirst();
103 1
                $pixFmt   = $vStream->getPixFmt();
104 1
                $bitRate  = $vStream->getBitRate();
105 1
                $fileSize = (int) filesize($videoFile);
106
                $row      = [
107 1
                    $video,
108 1
                    preg_replace('/\.([0-9])+$/', '', SeekTime::convertSecondsToHMSs(round($info->getDuration(), 1))),
109 1
                    $vStream->getCodecName(),
110 1
                    sprintf('%sx%s', $vStream->getWidth(), $vStream->getHeight()),
111 1
                    ($bitRate > 0 ? $bitRateFormatter->format((int) $bitRate) . '/s' : ''),
112 1
                    $sizeFormatter->format($fileSize),
113 1
                    $pixFmt,
114
                ];
115 1
                $rows[] = $row;
116 1
                $totalSize += $fileSize;
117 2
            } catch (VideoException\MissingFFProbeBinaryException $e) {
118 1
                throw new MissingFFProbeBinaryException('Unable to run ffprobe binary, check your config and ensure it\'s properly installed');
119 1
            } catch (VideoException\InfoReaderExceptionInterface $e) {
120 1
                $warnings[] = [$videoFile];
121
            }
122
123 1
            $progressBar->advance();
124
        }
125 1
        $progressBar->finish();
126
127 1
        $output->writeln('');
128 1
        $output->writeln('* Available media files:');
129
130 1
        $this->renderResultsTable($output, $rows, $totalSize);
131
132
        // display warnings
133 1
        if (count($warnings) > 0) {
134 1
            $output->writeln('* The following files were not detected as valid medias:');
135 1
            $table = new Table($output);
136 1
            $table->setHeaders([
137 1
                'Unsupported files',
138
            ]);
139 1
            $table->setStyle('box-double');
140 1
            $table->setRows($warnings);
141 1
            $table->render();
142
        }
143
144 1
        $output->writeln("");
145
146 1
        return 0;
147
    }
148
149 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

149
    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...
150
    {
151 1
        $sizeFormatter = new ByteFormatter();
152
153 1
        $table = new Table($output);
154
155 1
        $rightAlignstyle = new TableStyle();
156 1
        $rightAlignstyle->setPadType(STR_PAD_LEFT);
157
158
        //$table->setStyle($tableStyle);
159 1
        $table->setStyle('box');
160 1
        $table->setHeaders([
161 1
            'file',
162
            'duration',
163
            'codec',
164
            'size',
165
            'bitrate',
166
            'filesize',
167
            'pix_fmt',
168
        ]);
169
170 1
        foreach ($colIndexes = [1, 2, 3, 4, 5, 6] as $idx) {
0 ignored issues
show
Unused Code introduced by
The assignment to $colIndexes is dead and can be removed.
Loading history...
171 1
            $table->setColumnStyle($idx, $rightAlignstyle);
172
        }
173
174 1
        $previousPath = null;
175 1
        $first        = true;
176
177 1
        foreach ($rows as $row) {
178
            /** @var \SplFileInfo $file */
179 1
            $file   = $row[0];
180 1
            $row[0] = $file->getBasename();
181 1
            if ($previousPath !== $file->getPath()) {
182 1
                if (!$first) {
183
                    $table->addRow(new TableSeparator(['colspan' => count($row)]));
184
                }
185 1
                $table->addRow([new TableCell(sprintf('<fg=yellow>%s</>', $file->getPath()), ['colspan' => count($row)])]);
186 1
                $table->addRow(new TableSeparator(['colspan' => count($row)]));
187 1
                $previousPath = $file->getPath();
188
            }
189 1
            $table->addRow($row);
190 1
            $first = false;
191
        }
192
193 1
        $table->addRow(new TableSeparator());
194 1
        $table->addRow(['<fg=cyan>Total</>', '', '', '', '', sprintf('<fg=cyan>%s</>', $sizeFormatter->format($totalSize))]);
195
196 1
        $table->render();
197
198 1
    }
199
200
    /**
201
     * @param string $videoPath
202
     *
203
     * @return array<\SplFileInfo>
204
     */
205 2
    public function getVideoFiles(string $videoPath): array
206
    {
207 2
        $files = (new Finder())->files()
208 2
            ->in($videoPath)
209 2
            ->ignoreUnreadableDirs()
210 2
            ->name(sprintf(
211 2
                '/\.(%s)$/',
212 2
                implode('|', $this->supportedVideoExtensions)
213
            ));
214
215 2
        $videos = [];
216
217
        /** @var \SplFileInfo $file */
218 2
        foreach ($files as $file) {
219 2
            $videos[] = $file;
220
        }
221
222 2
        return $videos;
223
    }
224
}
225