PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.4.01
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.4.01
2.11.0 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 All 78 releases
fluent-community / Modules / Integrations / FluentPlayer / Http / Controllers / MediaController.php

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

318 lines 12.1 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\Media;
7 use FluentCommunity\App\Services\Helper;
8 use FluentCommunity\App\Services\Libs\FileSystem;
9 use FluentCommunity\Framework\Http\Request\Request;
10 use FluentCommunity\Framework\Support\Arr;
11 use FluentCommunity\Modules\Integrations\FluentPlayer\Bootstrap;
12
13 class MediaController extends Controller
14 {
15 public function uploadVideo(Request $request)
16 {
17 $getFluentPlayerStatus = Bootstrap::getPluginStatus();
18 if ($getFluentPlayerStatus !== 'active') {
19 return $this->sendError([
20 'message' => __('Uploading videos is not allowed. Activate FluentPlayer to upload videos.', 'fluent-community')
21 ]);
22 }
23
24 $fluentPlayerSettings = Bootstrap::getSettings();
25 if (!Arr::isTrue($fluentPlayerSettings, 'video_upload')) {
26 return $this->sendError([
27 'message' => __('Uploading videos is not allowed', 'fluent-community')
28 ]);
29 }
30 $uploadRole = Arr::get($fluentPlayerSettings, 'video_upload_role');
31 $uploadRole = $uploadRole ?: 'admin';
32 $isAllowed = false;
33 if ('admin' === $uploadRole && Helper::isSiteAdmin()) {
34 $isAllowed = true;
35 } else if ('admin_moderator' === $uploadRole && (Helper::isModerator() || Helper::isSiteAdmin())) {
36 $isAllowed = true;
37 } else if ('everyone' === $uploadRole) {
38 $isAllowed = true;
39 }
40 if (!$isAllowed) {
41 return $this->sendError([
42 'message' => __('You do not have permission to upload videos', 'fluent-community')
43 ]);
44 }
45
46 $allowedVideoTypes = apply_filters('fluent_community/support_video_types', [
47 'video/mp4',
48 'video/m3u8',
49 'video/mpd',
50 'video/webm',
51 'video/mov',
52 'video/quicktime'
53 ]);
54 $maxFileUnit = apply_filters('fluent_community/video_upload_max_file_unit', 'MB');
55 $maxFileSize = apply_filters('fluent_community/video_upload_max_file_size', 300);
56 $allowedFileSize = $maxFileSize;
57 if (strtoupper($maxFileUnit) == 'MB') {
58 $allowedFileSize = $maxFileSize * 1024;
59 } else if (strtoupper($maxFileUnit) == 'GB') {
60 $allowedFileSize = $maxFileSize * 1024 * 1024;
61 }
62
63 $allowedTypesString = implode(',', $allowedVideoTypes);
64
65 $files = $this->validate($request->files(), [
66 'file' => 'mimetypes:' . $allowedTypesString . '|max:' . $allowedFileSize,
67 ], [
68 'file.mimetypes' => __('The file must be a valid video type (MP4, M3U8, MPD, WebM, MOV).', 'fluent-community'),
69 'file.max' => sprintf(__('The file size must be less than %1$s%2$s.', 'fluent-community'), $maxFileSize, $maxFileUnit)
70 ]);
71 // File size check
72 if ($error = Helper::checkUploadSizeError()) {
73 return $this->sendError($error, 413);
74 }
75
76 $uploadedFiles = FileSystem::put($files);
77 $file = $uploadedFiles[0];
78
79 $upload_dir = wp_upload_dir();
80 $file['path'] = $upload_dir['basedir'] . '/fluent-community/' . $file['file'];
81
82 $mediaData = [
83 'media_type' => 'fluent_player',
84 'driver' => 'local',
85 'media_path' => $file['path'],
86 'media_url' => $file['url'],
87 'settings' => [
88 'src' => $file['url'],
89 'title' => $file['original_name']
90 ]
91 ];
92
93 $mediaData = apply_filters('fluent_community/media_upload_data', $mediaData, $file);
94
95 if (is_wp_error($mediaData)) {
96 return $this->sendError([
97 'message' => $mediaData->get_error_message(),
98 'errors' => $mediaData->get_error_data()
99 ]);
100 }
101
102 if (!$mediaData) {
103 return $this->sendError([
104 'message' => __('Error while uploading the video', 'fluent-community')
105 ]);
106 }
107 $mediaData['settings']['src'] = Arr::get($mediaData, 'media_url', $file['url']);
108
109 // Disable crossorigin for cloud storage drivers that may lack CORS headers
110 $driver = Arr::get($mediaData, 'driver', 'local');
111 if ($driver === 's3') {
112 $mediaData['settings']['crossorigin'] = false;
113 }
114
115 $media = Media::create($mediaData);
116 return [
117 'media' => [
118 'media_id' => $media->id,
119 'url' => $media->public_url,
120 'media_key' => $media->media_key,
121 'type' => $media->media_type,
122 'settings' => $media->settings,
123 'html' => ''
124 ]
125 ];
126 }
127
128 public function getFluentPlayerContent(Request $request)
129 {
130 if (!defined('FLUENT_PLAYER_VERSION')) {
131 return [
132 'html' => '',
133 ];
134 }
135 $mediaId = intval($request->get('media_id'));
136 $instanceKey = sanitize_key($request->get('player_instance_key'));
137 $shareUrl = esc_url_raw($request->get('share_url'));
138 $media = Media::find($mediaId);
139 if (!$media) {
140 $media = (object) $request->all();
141 } else if ($shareUrl) {
142 $media->share_url = $shareUrl;
143 }
144 return $this->generateFluentPlayerHtml($mediaId, $media, $instanceKey);
145 }
146
147 private function generateFluentPlayerHtml($mediaId, $media, $instanceKey = '')
148 {
149 $mediaVarName = 'fluentPlayerMedia_' . $mediaId . '_' . wp_rand(1000, 9999);
150 $playerKey = 'fcom_' . $mediaId;
151 $instanceId = $playerKey . ($instanceKey ? '_' . $instanceKey : '');
152 $mediaSettings = [];
153 $formattedMedia = [
154 'ID' => $mediaId
155 ];
156 if (!empty($media->settings)) {
157 $mediaSettings = $media->settings;
158 }
159 if (empty($mediaSettings['src']) && !empty($media->url)) {
160 $mediaSettings['src'] = $media->url;
161 }
162 if (empty($mediaSettings['title']) && !empty($media->title)) {
163 $mediaSettings['title'] = $media->title;
164 }
165 if (empty($mediaSettings['posterSrc']) && !empty($media->image)) {
166 $mediaSettings['posterSrc'] = $media->image;
167 }
168 if (empty($mediaSettings['share_url']) && !empty($media->share_url)) {
169 $mediaSettings['share_url'] = esc_url_raw($media->share_url);
170 }
171 $src = Arr::get($mediaSettings, 'src', '');
172 // Normalize YouTube /live/ URLs to /watch?v= so Vidstack can resolve them
173 if ($src && strpos($src, 'youtube.com/live/') !== false) {
174 $src = preg_replace('/youtube\.com\/live\/([^?&#]+)\??/', 'youtube.com/watch?v=$1&', $src);
175 $src = rtrim($src, '&');
176 $mediaSettings['src'] = $src;
177 }
178 if ($src && preg_match('#youtube\.com/shorts/#i', $src)) {
179 $mediaSettings['is_short'] = true;
180 }
181
182 $mediaSettings = wp_parse_args($mediaSettings, $this->getFluentplayerDefaultsSettings());
183 $formattedMedia['settings'] = $mediaSettings;
184 $formattedMedia['deep_link_ref'] = $playerKey;
185
186 // Generate the HTML using fluent-player's view system
187 ob_start();
188 if (class_exists('\FluentPlayer\App\App')) {
189 try {
190 $fluentPlayerApp = \FluentPlayer\App\App::getInstance();
191 if ($fluentPlayerApp) {
192 $fluentPlayerApp->view->render('player', [
193 'media_id' => $mediaId,
194 'instance_id' => $instanceId,
195 'media_var_name' => $mediaVarName,
196 'player_ref' => $playerKey,
197 'settings' => $mediaSettings
198 ]);
199 }
200 } catch (\Exception $e) {
201 }
202 }
203 $html = ob_get_clean();
204 return [
205 'html' => $html ? $html . $this->generateFluentPlayerCustomStyle($instanceId, $mediaSettings) : '',
206 'media' => $formattedMedia
207 ];
208 }
209
210 private function generateFluentPlayerCustomStyle($instanceId, $settings)
211 {
212 $customCss = '';
213 $playerWidth = Arr::get($settings, 'playerWidth');
214 if ($playerWidth) {
215 $customCss .= "
216 #fluent_player_" . esc_attr($instanceId) . " .fluent-player-container {
217 width: " . esc_attr($playerWidth) . 'px' . ";
218 }
219 ";
220 }
221 $brandingColor = Arr::get($settings, 'brandColor', '');
222 if ($brandingColor) {
223 $customCss .= "
224 #fluent_player_" . esc_attr($instanceId) . " {
225 --media-brand: " . esc_attr($brandingColor) . ";
226 }
227 ";
228 }
229 $brandingColor = Arr::get($settings, 'brandColor', '');
230 $controlBarColor = Arr::get($settings, 'controlBarColor', '');
231 if ($brandingColor || $controlBarColor) {
232 $customCss .= "
233 #fluent_player_" . esc_attr($instanceId) . " {";
234 if ($brandingColor) {
235 $customCss .= "
236 --media-brand: " . esc_attr($brandingColor) . ";";
237 }
238 if ($controlBarColor) {
239 $customCss .= "
240 --fp-control-bar-bg: " . esc_attr($controlBarColor) . ";";
241 }
242 $customCss .= "
243 }
244 ";
245 }
246 if (!empty(Arr::get($settings, 'posterSrc', ''))) {
247 $customCss .= "
248 #fluent_player_" . esc_attr($instanceId) . " .fluent-player-container {
249 background-image: url('" . esc_url(Arr::get($settings, 'posterSrc', '')) . "');
250 background-size: cover;
251 background-position: center;
252 }
253 ";
254 }
255
256 $aspectRatio = Arr::get($settings, 'aspectRatio');
257 if ($aspectRatio && $aspectRatio != 'original') {
258 $cssAspectRatio = preg_replace('/^(\d+):(\d+)$/', '$1/$2', $aspectRatio);
259 $customCss .= "
260 #fluent_player_" . esc_attr($instanceId) . " {
261 aspect-ratio: " . esc_attr($cssAspectRatio) . ";
262 }
263 #fluent_player_" . esc_attr($instanceId) . " media-player[data-view-type='video'] {
264 aspect-ratio: " . esc_attr($cssAspectRatio) . ";
265 }
266 ";
267 } else {
268 $customCss .= "
269 #fluent_player_" . esc_attr($instanceId) . " {
270 min-height: 300px;
271 }
272
273 @media (max-width: 768px) {
274 #fluent_player_" . esc_attr($instanceId) . " {
275 min-height: 200px;
276 }
277 }
278 ";
279 }
280 if (!empty($customCss)) {
281 return '<style>' . $customCss . '</style>';
282 }
283 return '';
284 }
285
286 private function getFluentplayerDefaultsSettings()
287 {
288 $settings = [
289 'src' => '',
290 'title' => '',
291 'posterSrc' => '',
292 'viewType' => 'video',
293 'brandColor' => '#4a90e2',
294 'aspectRatio' => 'original',
295 'playerWidth' => '',
296 ];
297 $mediaSettings = Bootstrap::getSettings();
298 if (Arr::isTrue($mediaSettings, 'behaviors.muted_autoplay')) {
299 $mediaSettings['autoplay'] = true;
300 $mediaSettings['muted'] = true;
301 }
302 $settings['loadStrategy'] = 'idle'; // for SPA context this needs to be set to 'idle'
303 $settings = array_merge($settings, $mediaSettings);
304
305 // Apply iOS Safari compatibility settings (playsinline, preload)
306 // The standalone player gets these via MediaService::getIOSSafariSettings(),
307 // but the community API path bypasses that — apply them here too.
308 if (class_exists('\FluentPlayer\App\Services\MediaService')) {
309 [$isIosSafari, $iosSafariSettings] = \FluentPlayer\App\Services\MediaService::getIOSSafariSettings();
310 if ($isIosSafari) {
311 $settings = array_merge($settings, $iosSafariSettings);
312 }
313 }
314
315 return apply_filters('fluent_community/fluentplayer_defaults_settings', $settings);
316 }
317 }
318