mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Updates
This commit is contained in:
@@ -4019,23 +4019,12 @@ $collections = [
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'output',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_profile',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['output', 'width', 'height', 'videoBitRate', 'audioBitRate' ],
|
||||
'attributes' => ['width', 'height', 'videoBitRate', 'audioBitRate' ],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
|
||||
@@ -8,7 +8,6 @@ return [
|
||||
'audioBitRate' => 64, //audio BitRate in Kbps
|
||||
'width' => 640, //width resolution in px
|
||||
'height' => 360, //height resolution in px
|
||||
'output' => 'hls'
|
||||
],
|
||||
[
|
||||
'name' => '576p',
|
||||
@@ -16,7 +15,6 @@ return [
|
||||
'audioBitRate' => 128,
|
||||
'width' => 1024,
|
||||
'height' => 576,
|
||||
'output' => 'hls'
|
||||
],
|
||||
[
|
||||
'name' => '720p',
|
||||
@@ -24,34 +22,5 @@ return [
|
||||
'audioBitRate' => 128,
|
||||
'width' => 1280,
|
||||
'height' => 720,
|
||||
'output' => 'hls'
|
||||
],
|
||||
|
||||
[
|
||||
|
||||
'name' => '360p',
|
||||
'videoBitRate' => 890, //video BitRate in Kbps
|
||||
'audioBitRate' => 64, //audio BitRate in Kbps
|
||||
'width' => 640, //width resolution in px
|
||||
'height' => 360, //height resolution in px
|
||||
'output' => 'dash'
|
||||
],
|
||||
[
|
||||
'name' => '576p',
|
||||
'videoBitRate' => 2538,
|
||||
'audioBitRate' => 128,
|
||||
'width' => 1024,
|
||||
'height' => 576,
|
||||
'output' => 'dash'
|
||||
],
|
||||
[
|
||||
'name' => '720p',
|
||||
'videoBitRate' => 3551,
|
||||
'audioBitRate' => 128,
|
||||
'width' => 1280,
|
||||
'height' => 720,
|
||||
'output' => 'dash'
|
||||
],
|
||||
|
||||
|
||||
];
|
||||
|
||||
@@ -177,8 +177,7 @@ App::post('/v1/projects')
|
||||
'videoBitRate' => $profile['videoBitRate'],
|
||||
'audioBitRate' => $profile['audioBitRate'],
|
||||
'width' => $profile['width'],
|
||||
'height' => $profile['height'],
|
||||
'output' => $profile['output']
|
||||
'height' => $profile['height']
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
+166
-167
@@ -97,6 +97,83 @@ App::post('/v1/videos')
|
||||
$response->dynamic($video, Response::MODEL_VIDEO);
|
||||
});
|
||||
|
||||
App::get('/v1/videos/:videoId')
|
||||
->desc('Get video ')
|
||||
->groups(['api', 'videos'])
|
||||
->label('scope', 'videos.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'videos')
|
||||
->label('sdk.method', 'get')
|
||||
->label('sdk.description', '/docs/references/videos/get-video.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)
|
||||
->param('videoId', '', new UID(), 'Video unique ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('mode')
|
||||
->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);
|
||||
}
|
||||
|
||||
validateFilePermissions($dbForProject, $video['bucketId'], $video['fileId'], $mode);
|
||||
|
||||
$response->dynamic($video, Response::MODEL_VIDEO);
|
||||
});
|
||||
|
||||
App::put('/v1/videos/:videoId')
|
||||
->desc('Update video')
|
||||
->groups(['api', 'videos'])
|
||||
->label('scope', 'videos.write')
|
||||
->label('audits.event', 'video.update')
|
||||
->label('audits.resource', 'video/{request.videoId}')
|
||||
->label('sdk.namespace', 'videos')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'update')
|
||||
->label('sdk.description', '/docs/references/videos/update.md') // TODO: Create markdown
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_VIDEO)
|
||||
->param('videoId', '', new UID(), 'Video unique ID.')
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new CustomId(), 'File ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForProject')
|
||||
->inject('mode')
|
||||
->action(function (string $videoId, $bucketId, $fileId, Response $response, Document $project, Database $dbForProject, string $mode) {
|
||||
|
||||
$video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId));
|
||||
|
||||
if ($video->isEmpty()) {
|
||||
throw new Exception(Exception::VIDEO_NOT_FOUND);
|
||||
}
|
||||
|
||||
validateFilePermissions($dbForProject, $video['bucketId'], $video['fileId'], $mode);
|
||||
|
||||
$file = validateFilePermissions($dbForProject, $bucketId, $fileId, $mode);
|
||||
$video = Authorization::skip(fn() =>
|
||||
$dbForProject->updateDocument('videos', $videoId, new Document([
|
||||
'bucketId' => $bucketId,
|
||||
'fileId' => $file->getId(),
|
||||
'size' => $file->getAttribute('sizeOriginal'),
|
||||
'duration' => null,
|
||||
'width' => null,
|
||||
'height' => null,
|
||||
'videoCodec' => null,
|
||||
'videoBitRate' => null,
|
||||
'videoFrameRate' => null,
|
||||
'audioCodec' => null,
|
||||
'audioBitRate' => null,
|
||||
'audioSampleRate' => null,
|
||||
])));
|
||||
|
||||
$response->dynamic($video, Response::MODEL_VIDEO);
|
||||
});
|
||||
|
||||
App::delete('/v1/videos/:videoId')
|
||||
->desc('Delete video')
|
||||
->groups(['api', 'videos'])
|
||||
@@ -137,83 +214,6 @@ App::delete('/v1/videos/:videoId')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::put('/v1/videos/:videoId')
|
||||
->desc('Update video')
|
||||
->groups(['api', 'videos'])
|
||||
->label('scope', 'videos.write')
|
||||
->label('audits.event', 'video.update')
|
||||
->label('audits.resource', 'video/{request.videoId}')
|
||||
->label('sdk.namespace', 'videos')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'update')
|
||||
->label('sdk.description', '/docs/references/videos/update.md') // TODO: Create markdown
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_VIDEO)
|
||||
->param('videoId', '', new UID(), 'Video unique ID.')
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new CustomId(), 'File ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForProject')
|
||||
->inject('mode')
|
||||
->action(function (string $videoId, $bucketId, $fileId, Response $response, Document $project, Database $dbForProject, string $mode) {
|
||||
|
||||
$video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId));
|
||||
|
||||
if ($video->isEmpty()) {
|
||||
throw new Exception(Exception::VIDEO_NOT_FOUND);
|
||||
}
|
||||
|
||||
validateFilePermissions($dbForProject, $video['bucketId'], $video['fileId'], $mode);
|
||||
|
||||
$file = validateFilePermissions($dbForProject, $bucketId, $fileId, $mode);
|
||||
$video = Authorization::skip(fn() =>
|
||||
$dbForProject->updateDocument('videos', $videoId, new Document([
|
||||
'bucketId' => $bucketId,
|
||||
'fileId' => $file->getId(),
|
||||
'size' => $file->getAttribute('sizeOriginal'),
|
||||
'duration' => null,
|
||||
'width' => null,
|
||||
'height' => null,
|
||||
'videoCodec' => null,
|
||||
'videoBitRate' => null,
|
||||
'videoFrameRate' => null,
|
||||
'audioCodec' => null,
|
||||
'audioBitRate' => null,
|
||||
'audioSampleRate' => null,
|
||||
])));
|
||||
|
||||
$response->dynamic($video, Response::MODEL_VIDEO);
|
||||
});
|
||||
|
||||
App::get('/v1/videos/:videoId')
|
||||
->desc('Get video ')
|
||||
->groups(['api', 'videos'])
|
||||
->label('scope', 'videos.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'videos')
|
||||
->label('sdk.method', 'get')
|
||||
->label('sdk.description', '/docs/references/videos/get-video.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)
|
||||
->param('videoId', '', new UID(), 'Video unique ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('mode')
|
||||
->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);
|
||||
}
|
||||
|
||||
validateFilePermissions($dbForProject, $video['bucketId'], $video['fileId'], $mode);
|
||||
|
||||
$response->dynamic($video, Response::MODEL_VIDEO);
|
||||
});
|
||||
|
||||
App::get('/v1/videos')
|
||||
->desc('Get video list')
|
||||
->groups(['api', 'videos'])
|
||||
@@ -299,6 +299,38 @@ App::post('/v1/videos/:videoId/subtitles')
|
||||
$response->dynamic($subtitle, Response::MODEL_SUBTITLE);
|
||||
});
|
||||
|
||||
App::get('/v1/videos/:videoId/subtitles')
|
||||
->desc('Get video subtitles')
|
||||
->groups(['api', 'videos'])
|
||||
->label('scope', 'videos.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'videos')
|
||||
->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_SUBTITLE_LIST)
|
||||
->param('videoId', null, new UID(), 'Video unique ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($videoId, Response $response, Database $dbForProject) {
|
||||
|
||||
$query = [
|
||||
Query::equal('videoId', [$videoId]),
|
||||
];
|
||||
|
||||
$subtitles = Authorization::skip(fn () => $dbForProject->find('videos_subtitles', $query));
|
||||
|
||||
if (empty($subtitles)) {
|
||||
throw new Exception(Exception::VIDEO_SUBTITLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $dbForProject->count('videos_subtitles', $query, APP_LIMIT_COUNT),
|
||||
'subtitles' => $subtitles,
|
||||
]), Response::MODEL_SUBTITLE_LIST);
|
||||
});
|
||||
|
||||
App::patch('/v1/videos/:videoId/subtitles/:subtitleId')
|
||||
->desc('Update video subtitle')
|
||||
->groups(['api', 'videos'])
|
||||
@@ -383,38 +415,6 @@ App::delete('/v1/videos/:videoId/subtitles/:subtitleId')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::get('/v1/videos/:videoId/subtitles')
|
||||
->desc('Get video subtitles')
|
||||
->groups(['api', 'videos'])
|
||||
->label('scope', 'videos.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'videos')
|
||||
->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_SUBTITLE_LIST)
|
||||
->param('videoId', null, new UID(), 'Video unique ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($videoId, Response $response, Database $dbForProject) {
|
||||
|
||||
$query = [
|
||||
Query::equal('videoId', [$videoId]),
|
||||
];
|
||||
|
||||
$subtitles = Authorization::skip(fn () => $dbForProject->find('videos_subtitles', $query));
|
||||
|
||||
if (empty($subtitles)) {
|
||||
throw new Exception(Exception::VIDEO_SUBTITLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $dbForProject->count('videos_subtitles', $query, APP_LIMIT_COUNT),
|
||||
'subtitles' => $subtitles,
|
||||
]), Response::MODEL_SUBTITLE_LIST);
|
||||
});
|
||||
|
||||
App::post('/v1/videos/:videoId/rendition')
|
||||
->alias('/v1/videos/:videoId/rendition', [])
|
||||
->desc('Create video rendition')
|
||||
@@ -430,12 +430,13 @@ App::post('/v1/videos/:videoId/rendition')
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('videoId', null, new UID(), 'Video unique ID.')
|
||||
->param('profileId', '', new CustomId(), 'Profile unique ID.')
|
||||
->param('output', '', new WhiteList(['hls', 'dash']), 'output name')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('mode')
|
||||
->action(action: function (string $videoId, string $profileId, Request $request, Response $response, Database $dbForProject, Document $project, string $mode) {
|
||||
->action(action: function (string $videoId, string $profileId, string $output, Request $request, Response $response, Database $dbForProject, Document $project, string $mode) {
|
||||
|
||||
$video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId));
|
||||
|
||||
@@ -452,6 +453,7 @@ App::post('/v1/videos/:videoId/rendition')
|
||||
|
||||
$transcoder = new Transcoding();
|
||||
$transcoder
|
||||
->setOutput($output)
|
||||
->setProject($project)
|
||||
->setVideo($video)
|
||||
->setProfile($profile)
|
||||
@@ -460,54 +462,6 @@ App::post('/v1/videos/:videoId/rendition')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::delete('/v1/videos/:videoId/renditions/:renditionId')
|
||||
->desc('Delete video rendition')
|
||||
->groups(['api', 'videos'])
|
||||
->label('scope', 'videos.write')
|
||||
->label('event', 'videos.[videoIdId].renditions.[renditionId].delete')
|
||||
->label('audits.event', 'rendition.delete')
|
||||
->label('audits.resource', 'video/{request.videoId}/rendition/{request.$id}')
|
||||
->label('sdk.namespace', 'videos')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'deleteRendition')
|
||||
->label('sdk.description', '/docs/references/videos/delete-rendition.md') // TODO: Create markdown
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('videoId', '', new UID(), 'Video unique ID.')
|
||||
->param('renditionId', '', new UID(), 'Video rendition unique ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('mode')
|
||||
->inject('deviceVideos')
|
||||
->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);
|
||||
}
|
||||
|
||||
validateFilePermissions($dbForProject, $video['bucketId'], $video['fileId'], $mode);
|
||||
|
||||
$rendition = Authorization::skip(fn() => $dbForProject->getDocument('videos_renditions', $renditionId));
|
||||
if ($rendition->isEmpty()) {
|
||||
throw new Exception(Exception::VIDEO_RENDITION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$deleted = $dbForProject->deleteDocument('videos_renditions', $renditionId);
|
||||
|
||||
if (!$deleted) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
Authorization::skip(fn() => $dbForProject->deleteDocument('videos_renditions', $rendition->getId()));
|
||||
if (!empty($rendition['path'])) {
|
||||
$deviceVideos->deletePath($rendition['path']);
|
||||
}
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::get('/v1/videos/:videoId/renditions/:renditionId')
|
||||
->desc('Get a single video rendition')
|
||||
->groups(['api', 'videos'])
|
||||
@@ -581,6 +535,54 @@ App::get('/v1/videos/:videoId/renditions')
|
||||
]), Response::MODEL_RENDITION_LIST);
|
||||
});
|
||||
|
||||
App::delete('/v1/videos/:videoId/renditions/:renditionId')
|
||||
->desc('Delete video rendition')
|
||||
->groups(['api', 'videos'])
|
||||
->label('scope', 'videos.write')
|
||||
->label('event', 'videos.[videoIdId].renditions.[renditionId].delete')
|
||||
->label('audits.event', 'rendition.delete')
|
||||
->label('audits.resource', 'video/{request.videoId}/rendition/{request.$id}')
|
||||
->label('sdk.namespace', 'videos')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.method', 'deleteRendition')
|
||||
->label('sdk.description', '/docs/references/videos/delete-rendition.md') // TODO: Create markdown
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('videoId', '', new UID(), 'Video unique ID.')
|
||||
->param('renditionId', '', new UID(), 'Video rendition unique ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('mode')
|
||||
->inject('deviceVideos')
|
||||
->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);
|
||||
}
|
||||
|
||||
validateFilePermissions($dbForProject, $video['bucketId'], $video['fileId'], $mode);
|
||||
|
||||
$rendition = Authorization::skip(fn() => $dbForProject->getDocument('videos_renditions', $renditionId));
|
||||
if ($rendition->isEmpty()) {
|
||||
throw new Exception(Exception::VIDEO_RENDITION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$deleted = $dbForProject->deleteDocument('videos_renditions', $renditionId);
|
||||
|
||||
if (!$deleted) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
Authorization::skip(fn() => $dbForProject->deleteDocument('videos_renditions', $rendition->getId()));
|
||||
if (!empty($rendition['path'])) {
|
||||
$deviceVideos->deletePath($rendition['path']);
|
||||
}
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::get('/v1/videos/:videoId/outputs/:output')
|
||||
->desc('Get video master renditions manifest')
|
||||
->groups(['api', 'videos'])
|
||||
@@ -593,7 +595,7 @@ App::get('/v1/videos/:videoId/outputs/:output')
|
||||
->label('sdk.methodType', 'location')
|
||||
->label('scope', 'videos.read')
|
||||
->param('videoId', null, new UID(), 'Video unique ID.')
|
||||
->param('output', '', new WhiteList(['hls', 'dash']), 'protocol name')
|
||||
->param('output', '', new WhiteList(['hls', 'dash']), 'output name')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('mode')
|
||||
@@ -961,19 +963,17 @@ App::post('/v1/videos/profiles')
|
||||
->param('audioBitRate', '', new Range(32, 5000), 'Audio profile bit rate in Kbps.')
|
||||
->param('width', '', new Range(6, 3000), 'Video profile width.')
|
||||
->param('height', '', new Range(6, 3000), 'Video profile height.')
|
||||
->param('output', false, new WhiteList(['hls', 'dash']), 'Video profile output.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(action: function (string $name, string $videoBitRate, string $audioBitRate, string $width, string $height, string $output, Response $response, Database $dbForProject) {
|
||||
->action(action: function (string $name, string $videoBitRate, string $audioBitRate, string $width, string $height, Response $response, Database $dbForProject) {
|
||||
|
||||
$profile = Authorization::skip(function () use ($dbForProject, $name, $videoBitRate, $audioBitRate, $width, $height, $output) {
|
||||
$profile = Authorization::skip(function () use ($dbForProject, $name, $videoBitRate, $audioBitRate, $width, $height) {
|
||||
return $dbForProject->createDocument('videos_profiles', new Document([
|
||||
'name' => $name,
|
||||
'videoBitRate' => (int)$videoBitRate,
|
||||
'audioBitRate' => (int)$audioBitRate,
|
||||
'width' => (int)$width,
|
||||
'height' => (int)$height,
|
||||
'output' => $output,
|
||||
]));
|
||||
});
|
||||
|
||||
@@ -1000,10 +1000,9 @@ App::patch('/v1/videos/profiles/:profileId')
|
||||
->param('audioBitrate', '', new Range(64, 4000), 'Audio profile bit rate in Kbps.')
|
||||
->param('width', '', new Range(100, 2000), 'Video profile width.')
|
||||
->param('height', '', new Range(100, 2000), 'Video profile height.')
|
||||
->param('output', false, new WhiteList(['hls', 'dash']), 'Video profile output.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(action: function (string $profileId, string $name, string $videoBitRate, string $audioBitRate, string $width, string $height, string $output, Response $response, Database $dbForProject) {
|
||||
->action(action: function (string $profileId, string $name, string $videoBitRate, string $audioBitRate, string $width, string $height, Response $response, Database $dbForProject) {
|
||||
|
||||
$profile = Authorization::skip(fn() => $dbForProject->getDocument('videos_profiles', $profileId));
|
||||
if ($profile->isEmpty()) {
|
||||
@@ -1014,8 +1013,8 @@ App::patch('/v1/videos/profiles/:profileId')
|
||||
->setAttribute('videoBitRate', (int)$videoBitRate)
|
||||
->setAttribute('audioBitRate', (int)$audioBitRate)
|
||||
->setAttribute('width', (int)$width)
|
||||
->setAttribute('height', (int)$height)
|
||||
->setAttribute('output', $output);
|
||||
->setAttribute('height', (int)$height);
|
||||
|
||||
|
||||
$profile = Authorization::skip(fn() => $dbForProject->updateDocument('videos_profiles', $profile->getId(), $profile));
|
||||
|
||||
|
||||
+16
-16
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\Resque\Worker;
|
||||
@@ -87,7 +88,7 @@ class TranscodingV1 extends Worker
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$startTime = \microtime(true);
|
||||
$startTime = time();
|
||||
$this->database = $this->getProjectDB($this->project->getId());
|
||||
$this->bucket = $this->database->getDocument('buckets', $this->video->getAttribute('bucketId'));
|
||||
$this->file = $this->database->getDocument('bucket_' . $this->bucket->getInternalId(), $this->video->getAttribute('fileId'));
|
||||
@@ -101,6 +102,11 @@ class TranscodingV1 extends Worker
|
||||
console::error('Storage transfer error');
|
||||
}
|
||||
|
||||
$this->renditionName = $this->profile->getAttribute('width')
|
||||
. 'X' . $this->profile->getAttribute('height')
|
||||
. '@' . ($this->profile->getAttribute('videoBitRate') + $this->profile->getAttribute('audioBitRate'));
|
||||
|
||||
|
||||
/**
|
||||
* FFMpeg init
|
||||
*/
|
||||
@@ -156,10 +162,6 @@ class TranscodingV1 extends Worker
|
||||
|
||||
$media = $this->ffmpeg->open($inPath);
|
||||
|
||||
$this->renditionName = $this->profile->getAttribute('width')
|
||||
. 'X' . $this->profile->getAttribute('height')
|
||||
. '@' . ($this->profile->getAttribute('videoBitRate') + $this->profile->getAttribute('audioBitRate'));
|
||||
|
||||
$subs = [];
|
||||
$subtitles = $this->database->find('videos_subtitles', [
|
||||
Query::equal('videoId', [$this->video->getId()]),
|
||||
@@ -198,7 +200,7 @@ class TranscodingV1 extends Worker
|
||||
'name' => $this->renditionName,
|
||||
'startedAt' => DateTime::now(),
|
||||
'status' => self::STATUS_START,
|
||||
'output' => $this->profile->getAttribute('output'),
|
||||
'output' => $this->args['output'],
|
||||
]));
|
||||
$this->send($query);
|
||||
$renditionRootPath = $this->getVideoDevice($this->project->getId())->getPath($this->video->getId()) . '/';
|
||||
@@ -228,17 +230,17 @@ class TranscodingV1 extends Worker
|
||||
}
|
||||
});
|
||||
|
||||
$this->transcode($this->profile->getAttribute('output'), $media, $format, $representation, $subs);
|
||||
$this->transcode($media, $format, $representation, $subs);
|
||||
|
||||
unset($media);
|
||||
//exec('/usr/bin/ffmpeg -y -i /usr/src/code/tests/tmp/637f59c88f9ff0fe3b1f/637e1b82aeab8980400e/in/637f59ab5bce0e36d05e.mp4 -c:v libx264 -c:a aac -bf 1 -keyint_min 25 -g 250 -sc_threshold 40 -use_timeline 0 -use_template 0 -seg_duration 10 -hls_playlist 0 -f dash -dn -sn -vf scale=iw:-2:force_original_aspect_ratio=increase,setsar=1:1 -b_strategy 1 -bf 3 -force_key_frames "expr:gte(t,n_forced*2)" -map 0 -s:v:0 1024x576 -b:v:0 2538k -b:a:0 128k -strict -2 -threads 12 /usr/src/code/tests/tmp/637f59c88f9ff0fe3b1f/637e1b82aeab8980400e/out/637f59c88f9ff0fe3b1f.mpd2>&1', $o, $v);
|
||||
//var_dump($o);
|
||||
//var_dump($v);
|
||||
|
||||
if ($this->profile->getAttribute('output') === self::OUTPUT_HLS) {
|
||||
if ($this->args['output'] === self::OUTPUT_HLS) {
|
||||
$streams = $this->getHlsSegmentsUrls($this->outDir . 'master.m3u8');
|
||||
foreach ($streams as $stream) {
|
||||
$m3u8 = $this->getSegments(self::OUTPUT_HLS, $this->outDir . $stream['path']);
|
||||
$m3u8 = $this->getSegments($this->outDir . $stream['path']);
|
||||
if (!empty($m3u8['segments'])) {
|
||||
foreach ($m3u8['segments'] as $segment) {
|
||||
$this->database->createDocument('videos_renditions_segments', new Document([
|
||||
@@ -255,7 +257,7 @@ class TranscodingV1 extends Worker
|
||||
$query->setAttribute('targetDuration', $m3u8['targetDuration']);
|
||||
}
|
||||
} else {
|
||||
$mpd = $this->getSegments(self::OUTPUT_DASH, $this->outPath . '.mpd');
|
||||
$mpd = $this->getSegments($this->outPath . '.mpd');
|
||||
if (!empty($mpd['segments'])) {
|
||||
foreach ($mpd['segments'] as $segment) {
|
||||
$this->database->createDocument('videos_renditions_segments', new Document([
|
||||
@@ -350,14 +352,13 @@ class TranscodingV1 extends Worker
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $output
|
||||
* @param $media Media
|
||||
* @param $format StreamFormat
|
||||
* @param $representation Representation
|
||||
* @param array $subtitles
|
||||
* @return void
|
||||
*/
|
||||
private function transcode(string $output, Media $media, StreamFormat $format, Representation $representation, array $subtitles): void
|
||||
private function transcode(Media $media, StreamFormat $format, Representation $representation, array $subtitles): void
|
||||
{
|
||||
|
||||
$additionalParams = [
|
||||
@@ -371,7 +372,7 @@ class TranscodingV1 extends Worker
|
||||
|
||||
$segmentSize = 10;
|
||||
|
||||
if ($output === self::OUTPUT_DASH) {
|
||||
if ($this->args['output'] === self::OUTPUT_DASH) {
|
||||
$media->dash()
|
||||
->setFormat($format)
|
||||
->setSegDuration($segmentSize)
|
||||
@@ -397,15 +398,14 @@ class TranscodingV1 extends Worker
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $output
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
private function getSegments(string $output, string $path): array
|
||||
private function getSegments(string $path): array
|
||||
{
|
||||
|
||||
$segments = [];
|
||||
if ($output === self::OUTPUT_DASH) {
|
||||
if ($this->args['output'] === self::OUTPUT_DASH) {
|
||||
$metadata = null;
|
||||
$handle = fopen($path, "r");
|
||||
if ($handle) {
|
||||
|
||||
@@ -11,11 +11,37 @@ class Transcoding extends Event
|
||||
|
||||
protected Document $profile;
|
||||
|
||||
protected string $output;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Event::TRANSCODING_QUEUE_NAME, Event::TRANSCODING_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets output.
|
||||
*
|
||||
* @param string $output
|
||||
* @return self
|
||||
*/
|
||||
public function setOutput(string $output): self
|
||||
{
|
||||
$this->output = $output;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns output.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOutput(): string
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets video.
|
||||
*
|
||||
@@ -71,6 +97,7 @@ class Transcoding extends Event
|
||||
public function trigger(): string|bool
|
||||
{
|
||||
return Resque::enqueue($this->queue, $this->class, [
|
||||
'output' => $this->output,
|
||||
'project' => $this->project,
|
||||
'user' => $this->user,
|
||||
'video' => $this->video,
|
||||
|
||||
Reference in New Issue
Block a user