Completed
Push — master ( 2f7c73...63a005 )
by Amin
04:27 queued 23s
created

Export::createPathInfoAndTmpDir()   A

Complexity

Conditions 6
Paths 8

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 7
c 0
b 0
f 0
nc 8
nop 2
dl 0
loc 13
rs 9.2222
1
<?php
2
3
/**
4
 * This file is part of the PHP-FFmpeg-video-streaming package.
5
 *
6
 * (c) Amin Yazdanpanah <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Streaming;
13
14
use FFMpeg\Exception\ExceptionInterface;
15
use Streaming\Clouds\AWS;
16
use Streaming\Clouds\Cloud;
17
use Streaming\Clouds\CloudInterface;
18
use Streaming\Clouds\GoogleCloudStorage;
19
use Streaming\Clouds\MicrosoftAzure;
20
use Streaming\Exception\Exception;
21
use Streaming\Exception\InvalidArgumentException;
22
use Streaming\Exception\RuntimeException;
23
use Streaming\Filters\Filter;
24
use Streaming\Traits\Formats;
25
26
abstract class Export
27
{
28
    use Formats;
29
30
    /** @var object */
31
    protected $media;
32
33
    /** @var array */
34
    protected $path_info;
35
36
    /** @var string */
37
    protected $strict = "-2";
38
39
    /** @var string */
40
    protected $tmp_dir;
41
42
    /**
43
     * Export constructor.
44
     * @param Media $media
45
     */
46
    public function __construct(Media $media)
47
    {
48
        $this->media = $media;
49
        $this->path_info = pathinfo($media->getPath());
50
    }
51
52
    /**
53
     * @param string $path
54
     * @param array $clouds
55
     * @param bool $metadata
56
     * @return mixed
57
     * @throws Exception
58
     */
59
    public function save(string $path = null, array $clouds = [], bool $metadata = true)
60
    {
61
        /**
62
         * Synopsis
63
         * ------------------------------------------------------------------------------
64
         * 1. Create directory path, path info array, and temporary folders(if it is required).
65
         * 2. Build object and run FFmpeg to package media content and save on the local machine.
66
         * 3. If the cloud is specified, entire packaged files will be uploaded to clouds.
67
         * 4. If files were saved into a tmp folder, then they will be moved to the local path(if the path is specified).
68
         * 5. Return all video and also streams' metadata and save as a json file on the local machine(it won't save metadata to clouds because of some security concerns).
69
         * 6. In the end, clear all tmp files.
70
         * ------------------------------------------------------------------------------
71
         */
72
73
        $this->createPathInfoAndTmpDir($path, $clouds);
74
        $this->runFFmpeg();
75
        $this->saveToClouds($clouds);
76
        $this->moveTmpFolder($path);
77
78
        return $metadata ? (new Metadata($this))->extract() : $this;
79
    }
80
81
    /**
82
     * @param $path
83
     * @param $clouds
84
     * @throws Exception
85
     */
86
    private function createPathInfoAndTmpDir($path, $clouds): void
87
    {
88
        if (null !== $path) {
89
            $this->path_info = pathinfo($path);
90
            FileManager::makeDir($this->path_info["dirname"]);
91
        }
92
93
        if ($clouds) {
94
            $this->tmpDirectory($path);
95
        }
96
97
        if (null === $path && $this->media->isTmp() && !$clouds) {
98
            throw new InvalidArgumentException("You need to specify a path. It is not possible to save to a tmp directory");
99
        }
100
    }
101
102
    /**
103
     * @param $path
104
     * @throws Exception
105
     */
106
    private function tmpDirectory($path)
107
    {
108
        if (null !== $path) {
109
            $basename = pathinfo($path, PATHINFO_BASENAME);
110
        } else {
111
            $basename = Helper::randomString();
112
        }
113
114
        $this->tmp_dir = FileManager::tmpDir();
115
        $this->path_info = pathinfo($this->tmp_dir . $basename);
116
    }
117
118
    /**
119
     * Run FFmpeg to package media content
120
     */
121
    private function runFFmpeg(): void
122
    {
123
        try {
124
            $this->media
125
                ->addFilter($this->getFilter())
126
                ->save($this->getFormat(), $this->getPath());
127
        } catch (ExceptionInterface $e) {
128
            throw new RuntimeException(sprintf("There was an error saving files: \n\n reason: \n %s", $e->getMessage()),
129
                $e->getCode(),
130
                $e
131
            );
132
        }
133
    }
134
135
    /**
136
     * @return Filter
137
     */
138
    abstract protected function getFilter(): Filter;
139
140
    /**
141
     * @return string
142
     */
143
    private function getPath(): string
