Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PS-670-rendition-video_enhance3 #494

Merged
merged 12 commits into from
Dec 9, 2024
2 changes: 2 additions & 0 deletions databox/api/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,5 @@ services:
$decorated: '@.inner'

Alchemy\RenditionFactory\Templating\TemplateResolverInterface: '@App\Asset\Attribute\TemplateResolver'

App\Validator\ValidRenditionDefinitionConstraintValidator: ~
31 changes: 31 additions & 0 deletions databox/api/src/Command/DocumentationDumperCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Alchemy\RenditionFactory\RenditionBuilderConfigurationDocumentation;


#[AsCommand('app:documentation:dump')]
class DocumentationDumperCommand extends Command
{
public function __construct(
private readonly RenditionBuilderConfigurationDocumentation $renditionBuilderConfigurationDocumentation,
)
{
parent::__construct();
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('# ' . $this->renditionBuilderConfigurationDocumentation::getName());
$output->writeln($this->renditionBuilderConfigurationDocumentation->generate());

return Command::SUCCESS;
}
}
7 changes: 4 additions & 3 deletions databox/api/src/Entity/Core/RenditionDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace App\Entity\Core;

use Alchemy\CoreBundle\Entity\AbstractUuidEntity;
use Alchemy\CoreBundle\Entity\Traits\CreatedAtTrait;
use Alchemy\CoreBundle\Entity\Traits\UpdatedAtTrait;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
Expand All @@ -16,10 +18,8 @@
use App\Api\Model\Input\RenditionDefinitionInput;
use App\Api\Provider\RenditionDefinitionCollectionProvider;
use App\Controller\Core\RenditionDefinitionSortAction;

use Alchemy\CoreBundle\Entity\Traits\CreatedAtTrait;
use Alchemy\CoreBundle\Entity\Traits\UpdatedAtTrait;
use App\Entity\Traits\WorkspaceTrait;
use App\Validator as CustomAssert;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection as DoctrineCollection;
use Doctrine\DBAL\Types\Types;
Expand Down Expand Up @@ -153,6 +153,7 @@ class RenditionDefinition extends AbstractUuidEntity implements \Stringable
#[Groups([RenditionDefinition::GROUP_LIST, RenditionDefinition::GROUP_READ, RenditionDefinition::GROUP_WRITE])]
#[ORM\Column(type: Types::TEXT, nullable: true)]
#[ApiProperty(security: self::GRANT_ADMIN_PROP)]
#[CustomAssert\ValidRenditionDefinitionConstraint]
private ?string $definition = null;

#[Groups([RenditionDefinition::GROUP_READ])]
Expand Down
17 changes: 17 additions & 0 deletions databox/api/src/Validator/ValidRenditionDefinitionConstraint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace App\Validator;

use Symfony\Component\Validator\Constraint;

/** @uses ValidRenditionDefinitionConstraintValidator */
#[\Attribute]
class ValidRenditionDefinitionConstraint extends Constraint
{
public function getTargets(): string|array
{
return self::PROPERTY_CONSTRAINT;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace App\Validator;

use Alchemy\RenditionFactory\Config\BuildConfigValidator;
use Alchemy\RenditionFactory\Config\YamlLoader;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class ValidRenditionDefinitionConstraintValidator extends ConstraintValidator
{
/** @uses BuildConfigValidator */
public function __construct(private readonly YamlLoader $yamlLoader, private readonly BuildConfigValidator $validator)
{
}

/**
* @param string $value
* @param ValidRenditionDefinitionConstraint $constraint
*/
public function validate($value, Constraint $constraint): void
{
if(!$value) {
return;
}
try {
$config = $this->yamlLoader->parse($value);
$this->validator->validate($config);
} catch (\Exception $e) {
$this->context
->buildViolation($e->getMessage())
->addViolation();
}
}
}
56 changes: 37 additions & 19 deletions lib/php/rendition-factory-bundle/Resources/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ services:
autoconfigure: true

Alchemy\RenditionFactory\Command\CreateCommand: ~
Alchemy\RenditionFactory\Command\ConfigurationValidateCommand: ~

Alchemy\RenditionFactory\Context\TransformationContextFactory: ~
Alchemy\RenditionFactory\FileFamilyGuesser: ~
Expand Down Expand Up @@ -44,43 +45,56 @@ services:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\TransformerModuleInterface::TAG }

# FFMpeg "formats"
Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\JpegFormat:
# Output "formats"
Alchemy\RenditionFactory\Transformer\Video\Format\JpegFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\MkvFormat:
Alchemy\RenditionFactory\Transformer\Video\Format\MkvFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\Mpeg4Format:
Alchemy\RenditionFactory\Transformer\Video\Format\Mpeg4Format:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\MpegFormat:
Alchemy\RenditionFactory\Transformer\Video\Format\MpegFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\QuicktimeFormat:
Alchemy\RenditionFactory\Transformer\Video\Format\QuicktimeFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\WebmFormat:
Alchemy\RenditionFactory\Transformer\Video\Format\WebmFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\AnimatedGifFormat:
Alchemy\RenditionFactory\Transformer\Video\Format\AnimatedGifFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\AnimatedPngFormat:
Alchemy\RenditionFactory\Transformer\Video\Format\AnimatedPngFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\AnimatedWebpFormat:
Alchemy\RenditionFactory\Transformer\Video\Format\AnimatedWebpFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\FFMpeg\Format\FormatInterface::TAG }
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\Format\WavFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\Format\AacFormat:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\Format\Mp3Format:
tags:
- { name: !php/const Alchemy\RenditionFactory\Transformer\Video\Format\FormatInterface::TAG }

