video: improve transcode logging (#428)

pull/460/head
Varun Patil 2023-02-24 00:21:38 -08:00
parent 9295f2d026
commit a2798c8763
3 changed files with 55 additions and 24 deletions

View File

@ -39,6 +39,7 @@ use OCP\IConfig;
use OCP\IDBConnection; use OCP\IDBConnection;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession; use OCP\IUserSession;
use Psr\Log\LoggerInterface;
class ApiBase extends Controller class ApiBase extends Controller
{ {
@ -48,6 +49,7 @@ class ApiBase extends Controller
protected IAppManager $appManager; protected IAppManager $appManager;
protected TimelineQuery $timelineQuery; protected TimelineQuery $timelineQuery;
protected IDBConnection $connection; protected IDBConnection $connection;
protected LoggerInterface $logger;
public function __construct( public function __construct(
IRequest $request, IRequest $request,
@ -55,7 +57,8 @@ class ApiBase extends Controller
IUserSession $userSession, IUserSession $userSession,
IDBConnection $connection, IDBConnection $connection,
IRootFolder $rootFolder, IRootFolder $rootFolder,
IAppManager $appManager IAppManager $appManager,
LoggerInterface $logger
) { ) {
parent::__construct(Application::APPNAME, $request); parent::__construct(Application::APPNAME, $request);
@ -64,6 +67,7 @@ class ApiBase extends Controller
$this->connection = $connection; $this->connection = $connection;
$this->rootFolder = $rootFolder; $this->rootFolder = $rootFolder;
$this->appManager = $appManager; $this->appManager = $appManager;
$this->logger = $logger;
$this->timelineQuery = new TimelineQuery($connection); $this->timelineQuery = new TimelineQuery($connection);
} }

View File

@ -80,8 +80,20 @@ class VideoController extends ApiBase
} }
// Request and check data was received // Request and check data was received
if (200 !== $this->getUpstream($client, $path, $profile)) { try {
return new JSONResponse(['message' => 'Transcode failed'], Http::STATUS_INTERNAL_SERVER_ERROR); $status = $this->getUpstream($client, $path, $profile);
if ($status === 409 || $status === -1) {
// Just a conflict (transcoding process changed)
return new JSONResponse(['message' => 'Conflict'], Http::STATUS_CONFLICT);
}
if ($status !== 200) {
throw new \Exception("Transcoder returned {$status}");
}
} catch (\Exception $e) {
$msg = 'Transcode failed: '.$e->getMessage();
$this->logger->error($msg, ['app' => 'memories']);
return new JSONResponse(['message' => $msg], Http::STATUS_INTERNAL_SERVER_ERROR);
} }
// The response was already streamed, so we have nothing to do here // The response was already streamed, so we have nothing to do here
@ -224,12 +236,15 @@ class VideoController extends ApiBase
// Get transcoder path // Get transcoder path
$transcoder = $this->config->getSystemValue('memories.transcoder', false); $transcoder = $this->config->getSystemValue('memories.transcoder', false);
if (!$transcoder) { if (!$transcoder) {
return 0; throw new \Exception('Transcoder not configured');
} }
// Make transcoder executable // Make transcoder executable
if (!is_executable($transcoder)) { if (!is_executable($transcoder)) {
@chmod($transcoder, 0755); @chmod($transcoder, 0755);
if (!is_executable($transcoder)) {
throw new \Exception("Transcoder not executable (chmod 755 {$transcoder})");
}
} }
// Kill the transcoder in case it's running // Kill the transcoder in case it's running
@ -271,20 +286,34 @@ class VideoController extends ApiBase
$tmpPath .= $this->config->getSystemValue('instanceid', 'default'); $tmpPath .= $this->config->getSystemValue('instanceid', 'default');
// (Re-)create temp dir // (Re-)create temp dir
shell_exec("rm -rf '{$tmpPath}'"); shell_exec("rm -rf '{$tmpPath}' && mkdir -p '{$tmpPath}' && chmod 755 '{$tmpPath}'");
mkdir($tmpPath, 0755, true);
// 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})");
}
// Set temp dir // Set temp dir
$env[] = "GOVOD_TEMPDIR='{$tmpPath}'"; $env[] = "GOVOD_TEMPDIR='{$tmpPath}'";
// Start transcoder // Start transcoder
$env = implode(' ', $env); $env = implode(' ', $env);
shell_exec("{$env} nohup {$transcoder} > '{$tmpPath}.log' 2>&1 & > /dev/null"); $logFile = $tmpPath.'.log';
shell_exec("{$env} nohup {$transcoder} > '{$logFile}' 2>&1 & > /dev/null");
// wait for 1s and try again // wait for 1s and try again
sleep(1); sleep(1);
return $this->getUpstreamInternal($client, $path, $profile); $returnCode = $this->getUpstreamInternal($client, $path, $profile);
if (0 === $returnCode) {
throw new \Exception("Transcoder could not be started, check {$logFile}");
}
return $returnCode;
} }
private function getUpstreamInternal($client, $path, $profile) private function getUpstreamInternal($client, $path, $profile)

View File

@ -170,7 +170,7 @@ class VideoContentSetup {
}); });
const overrideNative = !vidjs.browser.IS_SAFARI; const overrideNative = !vidjs.browser.IS_SAFARI;
content.videojs = vidjs(content.videoElement, { const vjs = (content.videojs = vidjs(content.videoElement, {
fill: true, fill: true,
autoplay: true, autoplay: true,
controls: false, controls: false,
@ -186,25 +186,23 @@ class VideoContentSetup {
nativeAudioTracks: !overrideNative, nativeAudioTracks: !overrideNative,
nativeVideoTracks: !overrideNative, nativeVideoTracks: !overrideNative,
}, },
}); }));
content.videojs.on("error", () => { vjs.on("error", () => {
if (content.videojs.error().code === 4) { if (vjs.error().code === 4 && vjs.src().includes("m3u8")) {
if (content.videojs.src().includes("m3u8")) {
// HLS could not be streamed // HLS could not be streamed
console.error("Video.js: HLS stream could not be opened."); console.error("Video.js: HLS stream could not be opened.");
if (getCurrentUser()?.isAdmin) { if (getCurrentUser()?.isAdmin) {
showError(t("memories", "Transcoding failed.")); showError(t("memories", "Transcoding failed, check Nextcloud logs."));
} }
content.videojs.src({ vjs.src({
src: content.data.src, src: content.data.src,
type: "video/mp4", type: "video/mp4",
}); });
this.updateRotation(content, 0); this.updateRotation(content, 0);
} }
}
}); });
setTimeout(() => { setTimeout(() => {