mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
logger moved to App::shutdown
This commit is contained in:
@@ -12,4 +12,5 @@ app/sdks
|
||||
dev/yasd_init.php
|
||||
/tests/resources/disk-a/very-large-file-1.mov
|
||||
/tests/resources/disk-a/very-big-file-2.mov
|
||||
/tests/resources/disk-a/video-srt.*
|
||||
/tests/tmp/
|
||||
@@ -3339,7 +3339,7 @@ $collections = [
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_bucket_file_stream',
|
||||
'$id' => '_key_video_stream',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['videoId', 'stream'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
@@ -3429,6 +3429,90 @@ $collections = [
|
||||
],
|
||||
]
|
||||
],
|
||||
'video_subtitles' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'video_subtitles',
|
||||
'$name' => 'Video_subtitles',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'videoId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'bucketId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'fileId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'code',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'default',
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
|
||||
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_bucket_video_subtitle',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['videoId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
return $collections;
|
||||
|
||||
@@ -8,7 +8,7 @@ return [
|
||||
'audioBitrate' => 64, //audio bitrate in Kbps
|
||||
'width' => 640, //width resolution in px
|
||||
'height' => 360, //height resolution in px
|
||||
'stream' => 'mpeg-dash'
|
||||
'stream' => 'hls'
|
||||
],
|
||||
[
|
||||
'name' => '576p',
|
||||
@@ -16,7 +16,7 @@ return [
|
||||
'audioBitrate' => 128,
|
||||
'width' => 1024,
|
||||
'height' => 576,
|
||||
'stream' => 'mpeg-dash'
|
||||
'stream' => 'hls'
|
||||
],
|
||||
[
|
||||
'name' => '720p',
|
||||
@@ -24,7 +24,7 @@ return [
|
||||
'audioBitrate' => 128,
|
||||
'width' => 1280,
|
||||
'height' => 720,
|
||||
'stream' => 'mpeg-dash'
|
||||
'stream' => 'hls'
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -132,6 +132,60 @@ App::get('/v1/video/profiles')
|
||||
});
|
||||
|
||||
|
||||
App::post('/v1/video/:videoId/subtitles')
|
||||
->alias('/v1/video/:videoId/subtitles', [])
|
||||
->desc('Link a subtitle file to a video')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'files.write')
|
||||
->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('code', '', new Text(128), 'Subtitle code name.')
|
||||
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->inject('mode')
|
||||
->inject('deviceFiles')
|
||||
->inject('deviceLocal')
|
||||
->action(action: function (string $videoId, string $bucketId, string $fileId, string $name, string $code, ?array $read, ?array $write, Request $request, Response $response, Database $dbForProject, $project, Document $user, Audit $audits, Stats $usage, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal) {
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
|
||||
$video = Authorization::skip(fn() => $dbForProject->findOne('videos', [new Query('_uid', Query::TYPE_EQUAL, [$videoId])]));
|
||||
|
||||
if ($video->isEmpty()) {
|
||||
throw new Exception('Video not found', 400, Exception::VIDEO_NOT_FOUND);
|
||||
}
|
||||
|
||||
validateFilePermissions($dbForProject, $video['bucketId'], $video['fileId'], $mode, $read, $write);
|
||||
validateFilePermissions($dbForProject, $bucketId, $fileId, $mode, $read, $write);
|
||||
|
||||
try {
|
||||
$subtitle = Authorization::skip(function () use ($dbForProject, $videoId, $bucketId, $fileId, $name, $code) {
|
||||
return $dbForProject->createDocument('video_subtitles', new Document([
|
||||
'videoId' => $videoId,
|
||||
'bucketId' => $bucketId,
|
||||
'fileId' => $fileId,
|
||||
'name' => $name,
|
||||
'code' => $code,
|
||||
]));
|
||||
});
|
||||
} catch (StructureException $exception) {
|
||||
throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
|
||||
}
|
||||
|
||||
$response->json(['result' => 'ok']);
|
||||
});
|
||||
|
||||
|
||||
App::post('/v1/video/buckets/:bucketId/files/:fileId')
|
||||
->alias('/v1/video/files', ['bucketId' => 'default'])
|
||||
->desc('Create video')
|
||||
@@ -278,6 +332,7 @@ App::get('/v1/video/:videoId/:stream/:profile/:fileName')
|
||||
->alias('/v1/video/:videoId/:stream/:profile/:fileName', [])
|
||||
->desc('Get video playlist manifests')
|
||||
->groups(['api', 'storage'])
|
||||
#->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
|
||||
->label('scope', 'files.read')
|
||||
->param('videoId', null, new UID(), 'Video unique ID.')
|
||||
->param('stream', '', new WhiteList(['hls', 'mpeg-dash']), 'stream protocol name')
|
||||
@@ -323,19 +378,31 @@ App::get('/v1/video/:videoId/:stream/:profile/:fileName')
|
||||
$ext = pathinfo($fileName, PATHINFO_EXTENSION);
|
||||
$baseUrl = 'http://127.0.0.1/v1/video/' . $videoId . '/' . $stream . '/' ;
|
||||
|
||||
|
||||
if ($profile === 'master') {
|
||||
if ($stream === 'hls') {
|
||||
$subtitles = Authorization::skip(fn () => $dbForProject->find('video_subtitles', [new Query('videoId', Query::TYPE_EQUAL, [$video->getId()])], 12, 0, [], ['ASC']));
|
||||
$paramsSubtitles = [];
|
||||
foreach ($subtitles as $subtitle) {
|
||||
$paramsSubtitles[] = [
|
||||
'name' => $subtitle->getAttribute('name'),
|
||||
'code' => $subtitle->getAttribute('code'),
|
||||
'uri' => $baseUrl . $videoId . '_subtitles_' . $subtitle->getAttribute('code') . '.m3u8',
|
||||
];
|
||||
}
|
||||
$paramsRenditions = [];
|
||||
foreach ($renditions as $rendition) {
|
||||
$t['bandwidth'] = $rendition->getAttribute('videoBitrate') + $rendition->getAttribute('audioBitrate');
|
||||
$t['resolution'] = $rendition->getAttribute('width') . 'X' . $rendition->getAttribute('height');
|
||||
$t['name'] = $rendition->getAttribute('name');
|
||||
$t['path'] = $baseUrl . $rendition->getAttribute('name') . '/' . $rendition->getAttribute('videoId') . '_' . $rendition->getAttribute('name') . '.m3u8';
|
||||
$params[] = $t;
|
||||
$paramsRenditions[] = [
|
||||
'bandwidth' => $rendition->getAttribute('videoBitrate') + $rendition->getAttribute('audioBitrate'),
|
||||
'resolution' => $rendition->getAttribute('width') . 'X' . $rendition->getAttribute('height'),
|
||||
'name' => $rendition->getAttribute('name'),
|
||||
'uri' => $baseUrl . $rendition->getAttribute('name') . '/' . $rendition->getAttribute('videoId') . '_' . $rendition->getAttribute('name') . '.m3u8',
|
||||
'subs' => !empty($paramsSubtitles) ? ' SUBTITLES="subs"' : '',
|
||||
];
|
||||
}
|
||||
|
||||
$template = new View(__DIR__ . '/../../views/video/hls.phtml');
|
||||
$template->setParam('params', $params);
|
||||
$template->setParam('paramsSubtitles', $paramsSubtitles);
|
||||
$template->setParam('paramsRenditions', $paramsRenditions);
|
||||
$output = $template->render();
|
||||
} else {
|
||||
$adaptations = [];
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#EXTM3U
|
||||
#EXT-X-VERSION:3
|
||||
<?php foreach ($this->getParam('params', []) as $param): ?>
|
||||
#EXT-X-STREAM-INF:BANDWIDTH="<?php echo $param['bandwidth']; ?>" ,RESOLUTION="<?php echo $param['resolution']; ?>" ,NAME="<?php echo $param['name']; ?>"<?php echo PHP_EOL?>
|
||||
<?php echo $param['path'] ?>
|
||||
<?php foreach ($this->getParam('paramsSubtitles', []) as $subtitle): ?>
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="<?php echo $subtitle['name']; ?>",DEFAULT=YES,AUTOSELECT=NO,FORCED=NO,LANGUAGE="<?php echo $subtitle['code']; ?>",URI="<?php echo $subtitle['uri']; ?>"<?php echo PHP_EOL?>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php foreach ($this->getParam('paramsRenditions', []) as $rendition): ?>
|
||||
#EXT-X-STREAM-INF:BANDWIDTH="<?php echo $rendition['bandwidth']; ?>" ,RESOLUTION="<?php echo $rendition['resolution']; ?>" ,NAME="<?php echo $rendition['name']; ?>" <?php if($rendition['subs'] !== null): echo $rendition['subs']; endif?><?php echo PHP_EOL?>
|
||||
<?php echo $rendition['uri'] ?>
|
||||
<?php endforeach; ?>
|
||||
}
|
||||
+87
-19
@@ -4,6 +4,7 @@ use Appwrite\Extend\Exception;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\Resque\Worker;
|
||||
use Streaming\Format\StreamFormat;
|
||||
use Streaming\HLSSubtitle;
|
||||
use Streaming\Media;
|
||||
use Streaming\Metadata;
|
||||
use Streaming\Representation;
|
||||
@@ -15,6 +16,8 @@ use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use Utopia\Storage\Compression\Algorithms\GZIP;
|
||||
use Captioning\Format\SubripFile;
|
||||
use Captioning\Format\WebvttFile;
|
||||
|
||||
require_once __DIR__ . '/../init.php';
|
||||
|
||||
@@ -81,12 +84,7 @@ class TranscodingV1 extends Worker
|
||||
}
|
||||
|
||||
$bucket = Authorization::skip(fn() => $this->database->getDocument('buckets', $sourceVideo['bucketId']));
|
||||
if ($bucket->getAttribute('permission') === 'bucket') {
|
||||
$file = Authorization::skip(fn() => $this->database->getDocument('bucket_' . $bucket->getInternalId(), $sourceVideo['fileId']));
|
||||
} else {
|
||||
$file = $this->database->getDocument('bucket_' . $bucket->getInternalId(), $sourceVideo['fileId']);
|
||||
}
|
||||
|
||||
$file = Authorization::skip(fn() => $this->database->getDocument('bucket_' . $bucket->getInternalId(), $sourceVideo['fileId']));
|
||||
$data = $this->getFilesDevice($project->getId())->read($file->getAttribute('path'));
|
||||
$fileName = basename($file->getAttribute('path'));
|
||||
$inPath = $this->inDir . $fileName;
|
||||
@@ -149,6 +147,43 @@ class TranscodingV1 extends Worker
|
||||
$video = $ffmpeg->open($inPath);
|
||||
$this->setRenditionName($profile);
|
||||
|
||||
$subs = [];
|
||||
$subtitles = Authorization::skip(fn () => $this->database->find('video_subtitles', [new Query('videoId', Query::TYPE_EQUAL, [$this->args['videoId']])], 12, 0, [], ['ASC']));
|
||||
foreach ($subtitles as $subtitle) {
|
||||
$subtitleBucket = Authorization::skip(fn() => $this->database->getDocument('buckets', $subtitle->getAttribute('bucketId')));
|
||||
$subtitleFile = Authorization::skip(fn() => $this->database->getDocument('bucket_' . $subtitleBucket->getInternalId(), $subtitle->getAttribute('fileId')));
|
||||
$subtitleData = $this->getFilesDevice($project->getId())->read($subtitleFile->getAttribute('path'));
|
||||
$subtitleFileName = basename($subtitleFile->getAttribute('path'));
|
||||
|
||||
if (!empty($subtitleFile->getAttribute('openSSLCipher'))) { // Decrypt
|
||||
$subtitleData = OpenSSL::decrypt(
|
||||
$subtitleData,
|
||||
$subtitleFile->getAttribute('openSSLCipher'),
|
||||
App::getEnv('_APP_OPENSSL_KEY_V' . $subtitleFile->getAttribute('openSSLVersion')),
|
||||
0,
|
||||
\hex2bin($subtitleFile->getAttribute('openSSLIV')),
|
||||
\hex2bin($subtitleFile->getAttribute('openSSLTag'))
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($subtitleFile->getAttribute('algorithm', ''))) {
|
||||
$compressor = new GZIP();
|
||||
$subtitleData = $compressor->decompress($subtitleData);
|
||||
}
|
||||
|
||||
$this->getFilesDevice($project->getId())->write($this->inDir . $subtitleFileName, $subtitleData, $subtitleFile->getAttribute('mimeType'));
|
||||
$ext = pathinfo($subtitleFileName, PATHINFO_EXTENSION);
|
||||
if ($ext === 'srt') {
|
||||
$srt = new SubripFile($this->inDir . $subtitleFileName);
|
||||
$srt->convertTo('webvtt')->save($this->inDir . $this->args['videoId'] . '.vtt');
|
||||
}
|
||||
$subs[] = [
|
||||
'name' => $subtitle->getAttribute('name'),
|
||||
'code' => $subtitle->getAttribute('code'),
|
||||
'path' => $this->inDir . $this->args['videoId'] . '.vtt',
|
||||
];
|
||||
}
|
||||
|
||||
$query = Authorization::skip(function () use ($collection, $profile) {
|
||||
return $this->database->createDocument($collection, new Document([
|
||||
'videoId' => $this->args['videoId'],
|
||||
@@ -179,7 +214,7 @@ class TranscodingV1 extends Worker
|
||||
});
|
||||
|
||||
|
||||
list($general, $metadata) = $this->transcode($profile['stream'], $video, $format, $representation);
|
||||
list($general, $metadata) = $this->transcode($profile['stream'], $video, $format, $representation, $subs);
|
||||
if (!empty($metadata)) {
|
||||
$query->setAttribute('metadata', json_encode($metadata));
|
||||
}
|
||||
@@ -214,11 +249,15 @@ class TranscodingV1 extends Worker
|
||||
$deviceFiles = $this->getVideoDevice($project->getId());
|
||||
$devicePath = $deviceFiles->getPath($this->args['videoId']);
|
||||
$data = $this->getFilesDevice($project->getId())->read($this->outDir . $fileName);
|
||||
$renditionPath = $devicePath . DIRECTORY_SEPARATOR . $this->getRenditionName();
|
||||
$this->getVideoDevice($project->getId())->write($renditionPath . DIRECTORY_SEPARATOR . $fileName, $data, \mime_content_type($this->outDir . $fileName));
|
||||
$to = $devicePath . '/' . $this->getRenditionName() . '/';
|
||||
if (str_contains($fileName, "_subtitles_") || str_contains($fileName, ".vtt")) {
|
||||
$to = $devicePath . '/';
|
||||
}
|
||||
|
||||
$this->getVideoDevice($project->getId())->write($to . $fileName, $data, \mime_content_type($this->outDir . $fileName));
|
||||
if ($start === 0) {
|
||||
$query->setAttribute('status', self::STATUS_UPLOADING);
|
||||
$query->setAttribute('path', $renditionPath);
|
||||
$query->setAttribute('path', $devicePath . '/' . $this->getRenditionName());
|
||||
Authorization::skip(fn() => $this->database->updateDocument(
|
||||
$collection,
|
||||
$query->getId(),
|
||||
@@ -266,7 +305,7 @@ class TranscodingV1 extends Worker
|
||||
* @param $representation Representation
|
||||
* @return array
|
||||
*/
|
||||
private function transcode(string $stream, Media $video, StreamFormat $format, Representation $representation): string | array
|
||||
private function transcode(string $stream, Media $video, StreamFormat $format, Representation $representation, array $subtitles): string | array
|
||||
{
|
||||
|
||||
$additionalParams = [
|
||||
@@ -299,25 +338,52 @@ class TranscodingV1 extends Worker
|
||||
}
|
||||
|
||||
|
||||
$hls = $video->hls()
|
||||
->setFormat($format)
|
||||
$hls = $video->hls();
|
||||
|
||||
|
||||
foreach ($subtitles as $subtitle) {
|
||||
$sub = new HLSSubtitle($subtitle['path'], $subtitle['name'], $subtitle['code']);
|
||||
$sub->default();
|
||||
$sub->setM3u8Uri($this->getHlsBaseUri(false) . $this->args['videoId'] . '_subtitles_' . $subtitle['code'] . '.m3u8');
|
||||
$hls->subtitle($sub);
|
||||
}
|
||||
|
||||
|
||||
$hls->setFormat($format)
|
||||
->setHlsTime($segementSize)
|
||||
->addRepresentation($representation)
|
||||
->setAdditionalParams($additionalParams)
|
||||
->setHlsBaseUrl('http://127.0.0.1/v1/video/' . $this->args['videoId'] . '/' . self::STREAM_HLS . '/' . $this->getRenditionName() . '/')
|
||||
->setHlsBaseUrl($this->getHlsBaseUri())
|
||||
->save($this->outPath);
|
||||
|
||||
$general = $this->getVideoInfo($hls->metadata()->getVideoStreams());
|
||||
$general['width'] = $representation->getWidth();
|
||||
$general['height'] = $representation->getHeight();
|
||||
|
||||
//$this->rewriteHlsLines($this->outPath . '_' . $this->getRenditionName() . '.m3u8');
|
||||
foreach ($subtitles as $subtitle) {
|
||||
$this->rewriteHlsLines($this->outPath . '_subtitles_' . $subtitle['code'] . '.m3u8');
|
||||
}
|
||||
|
||||
return [
|
||||
$general, []
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $nest
|
||||
* @return string
|
||||
*/
|
||||
private function getHlsBaseUri(bool $nest = true): string
|
||||
{
|
||||
$uri = 'http://127.0.0.1/v1/video/' . $this->args['videoId'] . '/' . self::STREAM_HLS . '/';
|
||||
|
||||
if (empty($nest)) {
|
||||
return $uri;
|
||||
}
|
||||
|
||||
return $uri . $this->getRenditionName() . '/';
|
||||
}
|
||||
|
||||
private function rewriteHlsLines($path)
|
||||
{
|
||||
$handle = fopen($path, "r");
|
||||
@@ -325,13 +391,15 @@ class TranscodingV1 extends Worker
|
||||
if ($handle) {
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
$newLine = str_replace(array("\r","\n"), "", $line);
|
||||
if (str_contains($line, ".ts")) {
|
||||
$newLine = 'http://127.0.0.1/v1/video/' . $this->args['videoId'] . '/' . self::STREAM_HLS . '/' . $this->getRenditionName() . '/' . $newLine;
|
||||
var_dump($newLine);
|
||||
if (
|
||||
str_contains($line, ".ts") ||
|
||||
str_contains($line, ".vtt") ||
|
||||
str_contains($line, ".m3u8")
|
||||
) {
|
||||
$newLine = $this->getHlsBaseUri(str_contains($line, ".vtt") ?? false) . $newLine;
|
||||
}
|
||||
fwrite($destination, $newLine . PHP_EOL);
|
||||
}
|
||||
var_dump($path . '_tmp -> ' . $path);
|
||||
rename($path . '_tmp', $path);
|
||||
fclose($handle);
|
||||
fclose($destination);
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"adhocore/jwt": "1.1.2",
|
||||
"slickdeals/statsd": "3.1.0",
|
||||
"aminyazdanpanah/php-ffmpeg-video-streaming": "^1.2",
|
||||
"captioning/captioning": "2.*",
|
||||
"squizlabs/php_codesniffer": "^3.6",
|
||||
"ext-simplexml": "*"
|
||||
},
|
||||
|
||||
Generated
-6267
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\E2E\Scopes;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
|
||||
trait VideoCustom
|
||||
{
|
||||
use ProjectCustom;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $bucket = [];
|
||||
protected static $video = [];
|
||||
protected static $subtitle = [];
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getBucket(): array
|
||||
{
|
||||
if (!empty(self::$bucket)) {
|
||||
return self::$bucket;
|
||||
}
|
||||
|
||||
$_bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'bucketId' => 'unique()',
|
||||
'name' => 'Test Bucket 2',
|
||||
'permission' => 'file',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all']
|
||||
]);
|
||||
|
||||
self::$bucket = [
|
||||
'$id' => $_bucket['body']['$id'],
|
||||
];
|
||||
|
||||
return self::$bucket;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getVideo(): array
|
||||
{
|
||||
|
||||
if (!empty(self::$video)) {
|
||||
return self::$video;
|
||||
}
|
||||
|
||||
$source = __DIR__ . "/../../resources/disk-a/video-srt.mp4";
|
||||
$totalSize = \filesize($source);
|
||||
$chunkSize = 5 * 1024 * 1024;
|
||||
$handle = @fopen($source, "rb");
|
||||
$fileId = 'unique()';
|
||||
$mimeType = mime_content_type($source);
|
||||
$counter = 0;
|
||||
$size = filesize($source);
|
||||
$headers = [
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
];
|
||||
$id = '';
|
||||
|
||||
while (!feof($handle)) {
|
||||
$curlFile = new \CURLFile('data:' . $mimeType . ';base64,' . base64_encode(@fread($handle, $chunkSize)), $mimeType, 'video-srt.mp4');
|
||||
$headers['content-range'] = 'bytes ' . ($counter * $chunkSize) . '-' . min(((($counter * $chunkSize) + $chunkSize) - 1), $size) . '/' . $size;
|
||||
|
||||
if (!empty($id)) {
|
||||
$headers['x-appwrite-id'] = $id;
|
||||
}
|
||||
|
||||
$_file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $this->getBucket()['$id'] . '/files', array_merge($headers, $this->getHeaders()), [
|
||||
'fileId' => $fileId,
|
||||
'file' => $curlFile,
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
]);
|
||||
$counter++;
|
||||
$id = $_file['body']['$id'];
|
||||
}
|
||||
@fclose($handle);
|
||||
|
||||
self::$video = [
|
||||
'$id' => $_file['body']['$id'],
|
||||
];
|
||||
|
||||
return self::$video;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSubtitle(): array
|
||||
{
|
||||
|
||||
if (!empty(self::$subtitle)) {
|
||||
return self::$subtitle;
|
||||
}
|
||||
|
||||
$res = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $this->getBucket()['$id'] . '/files', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'file' => new \CURLFile(realpath(__DIR__ . '/../../resources/disk-a//../../resources/disk-a/video-srt.srt'), 'text/plain', 'video-srt.srt'),
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
]);
|
||||
|
||||
|
||||
self::$subtitle = [
|
||||
'$id' => $res['body']['$id'],
|
||||
];
|
||||
|
||||
return self::$subtitle;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace Tests\E2E\Services\Storage;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\VideoCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
|
||||
@@ -11,71 +12,77 @@ class VideoCustomServerTest extends Scope
|
||||
{
|
||||
use StorageBase;
|
||||
use ProjectCustom;
|
||||
use VideoCustom;
|
||||
use SideServer;
|
||||
|
||||
public function testCreateBucketFile(): array
|
||||
public function testTranscodeWithSubs() :array
|
||||
{
|
||||
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/video/buckets/' . $this->getBucket()['$id'] . '/files/' . $this->getVideo()['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'bucketId' => 'unique()',
|
||||
'name' => 'Test Bucket 2',
|
||||
'permission' => 'file',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all']
|
||||
]);
|
||||
|
||||
//$source = __DIR__ . "/../../../resources/disk-a/large-file.mp4";
|
||||
$source = __DIR__ . "/../../../resources/disk-a/very-big-file-2.mov";
|
||||
$totalSize = \filesize($source);
|
||||
$chunkSize = 5 * 1024 * 1024;
|
||||
$handle = @fopen($source, "rb");
|
||||
$fileId = 'unique()';
|
||||
$mimeType = mime_content_type($source);
|
||||
$counter = 0;
|
||||
$size = filesize($source);
|
||||
$headers = [
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
];
|
||||
$id = '';
|
||||
$videoId = $response['body']['$id'];
|
||||
|
||||
while (!feof($handle)) {
|
||||
$curlFile = new \CURLFile('data:' . $mimeType . ';base64,' . base64_encode(@fread($handle, $chunkSize)), $mimeType, 'very-large-file-1.mov');
|
||||
$headers['content-range'] = 'bytes ' . ($counter * $chunkSize) . '-' . min(((($counter * $chunkSize) + $chunkSize) - 1), $size) . '/' . $size;
|
||||
$response = $this->client->call(Client::METHOD_POST, '/video/' . $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' => 'hebrew',
|
||||
'code' => 'heb',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all']
|
||||
]);
|
||||
|
||||
if (!empty($id)) {
|
||||
$headers['x-appwrite-id'] = $id;
|
||||
}
|
||||
$response = $this->client->call(Client::METHOD_POST, '/video/' . $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',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all']
|
||||
]);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucket['body']['$id'] . '/files', array_merge($headers, $this->getHeaders()), [
|
||||
'fileId' => $fileId,
|
||||
'file' => $curlFile,
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
]);
|
||||
$counter++;
|
||||
$response = $this->client->call(Client::METHOD_GET, '/video/profiles', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($file['body']['$id']);
|
||||
$id = $file['body']['$id'];
|
||||
}
|
||||
@fclose($handle);
|
||||
|
||||
$profileId = $response['body']['profiles'][0]['$id'];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/video/' . $videoId . '/rendition/' . $profileId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all']
|
||||
]);
|
||||
return [
|
||||
'bucketId' => $bucket['body']['$id'],
|
||||
'fileId' => $id,
|
||||
];
|
||||
'videoId' => $videoId,
|
||||
'stream' => ''
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateBucketFile
|
||||
*/
|
||||
public function testTranscodingRendition($data): array
|
||||
|
||||
public function testTranscodingRendition(): array
|
||||
{
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/video/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], [
|
||||
$response = $this->client->call(Client::METHOD_POST, '/video/buckets/' . $this->getBucket()['$id'] . '/files/' . $this->getVideo()['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
@@ -114,7 +121,7 @@ class VideoCustomServerTest extends Scope
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testTranscodingRendition
|
||||
* @depends testTranscodeWithSubs
|
||||
*/
|
||||
public function testGetRendition(array $data): array
|
||||
{
|
||||
@@ -130,8 +137,6 @@ class VideoCustomServerTest extends Scope
|
||||
'write' => ['role:all']
|
||||
]);
|
||||
|
||||
|
||||
|
||||
$this->assertNotEmpty($response['body']['renditions']);
|
||||
|
||||
$videoId = $response['body']['renditions'][0]['videoId'];
|
||||
@@ -139,6 +144,7 @@ class VideoCustomServerTest extends Scope
|
||||
$profileName = $response['body']['renditions'][0]['name'];
|
||||
$stream = $response['body']['renditions'][0]['stream'];
|
||||
|
||||
var_dump($response['body']);
|
||||
|
||||
return [
|
||||
'videoId' => $videoId,
|
||||
@@ -164,6 +170,8 @@ class VideoCustomServerTest extends Scope
|
||||
'write' => ['role:all']
|
||||
]);
|
||||
|
||||
var_dump($response['body']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/video/' . $data['videoId'] . '/' . $data['stream'] . '/' . $data['profileName'] . '/' . $data['videoId'] . '_360p.m3u8', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
@@ -175,7 +183,7 @@ class VideoCustomServerTest extends Scope
|
||||
|
||||
var_dump($response['body']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/video/' . $data['videoId'] . '/hls/' . $data['profileName'] . '/' . $data['videoId'] . '_360p_0000.ts', [
|
||||
$response = $this->client->call(Client::METHOD_GET, '/video/' . $data['videoId'] . '/' . $data['stream'] . '/' . $data['profileName'] . '/' . $data['videoId'] . '_360p_0000.ts', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
@@ -185,24 +193,25 @@ class VideoCustomServerTest extends Scope
|
||||
]);
|
||||
|
||||
var_dump($response['body']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testTranscodingRendition
|
||||
*/
|
||||
public function testDashStreamRender($data): void
|
||||
{
|
||||
sleep(20);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/video/' . $data['videoId'] . '/mpeg-dash/master/' . $data['videoId'] . '.mpd', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all']
|
||||
]);
|
||||
|
||||
var_dump($response['body']);
|
||||
}
|
||||
// /**
|
||||
// * @depends testTranscodingRendition
|
||||
// */
|
||||
// public function testDashStreamRender($data): void
|
||||
// {
|
||||
// sleep(20);
|
||||
//
|
||||
// $response = $this->client->call(Client::METHOD_GET, '/video/' . $data['videoId'] . '/mpeg-dash/master/' . $data['videoId'] . '.mpd', [
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// 'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
// ], [
|
||||
// 'read' => ['role:all'],
|
||||
// 'write' => ['role:all']
|
||||
// ]);
|
||||
//
|
||||
// var_dump($response['body']);
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user