From aa8334e7d8efbbb991fe9884d4a925970f8531f3 Mon Sep 17 00:00:00 2001 From: shimon Date: Mon, 13 Mar 2023 10:16:48 +0200 Subject: [PATCH] subtitle lan code --- app/config/errors.php | 10 +++ app/controllers/api/videos.php | 30 ++++++--- src/Appwrite/Extend/Exception.php | 2 +- .../Videos/VideosCustomServerTest.php | 67 ++++++++++--------- 4 files changed, 69 insertions(+), 40 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index aaab6f5f98..8377b12bcd 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -569,6 +569,16 @@ return [ 'description' => 'Video subtitle not found.', 'code' => 404, ], + Exception::VIDEO_LANGUAGE_CODE_NOT_VALID => [ + 'name' => Exception::VIDEO_LANGUAGE_CODE_NOT_VALID, + 'description' => 'Video language code not valid.', + 'code' => 400, + ], + Exception::VIDEO_SUBTITLE_NOT_VALID => [ + 'name' => Exception::VIDEO_SUBTITLE_NOT_VALID, + 'description' => 'Video subtitle not valid.', + 'code' => 400, + ], Exception::VIDEO_NOT_FOUND => [ 'name' => Exception::VIDEO_NOT_FOUND, 'description' => 'Video not found.', diff --git a/app/controllers/api/videos.php b/app/controllers/api/videos.php index 32a01a7339..46f4beea40 100644 --- a/app/controllers/api/videos.php +++ b/app/controllers/api/videos.php @@ -163,6 +163,15 @@ App::put('/v1/videos/:videoId') validateFilePermissions($dbForProject, $video['bucketId'], $video['fileId'], $mode); $file = validateFilePermissions($dbForProject, $bucketId, $fileId, $mode); + + if ( + !str_starts_with($file->getAttribute('mimeType'), 'video/') && + !str_starts_with($file->getAttribute('mimeType'), 'audio/') && + $file->getAttribute('mimeType') !== 'application/ogg' + ) { + throw new Exception(Exception::VIDEO_NOT_VALID); + } + $video = Authorization::skip(fn() => $dbForProject->updateDocument('videos', $videoId, new Document([ 'bucketId' => $bucketId, @@ -275,7 +284,7 @@ App::post('/v1/videos/:videoId/subtitles') ->param('videoId', null, new UID(), 'Video unique ID.') ->param('bucketId', '', new CustomId(), 'Subtitle bucket unique ID.') ->param('fileId', '', new CustomId(), 'Subtitle file unique ID.') - ->param('name', '', new Text(128), 'Subtitle name.') + ->param('name', '', new Text(32), 'Subtitle name.') ->param('code', '', new Text(3), 'Subtitle ISO 639-2 three letters alpha code.') ->param('default', false, new Boolean(true), 'Default subtitle.') ->inject('request') @@ -289,7 +298,7 @@ App::post('/v1/videos/:videoId/subtitles') $found = array_search($code, array_column($languages, 'code2')); if (!$found) { - throw new Exception(Exception::VIDEO_LANGUAGE_CODE_NOT_FOUND); + throw new Exception(Exception::VIDEO_LANGUAGE_CODE_NOT_VALID); } $video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId)); @@ -301,15 +310,10 @@ App::post('/v1/videos/:videoId/subtitles') validateFilePermissions($dbForProject, $video['bucketId'], $video['fileId'], $mode); $file = validateFilePermissions($dbForProject, $bucketId, $fileId, $mode); - if ( - $file->getAttribute('mimeType') !== 'text/vtt' && - $file->getAttribute('mimeType') !== 'text/plain' - ) { + if (!in_array($file->getAttribute('mimeType'), ['text/vtt','text/plain'])) { throw new Exception(Exception::VIDEO_SUBTITLE_NOT_VALID); } - - $subtitle = Authorization::skip(fn() => $dbForProject->createDocument('videos_subtitles', new Document([ 'videoId' => $videoId, @@ -374,12 +378,20 @@ App::patch('/v1/videos/:videoId/subtitles/:subtitleId') ->param('bucketId', '', new CustomId(), 'Subtitle bucket unique ID.') ->param('fileId', '', new CustomId(), 'Subtitle file unique ID.') ->param('name', '', new Text(32), 'Subtitle customized name.') - ->param('code', '', new Text(3), 'Subtitle 3 letter code name.') + ->param('code', '', new Text(3), 'Subtitle ISO 639-2 three letters alpha code.') ->param('default', false, new Boolean(true), 'Default subtitle.') ->inject('response') ->inject('dbForProject') ->action(action: function (string $subtitleId, string $videoId, string $bucketId, string $fileId, string $name, string $code, bool $default, Response $response, Database $dbForProject) { + $code = strtolower($code); + $languages = Config::getParam('locale-languages'); + $found = array_search($code, array_column($languages, 'code2')); + + if (!$found) { + throw new Exception(Exception::VIDEO_LANGUAGE_CODE_NOT_VALID); + } + $subtitle = Authorization::skip(fn() => $dbForProject->getDocument('videos_subtitles', $subtitleId)); if ($subtitle->isEmpty()) { diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 9237be6442..0d5a5b90a2 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -186,7 +186,7 @@ class Exception extends \Exception public const VIDEO_SUBTITLE_NOT_FOUND = 'video_subtitle_not_found'; public const VIDEO_NOT_FOUND = 'video_not_found'; public const VIDEO_RENDITION_SEGMENT_NOT_FOUND = 'video_rendition_segment_not_found'; - public const VIDEO_LANGUAGE_CODE_NOT_FOUND = 'video_language_code_not_found'; + public const VIDEO_LANGUAGE_CODE_NOT_VALID = 'video_language_code_not_valid'; public const VIDEO_SUBTITLE_SEGMENT_NOT_FOUND = 'video_subtitle_segment_not_found'; public const VIDEO_SUBTITLE_NOT_VALID = 'video_subtitle_not_valid'; diff --git a/tests/e2e/Services/Videos/VideosCustomServerTest.php b/tests/e2e/Services/Videos/VideosCustomServerTest.php index 2a9577a6dd..6c1a42708b 100644 --- a/tests/e2e/Services/Videos/VideosCustomServerTest.php +++ b/tests/e2e/Services/Videos/VideosCustomServerTest.php @@ -200,6 +200,7 @@ class VideosCustomServerTest extends Scope 'fileId' => $this->getSubtitle()['$id'] ]); + $this->assertEquals(400, $response['headers']['status-code']); $response = $this->client->call(Client::METHOD_POST, '/videos', [ @@ -235,7 +236,7 @@ class VideosCustomServerTest extends Scope 'code' => 'xxx', 'default' => true, ]); - $this->assertEquals(404, $response['headers']['status-code']); + $this->assertEquals(400, $response['headers']['status-code']); $response = $this->client->call(Client::METHOD_POST, '/videos/' . $videoId . '/subtitles', [ 'content-type' => 'application/json', @@ -301,7 +302,7 @@ class VideosCustomServerTest extends Scope $this->assertNotEmpty($response['body']['subtitles']); $this->assertNotEmpty($response['body']['subtitles'][0]['$id']); $this->assertEquals('English', $response['body']['subtitles'][0]['name']); - $this->assertEquals('Eng', $response['body']['subtitles'][0]['code']); + $this->assertEquals('eng', $response['body']['subtitles'][0]['code']); $this->assertEquals(true, $response['body']['subtitles'][0]['default']); return $response['body']['subtitles']; @@ -525,7 +526,6 @@ class VideosCustomServerTest extends Scope $this->assertEquals(2, $response['body']['total']); - return $videoId; } @@ -535,7 +535,7 @@ class VideosCustomServerTest extends Scope public function testGetRenditions(string $videoId): string { - sleep(50); + sleep(30); $response = $this->client->call(Client::METHOD_GET, '/videos/' . $videoId . '/renditions', [ 'content-type' => 'application/json', @@ -543,30 +543,36 @@ class VideosCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'], ]); + $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(2, $response['body']['total']); $this->assertNotEmpty($response['body']['renditions']); + $rendition = $response['body']['renditions'][0]; + $this->assertEquals('600X400@834', $rendition['name']); + $this->assertEquals('600', $rendition['width']); + $this->assertEquals('400', $rendition['height']); + $this->assertEquals('770', $rendition['videoBitRate']); + $this->assertEquals('64', $rendition['audioBitRate']); + $this->assertEquals('ready', $rendition['status']); + $this->assertEquals('100', $rendition['progress']); + + $rendition = $response['body']['renditions'][1]; + $this->assertEquals('300X200@634', $rendition['name']); + $this->assertEquals('300', $rendition['width']); + $this->assertEquals('200', $rendition['height']); + $this->assertEquals('570', $rendition['videoBitRate']); + $this->assertEquals('64', $rendition['audioBitRate']); + $this->assertEquals('ready', $rendition['status']); + $this->assertEquals('100', $rendition['progress']); + $renditionId = $response['body']['renditions'][0]['$id']; - foreach ($response['body']['renditions'] as $rendition) { - $this->assertEquals('600X400@834', $rendition['name']); - $this->assertEquals('600', $rendition['width']); - $this->assertEquals('400', $rendition['height']); - $this->assertEquals('770', $rendition['videoBitRate']); - $this->assertEquals('64', $rendition['audioBitRate']); - $this->assertEquals('ready', $rendition['status']); - $this->assertEquals('100', $rendition['progress']); - } - - $this->assertEquals(200, $response['headers']['status-code']); - $response = $this->client->call(Client::METHOD_GET, '/videos/' . $videoId . '/renditions/' . $renditionId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]); - $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($renditionId, $response['body']['$id']); @@ -585,17 +591,19 @@ class VideosCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); - preg_match_all('#\b/videos[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#', $response['body'], $match); - $this->assertEquals(4, count($match[0])); - $renditionUri = $match[0][0]; - $subtitleUri = $match[0][1]; + + $this->assertEquals(3, count($match[0])); + $subtitleUri = $match[0][0]; + $renditionUri = $match[0][2]; + $response = $this->client->call(Client::METHOD_GET, $renditionUri, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]); + $this->assertEquals(200, $response['headers']['status-code']); preg_match_all('#\b/videos[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#', $response['body'], $match); $this->assertEquals(10, count($match[0])); @@ -635,28 +643,27 @@ class VideosCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $xml = simplexml_load_string($response['body']); - $this->assertEquals("PT1M32.7S", $xml->attributes()->mediaPresentationDuration); $this->assertEquals("PT10.0S", $xml->attributes()->maxSegmentDuration); $this->assertEquals("PT20.0S", $xml->attributes()->minBufferTime); $subsCount = 0; $isVideo = false; $isAudio = false; - $subs[] = ['id' => '2', 'lang' => 'Eng',]; - $subs[] = ['id' => '3', 'lang' => 'It',]; + $subs[] = ['id' => '2', 'lang' => 'English',]; + $subs[] = ['id' => '3', 'lang' => 'Italian',]; foreach ($xml->Period->AdaptationSet as $adaptation) { if ((string)$adaptation['contentType'] === 'video') { $isVideo = true; $this->assertEquals("50/1", $adaptation['frameRate']); - $this->assertEquals("300", $adaptation['maxWidth']); + $this->assertEquals("960", $adaptation['maxWidth']); $this->assertEquals("30:17", $adaptation['par']); $this->assertEquals("und", $adaptation['lang']); foreach ($adaptation->Representation as $representation) { $this->assertEquals("video/mp4", $representation['mimeType']); - $this->assertEquals("avc1.640015", $representation['codecs']); - $this->assertEquals("300", $representation['width']); - $this->assertEquals("200", $representation['height']); - $this->assertEquals("20:17", $representation['sar']); + $this->assertEquals("avc1.64001f", $representation['codecs']); + $this->assertEquals("960", $representation['width']); + $this->assertEquals("544", $representation['height']); + $this->assertEquals("1:1", $representation['sar']); $this->assertEquals(10, $representation->SegmentList->SegmentURL->count()); $videoSegmentBaseUrl = (string)$representation->BaseURL; $videoSegmentInitialization = (string)$representation->SegmentList->Initialization['sourceURL']; @@ -825,7 +832,7 @@ class VideosCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); preg_match_all('#\b/videos[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#', $response['body'], $match); - + var_dump($response['body']); $this->assertEquals(2, count($match[0])); $renditionUri = $match[0][0];