subtitles crud

This commit is contained in:
shimon
2022-07-14 17:54:43 +03:00
parent 2a02784b0c
commit 817793a587
6 changed files with 204 additions and 27 deletions
+40 -17
View File
@@ -195,7 +195,7 @@ App::get('/v1/videos/profiles/:profileId')
$profile = Authorization::skip(fn() => $dbForProject->getDocument('videos_profiles', $profileId));
if ($profile->isEmpty()) {
throw new Exception('Video profile not found', 404, Exception::PROFILES_NOT_FOUND);
throw new Exception('Video profile not found', 404, Exception::VIDEO_PROFILE_NOT_FOUND);
}
$response->dynamic($profile, Response::MODEL_VIDEO_PROFILE);
@@ -219,7 +219,7 @@ App::get('/v1/videos/profiles')
$profiles = Authorization::skip(fn () => $dbForProject->find('videos_profiles', [], 12, 0, [], ['ASC']));
if (empty($profiles)) {
throw new Exception('Video profiles where not found', 404, Exception::PROFILES_NOT_FOUND);
throw new Exception('Video profiles where not found', 404, Exception::VIDEO_PROFILE_NOT_FOUND);
}
$response->dynamic(new Document([
@@ -246,7 +246,7 @@ App::delete('/v1/videos/profiles/:profileId')
$profile = Authorization::skip(fn() => $dbForProject->getDocument('videos_profiles', $profileId));
if ($profile->isEmpty()) {
throw new Exception('Video profile not found', 404, Exception::PROFILES_NOT_FOUND);
throw new Exception('Video profile not found', 404, Exception::VIDEO_PROFILE_NOT_FOUND);
}
$deleted = $dbForProject->deleteDocument('videos_profiles', $profileId);
@@ -260,8 +260,7 @@ App::delete('/v1/videos/profiles/:profileId')
App::post('/v1/videos/:videoId/subtitles')
->alias('/v1/videos/:videoId/subtitles', [])
->desc('Attach a subtitle file to a video')
->desc('Add subtitle to video')
->groups(['api', 'video'])
->label('scope', 'files.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -299,7 +298,7 @@ App::post('/v1/videos/:videoId/subtitles')
validateFilePermissions($dbForProject, $bucketId, $fileId, $mode, $user);
try {
Authorization::skip(function () use ($dbForProject, $videoId, $bucketId, $fileId, $name, $code, $default) {
$subtitle = Authorization::skip(function () use ($dbForProject, $videoId, $bucketId, $fileId, $name, $code, $default) {
return $dbForProject->createDocument('videos_subtitles', new Document([
'videoId' => $videoId,
'bucketId' => $bucketId,
@@ -313,9 +312,37 @@ App::post('/v1/videos/:videoId/subtitles')
throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
}
$response->json(['result' => 'ok']);
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($subtitle, Response::MODEL_VIDEO_SUBTITLE);
});
App::get('/v1/videos/:videoId/subtitles')
->desc('Get all video subtitles')
->groups(['api', 'video'])
->label('scope', 'files.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'video')
->label('sdk.method', 'getSubtitles')
->label('sdk.description', '/docs/references/videos/get-subtitles.md') // TODO: Create markdown
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VIDEO_SUBTITLE_LIST)
->param('videoId', null, new UID(), 'Video unique ID.')
->inject('response')
->inject('dbForProject')
->action(function ($videoId, Response $response, Database $dbForProject) {
$subtitles = Authorization::skip(fn () => $dbForProject->find('videos_subtitles', [new Query('videoId', Query::TYPE_EQUAL, [$videoId])], 12, 0, [], ['ASC']));
if (empty($subtitles)) {
throw new Exception('Video subtitles not found', 404, Exception::VIDEO_SUBTITLE_NOT_FOUND);
}
$response->dynamic(new Document([
'total' => $dbForProject->count('videos_subtitles', [], APP_LIMIT_COUNT),
'subtitles' => $subtitles,
]), Response::MODEL_VIDEO_SUBTITLE_LIST);
});
App::post('/v1/video')
->desc('Create Video')
@@ -344,19 +371,15 @@ App::post('/v1/video')
/** @var Utopia\Database\Document $project */
$file = validateFilePermissions($dbForProject, $bucketId, $fileId, $mode, $user);
try {
$video = Authorization::skip(function () use ($dbForProject, $bucketId, $file) {
$video = Authorization::skip(function () use ($dbForProject, $bucketId, $file) {
return $dbForProject->createDocument('videos', new Document([
'bucketId' => $bucketId,
'fileId' => $file->getId(),
'size' => $file->getAttribute('sizeOriginal'),
]));
});
} catch (StructureException $exception) {
throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
}
});
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic(new Document([
'$id' => $video->getId(),
'fileId' => $video['fileId'],
@@ -404,7 +427,7 @@ App::post('/v1/videos/:videoId/rendition')
$profile = Authorization::skip(fn() => $dbForProject->findOne('videos_profiles', [new Query('_uid', Query::TYPE_EQUAL, [$profileId])]));
if (!$profile) {
throw new Exception('Video profile not found', 400, Exception::PROFILES_NOT_FOUND);
throw new Exception('Video profile not found', 400, Exception::VIDEO_PROFILE_NOT_FOUND);
}
$transcoder = new Transcoding();
@@ -430,7 +453,7 @@ App::get('/v1/videos/:videoId/:stream/renditions')
->label('sdk.description', '/docs/references/videos/get-renditions.md') // TODO: Create markdown
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VIDEO_RENDITIONS_LIST)
->label('sdk.response.model', Response::MODEL_VIDEO_RENDITION_LIST)
->param('videoId', null, new UID(), 'Video unique ID.')
->param('stream', '', new WhiteList(['hls', 'mpeg-dash']), 'stream protocol name')
->inject('response')
@@ -460,7 +483,7 @@ App::get('/v1/videos/:videoId/:stream/renditions')
$response->dynamic(new Document([
'total' => $dbForProject->count('videos_renditions', $queries, APP_LIMIT_COUNT),
'renditions' => $renditions,
]), Response::MODEL_VIDEO_RENDITIONS_LIST);
]), Response::MODEL_VIDEO_RENDITION_LIST);
});
+3 -2
View File
@@ -171,8 +171,9 @@ class Exception extends \Exception
public const DOMAIN_VERIFICATION_FAILED = 'domain_verification_failed';
/** Video */
public const PROFILES_NOT_FOUND = 'profiles_not_found';
public const VIDEO_NOT_FOUND = 'video_not_found';
public const VIDEO_PROFILE_NOT_FOUND = 'profile_not_found';
public const VIDEO_NOT_FOUND = 'video_not_found';
public const VIDEO_SUBTITLE_NOT_FOUND = 'subtitle_not_found';
private $type = '';
+9 -5
View File
@@ -70,9 +70,10 @@ use Appwrite\Utopia\Response\Model\UsageFunctions;
use Appwrite\Utopia\Response\Model\UsageProject;
use Appwrite\Utopia\Response\Model\UsageStorage;
use Appwrite\Utopia\Response\Model\UsageUsers;
use Appwrite\Utopia\Response\Model\VideoRendition;
use Appwrite\Utopia\Response\Model\Video;
use Appwrite\Utopia\Response\Model\VideoProfile;
use Appwrite\Utopia\Response\Model\VideoRendition;
use Appwrite\Utopia\Response\Model\VideoSubtitle;
/**
* @method Response setStatusCode(int $code = 200)
@@ -140,7 +141,9 @@ class Response extends SwooleResponse
public const MODEL_VIDEO_PROFILE = 'videoProfile';
public const MODEL_VIDEO_PROFILE_LIST = 'videoProfileList';
public const MODEL_VIDEO_RENDITION = 'videoRendition';
public const MODEL_VIDEO_RENDITIONS_LIST = 'videoRenditionsList';
public const MODEL_VIDEO_RENDITION_LIST = 'videoRenditionList';
public const MODEL_VIDEO_SUBTITLE = 'videoSubtitle';
public const MODEL_VIDEO_SUBTITLE_LIST = 'videoSubtitleList';
// Locale
public const MODEL_LOCALE = 'locale';
@@ -252,9 +255,9 @@ class Response extends SwooleResponse
->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY))
->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE))
->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false))
->setModel(new BaseList('Video Renditions List', self::MODEL_VIDEO_RENDITIONS_LIST, 'renditions', self::MODEL_VIDEO_RENDITION))
->setModel(new BaseList('video profile List', self::MODEL_VIDEO_PROFILE_LIST, 'profiles', self::MODEL_VIDEO_PROFILE))
->setModel(new BaseList('Video Rendition List', self::MODEL_VIDEO_RENDITION_LIST, 'renditions', self::MODEL_VIDEO_RENDITION))
->setModel(new BaseList('Video Subtitle List', self::MODEL_VIDEO_SUBTITLE_LIST, 'subtitles', self::MODEL_VIDEO_SUBTITLE))
// Entities
->setModel(new Database())
->setModel(new Collection())
@@ -310,9 +313,10 @@ class Response extends SwooleResponse
->setModel(new UsageBuckets())
->setModel(new UsageFunctions())
->setModel(new UsageProject())
->setModel(new VideoRendition())
->setModel(new Video())
->setModel(new VideoProfile())
->setModel(new VideoRendition())
->setModel(new VideoSubtitle())
// Verification
// Recovery
@@ -74,7 +74,7 @@ class VideoRendition extends Model
*/
public function getName(): string
{
return 'File rendition';
return 'Video rendition';
}
/**
@@ -0,0 +1,89 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class VideoSubtitle extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('videoId', [
'type' => self::TYPE_STRING,
'description' => 'Video ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('bucketId', [
'type' => self::TYPE_STRING,
'description' => 'Bucket ID.',
'default' => '',
'example' => 'd5fg5ehg1c168g7c',
])
->addRule('fileId', [
'type' => self::TYPE_STRING,
'description' => 'file ID.',
'default' => '',
'example' => 'c5fg5emg1c168grr',
])
->addRule('path', [
'type' => self::TYPE_STRING,
'description' => 'Subtitle path.',
'default' => '',
'example' => '640x360@500',
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Subtitle name.',
'default' => '',
'example' => 'English',
])
->addRule('code', [
'type' => self::TYPE_STRING,
'description' => 'Subtitle code.',
'default' => '',
'example' => 'Eng',
])
->addRule('default', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Subtitle default',
'default' => '',
'example' => false,
])
->addRule('status', [
'type' => self::TYPE_STRING,
'description' => 'Subtitle packaging status',
'default' => '',
'example' => 'ready',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Video subtitle';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_VIDEO_SUBTITLE;
}
}
@@ -14,7 +14,6 @@ class VideoCustomServerTest extends Scope
use VideoCustom;
use SideServer;
public function testCreateVideoProfile()
{
@@ -95,9 +94,70 @@ class VideoCustomServerTest extends Scope
}
public function testCreateVideo(): string
{
$response = $this->client->call(Client::METHOD_POST, '/videos', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'bucketId' => $this->getBucket()['$id'],
'fileId' => $this->getVideo()['$id']
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
return $response['body']['$id'];
}
/**
* @depends testCreateVideo
*/
public function testCreateVideoSubtitle($videoId)
{
$response = $this->client->call(Client::METHOD_POST, '/videos/' . $videoId . '/subtitles', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'bucketId' => $this->getBucket()['$id'],
'fileId' => $this->getSubtitle()['$id'],
'name' => 'English',
'code' => 'Eng',
'default' => true,
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
$response = $this->client->call(Client::METHOD_GET, '/videos/'. $videoId . '/subtitles' , [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertEquals(1, $response['body']['total']);
$this->assertNotEmpty($response['body']['subtitles']);
$this->assertNotEmpty($response['body']['subtitles'][0]['$id']);
$this->assertEquals('Eng', $response['body']['subtitles'][0]['code']);
//
//// $this->assertEquals(404, $response['headers']['status-code']);
//// $this->assertNotEmpty($response['body']);
//// $this->assertEquals('Video profile not found', $response['body']['message']);
}
/**
* @depends testCreateVideo
*/
public function testTranscodeWithSubs(): array
{