From 19ad9914a394d413d2c2ace4b97da770d5f72b68 Mon Sep 17 00:00:00 2001 From: shimon Date: Tue, 22 Nov 2022 11:15:48 +0200 Subject: [PATCH] updates --- app/console | 2 +- app/controllers/api/videos.php | 30 ++++++------ app/preload.php | 6 ++- app/workers/deletes.php | 25 ++++++---- app/workers/transcoding.php | 83 ++++++++++++++-------------------- 5 files changed, 70 insertions(+), 76 deletions(-) diff --git a/app/console b/app/console index f89584bdd4..fae048b917 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit f89584bdd4ba3de07fb54cecbc275b131e23a4fb +Subproject commit fae048b91787d0b372c13caf27f14fc8b780ef60 diff --git a/app/controllers/api/videos.php b/app/controllers/api/videos.php index 00e3a40dac..ece2386221 100644 --- a/app/controllers/api/videos.php +++ b/app/controllers/api/videos.php @@ -158,8 +158,8 @@ App::put('/v1/videos/:videoId') } $file = validateFilePermissions($dbForProject, $bucketId, $fileId, $mode); - $video = Authorization::skip(function () use ($dbForProject, $videoId, $bucketId, $file) { - return $dbForProject->updateDocument('videos', $videoId, new Document([ + $video = Authorization::skip(fn() => + $dbForProject->updateDocument('videos', $videoId, new Document([ 'bucketId' => $bucketId, 'fileId' => $file->getId(), 'size' => $file->getAttribute('sizeOriginal'), @@ -172,8 +172,7 @@ App::put('/v1/videos/:videoId') 'audioCodec' => null, 'audioBitrate' => null, 'audioSamplerate' => null, - ])); - }); + ]))); $response->dynamic($video, Response::MODEL_VIDEO); }); @@ -273,16 +272,15 @@ App::post('/v1/videos/:videoId/subtitles') validateFilePermissions($dbForProject, $video['bucketId'], $video['fileId'], $mode); validateFilePermissions($dbForProject, $bucketId, $fileId, $mode); - $subtitle = Authorization::skip(function () use ($dbForProject, $videoId, $bucketId, $fileId, $name, $code, $default) { - return $dbForProject->createDocument('videos_subtitles', new Document([ + $subtitle = Authorization::skip(fn() => + $dbForProject->createDocument('videos_subtitles', new Document([ 'videoId' => $videoId, 'bucketId' => $bucketId, 'fileId' => $fileId, 'name' => $name, 'code' => $code, 'default' => $default, - ])); - }); + ]))); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($subtitle, Response::MODEL_SUBTITLE); @@ -465,6 +463,7 @@ App::delete('/v1/videos/:videoId/renditions/:renditionId') ->action(function (string $videoId, string $renditionId, Response $response, Database $dbForProject, string $mode, Device $deviceVideos) { $video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId)); + if ($video->isEmpty()) { throw new Exception(Exception::VIDEO_NOT_FOUND); } @@ -509,6 +508,7 @@ App::get('/v1/videos/:videoId/renditions/:renditionId') ->action(function ($videoId, $renditionId, Response $response, Database $dbForProject, string $mode) { $video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId)); + if ($video->isEmpty()) { throw new Exception(Exception::VIDEO_NOT_FOUND); } @@ -516,6 +516,7 @@ App::get('/v1/videos/:videoId/renditions/:renditionId') validateFilePermissions($dbForProject, $video['bucketId'], $video['fileId'], $mode); $rendition = Authorization::skip(fn() => $dbForProject->getDocument('videos_renditions', $renditionId)); + if ($rendition->isEmpty()) { throw new Exception('Video rendition not found', 404, Exception::VIDEO_RENDITION_NOT_FOUND); } @@ -541,6 +542,7 @@ App::get('/v1/videos/:videoId/renditions') ->action(function (string $videoId, Response $response, Database $dbForProject, string $mode) { $video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId)); + if ($video->isEmpty()) { throw new Exception(Exception::VIDEO_NOT_FOUND); } @@ -578,6 +580,7 @@ App::get('/v1/videos/:videoId/protocols/:protocolId') ->action(function (string $videoId, string $protocolId, Response $response, Database $dbForProject, string $mode) { $video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId)); + if ($video->isEmpty()) { throw new Exception(Exception::VIDEO_NOT_FOUND); } @@ -730,7 +733,7 @@ App::get('/v1/videos/:videoId/protocols/:protocolId/renditions/:renditionId/stre // TODO: Response model ->label('scope', 'videos.read') ->param('videoId', null, new UID(), 'Video unique ID.') - ->param('protocolId', '', new WhiteList(['hls']), 'protocol name') + ->param('protocolId', '', new WhiteList(['hls']), 'protocol name.') ->param('renditionId', '', new UID(), 'Rendition unique ID.') ->param('streamId', '', new Range(0, 10), 'Stream id.') ->inject('response') @@ -760,7 +763,7 @@ App::get('/v1/videos/:videoId/protocols/:protocolId/renditions/:renditionId/stre Query::equal('streamId', [$streamId]), ])); - if ($segments->isEmpty() || empty($segments)) { + if (empty($segments)) { throw new Exception(Exception::VIDEO_RENDITION_SEGMENT_NOT_FOUND); } @@ -779,7 +782,6 @@ App::get('/v1/videos/:videoId/protocols/:protocolId/renditions/:renditionId/stre ->send($template->render(false)); }); - App::get('/v1/videos/:videoId/protocols/:protocolId/renditions/:renditionId/segments/:segmentId') ->desc('Get video rendition segment') ->groups(['api', 'video']) @@ -816,7 +818,6 @@ App::get('/v1/videos/:videoId/protocols/:protocolId/renditions/:renditionId/segm } }); - App::get('/v1/videos/:videoId/protocols/:protocolId/subtitles/:subtitleId') ->desc('Get video subtitle') ->groups(['api', 'video']) @@ -858,7 +859,7 @@ App::get('/v1/videos/:videoId/protocols/:protocolId/subtitles/:subtitleId') Query::equal('subtitleId', [$subtitleId]), ])); - if ($segments->isEmpty() || empty($segments)) { + if (empty($segments)) { throw new Exception(Exception::VIDEO_SUBTITLE_SEGMENT_NOT_FOUND); } @@ -882,7 +883,6 @@ App::get('/v1/videos/:videoId/protocols/:protocolId/subtitles/:subtitleId') } }); - App::get('/v1/videos/:videoId/protocols/:protocolId/subtitles/:subtitleId/segments/:segmentId') ->desc('Get video subtitle segment') ->groups(['api', 'video']) @@ -1040,7 +1040,7 @@ App::get('/v1/videos/profiles') ->action(function (Response $response, Database $dbForProject) { $profiles = Authorization::skip(fn () => $dbForProject->find('videos_profiles')); - if ($profiles->isEmpty() || empty($profiles)) { + if (empty($profiles)) { throw new Exception(Exception::VIDEO_PROFILE_NOT_FOUND); } diff --git a/app/preload.php b/app/preload.php index 8789936417..a81f986637 100644 --- a/app/preload.php +++ b/app/preload.php @@ -34,8 +34,10 @@ foreach ( realpath(__DIR__ . '/../vendor/matomo'), realpath(__DIR__ . '/../vendor/symfony'), realpath(__DIR__ . '/../vendor/mongodb'), - realpath(__DIR__ . '/../vendor/utopia-php/websocket'), // TODO: remove workerman autoload - realpath(__DIR__ . '/../vendor/utopia-php/cache'), // TODO: remove memcache autoload + realpath(__DIR__ . '/../vendor/utopia-php/websocket'), + realpath(__DIR__ . '/../vendor/utopia-php/cache'), + realpath(__DIR__ . '/../vendor/alchemy'), + realpath(__DIR__ . '/../vendor/php-ffmpeg'), ] as $key => $value ) { if ($value !== false) { diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 4fcd6bccc4..6edbf6cd42 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -668,7 +668,12 @@ class DeletesV1 extends Worker $dbForProject = $this->getProjectDB($projectId); $dbForProject->deleteCollection('bucket_' . $document->getInternalId()); - $dbForProject->deleteCollection('bucket_' . $document->getInternalId() . '_video_renditions'); + $dbForProject->deleteCollection('videos_' . $document->getInternalId()); + $dbForProject->deleteCollection('videos_' . $document->getInternalId() . '_renditions'); + $dbForProject->deleteCollection('videos_' . $document->getInternalId() . '_renditions_segments'); + $dbForProject->deleteCollection('videos_' . $document->getInternalId() . '_subtitles'); + $dbForProject->deleteCollection('videos_' . $document->getInternalId() . '_subtitles_segments'); + $dbForProject->deleteCollection('videos_' . $document->getInternalId() . '_profiles'); $device = $this->getFilesDevice($projectId); $device->deletePath($document->getId()); @@ -686,25 +691,25 @@ class DeletesV1 extends Worker $videoId = $document->getId(); $dbForProject = $this->getProjectDB($projectId); - $renditions = Authorization::skip(fn() => $dbForProject->find('videos_renditions', [ - new Query('videoId', Query::TYPE_EQUAL, [$videoId])])); + $renditions = $dbForProject->find('videos_renditions', [ + new Query('videoId', Query::TYPE_EQUAL, [$videoId])]); foreach ($renditions as $rendition) { - $subtitles = Authorization::skip(fn() => $dbForProject->find('videos_subtitles', [ - new Query('videoId', Query::TYPE_EQUAL, [$videoId])])); + $subtitles = $dbForProject->find('videos_subtitles', [ + new Query('videoId', Query::TYPE_EQUAL, [$videoId])]); foreach ($subtitles as $subtitle) { - $segments = Authorization::skip(fn() => $dbForProject->find('videos_subtitles_segments', [ - new Query('subtitleId', Query::TYPE_EQUAL, [$subtitle->getId()])])); + $segments = fn() => $dbForProject->find('videos_subtitles_segments', [ + new Query('subtitleId', Query::TYPE_EQUAL, [$subtitle->getId()])]); foreach ($segments as $segment) { - Authorization::skip(fn() => $dbForProject->deleteDocument('videos_subtitles_segments', $segment->getId())); + $dbForProject->deleteDocument('videos_subtitles_segments', $segment->getId()); } - Authorization::skip(fn() => $dbForProject->deleteDocument('videos_subtitles', $subtitle->getId())); + $dbForProject->deleteDocument('videos_subtitles', $subtitle->getId()); } $this->deleteByGroup('videos_renditions_segments', [ new Query('renditionId', Query::TYPE_EQUAL, [$rendition->getId()]) ], $dbForProject); - Authorization::skip(fn() => $dbForProject->deleteDocument('videos_renditions', $rendition->getId())); + $dbForProject->deleteDocument('videos_renditions', $rendition->getId()); } $videosDevice = $this->getVideoDevice($projectId); diff --git a/app/workers/transcoding.php b/app/workers/transcoding.php index 8ad919b24a..ca4ec8c639 100644 --- a/app/workers/transcoding.php +++ b/app/workers/transcoding.php @@ -1,5 +1,6 @@ args['project']); $this->database = $this->getProjectDB($project->getId()); - $sourceVideo = Authorization::skip(fn() => $this->database->findOne('videos', [ + $sourceVideo = $this->database->findOne('videos', [ Query::equal('_uid', [$this->args['videoId']]), - ])); + ]); if (empty($sourceVideo)) { throw new Exception(Exception::VIDEO_NOT_FOUND); } - $profile = Authorization::skip(fn() => $this->database->findOne('videos_profiles', [ + $profile = $this->database->findOne('videos_profiles', [ Query::equal('_uid', [$this->args['profileId']]), - ])); + ]); if (empty($profile)) { throw new Exception(Exception::VIDEO_PROFILE_NOT_FOUND); } - $bucket = Authorization::skip( - fn() => $this->database->getDocument('buckets', $sourceVideo->getAttribute('bucketId')) - ); - - $file = Authorization::skip( - fn() => $this->database->getDocument('bucket_' . $bucket->getInternalId(), $sourceVideo->getAttribute('fileId')) - ); - + $bucket = $this->database->getDocument('buckets', $sourceVideo->getAttribute('bucketId')); + $file = $this->database->getDocument('bucket_' . $bucket->getInternalId(), $sourceVideo->getAttribute('fileId')); $path = basename($file->getAttribute('path')); $inPath = $this->inDir . $path; - $result = $this->writeData($project, $file); + if (empty($result)) { throw new Exception(Exception::GENERAL_UNKNOWN); } @@ -126,6 +120,7 @@ class TranscodingV1 extends Worker $audioStreamCount = $ffprobe->streams($inPath)->audios()->count(); $videoStreamCount = $ffprobe->streams($inPath)->videos()->count(); $streams = $ffprobe->streams($inPath); + $sourceVideo ->setAttribute('duration', $videoStreamCount > 0 ? $streams->videos()->first()->get('duration') : null) ->setAttribute('height', $videoStreamCount > 0 ? $streams->videos()->first()->get('height') : null) @@ -137,36 +132,32 @@ class TranscodingV1 extends Worker ->setAttribute('audioSamplerate', $audioStreamCount > 0 ? $streams->audios()->first()->get('sample_rate') : null) ->setAttribute('audioBitrate', $audioStreamCount > 0 ? $streams->audios()->first()->get('bit_rate') : null) ; - Authorization::skip(fn() => $this->database->updateDocument( + + $this->database->updateDocument( 'videos', $sourceVideo->getId(), $sourceVideo - )); + ); $video = $ffmpeg->open($inPath); $this->setRenditionName($profile); $subs = []; - $subtitles = Authorization::skip(fn() => $this->database->find('videos_subtitles', [ + $subtitles = $this->database->find('videos_subtitles', [ Query::equal('videoId', [$this->args['videoId']]), Query::equal('status', ['']), - ])); + ]); foreach ($subtitles as $subtitle) { $subtitle->setAttribute('status', self::STATUS_START); - Authorization::skip(fn() => $this->database->updateDocument( + $this->database->updateDocument( 'videos_subtitles', $subtitle->getId(), $subtitle - )); - - $bucket = Authorization::skip( - fn() => $this->database->getDocument('buckets', $subtitle->getAttribute('bucketId')) ); - $file = Authorization::skip( - fn() => $this->database->getDocument('bucket_' . $bucket->getInternalId(), $subtitle->getAttribute('fileId')) - ); + $bucket = $this->database->getDocument('buckets', $subtitle->getAttribute('bucketId')); + $file = $this->database->getDocument('bucket_' . $bucket->getInternalId(), $subtitle->getAttribute('fileId')); $path = basename($file->getAttribute('path')); $this->writeData($project, $file); @@ -185,8 +176,7 @@ class TranscodingV1 extends Worker ]; } - $query = Authorization::skip(function () use ($profile) { - return $this->database->createDocument('videos_renditions', new Document([ + $query = $this->database->createDocument('videos_renditions', new Document([ 'videoId' => $this->args['videoId'], 'profileId' => $profile->getId(), 'name' => $this->getRenditionName(), @@ -194,7 +184,6 @@ class TranscodingV1 extends Worker 'status' => self::STATUS_START, 'protocol' => $profile->getAttribute('protocol'), ])); - }); $renditionRootPath = $this->getVideoDevice($project->getId())->getPath($this->args['videoId']) . '/'; $renditionPath = $renditionRootPath . $this->getRenditionName() . '-' . $query->getId() . '/'; @@ -210,11 +199,11 @@ class TranscodingV1 extends Worker $format->on('progress', function ($video, $format, $percentage) use ($query) { if ($percentage % 3 === 0) { $query->setAttribute('progress', (string)$percentage); - Authorization::skip(fn() => $this->database->updateDocument( + $this->database->updateDocument( 'videos_renditions', $query->getId(), $query - )); + ); } }); @@ -231,15 +220,13 @@ class TranscodingV1 extends Worker $m3u8 = $this->getHlsSegments($this->outDir . $stream['path']); if (!empty($m3u8['segments'])) { foreach ($m3u8['segments'] as $segment) { - Authorization::skip(function () use ($segment, $project, $query, $renditionPath, $stream) { - return $this->database->createDocument('videos_renditions_segments', new Document([ + $this->database->createDocument('videos_renditions_segments', new Document([ 'renditionId' => $query->getId(), 'streamId' => (int)$stream['id'], 'fileName' => $segment['fileName'], 'path' => $renditionPath, 'duration' => $segment['duration'], ])); - }); } } @@ -250,15 +237,13 @@ class TranscodingV1 extends Worker $mpd = $this->getDashSegments($this->outPath . '.mpd'); if (!empty($mpd['segments'])) { foreach ($mpd['segments'] as $segment) { - Authorization::skip(function () use ($segment, $project, $query, $renditionPath) { - return $this->database->createDocument('videos_renditions_segments', new Document([ + $this->database->createDocument('videos_renditions_segments', new Document([ 'renditionId' => $query->getId(), 'streamId' => $segment['streamId'], 'fileName' => $segment['fileName'], 'path' => $renditionPath, 'isInit' => $segment['isInit'], ])); - }); } } @@ -269,20 +254,18 @@ class TranscodingV1 extends Worker $query->setAttribute('status', self::STATUS_END); $query->setAttribute('endedAt', DateTime::now()); - Authorization::skip(fn() => $this->database->updateDocument('videos_renditions', $query->getId(), $query)); + $this->database->updateDocument('videos_renditions', $query->getId(), $query); foreach ($subtitles ?? [] as $subtitle) { if ($profile->getAttribute('protocol') === 'hls') { $m3u8 = $this->getHlsSegments($this->outPath . '_subtitles_' . $subtitle['code'] . '.m3u8'); foreach ($m3u8['segments'] ?? [] as $segment) { - Authorization::skip(function () use ($segment, $project, $subtitle, $renditionRootPath) { - return $this->database->createDocument('videos_subtitles_segments', new Document([ + $this->database->createDocument('videos_subtitles_segments', new Document([ 'subtitleId' => $subtitle->getId(), 'fileName' => $segment['fileName'], 'path' => $renditionRootPath , 'duration' => $segment['duration'], ])); - }); } $subtitle->setAttribute('targetDuration', $m3u8['targetDuration']); } else { @@ -291,11 +274,11 @@ class TranscodingV1 extends Worker $subtitle->setAttribute('status', self::STATUS_READY); $subtitle->setAttribute('path', $renditionRootPath); - Authorization::skip(fn() => $this->database->updateDocument( + $this->database->updateDocument( 'videos_subtitles', $subtitle->getId(), $subtitle - )); + ); } /** Upload & cleanup **/ @@ -318,14 +301,14 @@ class TranscodingV1 extends Worker $query->setAttribute('progress', '100'); $query->setAttribute('status', self::STATUS_UPLOADING); $query->setAttribute('path', $renditionPath); - Authorization::skip(fn() => $this->database->updateDocument('videos_renditions', $query->getId(), $query)); + $this->database->updateDocument('videos_renditions', $query->getId(), $query); $start = 1; } //@unlink($this->outDir . $fileName); } $query->setAttribute('status', self::STATUS_READY); - Authorization::skip(fn() => $this->database->updateDocument('videos_renditions', $query->getId(), $query)); + $this->database->updateDocument('videos_renditions', $query->getId(), $query); } catch (\Throwable $th) { $query->setAttribute('metadata', json_encode([ 'code' => $th->getCode(), @@ -333,7 +316,7 @@ class TranscodingV1 extends Worker ])); $query->setAttribute('status', self::STATUS_ERROR); - Authorization::skip(fn() => $this->database->updateDocument('videos_renditions', $query->getId(), $query)); + $this->database->updateDocument('videos_renditions', $query->getId(), $query); throw new Exception($th->getMessage(), 500, Exception::GENERAL_UNKNOWN); } } @@ -348,6 +331,7 @@ class TranscodingV1 extends Worker */ private function transcode(string $protocol, Media $video, StreamFormat $format, Representation $representation, array $subtitles): string | array { + // $video->filters() // ->framerate(new FFMpeg\Coordinate\FrameRate(24), 2) // ; @@ -401,6 +385,7 @@ class TranscodingV1 extends Worker */ private function getDashSegments(string $path): array { + $segments = []; $metadata = null; $handle = fopen($path, "r"); @@ -437,6 +422,7 @@ class TranscodingV1 extends Worker */ private function getHlsSegmentsUrls(string $path): array { + $files = []; $handle = fopen($path, "r"); if ($handle) { @@ -482,6 +468,7 @@ class TranscodingV1 extends Worker */ private function getHlsSegments(string $path): array { + $segments = []; $targetDuration = 0; $handle = fopen($path, "r"); @@ -518,6 +505,7 @@ class TranscodingV1 extends Worker */ private function getVideoStreamInfo(array $metadata, RepresentationInterface $representation): array { + $info = []; // if (!empty($metadata['stream']['resolutions'][0])) { // $general = $metadata['stream']['resolutions'][0]; @@ -549,7 +537,6 @@ class TranscodingV1 extends Worker private function writeData(Document $project, Document $file): bool { - $fullPath = $file->getAttribute('path'); $path = basename($file->getAttribute('path'));