bug fixes

This commit is contained in:
shimon
2023-04-23 19:57:12 +03:00
parent 60098d71ee
commit a187aa4bc5
9 changed files with 75 additions and 62 deletions
+2 -2
View File
@@ -23,10 +23,10 @@ _APP_DB_SCHEMA=appwrite
_APP_DB_USER=user
_APP_DB_PASS=password
_APP_DB_ROOT_PASS=rootsecretpassword
_APP_STORAGE_DEVICE=DOSpaces
_APP_STORAGE_DEVICE=Local
_APP_STORAGE_S3_ACCESS_KEY=
_APP_STORAGE_S3_SECRET=
_APP_STORAGE_S3_REGION=us-east-1
_APP_STORAGE_S3_REGION=
_APP_STORAGE_S3_BUCKET=
_APP_STORAGE_DO_SPACES_ACCESS_KEY=
_APP_STORAGE_DO_SPACES_SECRET=
+50 -46
View File
@@ -807,7 +807,7 @@ App::delete('/v1/videos/:videoId/renditions/:renditionId')
$response->noContent();
});
App::get('/v1/videos/:videoId/outputs/:output')
App::get('/v1/videos/:videoId/outputs/:output/:fileName')
->desc('Get video master renditions manifest')
->groups(['api', 'videos'])
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
@@ -821,10 +821,11 @@ App::get('/v1/videos/:videoId/outputs/:output')
->label('scope', 'videos.read')
->param('videoId', null, new UID(), 'Video unique ID.')
->param('output', '', new WhiteList(['hls', 'dash']), 'output name')
->param('fileName', '', new WhiteList(['master.m3u8', 'master.mpd']), 'manifest filename')
->inject('response')
->inject('dbForProject')
->inject('mode')
->action(function (string $videoId, string $output, Response $response, Database $dbForProject, string $mode) {
->action(function (string $videoId, string $output, string $fileName, Response $response, Database $dbForProject, string $mode) {
$dbStartTime = microtime(true);
$video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId));
@@ -852,7 +853,7 @@ App::get('/v1/videos/:videoId/outputs/:output')
]));
$dbEnd = microtime(true) - $dbStartTime;
var_dump('$dbEnd = ' . $dbEnd);
var_dump('$dbTime = ' . $dbEnd);
$prosessStart = microtime(true);
$_renditions = $_subtitles = [];
@@ -862,7 +863,7 @@ App::get('/v1/videos/:videoId/outputs/:output')
'name' => $subtitle->getAttribute('name'),
'code' => $subtitle->getAttribute('code'),
'default' => !empty($subtitle->getAttribute('default')) ? 'YES' : 'NO',
'uri' => $baseUrl . '/subtitles/' . $subtitle->getId(),
'uri' => $baseUrl . '/subtitles/' . $subtitle->getId() . '/subtitles.m3u8',
];
}
@@ -878,10 +879,10 @@ App::get('/v1/videos/:videoId/outputs/:output')
'name' => $stream['language'],
'default' => ($i === 0) ? 'YES' : 'NO',
'language' => $stream['language'],
'uri' => $baseUrl . '/renditions/' . $rendition->getId() . '/streams/' . $stream['id'],
'uri' => $baseUrl . '/renditions/' . $rendition->getId() . '/streams/' . $stream['id'] . '/playlist.m3u8',
];
} elseif ($stream['type'] === 'video') {
$uri = $baseUrl . '/renditions/' . $rendition->getId() . '/streams/' . $stream['id'];
$uri = $baseUrl . '/renditions/' . $rendition->getId() . '/streams/' . $stream['id'] . '/playlist.m3u8';
$resolution = $stream['resolution'] ?? $rendition->getAttribute('width') . 'x' . $rendition->getAttribute('height');
$bandwidth = $stream['bandwidth'] ?? ($rendition->getAttribute('videoBitRate') + $rendition->getAttribute('audioBitRate')) * 1024;
$codecs = $stream['codecs'] ?? null;
@@ -910,7 +911,6 @@ App::get('/v1/videos/:videoId/outputs/:output')
// $prosessEnd = microtime(true) - $prosessStart;
// var_dump('$dbEnd = ' . $dbEnd);
// var_dump('$prosessEnd = ' . $prosessEnd);
} else {
$adaptationId = 0;
foreach ($renditions as $rendition) {
@@ -942,11 +942,11 @@ App::get('/v1/videos/:videoId/outputs/:output')
foreach ($segments ?? [] as $segment) {
if ($segment->getAttribute('isInit')) {
$representation['segmentList']['init'] = $segment->getId();
$representation['segmentList']['init'] = $segment->getId() . '/segment.m4s';
continue;
}
$representation['segmentList']['media'][] = $segment->getId();
$representation['segmentList']['media'][] = $segment->getId() . '/segment.m4s';
}
$attributes = (array)$adaptation->attributes();
@@ -964,7 +964,7 @@ App::get('/v1/videos/:videoId/outputs/:output')
foreach ($subtitles ?? [] as $subtitle) {
$_subtitles[] = [
'id' => $adaptationId,
'baseUrl' => $baseUrl . '/subtitles/' . $subtitle->getId(),
'baseUrl' => $baseUrl . '/subtitles/' . $subtitle->getId() . '/subtitle.vtt',
'name' => $subtitle->getAttribute('name'),
];
$adaptationId++;
@@ -981,7 +981,7 @@ App::get('/v1/videos/:videoId/outputs/:output')
}
});
App::get('/v1/videos/:videoId/outputs/:output/renditions/:renditionId/streams/:streamId')
App::get('/v1/videos/:videoId/outputs/:output/renditions/:renditionId/streams/:streamId/:fileName')
->desc('Get video rendition manifest')
->groups(['api', 'videos'])
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
@@ -997,10 +997,11 @@ App::get('/v1/videos/:videoId/outputs/:output/renditions/:renditionId/streams/:s
->param('output', '', new WhiteList(['hls']), 'Output name.')
->param('renditionId', null, new UID(), 'Rendition unique ID.')
->param('streamId', 0, new Range(0, 10), 'Stream ID.')
->param('fileName', '', new WhiteList(['playlist.m3u8']), 'Playlist filename')
->inject('response')
->inject('dbForProject')
->inject('mode')
->action(function (string $videoId, string $output, string $renditionId, string $streamId, Response $response, Database $dbForProject, string $mode) {
->action(function (string $videoId, string $output, string $renditionId, string $streamId, string $fileName, Response $response, Database $dbForProject, string $mode) {
$video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId));
@@ -1034,7 +1035,7 @@ App::get('/v1/videos/:videoId/outputs/:output/renditions/:renditionId/streams/:s
foreach ($segments as $segment) {
$_segments[] = [
'duration' => $segment->getAttribute('duration'),
'url' => TMP_HOST . 'v1/videos/' . $videoId . '/outputs/' . $output . '/renditions/' . $renditionId . '/segments/' . $segment->getId(),
'url' => TMP_HOST . 'v1/videos/' . $videoId . '/outputs/' . $output . '/renditions/' . $renditionId . '/segments/' . $segment->getId() . '/segment.ts',
];
}
@@ -1045,7 +1046,7 @@ App::get('/v1/videos/:videoId/outputs/:output/renditions/:renditionId/streams/:s
->send($template->render(false));
});
App::get('/v1/videos/:videoId/outputs/:output/renditions/:renditionId/segments/:segmentId')
App::get('/v1/videos/:videoId/outputs/:output/renditions/:renditionId/segments/:segmentId/:fileName')
->desc('Get video rendition segment')
->groups(['api', 'videos'])
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
@@ -1061,10 +1062,11 @@ App::get('/v1/videos/:videoId/outputs/:output/renditions/:renditionId/segments/:
->param('output', '', new WhiteList(['hls', 'dash']), 'Output name')
->param('renditionId', '', new UID(), 'Rendition unique ID.')
->param('segmentId', '', new UID(), 'Segment unique ID.')
->param('fileName', '', new WhiteList(['segment.ts', 'segment.m4s']), 'Segment filename')
->inject('response')
->inject('dbForProject')
->inject('deviceVideos')
->action(function (string $videoId, string $output, string $renditionId, string $segmentId, Response $response, Database $dbForProject, Device $deviceVideos) {
->action(function (string $videoId, string $output, string $renditionId, string $segmentId, string $fileName, Response $response, Database $dbForProject, Device $deviceVideos) {
$segment = Authorization::skip(fn() => $dbForProject->getDocument('videos_renditions_segments', $segmentId));
if ($segment->isEmpty()) {
@@ -1076,13 +1078,13 @@ App::get('/v1/videos/:videoId/outputs/:output/renditions/:renditionId/segments/:
if ($output === 'hls') {
$response->setContentType('video/MP2T')
->send($data);
} else {
$response->setContentType('video/iso.segment')
->send($data);
}
$response->setContentType('video/iso.segment')
->send($data);
});
App::get('/v1/videos/:videoId/outputs/:output/subtitles/:subtitleId')
App::get('/v1/videos/:videoId/outputs/:output/subtitles/:subtitleId/:fileName')
->desc('Get video subtitle')
->groups(['api', 'videos'])
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
@@ -1097,11 +1099,12 @@ App::get('/v1/videos/:videoId/outputs/:output/subtitles/:subtitleId')
->param('videoId', null, new UID(), 'Video unique ID.')
->param('output', '', new WhiteList(['hls', 'dash']), 'Protocol name')
->param('subtitleId', '', new UID(), 'Subtitle unique ID.')
->param('fileName', '', new WhiteList(['subtitles.m3u8', 'subtitle.vtt']), 'Subtitle filename')
->inject('response')
->inject('dbForProject')
->inject('deviceVideos')
->inject('mode')
->action(function (string $videoId, string $output, string $subtitleId, Response $response, Database $dbForProject, Device $deviceVideos, string $mode) {
->action(function (string $videoId, string $output, string $subtitleId, $fileName, Response $response, Database $dbForProject, Device $deviceVideos, string $mode) {
$video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId));
if ($video->isEmpty()) {
@@ -1119,36 +1122,36 @@ App::get('/v1/videos/:videoId/outputs/:output/subtitles/:subtitleId')
throw new Exception(Exception::VIDEO_SUBTITLE_NOT_FOUND);
}
if ($output === 'hls') {
$segments = Authorization::skip(fn () => $dbForProject->find('videos_subtitles_segments', [
Query::equal('subtitleId', [$subtitleId]),
]));
if (empty($segments)) {
throw new Exception(Exception::VIDEO_SUBTITLE_SEGMENT_NOT_FOUND);
}
$_segments = [];
foreach ($segments as $segment) {
$_segments[] = [
'duration' => $segment->getAttribute('duration'),
'url' => TMP_HOST . 'v1/videos/' . $videoId . '/outputs/' . $output . '/subtitles/' . $subtitleId . '/segments/' . $segment->getId(),
];
}
$template = new View(__DIR__ . '/../../views/videos/hls-subtitles.phtml');
$template->setParam('targetDuration', $subtitle->getAttribute('targetDuration'));
$template->setParam('segments', $_segments);
$response->setContentType('application/x-mpegurl')
->send($template->render(false));
} else {
$data = $deviceVideos->read($deviceVideos->getPath($subtitle->getAttribute('videoId')) . '/' . $subtitle->getId() . '.vtt');
if ($output === 'dash') {
$data = $deviceVideos->read($deviceVideos->getPath($subtitle->getAttribute('videoId')) . '/subtitles/' . $subtitle->getId() . '.vtt');
$response->setContentType('text/vtt')
->send($data);
}
$segments = Authorization::skip(fn () => $dbForProject->find('videos_subtitles_segments', [
Query::equal('subtitleId', [$subtitleId]),
]));
if (empty($segments)) {
throw new Exception(Exception::VIDEO_SUBTITLE_SEGMENT_NOT_FOUND);
}
$_segments = [];
foreach ($segments as $segment) {
$_segments[] = [
'duration' => $segment->getAttribute('duration'),
'url' => TMP_HOST . 'v1/videos/' . $videoId . '/outputs/' . $output . '/subtitles/' . $subtitleId . '/segments/' . $segment->getId() . '/subtitle.vtt',
];
}
$template = new View(__DIR__ . '/../../views/videos/hls-subtitles.phtml');
$template->setParam('targetDuration', $subtitle->getAttribute('targetDuration'));
$template->setParam('segments', $_segments);
$response->setContentType('application/x-mpegurl')
->send($template->render(false));
});
App::get('/v1/videos/:videoId/outputs/:output/subtitles/:subtitleId/segments/:segmentId')
App::get('/v1/videos/:videoId/outputs/:output/subtitles/:subtitleId/segments/:segmentId/:fileName')
->desc('Get video subtitle segment')
->groups(['api', 'videos'])
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
@@ -1164,11 +1167,12 @@ App::get('/v1/videos/:videoId/outputs/:output/subtitles/:subtitleId/segments/:se
->param('output', '', new WhiteList(['hls', 'dash']), 'output name')
->param('subtitleId', '', new UID(), 'Subtitle unique ID.')
->param('segmentId', '', new UID(), 'Segment unique ID.')
->param('fileName', '', new WhiteList(['subtitle.vtt']), 'Subtitle filename')
->inject('response')
->inject('dbForProject')
->inject('deviceVideos')
->inject('mode')
->action(function (string $videoId, string $output, string $subtitleId, string $segmentId, Response $response, Database $dbForProject, Device $deviceVideos, string $mode) {
->action(function (string $videoId, string $output, string $subtitleId, string $segmentId, string $fileName, Response $response, Database $dbForProject, Device $deviceVideos, string $mode) {
$video = Authorization::skip(fn() => $dbForProject->getDocument('videos', $videoId));
if ($video->isEmpty()) {
+1 -1
View File
@@ -889,7 +889,7 @@ App::setResource('project', function ($dbForConsole, $request, $console) {
/** @var Utopia\Database\Document $console */
$projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', 'console'));
//$projectId = '6434237946e602be6953';
$projectId = '6444e6ceb5f6d277860d';
if ($projectId === 'console') {
return $console;
+1 -1
View File
@@ -31,7 +31,7 @@ echo '<?xml version="1.0" encoding="utf-8"?>'; ?>
<AdaptationSet id="<?php if (isset($subtitle['id'])): echo $subtitle['id']; endif;?>" lang="<?php if (isset($subtitle['name'])): echo $this->escape($subtitle['name']); endif;?>" mimeType="text/vtt">
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="subtitle" />
<Representation>
<BaseURL><?php if (isset($subtitle['baseUrl'])): $this->escape($subtitle['baseUrl']); endif;?></BaseURL>
<BaseURL><?php if (isset($subtitle['baseUrl'])): echo $this->escape($subtitle['baseUrl']); endif;?></BaseURL>
</Representation>
</AdaptationSet>
<?php endforeach; ?>
+2 -2
View File
@@ -1,12 +1,12 @@
#EXTM3U
#EXT-X-VERSION:3
<?php foreach ($this->getParam('audios', []) as $audio):?>
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="group_audio"<?php if($audio['name'] !== null):?>,NAME="<?php echo $audio['name'];?>"<?php endif;?>,AUTOSELECT=YES,FORCED=NO,DEFAULT=<?php echo $audio['default']; ?><?php if($audio['language'] !== null):?>,LANGUAGE="<?php echo $audio['language'];?>"<?php endif;?><?php if($audio['uri'] !== null):?>,URI="<?php echo $audio['uri'];?>"<?php endif;?><?php echo PHP_EOL?>
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="group_audio"<?php if($audio['name'] !== null):?>,NAME="<?php echo $audio['name'];?>"<?php endif;?>,AUTOSELECT=YES<?php if($audio['language'] !== null):?>,LANGUAGE="<?php echo $audio['language'];?>"<?php endif;?><?php if($audio['uri'] !== null):?>,URI="<?php echo $audio['uri'];?>"<?php endif;?><?php echo PHP_EOL?>
<?php endforeach;?>
<?php foreach ($this->getParam('subtitles', []) as $subtitle):?>
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="<?php echo $this->escape($subtitle['name']); ?>",DEFAULT=<?php echo $subtitle['default']; ?>,AUTOSELECT=YES,FORCED=NO,LANGUAGE="<?php echo $this->escape($subtitle['code']); ?>",URI="<?php echo $subtitle['uri']; ?>"<?php echo PHP_EOL?>
<?php endforeach;?>
<?php foreach ($this->getParam('renditions', []) as $rendition):?>
#EXT-X-STREAM-INF:BANDWIDTH="<?php echo $rendition['bandwidth']; ?>",RESOLUTION="<?php echo $rendition['resolution']; ?>",NAME="<?php echo $rendition['name']; ?>"<?php if($rendition['codecs'] !== null):?>,CODECS="<?php echo $rendition['c>
#EXT-X-STREAM-INF:BANDWIDTH="<?php echo $rendition['bandwidth']; ?>",RESOLUTION="<?php echo $rendition['resolution']; ?>",NAME="<?php echo $rendition['name']; ?>"<?php if($rendition['codecs'] !== null):?>,CODECS="<?php echo $rendition['codecs']; ?>"<?php endif;?><?php if($rendition['subs'] !== null):?>,SUBTITLES="<?php echo $rendition['subs'];?>"<?php endif;?><?php if($rendition['audio'] !== null):?>,AUDIO="<?php echo $rendition['audio'];?>"<?php endif;?> <?php echo PHP_EOL?>
<?php echo $rendition['uri'];?><?php echo PHP_EOL?>
<?php endforeach;?>
+2 -2
View File
@@ -5,6 +5,6 @@
#EXT-X-TARGETDURATION:<?php echo $this->getParam('targetDuration', 0)?><?php echo PHP_EOL?>
#EXT-X-MEDIA-SEQUENCE:0
<?php foreach ($this->getParam('segments', []) as $segment): ?>
#EXTINF:<?php echo $segment['duration']; ?><?php echo PHP_EOL?>
<?php echo $segment['url']; ?><?php echo PHP_EOL?>
#EXTINF:<?php echo $segment['duration']; ?><?php echo PHP_EOL?>
<?php echo $segment['url']; ?><?php echo PHP_EOL?>
<?php endforeach; ?>
+13 -4
View File
@@ -58,6 +58,7 @@ class VideosV1 extends Worker
private Document $bucket;
private FFProbe $ffprobe;
private FFMpeg $ffmpeg;
private bool $isVideo = false;
public function getName(): string
{
@@ -128,6 +129,7 @@ class VideosV1 extends Worker
->setAttribute('format', $general->has('format') ? $general->get('format')->getShortName() : '');
foreach ($mediaInfoContainer->getVideos() ?? [] as $video) {
$this->isVideo = true;
$this->video
->setAttribute('height', $video->has('height') ? $video->get('height')->getAbsoluteValue() : 0)
->setAttribute('width', $video->has('width') ? $video->get('width')->getAbsoluteValue() : 0)
@@ -163,10 +165,13 @@ class VideosV1 extends Worker
),
);
}
$this->video->getAttribute('videoBitRate');
$media = $this->ffmpeg->open($inPath);
if ($this->action === 'preview') {
if (!$this->isVideo) {
return;
}
console::info('Creating preview image from second ' . $this->args['second']);
$path = $this->getVideoDevice($this->project->getId())->getPath($this->video->getId()) . '/preview/';
@@ -242,6 +247,9 @@ class VideosV1 extends Worker
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: playerjs
*/
if (!$this->isVideo) {
return;
}
$interval = 2;
$ranges = [
@@ -671,9 +679,10 @@ class VideosV1 extends Worker
$attr['resolution'] = $parts[1];
break;
case str_contains($parts[0], 'CODECS'):
$attr['codecs'] = $parts[1];
if(!empty($attributes[$key + 1])){
$attr['codecs'] .= ',' . $attributes[$key + 1];
$attr['codecs'] = trim($parts[1], " \n\r\t\v\x00");
if (!empty($attributes[$key + 1])) {
$attr['codecs'] .= ',' . trim($attributes[$key + 1], " \n\r\t\v\x00");
}
break;
}
+3 -3
View File
@@ -243,9 +243,9 @@ services:
- appwrite-uploads:/storage/uploads:rw
- appwrite-videos:/storage/videos:rw
#- ./tests:/usr/src/code/tests #TODO remove when done!!
#- ./vendor/aminyazdanpanah/php-ffmpeg-video-streaming:/usr/src/code/vendor/aminyazdanpanah/php-ffmpeg-video-streaming #TODO remove when done!!
#- ./vendor/php-ffmpeg/php-ffmpeg:/usr/src/code/vendor/php-ffmpeg/php-ffmpeg #TODO remove when done!!
#- ./vendor/alchemy/binary-driver:/usr/src/code/vendor/alchemy/binary-driver
- ./vendor/aminyazdanpanah/php-ffmpeg-video-streaming:/usr/src/code/vendor/aminyazdanpanah/php-ffmpeg-video-streaming #TODO remove when done!!
- ./vendor/php-ffmpeg/php-ffmpeg:/usr/src/code/vendor/php-ffmpeg/php-ffmpeg #TODO remove when done!!
- ./vendor/alchemy/binary-driver:/usr/src/code/vendor/alchemy/binary-driver
- ./vendor/utopia-php/storage:/usr/src/code/vendor/utopia-php/storage
#- ./vendor/symfony/process:/usr/src/code/vendor/symfony/process