144
    {
145
        $dirname = str_replace("\\", "/", $this->path_info["dirname"]);
146
        $filename = substr($this->path_info["filename"], -100);
147
        $path = '';
148
149
        if ($this instanceof DASH) {
150
            $path = $dirname . "/" . $filename . ".mpd";
151
        } elseif ($this instanceof HLS) {
152
            $representations = $this->getRepresentations();
153
            $path = $dirname . "/" . $filename . "_" . end($representations)->getHeight() . "p.m3u8";
154
            ExportHLSPlaylist::savePlayList($dirname . DIRECTORY_SEPARATOR . $filename . ".m3u8", $this->getRepresentations(), $filename);
155
        }
156
157
        return $path;
158
    }
159
160
    /**
161
     * @param array $clouds
162
     */
163
    private function saveToClouds(array $clouds): void
164
    {
165
        if ($clouds) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $clouds of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
166
167
            if (!is_array(current($clouds))) {
168
                $clouds = [$clouds];
169
            }
170
171
            sleep(1);
172
173
            foreach ($clouds as $cloud) {
174
                if (is_array($cloud) && $cloud['cloud'] instanceof CloudInterface) {
175
                    $cloud_obj = $cloud['cloud'];
176
                    $options = (isset($cloud['options']) && is_array($cloud['options'])) ? $cloud['options'] : [];
177
178
                    $cloud_obj->uploadDirectory($this->tmp_dir, $options);
179
                } else {
180
                    throw new InvalidArgumentException('You must pass an array of clouds to the save method. 
181
                    and the cloud must be instance of CloudInterface');
182
                }
183
            }
184
        }
185
    }
186
187
    /**
188
     * @param string|null $path
189
     * @throws Exception
190
     */
191
    private function moveTmpFolder(?string $path)
192
    {
193
        if ($this->tmp_dir && $path) {
194
            FileManager::moveDir($this->tmp_dir, pathinfo($path, PATHINFO_DIRNAME) . DIRECTORY_SEPARATOR);
195
        }
196
    }
197
198
    /**
199
     * @return array
200
     */
201
    public function getPathInfo(): array
202
    {
203
        return $this->path_info;
204
    }
205
206
    /**
207
     * @return object|Media
208
     */
209
    public function getMedia(): Media
210
    {
211
        return $this->media;
212
    }
213
214
    /**
215
     * @param string $strict
216
     * @return Export
217
     */
218
    public function setStrict(string $strict): Export
219
    {
220
        $this->strict = $strict;
221
        return $this;
222
    }
223
224
    /**
225
     * @return string
226
     */
227
    public function getStrict(): string
228
    {
229
        return $this->strict;
230
    }
231
232
    /**
233
     * clear tmp files
234
     */
235
    public function __destruct()
236
    {
237
        sleep(1);
238
239
        if ($this->media->isTmp()) {
240
            @unlink($this->media->getPath());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). 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

240
            /** @scrutinizer ignore-unhandled */ @unlink($this->media->getPath());

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...
241
        }
242
243
        if ($this->tmp_dir) {
244
            FileManager::deleteDirectory($this->tmp_dir);
245
        }
246
247
        if ($this instanceof HLS && $this->tmp_key_info_file) {
248
            @unlink($this->getHlsKeyInfoFile());
249
        }
250
    }
251
252
    /**
253
     * @param string $url
254
     * @param string $name
255
     * @param string|null $path
256
     * @param string $method
257
     * @param array $headers
258
     * @param array $options
259
     * @return mixed
260
     * @throws Exception
261
     * @deprecated this method is deprecated
262
     */
263
    // @TODO: should be removed in the next releases.
264
    public function saveToCloud(
265
        string $url,
266
        string $name,
267
        string $path = null,
268
        string $method = 'GET',
269
        array $headers = [],
270
        array $options = []
271
    )
272
    {
273
        @trigger_error('saveToCloud method is deprecated and will be removed in a future release. Use Cloud instead', E_USER_DEPRECATED);
274
        if ($this instanceof HLS && $this->getTsSubDirectory()) {
275
            throw new InvalidArgumentException("It is not possible to create subdirectory in a cloud");
276
        }
277
        $results = $this->saveToTemporaryFolder($path);
0 ignored issues
show
Deprecated Code introduced by
The function Streaming\Export::saveToTemporaryFolder() has been deprecated: this method is deprecated ( Ignorable by Annotation )

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

277
        $results = /** @scrutinizer ignore-deprecated */ $this->saveToTemporaryFolder($path);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
278
        sleep(1);
279
280
        $cloud = new Cloud($url, $method, $options);
281
        $cloud->uploadDirectory($this->tmp_dir, ['name' => $name, 'headers' => $headers]);
282
283
        $this->moveTmpFolder($path);
284
285
        return $results;
286
    }
