1 | <?php |
||||||||
2 | /** |
||||||||
3 | * @link https://dukt.net/videos/ |
||||||||
4 | * @copyright Copyright (c) Dukt |
||||||||
5 | * @license https://github.com/dukt/videos/blob/v2/LICENSE.md |
||||||||
6 | */ |
||||||||
7 | |||||||||
8 | namespace dukt\videos\helpers; |
||||||||
9 | |||||||||
10 | use Craft; |
||||||||
11 | use craft\helpers\FileHelper; |
||||||||
12 | use dukt\videos\errors\ApiResponseException; |
||||||||
13 | use dukt\videos\models\Video; |
||||||||
14 | use dukt\videos\Plugin; |
||||||||
15 | |||||||||
16 | /** |
||||||||
17 | * Videos helper |
||||||||
18 | */ |
||||||||
19 | class VideosHelper |
||||||||
20 | { |
||||||||
21 | // Public Methods |
||||||||
22 | // ========================================================================= |
||||||||
23 | |||||||||
24 | /** |
||||||||
25 | * Formats seconds to hh:mm:ss. |
||||||||
26 | * |
||||||||
27 | * @param $seconds |
||||||||
28 | * |
||||||||
29 | * @return string |
||||||||
30 | */ |
||||||||
31 | public static function getDuration($seconds): string |
||||||||
32 | { |
||||||||
33 | $hours = (int)((int)$seconds / 3600); |
||||||||
34 | $minutes = (($seconds / 60) % 60); |
||||||||
35 | $seconds %= 60; |
||||||||
36 | |||||||||
37 | $hms = ''; |
||||||||
38 | |||||||||
39 | if ($hours > 0) { |
||||||||
40 | $hms .= str_pad($hours, 2, '0', STR_PAD_LEFT) . ':'; |
||||||||
41 | } |
||||||||
42 | |||||||||
43 | $hms .= str_pad($minutes, 2, '0', STR_PAD_LEFT) . ':'; |
||||||||
44 | |||||||||
45 | $hms .= str_pad($seconds, 2, '0', STR_PAD_LEFT); |
||||||||
46 | |||||||||
47 | return $hms; |
||||||||
48 | } |
||||||||
49 | |||||||||
50 | /** |
||||||||
51 | * Formats seconds to ISO 8601 duration |
||||||||
52 | * |
||||||||
53 | * @param $seconds |
||||||||
54 | * |
||||||||
55 | * @return string |
||||||||
56 | */ |
||||||||
57 | public static function getDuration8601($seconds): string |
||||||||
58 | { |
||||||||
59 | $hours = (int)((int)$seconds / 3600); |
||||||||
60 | $minutes = (($seconds / 60) % 60); |
||||||||
61 | $seconds %= 60; |
||||||||
62 | |||||||||
63 | $iso8601 = 'PT'; |
||||||||
64 | |||||||||
65 | if ($hours > 0) { |
||||||||
66 | $iso8601 .= sprintf('%dH', $hours); |
||||||||
67 | } |
||||||||
68 | |||||||||
69 | if ($minutes > 0) { |
||||||||
70 | $iso8601 .= sprintf('%dM', $minutes); |
||||||||
71 | } |
||||||||
72 | |||||||||
73 | return $iso8601 . sprintf('%dS', $seconds); |
||||||||
74 | } |
||||||||
75 | |||||||||
76 | /** |
||||||||
77 | * Returns a video thumbnail’s published URL. |
||||||||
78 | * |
||||||||
79 | * @param $gatewayHandle |
||||||||
80 | * @param $videoId |
||||||||
81 | * @param $size |
||||||||
82 | * |
||||||||
83 | * @return null|string |
||||||||
84 | * @throws \GuzzleHttp\Exception\GuzzleException |
||||||||
85 | * @throws \craft\errors\ImageException |
||||||||
86 | * @throws \yii\base\Exception |
||||||||
87 | * @throws \yii\base\InvalidConfigException |
||||||||
88 | */ |
||||||||
89 | public static function getVideoThumbnail($gatewayHandle, $videoId, $size): ?string |
||||||||
90 | { |
||||||||
91 | $baseDir = Craft::$app->getPath()->getRuntimePath() . DIRECTORY_SEPARATOR . 'videos' . DIRECTORY_SEPARATOR . 'thumbnails' . DIRECTORY_SEPARATOR . $gatewayHandle . DIRECTORY_SEPARATOR . $videoId; |
||||||||
92 | $originalDir = $baseDir . DIRECTORY_SEPARATOR . 'original'; |
||||||||
93 | $dir = $baseDir . DIRECTORY_SEPARATOR . $size; |
||||||||
94 | |||||||||
95 | $file = self::getThumbnailFile($dir); |
||||||||
96 | |||||||||
97 | if (!$file) { |
||||||||
98 | // Retrieve original image |
||||||||
99 | $originalPath = null; |
||||||||
100 | |||||||||
101 | if (is_dir($originalDir)) { |
||||||||
102 | $originalFiles = FileHelper::findFiles($originalDir); |
||||||||
103 | |||||||||
104 | if ($originalFiles !== []) { |
||||||||
105 | $originalPath = $originalFiles[0]; |
||||||||
106 | } |
||||||||
107 | } |
||||||||
108 | |||||||||
109 | if (!$originalPath) { |
||||||||
110 | try { |
||||||||
111 | $video = Plugin::$plugin->getVideos()->getVideoById($gatewayHandle, $videoId); |
||||||||
112 | } catch (ApiResponseException $apiResponseException) { |
||||||||
113 | Craft::info('Couldn’t get video thumbnail:' . "\r\n" |
||||||||
114 | . 'Message: ' . "\r\n" . $apiResponseException->getMessage() . "\r\n" |
||||||||
115 | . 'Trace: ' . "\r\n" . $apiResponseException->getTraceAsString(), __METHOD__); |
||||||||
116 | return null; |
||||||||
117 | } |
||||||||
118 | |||||||||
119 | $url = $video->thumbnailSource; |
||||||||
120 | |||||||||
121 | $name = pathinfo($url, PATHINFO_BASENAME); |
||||||||
122 | $originalPath = $originalDir . DIRECTORY_SEPARATOR . $name; |
||||||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||||
123 | |||||||||
124 | FileHelper::createDirectory($originalDir); |
||||||||
125 | $client = new \GuzzleHttp\Client(); |
||||||||
126 | $response = $client->request('GET', $url, [ |
||||||||
127 | 'sink' => $originalPath, |
||||||||
128 | ]); |
||||||||
129 | |||||||||
130 | // Make sure the original file has an extension |
||||||||
131 | $mimeByExt = FileHelper::getMimeTypeByExtension($originalPath); |
||||||||
132 | |||||||||
133 | if (!$mimeByExt) { |
||||||||
134 | // Add the extension to the filename if it doesn’t have one |
||||||||
135 | $mime = FileHelper::getMimeType($originalPath); |
||||||||
136 | $ext = FileHelper::getExtensionByMimeType($mime); |
||||||||
137 | |||||||||
138 | if ($ext !== '' && $ext !== '0') { |
||||||||
139 | $name .= '.' . $ext; |
||||||||
140 | $targetPath = $originalDir . DIRECTORY_SEPARATOR . $name; |
||||||||
141 | |||||||||
142 | rename($originalPath, $targetPath); |
||||||||
143 | |||||||||
144 | $originalPath = $targetPath; |
||||||||
145 | } |
||||||||
146 | } |
||||||||
147 | |||||||||
148 | if ($response->getStatusCode() !== 200) { |
||||||||
149 | return null; |
||||||||
150 | } |
||||||||
151 | } else { |
||||||||
152 | $name = pathinfo($originalPath, PATHINFO_BASENAME); |
||||||||
153 | } |
||||||||
154 | |||||||||
155 | // Generate the thumb |
||||||||
156 | $path = $dir . DIRECTORY_SEPARATOR . $name; |
||||||||
157 | FileHelper::createDirectory($dir); |
||||||||
158 | Craft::$app->getImages()->loadImage($originalPath, false, $size) |
||||||||
159 | ->scaleToFit($size, $size) |
||||||||
160 | ->saveAs(parse_url($path, PHP_URL_PATH)); |
||||||||
161 | } else { |
||||||||
162 | $name = pathinfo($file, PATHINFO_BASENAME); |
||||||||
163 | } |
||||||||
164 | |||||||||
165 | return Craft::$app->getAssetManager()->getPublishedUrl($dir, true) . sprintf('/%s', $name); |
||||||||
0 ignored issues
–
show
The call to
yii\web\AssetManager::getPublishedUrl() has too many arguments starting with true .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() Are you sure
Craft::app->getAssetMana...ublishedUrl($dir, true) of type false|mixed|string can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() It seems like
$name can also be of type array ; however, parameter $values of sprintf() does only seem to accept double|integer|string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||||
166 | } |
||||||||
167 | |||||||||
168 | /** |
||||||||
169 | * Transforms a video model into an array. |
||||||||
170 | * |
||||||||
171 | * @param Video $videoModel |
||||||||
172 | * @return array |
||||||||
173 | * @throws \GuzzleHttp\Exception\GuzzleException |
||||||||
174 | * @throws \craft\errors\ImageException |
||||||||
175 | * @throws \yii\base\Exception |
||||||||
176 | * @throws \yii\base\InvalidConfigException |
||||||||
177 | */ |
||||||||
178 | public static function videoToArray(Video $videoModel): array |
||||||||
179 | { |
||||||||
180 | $video = $videoModel->toArray([ |
||||||||
181 | 'id', |
||||||||
182 | 'gatewayHandle', |
||||||||
183 | 'title', |
||||||||
184 | 'url', |
||||||||
185 | 'authorName', |
||||||||
186 | 'authorUrl', |
||||||||
187 | 'durationSeconds', |
||||||||
188 | 'plays', |
||||||||
189 | 'private' |
||||||||
190 | ]); |
||||||||
191 | |||||||||
192 | if ($videoModel->id && $videoModel->gatewayHandle) { |
||||||||
0 ignored issues
–
show
The expression
$videoModel->id of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||||||||
193 | $video['thumbnail'] = $videoModel->thumbnailSource; |
||||||||
194 | $video['embedUrl'] = $videoModel->getEmbedUrl(); |
||||||||
195 | $video['duration'] = $videoModel->getDuration(); |
||||||||
196 | } |
||||||||
197 | |||||||||
198 | $video['errors'] = $videoModel->getErrors(); |
||||||||
199 | $video['hasErrors'] = $videoModel->hasErrors(); |
||||||||
200 | |||||||||
201 | return $video; |
||||||||
202 | } |
||||||||
203 | |||||||||
204 | // Private Methods |
||||||||
205 | // ========================================================================= |
||||||||
206 | |||||||||
207 | /** |
||||||||
208 | * Get thumbnail file. |
||||||||
209 | * |
||||||||
210 | * @param $dir |
||||||||
211 | * |
||||||||
212 | * @return null|string |
||||||||
213 | */ |
||||||||
214 | private static function getThumbnailFile($dir) |
||||||||
215 | { |
||||||||
216 | if (!is_dir($dir)) { |
||||||||
217 | return null; |
||||||||
218 | } |
||||||||
219 | |||||||||
220 | $files = FileHelper::findFiles($dir); |
||||||||
221 | |||||||||
222 | if ($files === []) { |
||||||||
223 | return null; |
||||||||
224 | } |
||||||||
225 | |||||||||
226 | return $files[0]; |
||||||||
227 | } |
||||||||
228 | } |
||||||||
229 |