Alchemy\RenditionFactory\Transformer\Video\Format\OutputFormatsDocumentation: ~

Imagine\Imagick\Imagine: ~
Imagine\Image\ImagineInterface: '@Imagine\Imagick\Imagine'
Expand All @@ -89,3 +103,7 @@ services:
Alchemy\RenditionFactory\Format\FormatGuesser: ~
Alchemy\RenditionFactory\Format\FormatFactory: ~
Alchemy\RenditionFactory\Config\ModuleOptionsResolver: ~

Alchemy\RenditionFactory\RenditionBuilderConfigurationDocumentation: ~

Alchemy\RenditionFactory\Config\BuildConfigValidator: ~
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Alchemy\RenditionFactory\Command;

use Alchemy\RenditionFactory\Config\BuildConfigValidator;
use Alchemy\RenditionFactory\Config\YamlLoader;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand('alchemy:rendition-factory:conf:validate')]
class ConfigurationValidateCommand extends Command
{
public function __construct(
private readonly YamlLoader $yamlLoader,
private readonly BuildConfigValidator $validator,
) {
parent::__construct();
}

protected function configure(): void
{
parent::configure();

$this->addArgument('config', InputArgument::REQUIRED, 'A build config YAML file to validate')
->setHelp('Validate a config file.')
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$config = $this->yamlLoader->load($input->getArgument('config'));
$this->validator->validate($config);

$output->writeln('Configuration is valid.');

return Command::SUCCESS;
}
}
22 changes: 13 additions & 9 deletions lib/php/rendition-factory/src/Command/CreateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,34 +45,38 @@ protected function configure(): void

protected function execute(InputInterface $input, OutputInterface $output): int
{
$ret = 0;
$ret = Command::SUCCESS;
$src = $input->getArgument('src');
if (is_dir($src)) {
if (false === ($od = opendir($src))) {
$output->writeln(sprintf('Directory "%s" could not be opened.', $src));

return 1;
return Command::FAILURE;
}
while ($f = readdir($od)) {
if ('.' === $f || '..' === $f) {
continue;
}
$ret |= $this->doFile($input, $output, $src.'/'.$f);
if(false === $this->doFile($input, $output, $src.'/'.$f)) {
$ret = Command::FAILURE;
}
}
closedir($od);
} else {
$ret = $this->doFile($input, $output, $src);
if(false === $this->doFile($input, $output, $src)) {
$ret = Command::FAILURE;
}
}

return $ret;
}

protected function doFile(InputInterface $input, OutputInterface $output, string $src): int
protected function doFile(InputInterface $input, OutputInterface $output, string $src): bool
{
if (!file_exists($src)) {
$output->writeln(sprintf('File "%s" does not exist.', $src));

return 1;
return false;
}

$time = microtime(true);
Expand Down Expand Up @@ -104,7 +108,7 @@ protected function doFile(InputInterface $input, OutputInterface $output, string
} catch (\InvalidArgumentException $e) {
$output->writeln(sprintf('<error>%s</error>', $e->getMessage()));

return 1;
return false;
}

if ($outputPath = $input->getOption('output')) {
Expand All @@ -121,7 +125,7 @@ protected function doFile(InputInterface $input, OutputInterface $output, string
if ($src === $outputFile->getPath()) {
$output->writeln('No transformation needed');

return 1;
return false;
}

if (!$input->getOption('debug')) {
Expand All @@ -130,6 +134,6 @@ protected function doFile(InputInterface $input, OutputInterface $output, string

$output->writeln(sprintf('Execution time: %0.2f', microtime(true) - $time));

return 0;
return true;
}
}
57 changes: 57 additions & 0 deletions lib/php/rendition-factory/src/Config/BuildConfigValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Alchemy\RenditionFactory\Config;

use Alchemy\RenditionFactory\DTO\BuildConfig\BuildConfig;
use Alchemy\RenditionFactory\DTO\FamilyEnum;
use Alchemy\RenditionFactory\Transformer\TransformerModuleInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\DependencyInjection\Attribute\TaggedLocator;
use Symfony\Component\DependencyInjection\ServiceLocator;

readonly class BuildConfigValidator
{
public function __construct(
#[TaggedLocator(TransformerModuleInterface::TAG, defaultIndexMethod: 'getName')]
private ServiceLocator $transformers,
) {
}

public function getTransformers(): ServiceLocator
{
return $this->transformers;
}

public function validate(BuildConfig $config): void
{
foreach (FamilyEnum::cases() as $family) {
$familyConfig = $config->getFamily($family);
if (null === $familyConfig) {
continue;
}
foreach ($familyConfig->getTransformations() as $transformation) {
$transformerName = $transformation->getModule();

/** @var TransformerModuleInterface $transformer */
$transformer = $this->transformers->get($transformerName);

try {
$this->checkTransformerConfiguration($transformer, $transformation->toArray());
} catch (\Throwable $e) {
$msg = sprintf("Error in module \"%s\"\n%s", $transformerName, $e->getMessage());
throw new InvalidConfigurationException($msg);
}
}
}
}

private function checkTransformerConfiguration(TransformerModuleInterface $transformer, array $options): void
{
$documentation = $transformer->getDocumentation();
$treeBuilder = $documentation->getTreeBuilder();

$processor = new Processor();
$processor->process($treeBuilder->buildTree(), ['root' => $options]);
}
}
Loading
Loading