Passed
Pull Request — master (#262)
by Pascal
03:06
created

DynamicHLSPlaylist::parseLines()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace ProtoneMedia\LaravelFFMpeg\Http;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Support\Str;
7
use ProtoneMedia\LaravelFFMpeg\Filesystem\Disk;
8
use ProtoneMedia\LaravelFFMpeg\Filesystem\Media;
9
10
class DynamicHLSPlaylist
11
{
12
    /**
13
     * @var \ProtoneMedia\LaravelFFMpeg\Filesystem\Disk
14
     */
15
    private $disk;
16
17
    /**
18
     * @var \ProtoneMedia\LaravelFFMpeg\Filesystem\Media
19
     */
20
    private $media;
21
22
    /**
23
     * Callable to retrieve the path to the given key.
24
     *
25
     * @var callable
26
     */
27
    private $keyResolver;
28
29
    /**
30
     * Callable to retrieve the path to the given media.
31
     *
32
     * @var callable
33
     */
34
    private $mediaResolver;
35
36
    /**
37
     * @var array
38
     */
39
    private $keyCache = [];
40
41
    /**
42
     * @var array
43
     */
44
    private $mediaCache = [];
45
46
    /**
47
     * Uses the 'filesystems.default' disk as default.
48
     */
49
    public function __construct()
50
    {
51
        $this->disk = Disk::make(config('filesystems.default'));
52
    }
53
54
    /**
55
     * Set the disk to open files from.
56
     */
57
    public function fromDisk($disk): self
58
    {
59
        $this->disk = Disk::make($disk);
60
61
        return $this;
62
    }
63
64
    /**
65
     * Instantiates a Media object for the given path and clears the cache.
66
     */
67
    public function open(string $path): self
68
    {
69
        $this->media = Media::make($this->disk, $path);
70
71
        $this->keyCache   = [];
72
        $this->mediaCache = [];
73
74
        return $this;
75
    }
76
77
    public function setMediaResolver(callable $mediaResolver): self
78
    {
79
        $this->mediaResolver = $mediaResolver;
80
81
        return $this;
82
    }
83
84
    public function setKeyResolver(callable $keyResolver): self
85
    {
86
        $this->keyResolver = $keyResolver;
87
88
        return $this;
89
    }
90
91
    /**
92
     * Returns the resolved key filename from the cache or resolves it.
93
     *
94
     * @param string $key
95
     * @return string
96
     */
97
    public function resolveKeyFilename(string $key): string
98
    {
99
        if (array_key_exists($key, $this->keyCache)) {
100
            return $this->keyCache[$key];
101
        }
102
103
        return $this->keyCache[$key] = call_user_func($this->keyResolver, $key);
104
    }
105
106
    /**
107
     * Returns the resolved media filename from the cache or resolves it.
108
     *
109
     * @param string $key
110
     * @return string
111
     */
112
    public function resolveMediaFilename(string $media): string
113
    {
114
        if (array_key_exists($media, $this->mediaCache)) {
115
            return $this->mediaCache[$media];
116
        }
117
118
        return $this->mediaCache[$media] = call_user_func($this->mediaResolver, $media);
119
    }
120
121
    /**
122
     * Parses the lines into a Collection
123
     *
124
     * @param string $lines
125
     * @return \Illuminate\Support\Collection
126
     */
127
    private static function parseLines(string $lines): Collection
128
    {
129
        return Collection::make(preg_split('/\n|\r\n?/', $lines));
130
    }
131
132
    /**
133
     * Returns a boolean wether the line contains a .M3U8 playlist filename
134
     * or a .TS segment filename.
135
     *
136
     * @param string $line
137
     * @return boolean
138
     */
139
    private static function lineHasMediaFilename(string $line): bool
140
    {
141
        return !Str::startsWith($line, '#') && Str::endsWith($line, ['.m3u8', '.ts']);
142
    }
143
144
    /**
145
     * Returns the filename of the encryption key.
146
     *
147
     * @param string $line
148
     * @return string|null
149
     */
150
    private static function extractKeyFromExtLine(string $line): ?string
151
    {
152
        preg_match_all('/#EXT-X-KEY:METHOD=AES-128,URI="([a-zA-Z0-9-_\/]+.key)",IV=[a-z0-9]+/', $line, $matches);
153
154
        return $matches[1][0] ?? null;
155
    }
156
157
    /**
158
     * Returns the processed content of the master playlist.
159
     *
160
     * @return string
161
     */
162
    public function getMasterPlaylist(): string
163
    {
164
        return $this->get($this->media->getPath());
165
    }
166
167
    /**
168
     * Returns a collection of all processed segment playlists
169
     * and the processed master playlist.
170
     *
171
     * @return \Illuminate\Support\Collection
172
     */
173
    public function all(): Collection
174
    {
175
        return static::parseLines(
176
            $this->disk->get($this->media->getPath())
0 ignored issues
show
Bug introduced by
The method get() does not exist on ProtoneMedia\LaravelFFMpeg\Filesystem\Disk. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

176
            $this->disk->/** @scrutinizer ignore-call */ 
177
                         get($this->media->getPath())
Loading history...
177
        )->filter(function ($line) {
178
            return static::lineHasMediaFilename($line);
179
        })->mapWithKeys(function ($segmentPlaylist) {
180
            return [$segmentPlaylist => $this->get($segmentPlaylist)];
181
        })->prepend(
182
            $this->get($this->media->getPath()),
183
            $this->media->getPath()
184
        );
185
    }
186
187
    /**
188
     * Processes the given playlist.
189
     *
190
     * @param string $playlistPath
191
     * @return string
192
     */
193
    public function get(string $playlistPath): string
194
    {
195
        return static::parseLines($this->disk->get($playlistPath))->map(function (string $line) {
196
            if (static::lineHasMediaFilename($line)) {
197
                return $this->resolveMediaFilename($line);
198
            }
199
200
            $key = static::extractKeyFromExtLine($line);
201
202
            if (!$key) {
203
                return $line;
204
            }
205
206
            return str_replace(
207
                '#EXT-X-KEY:METHOD=AES-128,URI="' . $key . '"',
208
                '#EXT-X-KEY:METHOD=AES-128,URI="' . $this->resolveKeyFilename($key) . '"',
209
                $line
210
            );
211
        })->implode(PHP_EOL);
212
    }
213
}
214