videocutting/src/Cutting.php
Daniel Siepmann 695a17e880
Increase timeout of file concatenation command
Double the timeout of the command in order to allow concatenation of
more and larger files.
2023-06-11 17:00:26 +02:00

172 lines
5.2 KiB
PHP

<?php
namespace DanielSiepmann\Videcutting;
/*
* Copyright (C) 2020 Daniel Siepmann <coding@daniel-siepmann.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
use Symfony\Component\Process\Process;
class Cutting
{
/**
* @var VideoInfo
*/
private $video;
/**
* @var string
*/
private $tempFolder = '';
/**
* @var array
*/
private $tempFilenames = [];
public function __construct(
VideoInfo $video,
?string $tempFolder = null
) {
$this->video = $video;
$this->tempFolder = $tempFolder ?? sys_get_temp_dir();
}
public function getCommandsForCutting(): \Generator
{
$start = $this->video->getStart();
$end = $this->video->getEnd();
if ($this->video->getAds() === []) {
yield $this->getCuttingCommand($start, $end);
return;
}
// Each video starts with end of last ad, and end at next ad start.
foreach ($this->video->getAds() as [$adStart, $adEnd]) {
$end = $adStart;
yield $this->getCuttingCommand($start, $end);
$start = $adEnd;
}
yield $this->getCuttingCommand($start, $this->video->getEnd());
}
public function getCommandForGeneratingMetadata(): Process
{
$command = sprintf(
'ffmpeg -y -i %s -f ffmetadata %s',
escapeshellarg($this->video->getOriginalFilename()),
escapeshellarg($this->getMetadataFilename())
);
return Process::fromShellCommandline($command);
}
public function getCommandForGeneratingConcatInputFile(): Process
{
if ($this->tempFilenames === []) {
throw new \InvalidArgumentException(
'No temp files for concat available. Did you call "getCommandsForCutting()" before?',
1601455216
);
}
$lines = [];
foreach ($this->tempFilenames as $filename) {
$lines[] = 'file ' . $filename;
}
$command = 'printf ' . escapeshellarg(implode('\n', $lines)) . ' > ' . $this->getConcatFilename();
$process = Process::fromShellCommandline($command);
$process->setTimeout(2 * 60);
return $process;
}
public function getCommandForGeneratingVideo(): Process
{
$command = sprintf(
'ffmpeg -y -f concat -safe 0 -i %s -c copy'
. ' -f ffmetadata -i %s -c copy -map_metadata 1'
. ' -metadata Title=' . escapeshellarg($this->video->getTitleForHumans())
. ' %s',
escapeshellarg($this->getConcatFilename()),
escapeshellarg($this->getMetadataFilename()),
escapeshellarg($this->video->getTargetFilePath())
);
return Process::fromShellCommandline($command);
}
public function getCommandForCleanup(): Process
{
$tempfiles = implode(' ', array_map(function (string $filename) {
return escapeshellarg($filename);
}, $this->tempFilenames));
$command = sprintf(
'rm %s %s %s',
$tempfiles,
escapeshellarg($this->getConcatFilename()),
escapeshellarg($this->getMetadataFilename())
);
return Process::fromShellCommandline($command);
}
private function getCuttingCommand(string $start, string $end): Process
{
$command = sprintf(
'ffmpeg -y -ss %s -to %s -i %s -c copy %s',
escapeshellarg($start),
escapeshellarg($end),
escapeshellarg($this->video->getOriginalFilename()),
escapeshellarg($this->getNextTempFilename())
);
return Process::fromShellCommandline($command);
}
private function getNextTempFilename(): string
{
$count = count($this->tempFilenames) + 1;
$nextName = $this->tempFolder
. DIRECTORY_SEPARATOR
. $this->video->getOriginalFilenameWithSuffix('cut-' . $count);
$this->tempFilenames[] = $nextName;
return $nextName;
}
private function getMetadataFilename(): string
{
$file = new \SplFileInfo($this->video->getOriginalFilenameWithSuffix('metadata'));
return $this->tempFolder . DIRECTORY_SEPARATOR . $file->getBasename('.' . $file->getExtension()) . '.txt';
}
private function getConcatFilename(): string
{
$file = new \SplFileInfo($this->video->getOriginalFilenameWithSuffix('concat'));
return $this->tempFolder . DIRECTORY_SEPARATOR . $file->getBasename('.' . $file->getExtension()) . '.txt';
}
}