Skip to content
Projects
Groups
Snippets
Help
Sign in / Register
Toggle navigation
Minds Backend - Engine
Project overview
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Locked Files
Issues
286
Merge Requests
38
CI / CD
Security & Compliance
Packages
Wiki
Snippets
Members
Collapse sidebar
Close sidebar
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Minds
Minds Backend - Engine
Commits
9ebb0a19
Commit
9ebb0a19
authored
5 minutes ago
by
Mark Harding
Browse files
Options
Download
(feat): transcoder to support public endpoints, cli and fallback for legacy
parent
0544ae82
epic/transcoder-improvements
1 merge request
!414
WIP: New transcoder
Pipeline
#101949165
running with stages
Changes
12
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
371 additions
and
17 deletions
+371
-17
Controllers/Cli/Transcode.php
View file @
9ebb0a19
...
...
@@ -21,9 +21,14 @@ class Transcode extends Cli\Controller implements Interfaces\CliControllerInterf
public
function
exec
()
{
$transcoder
=
new
Core\Media\Services\FFMpeg
;
$transcoder
->
setKey
(
$this
->
getOpt
(
'guid'
));
$transcoder
->
setFullHD
(
$this
->
getOpt
(
'full_hd'
)
??
false
);
$transcoder
->
onQueue
();
$entity
=
Di
::
_
()
->
get
(
'EntitiesBuilder'
)
->
single
(
$this
->
getOpt
(
'guid'
));
if
(
!
$entity
)
{
$this
->
out
(
'Entity not found'
);
return
;
}
$manager
=
Di
::
_
()
->
get
(
'Media\Video\Transcoder\Manager'
);
$manager
->
createTranscodes
(
$entity
);
}
}
This diff is collapsed.
Controllers/api/v2/media/video.php
0 → 100644
View file @
9ebb0a19
<?php
/**
* Minds Video Controller
*
* @author Mark Harding
*/
namespace
Minds\Controllers\api\v2\media
;
use
Minds\Api\Factory
;
use
Minds\Core\Di\Di
;
use
Minds\Core\Media\Proxy\Download
;
use
Minds\Core\Media\Proxy\Resize
;
use
Minds\Interfaces
;
class
video
implements
Interfaces\Api
,
Interfaces\ApiIgnorePam
{
/**
* Equivalent to HTTP GET method
* @param array $pages
* @return mixed|null
*/
public
function
get
(
$pages
)
{
$videoManager
=
Di
::
_
()
->
get
(
'Media\Video\Manager'
);
$video
=
$videoManager
->
get
(
$pages
[
0
]);
Factory
::
response
([
'entity'
=>
$video
->
export
(),
'sources'
=>
Factory
::
exportable
(
$videoManager
->
getSources
(
$video
)),
'poster'
=>
$video
->
getIconUrl
(),
]);
}
/**
* Equivalent to HTTP POST method
* @param array $pages
* @return mixed|null
*/
public
function
post
(
$pages
)
{
http_response_code
(
501
);
exit
;
}
/**
* Equivalent to HTTP PUT method
* @param array $pages
* @return mixed|null
*/
public
function
put
(
$pages
)
{
http_response_code
(
501
);
exit
;
}
/**
* Equivalent to HTTP DELETE method
* @param array $pages
* @return mixed|null
*/
public
function
delete
(
$pages
)
{
http_response_code
(
501
);
exit
;
}
}
This diff is collapsed.
Core/Media/Video/Manager.php
View file @
9ebb0a19
...
...
@@ -11,17 +11,29 @@ use Minds\Core\Di\Di;
use
Minds\Entities\Activity
;
use
Minds\Entities\Entity
;
use
Minds\Entities\Video
;
use
Minds\Core\EntitiesBuilder
;
use
Minds\Common\Repository\Response
;
class
Manager
{
/** @var Config
$config
*/
/** @var Config */
private
$config
;
/** @var S3Client
$s3
*/
/** @var S3Client */
private
$s3
;
public
function
__construct
(
$config
=
null
,
$s3
=
null
)
{
/** @var EntitiesBuilder */
private
$entitiesBuilder
;
/** @var Transcoder\Manager */
private
$transcoderManager
;
public
function
__construct
(
$config
=
null
,
$s3
=
null
,
$entitiesBuilder
=
null
,
$transcoderManager
=
null
)
{
$this
->
config
=
$config
??
Di
::
_
()
->
get
(
'Config'
);
// AWS
...
...
@@ -36,6 +48,63 @@ class Manager
];
}
$this
->
s3
=
$s3
?:
new
S3Client
(
array_merge
([
'version'
=>
'2006-03-01'
],
$opts
));
$this
->
entitiesBuilder
=
$entitiesBuilder
??
Di
::
_
()
->
get
(
'EntitiesBuilder'
);
$this
->
transcoderManager
=
$transcoderManager
??
Di
::
_
()
->
get
(
'Media\Video\Transcoder\Manager'
);
}
/**
* Return a video
* @param string $guid
* @return Video
*/
public
function
get
(
$guid
)
:
Video
{
return
$this
->
entitiesBuilder
->
single
(
$guid
);
}
/**
* Return transcodes
* @param Video $video
* @return Source[]
*/
public
function
getSources
(
Video
$video
)
:
array
{
$transcodes
=
$this
->
transcoderManager
->
getList
([
'guid'
=>
$video
->
getGuid
(),
'legacyPolyfill'
=>
true
,
]);
$sources
=
[];
foreach
(
$transcodes
as
$transcode
)
{
if
(
$transcode
->
getStatus
()
!=
'completed'
)
{
continue
;
}
if
(
$transcode
->
getProfile
()
instanceof
Transcoder\TranscodeProfiles\Thumbnails
)
{
continue
;
}
$source
=
new
Source
();
$source
->
setGuid
(
$transcode
->
getGuid
())
->
setType
(
$transcode
->
getProfile
()
->
getFormat
())
->
setLabel
(
$transcode
->
getProfile
()
->
getId
())
->
setSize
(
$transcode
->
getProfile
()
->
getHeight
())
->
setSrc
(
implode
(
'/'
,
[
$this
->
config
->
get
(
'transcoder'
)[
'cdn_url'
]
??
'https://cdn-cinemr.minds.com/cinemr_dev'
,
$transcode
->
getGuid
(),
$transcode
->
getProfile
()
->
getStorageName
()
]));
$sources
[]
=
$source
;
}
// Sort the array so that mp4's are first
usort
(
$sources
,
function
(
$a
,
$b
)
{
if
(
$a
->
getType
()
===
'video/mp4'
)
{
return
-
1
;
}
return
1
;
});
return
$sources
;
}
/**
...
...
This diff is collapsed.
Core/Media/Video/Source.php
0 → 100644
View file @
9ebb0a19
<?php
namespace
Minds\Core\Media\Video
;
use
Minds\Traits\MagicAttributes
;
/**
* @method string getGuid()
* @method Source setGuid(string $guid)
* @method string getSrc()
* @method Source setSrc(string $src)
* @method string getType()
* @method Source setType(string $type)
* @method int getSize()
* @method Source setSize(int $size)
* @method string getLabel()
* @method Source setLabel(string $label)
*/
class
Source
{
use
MagicAttributes
;
/** @var string */
protected
$guid
;
/** @var string */
protected
$src
;
/** @var string */
protected
$type
;
/** @var int */
protected
$size
;
/** @var string */
protected
$label
;
/**
* Export source
* @param array $extras
* @return array
*/
public
function
export
(
$extras
=
[])
:
array
{
return
[
'guid'
=>
$this
->
guid
,
'src'
=>
$this
->
src
,
'type'
=>
$this
->
type
,
'size'
=>
$this
->
size
,
'label'
=>
$this
->
label
,
];
}
}
This diff is collapsed.
Core/Media/Video/Transcoder/Manager.php
View file @
9ebb0a19
...
...
@@ -8,6 +8,8 @@ use Minds\Core\Media\Video\Transcoder\Delegates\QueueDelegate;
use
Minds\Core\Media\Video\Transcoder\Delegates\NotificationDelegate
;
use
Minds\Entities\Video
;
use
Minds\Traits\MagicAttributes
;
use
Minds\Common\Repository\Response
;
use
Minds\Core\Media\Video\Source
;
class
Manager
{
...
...
@@ -53,15 +55,53 @@ class Manager
* Return a list of transcodes
* @return Response
*/
public
function
getList
(
$opts
)
:
Response
public
function
getList
(
$opts
)
:
?
Response
{
$opts
=
array_merge
([
'guid'
=>
null
,
'profileId'
=>
null
,
'status'
=>
null
,
'legacyPolyfill'
=>
false
,
],
$opts
);
return
$this
->
repository
->
getList
(
$opts
);
$response
=
$this
->
repository
->
getList
(
$opts
);
if
(
$opts
[
'legacyPolyfill'
]
&&
!
$response
->
count
())
{
$response
=
$this
->
getLegacyPolyfill
(
$opts
);
}
return
$response
;
}
/**
* Return a list of legacy transcodes by reading from storage
* @param array
* @return Response
*/
private
function
getLegacyPolyfill
(
array
$opts
)
:
?
Response
{
$files
=
$this
->
transcodeStorage
->
ls
(
$opts
[
'guid'
]);
if
(
!
$files
)
{
return
null
;
}
$response
=
new
Response
();
foreach
(
$files
as
$fileName
)
{
// Loop through each profile to see if fileName is a match
foreach
(
self
::
TRANSCODE_PROFILES
as
$profile
)
{
$profile
=
new
$profile
();
if
(
$profile
->
getStorageName
()
&&
strpos
(
$fileName
,
$profile
->
getStorageName
())
!==
false
)
{
$transcode
=
new
Transcode
();
$transcode
->
setGuid
(
$opts
[
'guid'
])
->
setProfile
(
$profile
)
->
setStatus
(
TranscodeStates
::
COMPLETED
);
$response
[]
=
$transcode
;
}
}
}
return
$response
;
}
/**
...
...
This diff is collapsed.
Core/Media/Video/Transcoder/Repository.php
View file @
9ebb0a19
...
...
@@ -133,7 +133,7 @@ class Repository
*/
public
function
add
(
Transcode
$transcode
)
:
bool
{
$statement
=
"INSERT INTO video_transcodes (guid, profile_id, status) VALUES (?, ?)"
;
$statement
=
"INSERT INTO video_transcodes (guid, profile_id, status) VALUES (?, ?
, ?
)"
;
$values
=
[
new
Bigint
(
$transcode
->
getGuid
()),
$transcode
->
getProfile
()
->
getId
(),
...
...
@@ -252,7 +252,7 @@ class Repository
->
setProfile
(
TranscodeProfiles\Factory
::
build
((
string
)
$row
[
'profile_id'
]))
->
setProgress
(
$row
[
'progress'
])
->
setStatus
(
$row
[
'status'
])
->
setLastEventTimestampMs
(
round
(
$row
[
'last_event_timestamp_ms'
]
->
microtime
(
true
)
*
1000
)
)
->
setLastEventTimestampMs
(
$row
[
'last_event_timestamp_ms'
]
?
round
(
$row
[
'last_event_timestamp_ms'
]
->
microtime
(
true
)
*
1000
)
:
null
)
->
setLengthSecs
(
$row
[
'length_secs'
])
->
setBytes
(
$row
[
'bytes'
]);
return
$transcode
;
...
...
This diff is collapsed.
Core/Media/Video/Transcoder/TranscodeExecutors/FFMpegExecutor.php
View file @
9ebb0a19
...
...
@@ -64,7 +64,8 @@ class FFMpegExecutor implements TranscodeExecutorInterface
// Prepare the source of this transcode
$source
=
new
Transcode
();
$source
->
setProfile
(
new
TranscodeProfiles\Source
());
// Simply change the source
$source
->
setGuid
(
$transcode
->
getGuid
())
->
setProfile
(
new
TranscodeProfiles\Source
());
// Simply change the source
// Download the source
$sourcePath
=
$this
->
transcodeStorage
->
downloadToTmp
(
$source
);
...
...
This diff is collapsed.
Core/Media/Video/Transcoder/TranscodeStorage/S3Storage.php
View file @
9ebb0a19
...
...
@@ -84,7 +84,23 @@ class S3Storage implements TranscodeStorageInterface
'Key'
=>
"
$this->dir
/
{
$transcode
->
getGuid
()
}
/
{
$transcode
->
getProfile
()
->
getStorageName
()
}
"
,
'SaveAs'
=>
$sourcePath
,
]);
return
$sourcePath
;
}
/**
* Return a list of files from storage
* @param string $guid
* @return array
*/
public
function
ls
(
string
$guid
)
:
array
{
$awsResult
=
$this
->
s3
->
listObjects
([
'Bucket'
=>
'cinemr'
,
'Prefix'
=>
"
{
$this
->
dir
}
/
{
$guid
}
"
,
]);
$s3Contents
=
$awsResult
[
'Contents'
];
return
array_column
(
$s3Contents
,
'Key'
);
}
}
This diff is collapsed.
Core/Media/Video/Transcoder/TranscodeStorage/TranscodeStorageInterface.php
View file @
9ebb0a19
...
...
@@ -25,4 +25,11 @@ interface TranscodeStorageInterface
* @return string
*/
public
function
downloadToTmp
(
Transcode
$transcode
)
:
string
;
/**
* Return a list of files from storage
* @param string $guid
* @return array
*/
public
function
ls
(
string
$guid
)
:
array
;
}
This diff is collapsed.
Spec/Core/Media/Video/ManagerSpec.php
View file @
9ebb0a19
...
...
@@ -5,7 +5,10 @@ namespace Spec\Minds\Core\Media\Video;
use
Minds\Core\Config
;
use
Aws\S3\S3Client
;
use
Minds\Core\Media\Video\Manager
;
use
Minds\Core\Media\Video\Transcoder
;
use
Minds\Entities\Video
;
use
Minds\Core\EntitiesBuilder
;
use
Minds\Common\Repository\Response
;
use
Psr\Http\Message\RequestInterface
;
use
PhpSpec\ObjectBehavior
;
use
Prophecy\Argument
;
...
...
@@ -14,12 +17,16 @@ class ManagerSpec extends ObjectBehavior
{
private
$config
;
private
$s3
;
private
$entitiesBuilder
;
private
$transcoderManager
;
public
function
let
(
Config
$config
,
S3Client
$s3
)
public
function
let
(
Config
$config
,
S3Client
$s3
,
EntitiesBuilder
$entitiesBuilder
,
Transcoder\Manager
$transcoderManager
)
{
$this
->
beConstructedWith
(
$config
,
$s3
);
$this
->
beConstructedWith
(
$config
,
$s3
,
$entitiesBuilder
,
$transcoderManager
);
$this
->
config
=
$config
;
$this
->
s3
=
$s3
;
$this
->
entitiesBuilder
=
$entitiesBuilder
;
$this
->
transcoderManager
=
$transcoderManager
;
}
public
function
it_is_initializable
()
...
...
@@ -27,6 +34,55 @@ class ManagerSpec extends ObjectBehavior
$this
->
shouldHaveType
(
Manager
::
class
);
}
public
function
it_should_return_a_video
()
{
$video
=
new
Video
();
$this
->
entitiesBuilder
->
single
(
123
)
->
shouldBeCalled
()
->
willReturn
(
$video
);
$this
->
get
(
123
)
->
shouldReturn
(
$video
);
}
public
function
it_should_return_available_sources
()
{
$video
=
new
Video
();
$video
->
set
(
'guid'
,
'123'
);
$this
->
transcoderManager
->
getList
([
'guid'
=>
'123'
,
'legacyPolyfill'
=>
true
,
])
->
shouldBeCalled
()
->
willReturn
(
new
Response
([
(
new
Transcoder\Transcode
())
->
setGuid
(
'123'
)
->
setProfile
(
new
Transcoder\TranscodeProfiles\X264_720p
())
->
setStatus
(
'created'
),
(
new
Transcoder\Transcode
())
->
setGuid
(
'123'
)
->
setProfile
(
new
Transcoder\TranscodeProfiles\X264_360p
())
->
setStatus
(
'completed'
),
(
new
Transcoder\Transcode
())
->
setGuid
(
'123'
)
->
setProfile
(
new
Transcoder\TranscodeProfiles\Webm_360p
())
->
setStatus
(
'completed'
),
]));
$sources
=
$this
->
getSources
(
$video
);
$sources
->
shouldHaveCount
(
2
);
$sources
[
0
]
->
getType
()
->
shouldBe
(
'video/mp4'
);
$sources
[
0
]
->
getSize
(
360
);
$sources
[
1
]
->
getType
()
->
shouldBe
(
'video/webm'
);
$sources
[
1
]
->
getSize
(
360
);
}
public
function
it_should_get_a_signed_720p_video_url
(
RequestInterface
$request
,
\Aws\CommandInterface
$cmd
)
{
$this
->
config
->
get
(
'transcoder'
)
...
...
This diff is collapsed.
Spec/Core/Media/Video/Transcoder/ManagerSpec.php
View file @
9ebb0a19
...
...
@@ -12,6 +12,7 @@ use Minds\Core\Media\Video\Transcoder\Transcode;
use
Minds\Core\Media\Video\Transcoder\Delegates\NotificationDelegate
;
use
Minds\Entities\Video
;
use
Minds\Entities\User
;
use
Minds\Common\Repository\Response
;
use
PhpSpec\ObjectBehavior
;
use
Prophecy\Argument
;
...
...
@@ -146,4 +147,32 @@ class ManagerSpec extends ObjectBehavior
$this
->
transcode
(
$transcode
);
}
public
function
it_should_get_legacy_files
()
{
$this
->
repository
->
getList
([
'guid'
=>
'123'
,
'profileId'
=>
null
,
'status'
=>
null
,
'legacyPolyfill'
=>
true
,
])
->
shouldBeCalled
()
->
willReturn
(
new
Response
());
$this
->
transcodeStorage
->
ls
(
'123'
)
->
shouldBeCalled
()
->
willReturn
([
'/my-dir/123/360p.mp4'
,
'/my-dir/123/720p.mp4'
,
'/my-dir/123/360p.webm'
,
]);
$transcodes
=
$this
->
getList
([
'guid'
=>
'123'
,
'legacyPolyfill'
=>
true
,
]);
$transcodes
->
shouldHaveCount
(
3
);
$transcodes
[
0
]
->
getProfile
()
->
getStorageName
(
'360p.mp4'
);
}
}
This diff is collapsed.
Spec/Core/Media/Video/Transcoder/TranscodeExecutors/FFMpegExecutorSpec.php
View file @
9ebb0a19
...
...
@@ -38,7 +38,10 @@ class FFMpegExecutorSpec extends ObjectBehavior
\FFMpeg\FFProbe\DataMapping\Format
$ffprobeFormat
,
\FFMpeg\Media\Frame
$ffmpegFrame
)
{
//$transcode = new Transcode();
$transcode
->
getGuid
()
->
shouldBeCalled
()
->
willReturn
(
'123'
);
$transcode
->
getProfile
()
->
shouldBeCalled
()
->
willReturn
(
new
TranscodeProfiles\Thumbnails
());
...
...
@@ -97,6 +100,10 @@ class FFMpegExecutorSpec extends ObjectBehavior
\FFMpeg\FFProbe\DataMapping\Format
$ffprobeFormat
,
\FFMpeg\Filters\Video\VideoFilters
$ffmpegVideoFilters
)
{
$transcode
->
getGuid
()
->
shouldBeCalled
()
->
willReturn
(
'123'
);
$transcode
->
getProfile
()
->
shouldBeCalled
()
->
willReturn
(
new
TranscodeProfiles\X264_360p
());
...
...
@@ -146,6 +153,10 @@ class FFMpegExecutorSpec extends ObjectBehavior
\FFMpeg\FFProbe\DataMapping\Format
$ffprobeFormat
,
\FFMpeg\Filters\Video\VideoFilters
$ffmpegVideoFilters
)
{
$transcode
->
getGuid
()
->
shouldBeCalled
()
->
willReturn
(
'123'
);
$transcode
->
getProfile
()
->
shouldBeCalled
()
->
willReturn
(
new
TranscodeProfiles\X264_360p
());
...
...
This diff is collapsed.
Please
register
or
sign in
to comment