Passed
Push — master ( 8e077c...e6cc46 )
by Amin
03:50
created

Export::saveToMAS()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 21
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 2
nop 3
dl 0
loc 21
rs 9.9666
c 0
b 0
f 0
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\CloudManager;
16
use Streaming\Exception\Exception;
17
use Streaming\Exception\InvalidArgumentException;
18
use Streaming\Exception\RuntimeException;
19
use Streaming\Filters\Filter;
20
use Streaming\Traits\Formats;
21
22
abstract class Export
23
{
24
    use Formats;
25
26
    /** @var object */
27
    protected $media;
28
29
    /** @var array */
30
    protected $path_info;
31
32
    /** @var string */
33
    protected $strict = "-2";
34
35
    /** @var string */
36
    protected $tmp_dir;
37
38
    /**
39
     * Export constructor.
40
     * @param Media $media
41
     */
42
    public function __construct(Media $media)
43
    {
44
        $this->media = $media;
45
        $this->path_info = pathinfo($media->getPath());
46
    }
47
48
    /**
49
     * @param string $path
50
     * @param array $clouds
51
     * @param bool $metadata
52
     * @return mixed
53
     * @throws Exception
54
     */
55
    public function save(string $path = null, array $clouds = [], bool $metadata = true)
56
    {
57
        /**
58
         * Synopsis
59
         * ------------------------------------------------------------------------------
60
         * 1. Create directory path, path info array, and temporary folders(if it is required).
61
         * 2. Build object and run FFmpeg to package media content and save on the local machine.
62
         * 3. If the cloud is specified, entire packaged files will be uploaded to clouds.
63
         * 4. If files were saved into a tmp folder, then they will be moved to the local path(if the path is specified).
64
         * 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).
65
         * 6. In the end, clear all tmp files.
66
         * ------------------------------------------------------------------------------
67
         */
68
69
        $this->createPathInfoAndTmpDir($path, $clouds);
70
        $this->runFFmpeg();
71
        CloudManager::uploadDirectory($clouds, $this->tmp_dir);
72
        $this->moveTmpFolder($path);
73
74
        return $metadata ? (new Metadata($this))->extract() : $this;
75
    }
76
77
    /**
78
     * @param $path
79
     * @param $clouds
80
     * @throws Exception
81
     */
82
    private function createPathInfoAndTmpDir($path, $clouds): void
83
    {
84
        if (null !== $path) {
85
            $this->path_info = pathinfo($path);
86
            FileManager::makeDir($this->path_info["dirname"]);
87
        }
88
89
        if ($clouds) {
90
            $this->tmpDirectory($path);
91
        }
92
93
        if (null === $path && $this->media->isTmp() && !$clouds) {
94
            throw new InvalidArgumentException("You need to specify a path. It is not possible to save to a tmp directory");
95
        }
96
    }
97
98
    /**
99
     * @param $path
100
     * @throws Exception
101
     */
102
    private function tmpDirectory($path)
103
    {
104
        if (null !== $path) {
105
            $basename = pathinfo($path, PATHINFO_BASENAME);
106
        } else {
107
            $basename = Utilities::randomString();
108
        }
109
110
        $this->tmp_dir = FileManager::tmpDir();
111
        $this->path_info = pathinfo($this->tmp_dir . $basename);
112
    }
113
114
    /**
115
     * Run FFmpeg to package media content
116
     */
117
    private function runFFmpeg(): void
118
    {
119
        try {
120
            $this->media
121
                ->addFilter($this->getFilter())
122
                ->save($this->getFormat(), $this->getPath());
123
        } catch (ExceptionInterface $e) {
124
            throw new RuntimeException(sprintf("There was an error saving files: \n\n reason: \n %s", $e->getMessage()),
125
                $e->getCode(),
126
                $e
127
            );
128
        }
129
    }
130
131
    /**
132
     * @return Filter
133
     */
134
    abstract protected function getFilter(): Filter;
135
136
    /**
137
     * @return string
138
     */
139
    private function getPath(): string
140
    {
141
        $dirname = str_replace("\\", "/", $this->path_info["dirname"]);
142
        $filename = substr($this->path_info["filename"], -100);
143
        $path = '';
144
145
        if ($this instanceof DASH) {
146
            $path = $dirname . "/" . $filename . ".mpd";
147
        } elseif ($this instanceof HLS) {
148
            $representations = $this->getRepresentations();
149
            $path = $dirname . "/" . $filename . "_" . end($representations)->getHeight() . "p.m3u8";
150
            ExportHLSPlaylist::savePlayList($dirname . DIRECTORY_SEPARATOR . $filename . ".m3u8", $this->getRepresentations(), $filename);
151
        }
152
153
        return $path;
154
    }
155
156
    /**
157
     * @param string|null $path
158
     * @throws Exception
159
     */
160
    private function moveTmpFolder(?string $path)
161
    {
162
        if ($this->tmp_dir && $path) {
163
            FileManager::moveDir($this->tmp_dir, pathinfo($path, PATHINFO_DIRNAME) . DIRECTORY_SEPARATOR);
164
            $this->path_info = pathinfo($path);
165
            $this->tmp_dir = '';
166
        }
167
    }
168
169
    /**
170
     * @return array
171
     */
172
    public function getPathInfo(): array
173
    {
174
        return $this->path_info;
175
    }
176
177
    /**
178
     * @return object|Media
179
     */
180
    public function getMedia(): Media
181
    {
182
        return $this->media;
183
    }
184
185
    /**
186
     * @param string $strict
187
     * @return Export
188
     */
189
    public function setStrict(string $strict): Export
190
    {
191
        $this->strict = $strict;
192
        return $this;
193
    }
194
195
    /**
196
     * @return string
197
     */
198
    public function getStrict(): string
199
    {
200
        return $this->strict;
201
    }
202
203
    /**
204
     * @return bool
205
     */
206
    public function isTmpDir(): bool
207
    {
208
        return (bool)$this->tmp_dir;
209
    }
210
211
    /**
212
     * clear tmp files
213
     */
214
    public function __destruct()
215
    {
216
        sleep(1);
217
218
        if ($this->media->isTmp()) {
219
            @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

219
            /** @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...
220
        }
221
222
        if ($this->tmp_dir) {
223
            FileManager::deleteDirectory($this->tmp_dir);
224
        }
225
226
        if ($this instanceof HLS && $this->tmp_key_info_file) {
227
            @unlink($this->getHlsKeyInfoFile());
228
        }
229
    }
230
}