ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
MediaObjectManager.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
21namespace ILIAS\MediaObjects;
22
27use _PHPStan_9815bbba4\Nette\Neon\Exception;
32
34{
35 protected \ilLogger $logger;
39
40 public function __construct(
41 protected InternalDataService $data,
43 protected InternalDomainService $domain,
44 protected \ilMobStakeholder $stakeholder
45 ) {
46 $this->repo = $repo->mediaObject();
47 $this->image_converters = new Images(true);
48 $this->output_options = new ImageOutputOptions();
49 $this->logger = \ilLoggerFactory::getLogger('mob');
50 }
51
52 public function create(
53 int $id,
54 string $title,
55 int $from_mob_id = 0
56 ): void {
57 $this->repo->create(
58 $id,
59 $title,
60 $this->stakeholder,
61 $from_mob_id
62 );
63 }
64
65 public function addLocalDirectory(int $mob_id, string $dir): void
66 {
67 $this->repo->addLocalDirectory($mob_id, $dir);
68 }
69
70 public function addFileFromLegacyUpload(int $mob_id, string $tmp_name, string $target_path = ""): void
71 {
72 $this->repo->addFileFromLegacyUpload($mob_id, $tmp_name, $target_path);
73 }
74
75 public function addFileFromUpload(
76 int $mob_id,
77 UploadResult $result,
78 string $path = "/"
79 ): void {
80 $this->repo->addFileFromUpload($mob_id, $result, $path);
81 }
82
83 public function addFileFromLocal(int $mob_id, string $tmp_name, string $path): void
84 {
85 $this->repo->addFileFromLocal($mob_id, $tmp_name, $path);
86 }
87
88 public function removeLocation(
89 int $mob_id,
90 string $location
91 ): void {
92 $this->repo->removeLocation($mob_id, $location);
93 }
94
95 public function getLocationStream(
96 int $mob_id,
97 string $location
98 ): ZIPStream {
99 return $this->repo->getLocationStream($mob_id, $location);
100 }
101
102 public function addStream(
103 int $mob_id,
104 string $location,
105 FileStream $stream
106 ): void {
107 $this->repo->addStream($mob_id, $location, $stream);
108 }
109
110 public function getLocalSrc(int $mob_id, string $location): string
111 {
112 $src = $this->repo->getLocalSrc(
113 $mob_id,
115 );
116 if ($src === "") { // fallback: old source
117 $path_to_file = \ilObjMediaObject::_getURL($mob_id) . "/" . $location;
118 try {
119 $src = \ilWACSignedPath::signFile($path_to_file);
120 } catch (Exception $e) {
121 }
122 }
123 return $src;
124 }
125
126 public function hasLocalFile(int $mob_id, string $location): bool
127 {
128 return $this->repo->hasLocalFile($mob_id, $location);
129 }
130
131 public function getContainerResource(
132 int $mob_id
133 ): ?StorableResource {
134 return $this->repo->getContainerResource($mob_id);
135 }
136
137 public function getContainerResourceId(
138 int $mob_id
140 return $this->repo->getContainerResourceId($mob_id);
141 }
142
143 public function getFilesOfPath(
144 int $mob_id,
145 string $dir_path
146 ): array {
147 return $this->repo->getFilesOfPath($mob_id, $dir_path);
148 }
149
150 public function getInfoOfEntry(
151 int $mob_id,
152 string $path
153 ): array {
154 return $this->repo->getInfoOfEntry(
155 $mob_id,
156 $path
157 );
158 }
159
160 public function deliverEntry(
161 int $mob_id,
162 string $path
163 ): void {
164 $this->repo->deliverEntry($mob_id, $path);
165 }
166
167
168 public function generatePreview(
169 int $mob_id,
170 string $location,
171 bool $local,
172 string $format,
173 int $sec = 1,
174 string $target_location = "mob_vpreview.png"
175 ): void {
176
177 $is_image = is_int(strpos($format, "image/"));
178 $is_video = in_array($format, ["video/mp4", "video/webm"]);
179
180 if ($local) {
181 if ($is_image) {
183 $image_quality = 60;
184
185 // the zip stream is not seekable, which is needed by Imagick
186 // so we create a seekable stream first
187 $tempStream = fopen('php://temp', 'w+');
188 stream_copy_to_stream($this->repo->getLocationStream($mob_id, $location)->detach(), $tempStream);
189 rewind($tempStream);
190 $stream = new Stream($tempStream);
191
192 $converter = $this->image_converters->resizeToFixedSize(
193 $stream,
194 $width,
195 $height,
196 true,
197 $this->output_options
198 ->withQuality($image_quality)
199 ->withFormat(ImageOutputOptions::FORMAT_PNG)
200 );
201 $this->repo->addStream(
202 $mob_id,
203 $target_location,
204 $converter->getStream()
205 );
206 fclose($tempStream);
207 }
208 if ($is_video) {
209 $zip_uri = $this->repo->getContainerPath($mob_id);
211 $zip_uri,
212 $location,
213 $sec
214 );
215 $png_res = fopen('php://memory', 'r+');
216 fwrite($png_res, $image_str);
217 rewind($png_res);
218 $png_stream = new Stream($png_res);
219 $this->repo->addStream(
220 $mob_id,
221 $target_location,
222 $png_stream
223 );
224 }
225 }
226 }
227
228 public function addPreviewFromUrl(
229 int $mob_id,
230 string $url,
231 string $target_location
232 ): void {
233 $log = $this->logger;
234 try {
235 $log->debug('Trying to fetch thumbnail from URL: {thumbnail_url}', [
236 'thumbnail_url' => $url,
237 ]);
238 $curl = new \ilCurlConnection($url);
239 $curl->init(true);
240 $curl->setOpt(CURLOPT_RETURNTRANSFER, true);
241 $curl->setOpt(CURLOPT_VERBOSE, true);
242 $curl->setOpt(CURLOPT_FOLLOWLOCATION, true);
243 $curl->setOpt(CURLOPT_TIMEOUT_MS, 5000);
244 $curl->setOpt(CURLOPT_TIMEOUT, 5);
245 $curl->setOpt(CURLOPT_FAILONERROR, true);
246 $curl->setOpt(CURLOPT_SSL_VERIFYPEER, 1);
247 $curl->setOpt(CURLOPT_SSL_VERIFYHOST, 2);
248
249 $str = $curl->exec();
250 $info = $curl->getInfo();
251
252 $log->debug('cURL Info: {info}', [
253 'info' => print_r($info, true)
254 ]);
255
256 $status = $info['http_code'] ?? '';
257 if ((int) $status === 200) {
258 $log->debug('Successfully fetched preview file from URL: Received {bytes} bytes', [
259 'bytes' => (string) strlen($str),
260 ]);
261 } else {
262 $log->error('Could not fetch thumbnail from YouTube: {thumbnail_url}', [
263 'thumbnail_url' => $url,
264 ]);
265 }
266
267 $res = fopen('php://memory', 'r+');
268 fwrite($res, $str);
269 rewind($res);
270 $stream = new Stream($res);
271 $this->repo->addStream(
272 $mob_id,
273 $target_location,
274 $stream
275 );
276 } catch (\Exception $e) {
277 $log->error('Could not fetch thumbnail from Url: {message}', [
278 'message' => $e->getMessage(),
279 ]);
280 $log->error($e->getTraceAsString());
281 }
282 }
283
284 public function getSrtFiles(int $mob_id, bool $vtt_only = false): array
285 {
286 $srt_files = [];
287 $valid_suffixes = $vtt_only
288 ? ["vtt"]
289 : ["srt", "vtt"];
290 foreach ($this->getFilesOfPath($mob_id, "/srt") as $i) {
291 $name = explode(".", $i["basename"]);
292 if (in_array($name[1], $valid_suffixes) && substr($name[0], 0, 9) == "subtitle_") {
293 $srt_files[] = [
294 "file" => $i["basename"],
295 "full_path" => $i["path"],
296 "src" => $this->getLocalSrc($mob_id, $i["path"]),
297 "language" => substr($name[0], 9, 2)
298 ];
299 }
300 }
301 return $srt_files;
302 }
303
304 public function generateMissingVTT(int $mob_id): void
305 {
306 $names = array_map(static function (array $i) {
307 return $i["file"];
308 }, $this->getSrtFiles($mob_id));
309 $missing_vtt = [];
310 foreach ($names as $name) {
311 if (str_ends_with($name, ".srt")) {
312 $vtt = str_replace(".srt", ".vtt", $name);
313 if (!in_array($vtt, $names) && !in_array($vtt, $missing_vtt)) {
314 $missing_vtt[] = $vtt;
315 }
316 }
317 }
318 foreach ($missing_vtt as $vtt_name) {
319 $srt_name = str_replace(".vtt", ".srt", $vtt_name);
320 $srt_content = stream_get_contents($this->repo->getLocationStream($mob_id, "srt/" . $srt_name)->detach());
321 $vtt_content = $this->srtToVtt($srt_content);
322 $this->repo->addString($mob_id, "/srt/" . $vtt_name, $vtt_content);
323 }
324 }
325
326 public function srtToVtt(string $srt_text): string
327 {
328 // Remove UTF-8 BOM if present
329 $srt_text = preg_replace('/^\xEF\xBB\xBF/', '', $srt_text);
330
331 // Normalise line-endings and split cues
332 $srt_text = preg_replace('~\r\n?~', "\n", $srt_text);
333 $blocks = preg_split("/\n{2,}/", trim($srt_text));
334
335 $vttLines = ['WEBVTT', '']; // header + blank line
336
337 foreach ($blocks as $block) {
338 $lines = explode("\n", $block);
339
340 if (count($lines) < 2) {
341 continue; // malformed cue
342 }
343
344 /* cue number? allow BOM or spaces either side */
345 if (preg_match('/^\s*\d+\s*$/u', $lines[0])) {
346 array_shift($lines); // drop it
347 }
348
349 /* now $lines[0] *is* the time-code line → , → . */
350 $lines[0] = preg_replace(
351 '/(\d{2}:\d{2}:\d{2}),(\d{3})/',
352 '$1.$2',
353 $lines[0]
354 );
355
356 $vttLines = array_merge($vttLines, $lines, ['']);
357 }
358
359 return implode("\n", $vttLines);
360 }
361}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$location
Definition: buildRTE.php:22
create(int $id, string $title, int $from_mob_id=0)
addFileFromUpload(int $mob_id, UploadResult $result, string $path="/")
getLocalSrc(int $mob_id, string $location)
addFileFromLegacyUpload(int $mob_id, string $tmp_name, string $target_path="")
hasLocalFile(int $mob_id, string $location)
addLocalDirectory(int $mob_id, string $dir)
getInfoOfEntry(int $mob_id, string $path)
deliverEntry(int $mob_id, string $path)
addFileFromLocal(int $mob_id, string $tmp_name, string $path)
getLocationStream(int $mob_id, string $location)
generatePreview(int $mob_id, string $location, bool $local, string $format, int $sec=1, string $target_location="mob_vpreview.png")
addPreviewFromUrl(int $mob_id, string $url, string $target_location)
__construct(protected InternalDataService $data, InternalRepoService $repo, protected InternalDomainService $domain, protected \ilMobStakeholder $stakeholder)
addStream(int $mob_id, string $location, FileStream $stream)
removeLocation(int $mob_id, string $location)
getFilesOfPath(int $mob_id, string $dir_path)
getSrtFiles(int $mob_id, bool $vtt_only=false)
static extractPNGFromVideoInZip(string $zip, string $path, int $sec=1)
static getLogger(string $a_component_id)
Get component logger.
static _getURL(int $a_mob_id)
get directory for files of media object
static signFile(string $path_to_file)
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$info
Definition: entry_point.php:21
The base interface for all filesystem streams.
Definition: FileStream.php:32
$log
Definition: ltiresult.php:34
$path
Definition: ltiservices.php:30
$res
Definition: ltiservices.php:69
$url
Definition: shib_logout.php:68