PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / trunk
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses vtrunk
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
fluent-community / Modules / Integrations / FluentPlayer / Http / Controllers / MediaController.php

MediaController.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses trunk, at Modules/Integrations/FluentPlayer/Http/Controllers/MediaController.php

622 lines 26.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\Modules\Integrations\FluentPlayer\Http\Controllers;
4
5 use FluentCommunity\App\Http\Controllers\Controller;
6 use FluentCommunity\App\Models\Feed;
7 use FluentCommunity\App\Models\Media;
8 use FluentCommunity\App\Services\Helper;
9 use FluentCommunity\App\Services\Libs\FileSystem;
10 use FluentCommunity\Framework\Http\Request\Request;
11 use FluentCommunity\Framework\Support\Arr;
12 use FluentCommunity\Modules\Integrations\FluentPlayer\Bootstrap;
13
14 class MediaController extends Controller
15 {
16 public function uploadVideo(Request $request)
17 {
18 $getFluentPlayerStatus = Bootstrap::getPluginStatus();
19 if ($getFluentPlayerStatus !== 'active') {
20 return $this->sendError([
21 'message' => __('Uploading videos is not allowed. Activate FluentPlayer to upload videos.', 'fluent-community')
22 ]);
23 }
24
25 $fluentPlayerSettings = Bootstrap::getSettings();
26 if (!Arr::isTrue($fluentPlayerSettings, 'video_upload')) {
27 return $this->sendError([
28 'message' => __('Uploading videos is not allowed', 'fluent-community')
29 ]);
30 }
31 $uploadRole = Arr::get($fluentPlayerSettings, 'video_upload_role');
32 $uploadRole = $uploadRole ?: 'admin';
33 $isAllowed = false;
34 if ('admin' === $uploadRole && Helper::isSiteAdmin()) {
35 $isAllowed = true;
36 } else if ('admin_moderator' === $uploadRole && (Helper::isModerator() || Helper::isSiteAdmin())) {
37 $isAllowed = true;
38 } else if ('everyone' === $uploadRole) {
39 $isAllowed = true;
40 }
41 if (!$isAllowed) {
42 return $this->sendError([
43 'message' => __('You do not have permission to upload videos', 'fluent-community')
44 ]);
45 }
46
47 $hasAudioUpload = Arr::get($fluentPlayerSettings, 'enable_audio') === 'yes';
48 $mediaKind = $request->getSafe('media_kind', 'sanitize_text_field', '');
49 if (!in_array($mediaKind, ['video', 'audio'], true)) {
50 $mediaKind = null;
51 }
52
53 if ($mediaKind === 'audio' && !$hasAudioUpload) {
54 return $this->sendError([
55 'message' => __('Uploading audio files is not allowed', 'fluent-community')
56 ]);
57 }
58
59 $allowedMediaTypes = Bootstrap::getAllowedMediaTypes($fluentPlayerSettings, $mediaKind);
60 $maxFileUnit = apply_filters('fluent_community/video_upload_max_file_unit', 'MB');
61 $maxFileSize = apply_filters('fluent_community/video_upload_max_file_size', 300);
62 $allowedFileSize = $maxFileSize;
63 if (strtoupper($maxFileUnit) == 'MB') {
64 $allowedFileSize = $maxFileSize * 1024;
65 } else if (strtoupper($maxFileUnit) == 'GB') {
66 $allowedFileSize = $maxFileSize * 1024 * 1024;
67 }
68
69 $allowedTypesString = implode(',', $allowedMediaTypes);
70
71 $files = $this->validate($request->files(), [
72 'file' => 'mimetypes:' . $allowedTypesString . '|max:' . $allowedFileSize,
73 ], [
74 'file.mimetypes' => $this->getMimeTypeErrorMessage($mediaKind, $hasAudioUpload),
75 /* translators: %1$s is the maximum file size value, %2$s is the size unit (KB, MB, etc.) */
76 'file.max' => sprintf(__('The file size must be less than %1$s%2$s.', 'fluent-community'), $maxFileSize, $maxFileUnit)
77 ]);
78 // File size check
79 if ($error = Helper::checkUploadSizeError()) {
80 return $this->sendError($error, 413);
81 }
82
83 $uploadedFiles = FileSystem::put($files);
84 $file = $uploadedFiles[0];
85
86 $upload_dir = wp_upload_dir();
87 $file['path'] = $upload_dir['basedir'] . '/fluent-community/' . $file['file'];
88
89 $mediaData = [
90 'media_type' => 'fluent_player',
91 'driver' => 'local',
92 'media_path' => $file['path'],
93 'media_url' => $file['url'],
94 'settings' => [
95 'src' => $file['url'],
96 'title' => $file['original_name'],
97 'viewType' => $this->resolveViewType($file, $mediaKind)
98 ]
99 ];
100
101 $mediaData = apply_filters('fluent_community/media_upload_data', $mediaData, $file);
102
103 if (is_wp_error($mediaData)) {
104 return $this->sendError([
105 'message' => $mediaData->get_error_message(),
106 'errors' => $mediaData->get_error_data()
107 ]);
108 }
109
110 if (!$mediaData) {
111 return $this->sendError([
112 'message' => __('Error while uploading the video', 'fluent-community')
113 ]);
114 }
115 $mediaData['settings']['src'] = Arr::get($mediaData, 'media_url', $file['url']);
116
117 // Disable crossorigin for cloud storage drivers that may lack CORS headers
118 $driver = Arr::get($mediaData, 'driver', 'local');
119 if ($driver === 's3') {
120 $mediaData['settings']['crossorigin'] = false;
121 }
122
123 $media = Media::create($mediaData);
124 return [
125 'media' => [
126 'media_id' => $media->id,
127 'url' => $media->public_url,
128 'media_key' => $media->media_key,
129 'type' => $media->media_type,
130 'settings' => $media->settings,
131 'html' => ''
132 ]
133 ];
134 }
135
136 /**
137 * Whether the current user may edit an audio media's metadata.
138 * Owner (upload sets user_id) or a moderator/site admin.
139 */
140 public function canEditAudioMedia($media)
141 {
142 if (!$media) {
143 return false;
144 }
145 // Read user_id via the model accessor. A (array) cast of a WPFluent model exposes
146 // the protected $attributes under a mangled key, not a flat user_id.
147 if ((int) $media->user_id === (int) get_current_user_id()) {
148 return true;
149 }
150 return Helper::isModerator() || Helper::isSiteAdmin();
151 }
152
153 /**
154 * Update an audio media's display title and poster thumbnail from the composer.
155 */
156 public function updateAudioMeta(Request $request)
157 {
158 $media = Media::find(intval($request->get('media_id')));
159 if (!$media || !$this->canEditAudioMedia($media)) {
160 return $this->sendError([
161 'message' => __('You are not allowed to edit this audio', 'fluent-community'),
162 ], 403);
163 }
164
165 // Only fluent-player audio media may be edited through this endpoint.
166 if ($media->media_type !== 'fluent_player' || !$this->isAudioMedia((array) $media->settings)) {
167 return $this->sendError([
168 'message' => __('This media is not an editable audio', 'fluent-community'),
169 ], 422);
170 }
171
172 $settings = (array) $media->settings;
173 $title = $request->getSafe('title', 'sanitize_text_field', '');
174 $posterSrc = $request->getSafe('posterSrc', 'sanitize_url', '');
175
176 if ($title !== '') {
177 $settings['title'] = $title;
178 }
179 if ($posterSrc !== '') {
180 $settings['posterSrc'] = $posterSrc;
181 // poster_is_custom gates whether the render path shows the custom thumbnail
182 // (see show_poster in generateFluentPlayerHtml).
183 $settings['poster_is_custom'] = true;
184 } else {
185 $settings['posterSrc'] = '';
186 $settings['poster_is_custom'] = false;
187 }
188
189 $media->settings = $settings;
190 $media->save();
191
192 return [
193 'media' => [
194 'media_id' => $media->id,
195 'settings' => $media->settings,
196 ],
197 ];
198 }
199
200 private function resolveViewType($file, $mediaKind)
201 {
202 $audioExtensions = ['mp3', 'wav', 'm4a', 'aac', 'ogg', 'oga', 'flac'];
203 $name = Arr::get($file, 'original_name', Arr::get($file, 'url', ''));
204 $extension = strtolower(pathinfo($name, PATHINFO_EXTENSION));
205
206 if (in_array($extension, $audioExtensions, true)) {
207 return 'audio';
208 }
209
210 if ($extension) {
211 return 'video';
212 }
213
214 return $mediaKind === 'audio' ? 'audio' : 'video';
215 }
216
217 private function getMimeTypeErrorMessage($mediaKind, $hasAudioUpload)
218 {
219 if ($mediaKind === 'audio') {
220 return __('The file must be a valid audio (MP3, WAV, M4A, AAC, OGG, FLAC) type.', 'fluent-community');
221 }
222
223 if ($mediaKind === 'video' || !$hasAudioUpload) {
224 return __('The file must be a valid video (MP4, WebM, MOV) type.', 'fluent-community');
225 }
226
227 return __('The file must be a valid video (MP4, WebM, MOV) or audio (MP3, WAV, M4A, AAC, OGG, FLAC) type.', 'fluent-community');
228 }
229
230 public function getFluentPlayerContent(Request $request)
231 {
232 if (!defined('FLUENT_PLAYER_VERSION')) {
233 return [
234 'html' => '',
235 ];
236 }
237 $mediaId = intval($request->get('media_id'));
238 $instanceKey = sanitize_key($request->get('player_instance_key'));
239 $shareUrl = esc_url_raw((string) $request->get('share_url', ''));
240 $media = Media::find($mediaId);
241 if (!$media) {
242 // Non-DB media (external embed / share URL) from a synthetic hash id: build from
243 // allowlisted scalars only. Never trust request `settings`/`layers` — accepting
244 // them lets an anon caller inject shortcode layers into FluentPlayer's renderer.
245 $media = (object) [
246 'url' => esc_url_raw((string) $request->get('url', '')),
247 'title' => sanitize_text_field($request->get('title')),
248 'image' => esc_url_raw((string) $request->get('image', '')),
249 'share_url' => esc_url_raw((string) $request->get('share_url', '')),
250 'provider' => sanitize_text_field($request->get('provider')),
251 'type' => sanitize_text_field($request->get('type')),
252 ];
253 } else {
254 $currentUserId = get_current_user_id();
255 $canView = ($currentUserId && (int) $media->user_id === $currentUserId)
256 || ($media->feed_id && Feed::byUserAccess($currentUserId)
257 ->byContentModerationAccessStatus($this->getUser())
258 ->find($media->feed_id));
259 if (!$canView) {
260 return ['html' => ''];
261 }
262 if ($shareUrl) {
263 $media->share_url = $shareUrl;
264 }
265 }
266 return $this->generateFluentPlayerHtml($mediaId, $media, $instanceKey);
267 }
268
269 private function generateFluentPlayerHtml($mediaId, $media, $instanceKey = '')
270 {
271 $mediaVarName = 'fluentPlayerMedia_' . $mediaId . '_' . wp_rand(1000, 9999);
272 $playerKey = 'fcom_' . $mediaId;
273 $instanceId = $playerKey . ($instanceKey ? '_' . $instanceKey : '');
274 $mediaSettings = [];
275 $formattedMedia = [
276 'ID' => $mediaId
277 ];
278 if (!empty($media->settings)) {
279 $mediaSettings = $media->settings;
280 }
281 if (empty($mediaSettings['src']) && !empty($media->url)) {
282 $mediaSettings['src'] = $media->url;
283 }
284 if (empty($mediaSettings['title']) && !empty($media->title)) {
285 $mediaSettings['title'] = $media->title;
286 }
287 if (empty($mediaSettings['posterSrc']) && !empty($media->image)) {
288 $mediaSettings['posterSrc'] = $media->image;
289 }
290 if (empty($mediaSettings['share_url']) && !empty($media->share_url)) {
291 $mediaSettings['share_url'] = esc_url_raw($media->share_url);
292 }
293 $src = Arr::get($mediaSettings, 'src', '');
294 // Normalize YouTube /live/ URLs to /watch?v= so Vidstack can resolve them
295 if ($src && strpos($src, 'youtube.com/live/') !== false) {
296 $src = preg_replace('/youtube\.com\/live\/([^?&#]+)\??/', 'youtube.com/watch?v=$1&', $src);
297 $src = rtrim($src, '&');
298 $mediaSettings['src'] = $src;
299 }
300 if ($src && preg_match('#youtube\.com/shorts/#i', $src)) {
301 $mediaSettings['is_short'] = true;
302 }
303
304 $mediaSettings = wp_parse_args($mediaSettings, $this->getFluentplayerDefaultsSettings());
305
306 if ($this->isAudioMedia($mediaSettings)) {
307 // Only show a thumbnail block when the author uploaded one; otherwise render a
308 // clean player with no poster (per design). poster_is_custom is set by the
309 // audio edit modal / updateAudioMeta.
310 $hasCustomPoster = Arr::get($mediaSettings, 'posterSrc', '') !== ''
311 && filter_var(Arr::get($mediaSettings, 'poster_is_custom'), FILTER_VALIDATE_BOOLEAN);
312 $mediaSettings['show_poster'] = $hasCustomPoster;
313 // Show the inline "1x" speed control instead of the settings gear menu.
314 if (!isset($mediaSettings['controls']) || !is_array($mediaSettings['controls'])) {
315 $mediaSettings['controls'] = [];
316 }
317 $mediaSettings['controls']['settings'] = false;
318 }
319
320 $formattedMedia['settings'] = $mediaSettings;
321 $formattedMedia['deep_link_ref'] = $playerKey;
322
323 // Generate the HTML using fluent-player's view system
324 ob_start();
325 if (class_exists('\FluentPlayer\App\App')) {
326 try {
327 $fluentPlayerApp = \FluentPlayer\App\App::getInstance();
328 if ($fluentPlayerApp) {
329 $fluentPlayerApp->view->render('player', [
330 'media_id' => $mediaId,
331 'instance_id' => $instanceId,
332 'media_var_name' => $mediaVarName,
333 'player_ref' => $playerKey,
334 'settings' => $mediaSettings
335 ]);
336 }
337 } catch (\Exception $e) {
338 }
339 }
340 $html = ob_get_clean();
341 return [
342 'html' => $html ? $html . $this->generateFluentPlayerCustomStyle($instanceId, $mediaSettings) : '',
343 'media' => $formattedMedia
344 ];
345 }
346
347 private function isAudioMedia($settings)
348 {
349 if (class_exists('\FluentPlayer\App\Helpers\Helper')
350 && method_exists('\FluentPlayer\App\Helpers\Helper', 'isAudioSource')) {
351 return \FluentPlayer\App\Helpers\Helper::isAudioSource($settings);
352 }
353 return Arr::get($settings, 'viewType') === 'audio';
354 }
355
356 private function generateFluentPlayerCustomStyle($instanceId, $settings)
357 {
358 $customCss = '';
359 $isAudio = $this->isAudioMedia($settings);
360 $playerWidth = intval(Arr::get($settings, 'playerWidth'));
361 if ($playerWidth > 0) {
362 $customCss .= "
363 #fluent_player_" . esc_attr($instanceId) . " .fluent-player-container {
364 width: " . $playerWidth . 'px' . ";
365 }
366 ";
367 }
368 $colorVars = array_filter([
369 '--media-brand' => Bootstrap::sanitizeColor(Arr::get($settings, 'brandColor', '')),
370 '--fp-control-bar-bg' => Bootstrap::sanitizeColor(Arr::get($settings, 'controlBarColor', '')),
371 '--media-play-color' => Bootstrap::sanitizeColor(Arr::get($settings, 'playButtonColor', '')),
372 '--media-play-bg' => Bootstrap::sanitizeColor(Arr::get($settings, 'playButtonBgColor', ''))
373 ]);
374 if ($colorVars) {
375 $customCss .= "
376 #fluent_player_" . esc_attr($instanceId) . " {";
377 foreach ($colorVars as $varName => $varValue) {
378 $customCss .= "
379 " . $varName . ": " . esc_attr($varValue) . ";";
380 }
381 $customCss .= "
382 }
383 ";
384 }
385 if (!empty(Arr::get($settings, 'posterSrc', ''))) {
386 $customCss .= "
387 #fluent_player_" . esc_attr($instanceId) . " .fluent-player-container {
388 background-image: url('" . esc_url(Arr::get($settings, 'posterSrc', '')) . "');
389 background-size: cover;
390 background-position: center;
391 min-height: 100%;
392 }
393 ";
394 }
395
396 if ($isAudio) {
397 // Audio follows the FluentCommunity color schema (Utility::getColorSchemas()).
398 // The player renders inside the portal DOM and inherits the --fcom-* CSS vars that
399 // generateCss() emits on body (and html.dark body), so theme/skin/dark changes recolor
400 // audio live with no re-save. The stock "modern" audio skin is built for a dark
401 // background (white title, translucent-white controls, brand-colored bar); we override
402 // those to a flat, theme-surface card. Play circle + progress fill use the auto-
403 // inverting primary_text/primary_bg pair so the same rules match a light card
404 // (dark on light) and a dark card (light on dark) without per-theme branching.
405 $playerId = '#fluent_player_' . esc_attr($instanceId);
406 $customCss .= "
407 {$playerId} {
408 --media-brand: var(--fcom-primary-text, #19283a);
409 --fp-control-bar-bg: transparent;
410 }
411 {$playerId} .fp-audio-layout {
412 background-color: var(--fcom-secondary-content-bg, var(--fcom-active-bg, #f0f2f5));
413 border-radius: 12px;
414 }
415 {$playerId} .fp-audio-main-controls {
416 background: transparent;
417 }
418 {$playerId} .fp-audio-title {
419 color: var(--fcom-primary-text, #19283a);
420 font-size: 14px;
421 font-weight: 600;
422 }
423 {$playerId} .fp-audio-subtitle {
424 color: var(--fcom-primary-text, #19283a);
425 }
426 {$playerId} media-play-button {
427 background: var(--fcom-primary-text, #19283a);
428 }
429 {$playerId} media-play-button media-icon {
430 color: var(--fcom-primary-bg, #ffffff);
431 }
432 {$playerId} media-seek-button {
433 background: transparent;
434 }
435 {$playerId} media-time-slider .fp-media-slider-track {
436 background: var(--fcom-secondary-border, #9CA3AF);
437 background: color-mix(in srgb, var(--fcom-primary-text, #19283a) 20%, transparent);
438 }
439 {$playerId} media-time-slider .fp-media-slider-track-fill {
440 background: var(--fcom-primary-text, #19283a);
441 }
442 {$playerId} .fp-seek-backward-10,
443 {$playerId} .fp-seek-forward-10,
444 {$playerId} .fp-media-mute-icon,
445 {$playerId} .fp-media-volume-low-icon,
446 {$playerId} .fp-media-volume-high-icon,
447 {$playerId} media-seek-button media-icon,
448 {$playerId} media-icon[type=\"odometer\"],
449 {$playerId} media-icon[type=\"settings\"] {
450 color: var(--fcom-secondary-text, #525866);
451 }
452 {$playerId} .fp-media-time-group,
453 {$playerId} .fp-media-time-group media-time,
454 {$playerId} .fp-media-time-divider {
455 color: var(--fcom-primary-text, #19283a);
456 }
457 ";
458
459 // Exact layout: large play circle on the left (spanning title + controls),
460 // a single control row (seek / seek / volume / speed / time) and a full-width
461 // progress bar at the bottom. The volume slider is hidden until hover.
462 $customCss .= "
463 {$playerId} .fp-audio-layout {
464 position: relative;
465 padding: 22px 24px 14px;
466 }
467 {$playerId} .fp-audio-layout .fp-audio-content {
468 padding: 0 0 0 82px;
469 gap: 6px;
470 width: 100%;
471 }
472 {$playerId} .fp-audio-header {
473 padding-left: 8px;
474 }
475 {$playerId} media-play-button {
476 position: absolute;
477 left: 22px;
478 top: 50%;
479 transform: translateY(-50%);
480 width: 64px;
481 height: 64px;
482 min-width: 64px;
483 }
484 {$playerId} .fp-audio-main-controls {
485 display: grid;
486 grid-template-columns: max-content max-content max-content max-content 1fr max-content;
487 grid-template-areas: \"sb sf vol spd play time\" \"pr pr pr pr pr pr\";
488 align-items: center;
489 column-gap: 0;
490 row-gap: 6px;
491 padding: 0;
492 width: 100%;
493 }
494 {$playerId} .fp-audio-main-controls media-tooltip:nth-of-type(1) { grid-area: sb; margin: 0; }
495 {$playerId} .fp-audio-main-controls media-tooltip:nth-of-type(2) { grid-area: play; }
496 {$playerId} .fp-audio-main-controls media-tooltip:nth-of-type(3) { grid-area: sf; margin: 0; }
497 {$playerId} media-seek-button,
498 {$playerId} .fp-audio-volume-group media-mute-button {
499 width: 32px;
500 height: 32px;
501 min-width: 32px;
502 }
503 {$playerId} .fp-audio-secondary-controls media-menu-button { min-width: 32px; padding: 0 6px; }
504 {$playerId} .fp-audio-volume-group { grid-area: vol; margin: 0; overflow: visible; }
505 {$playerId} .fp-audio-secondary-controls { grid-area: spd; margin: 0; }
506 {$playerId} .fp-media-time-group { grid-area: time; margin: 0; justify-self: end; }
507 {$playerId} media-player[data-view-type=\"audio\"] media-time-slider,
508 {$playerId} media-time-slider { grid-area: pr; margin: 8px; }
509 {$playerId} media-seek-button:hover,
510 {$playerId} .fp-audio-volume-group media-mute-button:hover,
511 {$playerId} .fp-audio-secondary-controls media-menu-button:hover {
512 background: transparent;
513 }
514 {$playerId} .fp-audio-volume-group media-volume-slider {
515 width: 0;
516 max-width: 0;
517 min-width: 0;
518 opacity: 0;
519 overflow: hidden;
520 margin: 0;
521 transition: width .2s ease, opacity .2s ease, margin .2s ease;
522 }
523 {$playerId} .fp-audio-volume-group:hover media-volume-slider,
524 {$playerId} .fp-audio-volume-group:focus-within media-volume-slider {
525 width: 80px;
526 max-width: 80px;
527 opacity: 1;
528 margin-left: 6px;
529 }
530 {$playerId} .fp-speed-menu .fcom_speed_label {
531 font-weight: 600;
532 font-size: 15px;
533 line-height: 1;
534 color: var(--fcom-primary-text, #19283a);
535 }
536 ";
537
538 // When a custom thumbnail is set, lay the card out as poster -> play -> content
539 // (matching the reference). The poster sits on the left, the play circle after it.
540 $customCss .= "
541 {$playerId} .fp-audio-layout:not(.fp-audio-no-poster) {
542 gap: 16px;
543 padding-left: 16px;
544 }
545 {$playerId} .fp-audio-layout:not(.fp-audio-no-poster) .fp-audio-poster {
546 width: 96px;
547 height: 96px;
548 min-width: 96px;
549 border-radius: 12px;
550 }
551 {$playerId} .fp-audio-layout:not(.fp-audio-no-poster) media-play-button {
552 left: 128px;
553 }
554 {$playerId} .fp-audio-layout:not(.fp-audio-no-poster) .fp-audio-content {
555 padding-left: 82px;
556 }
557 ";
558 }
559
560 $aspectRatio = Arr::get($settings, 'aspectRatio');
561 if ($aspectRatio && $aspectRatio != 'original' && preg_match('/^\d+:\d+$/', $aspectRatio)) {
562 $cssAspectRatio = preg_replace('/^(\d+):(\d+)$/', '$1/$2', $aspectRatio);
563 $customCss .= "
564 #fluent_player_" . esc_attr($instanceId) . " {
565 aspect-ratio: " . esc_attr($cssAspectRatio) . ";
566 }
567 #fluent_player_" . esc_attr($instanceId) . " media-player[data-view-type='video'] {
568 aspect-ratio: " . esc_attr($cssAspectRatio) . ";
569 }
570 ";
571 } else if (!$isAudio) {
572 $customCss .= "
573 #fluent_player_" . esc_attr($instanceId) . " {
574 max-width: 100%;
575 min-height: 300px;
576 }
577
578 @media (max-width: 768px) {
579 #fluent_player_" . esc_attr($instanceId) . " {
580 min-height: 200px;
581 }
582 }
583 ";
584 }
585 if (!empty($customCss)) {
586 return '<style>' . $customCss . '</style>';
587 }
588 return '';
589 }
590
591 private function getFluentplayerDefaultsSettings()
592 {
593 $settings = [
594 'src' => '',
595 'title' => '',
596 'posterSrc' => '',
597 'viewType' => 'video',
598 'brandColor' => '#4a90e2',
599 'aspectRatio' => 'original',
600 'playerWidth' => '',
601 'playsInline' => true
602 ];
603 $mediaSettings = Bootstrap::getSettings();
604 if (Arr::isTrue($mediaSettings, 'behaviors.muted_autoplay')) {
605 $mediaSettings['autoplay'] = true;
606 $mediaSettings['muted'] = true;
607 }
608 $settings['loadStrategy'] = 'idle'; // for SPA context this needs to be set to 'idle'
609 $settings = array_merge($settings, $mediaSettings);
610
611 // Apply iOS Safari compatibility (playsinline, preload; muted only when autoplay is on)
612 if (class_exists('\FluentPlayer\App\Services\MediaService')) {
613 [$isIosSafari, $iosSafariSettings] = \FluentPlayer\App\Services\MediaService::getIOSSafariSettings($settings);
614 if ($isIosSafari) {
615 $settings = array_merge($settings, $iosSafariSettings);
616 }
617 }
618
619 return apply_filters('fluent_community/fluentplayer_defaults_settings', $settings);
620 }
621 }
622