nystudio107 /
craft-transcoder
| 1 | <?php |
||||
| 2 | /** |
||||
| 3 | * Transcoder plugin for Craft CMS |
||||
| 4 | * |
||||
| 5 | * Transcode videos to various formats, and provide thumbnails of the video |
||||
| 6 | * |
||||
| 7 | * @link https://nystudio107.com |
||||
|
0 ignored issues
–
show
Coding Style
introduced
by
Loading history...
|
|||||
| 8 | * @copyright Copyright (c) 2017 nystudio107 |
||||
|
0 ignored issues
–
show
|
|||||
| 9 | */ |
||||
|
0 ignored issues
–
show
|
|||||
| 10 | |||||
| 11 | namespace nystudio107\transcoder\controllers; |
||||
| 12 | |||||
| 13 | use Craft; |
||||
| 14 | use craft\errors\AssetDisallowedExtensionException; |
||||
| 15 | use craft\helpers\Path as PathHelper; |
||||
| 16 | use craft\web\Controller; |
||||
| 17 | use nystudio107\transcoder\Transcoder; |
||||
| 18 | use yii\base\ExitException; |
||||
| 19 | use yii\web\BadRequestHttpException; |
||||
| 20 | use yii\web\Response; |
||||
| 21 | use function count; |
||||
| 22 | use function is_array; |
||||
| 23 | |||||
| 24 | /** |
||||
|
0 ignored issues
–
show
|
|||||
| 25 | * @author nystudio107 |
||||
|
0 ignored issues
–
show
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
|
|||||
| 26 | * @package Transcode |
||||
|
0 ignored issues
–
show
|
|||||
| 27 | * @since 1.0.0 |
||||
|
0 ignored issues
–
show
|
|||||
| 28 | */ |
||||
|
0 ignored issues
–
show
|
|||||
| 29 | class DefaultController extends Controller |
||||
| 30 | { |
||||
| 31 | // Protected Properties |
||||
| 32 | // ========================================================================= |
||||
| 33 | |||||
| 34 | protected array|bool|int $allowAnonymous = [ |
||||
| 35 | 'download-file', |
||||
| 36 | 'progress', |
||||
| 37 | ]; |
||||
| 38 | |||||
| 39 | // Public Methods |
||||
| 40 | // ========================================================================= |
||||
| 41 | |||||
| 42 | /** |
||||
|
0 ignored issues
–
show
|
|||||
| 43 | * @inheritDoc |
||||
| 44 | */ |
||||
|
0 ignored issues
–
show
|
|||||
| 45 | public function beforeAction($action): bool |
||||
| 46 | { |
||||
| 47 | if (!Transcoder::$settings->enableDownloadFileEndpoint) { |
||||
| 48 | $this->allowAnonymous = false; |
||||
| 49 | } |
||||
| 50 | |||||
| 51 | return parent::beforeAction($action); |
||||
| 52 | } |
||||
| 53 | |||||
| 54 | /** |
||||
| 55 | * Force the download of a given $url. We do it this way to prevent people |
||||
| 56 | * from downloading things that are outside of the server root. |
||||
| 57 | * |
||||
| 58 | * @param $url |
||||
|
0 ignored issues
–
show
|
|||||
| 59 | * |
||||
| 60 | * @throws AssetDisallowedExtensionException |
||||
| 61 | * @throws BadRequestHttpException |
||||
| 62 | * @throws ExitException |
||||
| 63 | */ |
||||
|
0 ignored issues
–
show
|
|||||
| 64 | public function actionDownloadFile($url): void |
||||
| 65 | { |
||||
| 66 | $filePath = parse_url($url, PHP_URL_PATH); |
||||
| 67 | // Remove any relative paths |
||||
| 68 | if (!PathHelper::ensurePathIsContained($filePath)) { |
||||
| 69 | throw new BadRequestHttpException('Invalid resource path: ' . $filePath); |
||||
| 70 | } |
||||
| 71 | // Only work for `allowedFileExtensions` file extensions |
||||
| 72 | $extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION)); |
||||
|
0 ignored issues
–
show
It seems like
pathinfo($filePath, nyst...ers\PATHINFO_EXTENSION) can also be of type array; however, parameter $string of strtolower() does only seem to accept 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
Loading history...
|
|||||
| 73 | $allowedExtensions = Craft::$app->getConfig()->getGeneral()->allowedFileExtensions; |
||||
| 74 | if (!in_array($extension, $allowedExtensions, true)) { |
||||
| 75 | throw new AssetDisallowedExtensionException("File “{$filePath}” cannot be downloaded because “{$extension}” is not allowed."); |
||||
| 76 | } |
||||
| 77 | |||||
| 78 | $filePath = $_SERVER['DOCUMENT_ROOT'] . $filePath; |
||||
| 79 | Craft::$app->getResponse()->sendFile( |
||||
| 80 | $filePath, |
||||
| 81 | null, |
||||
| 82 | ['inline' => false] |
||||
| 83 | ); |
||||
| 84 | Craft::$app->end(); |
||||
| 85 | } |
||||
| 86 | |||||
| 87 | /** |
||||
| 88 | * Return a JSON-encoded array providing the progress of the transcoding: |
||||
| 89 | * |
||||
| 90 | * 'filename' => the name of the file |
||||
| 91 | * 'duration' => the duration of the video/audio stream |
||||
| 92 | * 'time' => the time of the current encoding |
||||
| 93 | * 'progress' => a percentage indicating how much of the encoding is done |
||||
| 94 | * |
||||
| 95 | * @param $filename |
||||
|
0 ignored issues
–
show
|
|||||
| 96 | * |
||||
| 97 | * @return Response |
||||
| 98 | */ |
||||
| 99 | public function actionProgress($filename): Response |
||||
| 100 | { |
||||
| 101 | $result = []; |
||||
| 102 | $progressFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $filename . '.progress'; |
||||
| 103 | if (file_exists($progressFile)) { |
||||
| 104 | $content = @file_get_contents($progressFile); |
||||
| 105 | if ($content) { |
||||
| 106 | // get duration of source |
||||
| 107 | preg_match('/Duration: (.*?), start:/', $content, $matches); |
||||
| 108 | if (count($matches) > 0) { |
||||
| 109 | $rawDuration = $matches[1]; |
||||
| 110 | |||||
| 111 | // rawDuration is in 00:00:00.00 format. This converts it to seconds. |
||||
| 112 | $ar = array_reverse(explode(':', $rawDuration)); |
||||
| 113 | $duration = (float)$ar[0]; |
||||
| 114 | if (!empty($ar[1])) { |
||||
| 115 | $duration += (int)$ar[1] * 60; |
||||
| 116 | } |
||||
| 117 | if (!empty($ar[2])) { |
||||
| 118 | $duration += (int)$ar[2] * 60 * 60; |
||||
| 119 | } |
||||
| 120 | } else { |
||||
| 121 | $duration = 'unknown'; // with GIF as input, duration is unknown |
||||
| 122 | } |
||||
| 123 | |||||
| 124 | // Get the time in the file that is already encoded |
||||
| 125 | preg_match_all('/time=(.*?) bitrate/', $content, $matches); |
||||
| 126 | $rawTime = array_pop($matches); |
||||
| 127 | |||||
| 128 | // this is needed if there is more than one match |
||||
| 129 | if (is_array($rawTime)) { |
||||
| 130 | $rawTime = array_pop($rawTime); |
||||
| 131 | } |
||||
| 132 | |||||
| 133 | //rawTime is in 00:00:00.00 format. This converts it to seconds. |
||||
| 134 | $ar = array_reverse(explode(':', $rawTime)); |
||||
| 135 | $time = (float)$ar[0]; |
||||
| 136 | if (!empty($ar[1])) { |
||||
| 137 | $time += (int)$ar[1] * 60; |
||||
| 138 | } |
||||
| 139 | if (!empty($ar[2])) { |
||||
| 140 | $time += (int)$ar[2] * 60 * 60; |
||||
| 141 | } |
||||
| 142 | |||||
| 143 | //calculate the progress |
||||
| 144 | if ($duration !== 'unknown') { |
||||
| 145 | $progress = round(($time / $duration) * 100); |
||||
| 146 | } else { |
||||
| 147 | $progress = 'unknown'; |
||||
| 148 | } |
||||
| 149 | |||||
| 150 | // return results |
||||
| 151 | if ($progress !== 'unknown' && $progress < 100) { |
||||
| 152 | $result = [ |
||||
| 153 | 'filename' => $filename, |
||||
| 154 | 'duration' => $duration, |
||||
| 155 | 'time' => $time, |
||||
| 156 | 'progress' => $progress, |
||||
| 157 | ]; |
||||
| 158 | } elseif ($progress === 'unknown') { |
||||
| 159 | $result = [ |
||||
| 160 | 'filename' => $filename, |
||||
| 161 | 'duration' => 'unknown', |
||||
| 162 | 'time' => $time, |
||||
| 163 | 'progress' => 'unknown', |
||||
| 164 | 'message' => 'encoding GIF, can\'t determine duration', |
||||
| 165 | ]; |
||||
| 166 | } |
||||
| 167 | } |
||||
| 168 | } |
||||
| 169 | |||||
| 170 | return $this->asJson($result); |
||||
| 171 | } |
||||
| 172 | } |
||||
| 173 |