video: configure external go-vod too
Signed-off-by: Varun Patil <varunpatil@ucla.edu>pull/563/head
parent
cb8ebcb6a8
commit
ac0cc6460b
187
lib/BinExt.php
187
lib/BinExt.php
|
@ -5,6 +5,7 @@ namespace OCA\Memories;
|
|||
class BinExt
|
||||
{
|
||||
public const EXIFTOOL_VER = '12.58';
|
||||
public const GOVOD_VER = '0.0.34';
|
||||
|
||||
/** Test configured exiftool binary */
|
||||
public static function testExiftool(): bool
|
||||
|
@ -76,6 +77,192 @@ class BinExt
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the upstream URL for a video.
|
||||
*/
|
||||
public static function getGoVodUrl(string $client, string $path, string $profile): string
|
||||
{
|
||||
$path = rawurlencode($path);
|
||||
|
||||
$bind = Util::getSystemConfig('memories.vod.bind');
|
||||
$connect = Util::getSystemConfig('memories.vod.connect', $bind);
|
||||
|
||||
return "http://{$connect}/{$client}{$path}/{$profile}";
|
||||
}
|
||||
|
||||
public static function getGoVodConfig($local = false)
|
||||
{
|
||||
// Get config from system values
|
||||
$env = [
|
||||
'vaapi' => Util::getSystemConfig('memories.vod.vaapi'),
|
||||
'vaapiLowPower' => Util::getSystemConfig('memories.vod.vaapi.low_power'),
|
||||
|
||||
'nvenc' => Util::getSystemConfig('memories.vod.nvenc', false),
|
||||
'nvencTemporalAQ' => Util::getSystemConfig('memories.vod.nvenc.temporal_aq'),
|
||||
'nvencScale' => Util::getSystemConfig('memories.vod.nvenc.scale'),
|
||||
];
|
||||
|
||||
if (!$local) {
|
||||
return $env;
|
||||
}
|
||||
|
||||
// Get temp directory
|
||||
$tmpPath = Util::getSystemConfig('memories.vod.tempdir', sys_get_temp_dir().'/go-vod/');
|
||||
|
||||
// Make sure path ends with slash
|
||||
if ('/' !== substr($tmpPath, -1)) {
|
||||
$tmpPath .= '/';
|
||||
}
|
||||
|
||||
// Add instance ID to path
|
||||
$tmpPath .= Util::getSystemConfig('instanceid', 'default');
|
||||
|
||||
return array_merge($env, [
|
||||
'bind' => Util::getSystemConfig('memories.vod.bind'),
|
||||
'ffmpeg' => Util::getSystemConfig('memories.vod.ffmpeg'),
|
||||
'ffprobe' => Util::getSystemConfig('memories.vod.ffprobe'),
|
||||
'tempdir' => $tmpPath,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* If local, restart the go-vod instance.
|
||||
* If external, configure the go-vod instance.
|
||||
*/
|
||||
public static function startGoVod()
|
||||
{
|
||||
// Check if external
|
||||
if (Util::getSystemConfig('memories.vod.external')) {
|
||||
self::configureGoVod();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get transcoder path
|
||||
$transcoder = Util::getSystemConfig('memories.vod.path');
|
||||
if (empty($transcoder)) {
|
||||
throw new \Exception('Transcoder not configured');
|
||||
}
|
||||
|
||||
// Make sure transcoder exists
|
||||
if (!file_exists($transcoder)) {
|
||||
throw new \Exception("Transcoder not found; ({$transcoder})");
|
||||
}
|
||||
|
||||
// Make transcoder executable
|
||||
if (!is_executable($transcoder)) {
|
||||
@chmod($transcoder, 0755);
|
||||
if (!is_executable($transcoder)) {
|
||||
throw new \Exception("Transcoder not executable (chmod 755 {$transcoder})");
|
||||
}
|
||||
}
|
||||
|
||||
// Get local config
|
||||
$env = self::getGoVodConfig(true);
|
||||
$tmpPath = $env['tempdir'];
|
||||
|
||||
// (Re-)create temp dir
|
||||
shell_exec("rm -rf '{$tmpPath}' && mkdir -p '{$tmpPath}' && chmod 755 '{$tmpPath}'");
|
||||
|
||||
// Check temp directory exists
|
||||
if (!is_dir($tmpPath)) {
|
||||
throw new \Exception("Temp directory could not be created ({$tmpPath})");
|
||||
}
|
||||
|
||||
// Check temp directory is writable
|
||||
if (!is_writable($tmpPath)) {
|
||||
throw new \Exception("Temp directory is not writable ({$tmpPath})");
|
||||
}
|
||||
|
||||
// Write config to file
|
||||
$logFile = $tmpPath.'.log';
|
||||
$configFile = $tmpPath.'.json';
|
||||
file_put_contents($configFile, json_encode($env, JSON_PRETTY_PRINT));
|
||||
|
||||
// Kill the transcoder in case it's running
|
||||
\OCA\Memories\Util::pkill($transcoder);
|
||||
|
||||
// Start transcoder
|
||||
shell_exec("nohup {$transcoder} {$configFile} >> '{$logFile}' 2>&1 & > /dev/null");
|
||||
|
||||
// wait for 500ms
|
||||
usleep(500000);
|
||||
|
||||
return $logFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test go-vod and (re)-start if it is not external.
|
||||
*/
|
||||
public static function testStartGoVod(): bool
|
||||
{
|
||||
try {
|
||||
return self::testGoVod();
|
||||
} catch (\Exception $e) {
|
||||
// silently try to restart
|
||||
}
|
||||
|
||||
// Attempt to (re)start go-vod
|
||||
// If it is external, this only attempts to reconfigure
|
||||
self::startGoVod();
|
||||
|
||||
// Test again
|
||||
return self::testGoVod();
|
||||
}
|
||||
|
||||
/** Test the go-vod instance that is running */
|
||||
public static function testGoVod(): bool
|
||||
{
|
||||
// TODO: check data mount; ignoring the result of the file for now
|
||||
$testfile = realpath(__DIR__.'/../exiftest.jpg');
|
||||
|
||||
// Make request
|
||||
$url = self::getGoVodUrl('test', $testfile, 'test');
|
||||
|
||||
try {
|
||||
$client = new \GuzzleHttp\Client();
|
||||
$res = $client->request('GET', $url);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception('failed to connect to go-vod: '.$e->getMessage());
|
||||
}
|
||||
|
||||
// Parse body
|
||||
$json = json_decode($res->getBody(), true);
|
||||
if (!$json) {
|
||||
throw new \Exception('failed to parse go-vod response');
|
||||
}
|
||||
|
||||
// Check version
|
||||
$version = $json['version'];
|
||||
$target = self::GOVOD_VER;
|
||||
if (!version_compare($version, $target, '=')) {
|
||||
throw new \Exception("version does not match {$version} <==> {$target}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** POST a new configuration to go-vod */
|
||||
public static function configureGoVod()
|
||||
{
|
||||
// Get config
|
||||
$config = self::getGoVodConfig();
|
||||
|
||||
// Make request
|
||||
$url = self::getGoVodUrl('config', '/config', 'config');
|
||||
|
||||
try {
|
||||
$client = new \GuzzleHttp\Client();
|
||||
$client->request('POST', $url, [
|
||||
'json' => $config,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception('failed to connect to go-vod: '.$e->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Detect the go-vod binary to use */
|
||||
public static function detectGoVod()
|
||||
{
|
||||
|
|
|
@ -92,7 +92,11 @@ class OtherController extends GenericApiController
|
|||
|
||||
// If changing vod settings, kill any running go-vod instances
|
||||
if (0 === strpos($key, 'memories.vod.')) {
|
||||
Util::pkill('go-vod');
|
||||
try {
|
||||
BinExt::startGoVod();
|
||||
} catch (\Exception $e) {
|
||||
error_log('Failed to start go-vod: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
|
@ -129,6 +133,14 @@ class OtherController extends GenericApiController
|
|||
|
||||
// Check go-vod binary
|
||||
$status['govod'] = $this->getExecutableStatus(Util::getSystemConfig('memories.vod.path'));
|
||||
if ('ok' === $status['govod'] || Util::getSystemConfig('memories.vod.external')) {
|
||||
try {
|
||||
BinExt::testStartGoVod();
|
||||
$status['govod'] = 'test_ok';
|
||||
} catch (\Exception $e) {
|
||||
$status['govod'] = 'test_fail:'.$e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for VA-API device
|
||||
$devPath = '/dev/dri/renderD128';
|
||||
|
|
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\BinExt;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCA\Memories\Exif;
|
||||
use OCA\Memories\Util;
|
||||
|
@ -218,123 +219,18 @@ class VideoController extends GenericApiController
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the transcoder.
|
||||
*
|
||||
* @return string Path to log file
|
||||
*/
|
||||
public static function startGoVod()
|
||||
{
|
||||
$config = \OC::$server->get(\OCP\IConfig::class);
|
||||
|
||||
// Get transcoder path
|
||||
$transcoder = $config->getSystemValue('memories.vod.path', false);
|
||||
if (!$transcoder) {
|
||||
throw new \Exception('Transcoder not configured');
|
||||
}
|
||||
|
||||
// Make sure transcoder exists
|
||||
if (!file_exists($transcoder)) {
|
||||
throw new \Exception("Transcoder not found; run occ memories video-setup! ({$transcoder})");
|
||||
}
|
||||
|
||||
// Make transcoder executable
|
||||
if (!is_executable($transcoder)) {
|
||||
@chmod($transcoder, 0755);
|
||||
if (!is_executable($transcoder)) {
|
||||
throw new \Exception("Transcoder not executable (chmod 755 {$transcoder})");
|
||||
}
|
||||
}
|
||||
|
||||
// Kill the transcoder in case it's running
|
||||
\OCA\Memories\Util::pkill($transcoder);
|
||||
|
||||
// Start transcoder
|
||||
[$configFile, $logFile] = self::makeGoVodConfig($config);
|
||||
shell_exec("nohup {$transcoder} {$configFile} >> '{$logFile}' 2>&1 & > /dev/null");
|
||||
|
||||
// wait for 1s
|
||||
sleep(1);
|
||||
|
||||
return $logFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the upstream URL for a video.
|
||||
*/
|
||||
public static function getGoVodUrl(string $client, string $path, string $profile): string
|
||||
{
|
||||
$config = \OC::$server->get(\OCP\IConfig::class);
|
||||
$path = rawurlencode($path);
|
||||
|
||||
$bind = $config->getSystemValue('memories.vod.bind', '127.0.0.1:47788');
|
||||
$connect = $config->getSystemValue('memories.vod.connect', $bind);
|
||||
|
||||
return "http://{$connect}/{$client}{$path}/{$profile}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the goVod config JSON.
|
||||
*/
|
||||
public static function getGoVodConfig(\OCP\IConfig $config): array
|
||||
{
|
||||
// Migrate legacy config: remove in 2024
|
||||
self::migrateLegacyConfig($config);
|
||||
|
||||
// Get temp directory
|
||||
$defaultTmp = sys_get_temp_dir().'/go-vod/';
|
||||
$tmpPath = $config->getSystemValue('memories.vod.tempdir', $defaultTmp);
|
||||
|
||||
// Make sure path ends with slash
|
||||
if ('/' !== substr($tmpPath, -1)) {
|
||||
$tmpPath .= '/';
|
||||
}
|
||||
|
||||
// Add instance ID to path
|
||||
$tmpPath .= $config->getSystemValue('instanceid', 'default');
|
||||
|
||||
// (Re-)create temp dir
|
||||
shell_exec("rm -rf '{$tmpPath}' && mkdir -p '{$tmpPath}' && chmod 755 '{$tmpPath}'");
|
||||
|
||||
// Check temp directory exists
|
||||
if (!is_dir($tmpPath)) {
|
||||
throw new \Exception("Temp directory could not be created ({$tmpPath})");
|
||||
}
|
||||
|
||||
// Check temp directory is writable
|
||||
if (!is_writable($tmpPath)) {
|
||||
throw new \Exception("Temp directory is not writable ({$tmpPath})");
|
||||
}
|
||||
|
||||
// Get config from system values
|
||||
return [
|
||||
'bind' => $config->getSystemValue('memories.vod.bind', '127.0.0.1:47788'),
|
||||
'ffmpeg' => $config->getSystemValue('memories.vod.ffmpeg', 'ffmpeg'),
|
||||
'ffprobe' => $config->getSystemValue('memories.vod.ffprobe', 'ffprobe'),
|
||||
'tempdir' => $tmpPath,
|
||||
|
||||
'vaapi' => $config->getSystemValue('memories.vod.vaapi', false),
|
||||
'vaapiLowPower' => $config->getSystemValue('memories.vod.vaapi.low_power', false),
|
||||
|
||||
'nvenc' => $config->getSystemValue('memories.vod.nvenc', false),
|
||||
'nvencTemporalAQ' => $config->getSystemValue('memories.vod.nvenc.temporal_aq', false),
|
||||
'nvencScale' => $config->getSystemValue('memories.vod.nvenc.scale', 'npp'),
|
||||
];
|
||||
}
|
||||
|
||||
private function getUpstream(string $client, string $path, string $profile)
|
||||
{
|
||||
$returnCode = $this->getUpstreamInternal($client, $path, $profile);
|
||||
$isExternal = $this->config->getSystemValue('memories.vod.external', false);
|
||||
|
||||
// If status code was 0, it's likely the server is down
|
||||
// Make one attempt to start after killing whatever is there
|
||||
if (0 !== $returnCode || $isExternal) {
|
||||
if (0 !== $returnCode && 503 !== $returnCode) {
|
||||
return $returnCode;
|
||||
}
|
||||
|
||||
// Start goVod and get log file
|
||||
$logFile = self::startGoVod();
|
||||
$logFile = BinExt::startGoVod();
|
||||
|
||||
$returnCode = $this->getUpstreamInternal($client, $path, $profile);
|
||||
if (0 === $returnCode) {
|
||||
|
@ -348,7 +244,7 @@ class VideoController extends GenericApiController
|
|||
{
|
||||
// Make sure query params are repeated
|
||||
// For example, in folder sharing, we need the params on every request
|
||||
$url = self::getGoVodUrl($client, $path, $profile);
|
||||
$url = BinExt::getGoVodUrl($client, $path, $profile);
|
||||
if ($params = $_SERVER['QUERY_STRING']) {
|
||||
$url .= "?{$params}";
|
||||
}
|
||||
|
@ -410,7 +306,7 @@ class VideoController extends GenericApiController
|
|||
*/
|
||||
private static function postFile(string $client, $blob)
|
||||
{
|
||||
$url = self::getGoVodUrl($client, '/create', 'ignore');
|
||||
$url = BinExt::getGoVodUrl($client, '/create', 'ignore');
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
@ -429,60 +325,4 @@ class VideoController extends GenericApiController
|
|||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the goVod config JSON and put it to a file.
|
||||
*
|
||||
* @return array [config file, log file]
|
||||
*/
|
||||
private static function makeGoVodConfig(\OCP\IConfig $config): array
|
||||
{
|
||||
$env = self::getGoVodConfig($config);
|
||||
$tmpPath = $env['tempdir'];
|
||||
|
||||
// Write config to file
|
||||
$logFile = $tmpPath.'.log';
|
||||
$configFile = $tmpPath.'.json';
|
||||
file_put_contents($configFile, json_encode($env, JSON_PRETTY_PRINT));
|
||||
|
||||
// Log file is not in config
|
||||
// go-vod just writes to stdout/stderr
|
||||
return [$configFile, $logFile];
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate legacy config to new.
|
||||
*
|
||||
* Remove in year 2024
|
||||
*/
|
||||
private static function migrateLegacyConfig(\OCP\IConfig $config)
|
||||
{
|
||||
if (null === $config->getSystemValue('memories.no_transcode', null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mapping
|
||||
$legacyConfig = [
|
||||
'memories.no_transcode' => 'memories.vod.disable',
|
||||
'memories.transcoder' => 'memories.vod.path',
|
||||
'memories.ffmpeg_path' => 'memories.vod.ffmpeg',
|
||||
'memories.ffprobe_path' => 'memories.vod.ffprobe',
|
||||
'memories.qsv' => 'memories.vod.vaapi',
|
||||
'memories.nvenc' => 'memories.vod.nvenc',
|
||||
'memories.tmp_path' => 'memories.vod.tempdir',
|
||||
];
|
||||
|
||||
foreach ($legacyConfig as $old => $new) {
|
||||
if (null !== $config->getSystemValue($old, null)) {
|
||||
$config->setSystemValue($new, $config->getSystemValue($old));
|
||||
$config->deleteSystemValue($old);
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate bind address
|
||||
if ($port = null !== $config->getSystemValue('memories.govod_port', null)) {
|
||||
$config->setSystemValue('memories.vod.bind', "127.0.0.1:{$port}");
|
||||
$config->deleteSystemValue('memories.govod_port');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue