HLS   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 118
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 58
c 1
b 0
f 0
dl 0
loc 118
rs 10
wmc 11

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A generatePlaylist() 0 6 1
A export() 0 19 3
A makeRepresentationDirectory() 0 15 2
A format() 0 14 1
A processRepresentation() 0 18 2
A getStreamInfo() 0 16 1
1
<?php
2
3
namespace Mostafaznv\Larupload\Storage\FFMpeg;
4
5
6
use FFMpeg\Format\Video\X264;
7
use Illuminate\Support\Collection;
8
use Illuminate\Support\Facades\File;
9
use Mostafaznv\Larupload\DTOs\FFMpeg\FFMpegStreamRepresentation;
10
use Mostafaznv\Larupload\DTOs\Style\StreamStyle;
11
12
class HLS
13
{
14
    private const MASTER_FILE_NAME = 'master.m3u8';
15
    private const PLAYLIST_START   = '#EXTM3U';
16
    private const PLAYLIST_END     = '#EXT-X-ENDLIST';
17
18
    private bool  $driverIsLocal;
19
    private array $playlist;
20
21
22
    public function __construct(private readonly FFMpeg $ffmpeg, private readonly string $disk)
23
    {
24
        $this->driverIsLocal = disk_driver_is_local($this->disk);
25
        $this->playlist = [
26
            self::PLAYLIST_START
27
        ];
28
    }
29
30
31
    public function export(array $styles, string $basePath, string $fileName): bool
32
    {
33
        $saveTo = get_larupload_save_path($this->disk, $basePath);
34
35
        foreach ($styles as $style) {
36
            $representation = $this->processRepresentation($style, $saveTo['local']);
37
            $this->playlist[] = $this->getStreamInfo($representation);
38
        }
39
40
        $res = $this->generatePlaylist($saveTo['local'], $fileName);
41
42
        if ($res) {
43
            larupload_finalize_save($this->disk, $saveTo, true);
44
45
            return true;
46
        }
47
48
        // @codeCoverageIgnoreStart
49
        return false;
50
        // @codeCoverageIgnoreEnd
51
    }
52
53
    private function processRepresentation(StreamStyle $style, string $path): FFMpegStreamRepresentation
54
    {
55
        $ffmpeg = $this->ffmpeg->clone(true);
56
57
        $m3u8ListName = "$style->name-list.m3u8";
58
        $streamBasePath = $this->makeRepresentationDirectory($path, $style->name);
59
        $saveTo = "$streamBasePath/$m3u8ListName";
60
61
62
        $format = $this->format($style, $streamBasePath);
63
64
        $style->mode->ffmpegResizeFilter()
65
            ? $ffmpeg->resize($style)
66
            : $ffmpeg->crop($style);
67
68
        $ffmpeg->getMedia()->save($format, $saveTo);
69
70
        return FFMpegStreamRepresentation::make($style->name, $streamBasePath, $m3u8ListName);
71
    }
72
73
    private function format(StreamStyle $style, string $path): X264
74
    {
75
        $format = $style->format;
76
        $format->setAdditionalParameters([
77
            '-sc_threshold', '0',
78
            '-g', '60',
79
            '-hls_playlist_type', 'vod',
80
            '-hls_time', '10',
81
            '-hls_list_size', '0',
82
            '-hls_segment_filename', "$path/$style->name-%d.ts",
83
            '-master_pl_name', self::MASTER_FILE_NAME,
84
        ]);
85
86
        return $format;
87
    }
88
89
    private function getStreamInfo(FFMpegStreamRepresentation $representation): string
90
    {
91
        $fileName = $representation->path . '/' . self::MASTER_FILE_NAME;
92
        $file = file_get_contents($fileName);
93
94
        $lines = preg_split('/\n|\r\n?/', $file);
95
        $lines = Collection::make($lines)->filter();
0 ignored issues
show
Bug introduced by
$lines of type string[] is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::make(). ( Ignorable by Annotation )

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

95
        $lines = Collection::make(/** @scrutinizer ignore-type */ $lines)->filter();
Loading history...
96
97
        $list = "$representation->name/$representation->listName";
98
        $info = $lines->get(
99
            $lines->search($representation->listName) - 1
100
        );
101
102
        $info = $info . ',NAME="' . $representation->name . '"';
103
104
        return implode(PHP_EOL, [$info, $list]);
105
    }
106
107
    private function generatePlaylist(string $path, string $filename): bool
108
    {
109
        $this->playlist[] = self::PLAYLIST_END;
110
        $playlist = implode(PHP_EOL, $this->playlist);
111
112
        return File::put("$path/$filename", $playlist);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Illuminate\Suppor...'.$filename, $playlist) could return the type integer which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
113
    }
114
115
    private function makeRepresentationDirectory(string $path, string $folder): string
116
    {
117
        $directory = "$path/$folder";
118
119
        if ($this->driverIsLocal) {
120
            File::makeDirectory($directory);
121
        }
122
        else {
123
            @mkdir(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

123
            /** @scrutinizer ignore-unhandled */ @mkdir(

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
124
                directory: $directory,
125
                recursive: true
126
            );
127
        }
128
129
        return $directory;
130
    }
131
}
132