287
288
    /**
289
     * @param array $config
290
     * @param string $dest
291
     * @param string|null $path
292
     * @return mixed
293
     * @throws Exception
294
     * @deprecated this method is deprecated
295
     */
296
    // @TODO: should be removed in the next releases.
297
    public function saveToS3(
298
        array $config,
299
        string $dest,
300
        string $path = null
301
    )
302
    {
303
        @trigger_error('saveToS3 method is deprecated and will be removed in a future release. Use AWS instead', E_USER_DEPRECATED);
304
        $results = $this->saveToTemporaryFolder($path);
0 ignored issues
show
Deprecated Code introduced by
The function Streaming\Export::saveToTemporaryFolder() has been deprecated: this method is deprecated ( Ignorable by Annotation )

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

304
        $results = /** @scrutinizer ignore-deprecated */ $this->saveToTemporaryFolder($path);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
305
        sleep(1);
306
307
        $aws = new AWS($config);
308
        $aws->uploadDirectory($this->tmp_dir, ['dest' => $dest]);
309
310
        $this->moveTmpFolder($path);
311
312
        return $results;
313
    }
314
315
    /**
316
     * @param array $config
317
     * @param string $bucket
318
     * @param string|null $path
319
     * @param array $options
320
     * @param bool $userProject
321
     * @return mixed
322
     * @throws Exception
323
     * @deprecated this method is deprecated
324
     */
325
    // @TODO: should be removed in the next releases.
326
    public function saveToGCS(
327
        array $config,
328
        string $bucket,
329
        string $path = null,
330
        array $options = [],
331
        bool $userProject = false
332
    )
333
    {
334
        @trigger_error('saveToGCS method is deprecated and will be removed in a future release. Use GoogleCloudStorage instead', E_USER_DEPRECATED);
335
        if ($this instanceof HLS && $this->getTsSubDirectory()) {
336
            throw new InvalidArgumentException("It is not possible to create subdirectory in a cloud");
337
        }
338
339
        $results = $this->saveToTemporaryFolder($path);
0 ignored issues
show
Deprecated Code introduced by
The function Streaming\Export::saveToTemporaryFolder() has been deprecated: this method is deprecated ( Ignorable by Annotation )

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

339
        $results = /** @scrutinizer ignore-deprecated */ $this->saveToTemporaryFolder($path);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
340
        sleep(1);
341
342
        $google_cloud = new GoogleCloudStorage($config, $bucket, $userProject);
343
        $google_cloud->uploadDirectory($this->tmp_dir, $options);
344
345
        $this->moveTmpFolder($path);
346
347
        return $results;
348
    }
349
350
    /**
351
     * @param string $connectionString
352
     * @param string $container
353
     * @param string|null $path
354
     * @return mixed
355
     * @throws Exception
356
     * @deprecated this method is deprecated
357
     */
358
    // @TODO: should be removed in the next releases.
359
    public function saveToMAS(
360
        string $connectionString,
361
        string $container,
362
        string $path = null
363
    )
364
    {
365
        @trigger_error('saveToMAS method is deprecated and will be removed in a future release. Use MicrosoftAzure instead', E_USER_DEPRECATED);
366
367
        if ($this instanceof HLS && $this->getTsSubDirectory()) {
368
            throw new InvalidArgumentException("It is not possible to create subdirectory in a cloud");
369
        }
370
371
        $results = $this->saveToTemporaryFolder($path);
0 ignored issues
show
Deprecated Code introduced by
The function Streaming\Export::saveToTemporaryFolder() has been deprecated: this method is deprecated ( Ignorable by Annotation )

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

371
        $results = /** @scrutinizer ignore-deprecated */ $this->saveToTemporaryFolder($path);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
372
        sleep(1);
373
374
        $google_cloud = new MicrosoftAzure($connectionString);
375
        $google_cloud->uploadDirectory($this->tmp_dir, ['container' => $container]);
376
377
        $this->moveTmpFolder($path);
378
379
        return $results;
380
    }
381
382
    /**
383
     * @param $path
384
     * @return array
385
     * @throws Exception
386
     * @deprecated this method is deprecated
387
     */
388
    // @TODO: should be removed in the next releases.
389
    private function saveToTemporaryFolder($path)
390
    {
391
        $basename = Helper::randomString();
392
393
        if (null !== $path) {
394
            $basename = pathinfo($path, PATHINFO_BASENAME);
395
        }
396
397
        $this->tmp_dir = FileManager::tmpDir();
398
399
        return $this->save($this->tmp_dir . $basename);
400
    }
401
}