diff --git a/php/src/ProFileGenerator.php b/php/src/ProFileGenerator.php index 8f7b3d5..cd579ff 100644 --- a/php/src/ProFileGenerator.php +++ b/php/src/ProFileGenerator.php @@ -9,13 +9,17 @@ use Rv\Data\Action\ActionType; use Rv\Data\Action\LayerType; use Rv\Data\Action\MacroType; use Rv\Data\Action\MediaType; +use Rv\Data\Action\MediaType\Audio; use Rv\Data\Action\SlideType; +use Rv\Data\AlphaType; use Rv\Data\ApplicationInfo; use Rv\Data\ApplicationInfo\Application; use Rv\Data\ApplicationInfo\Platform; use Rv\Data\Color; use Rv\Data\CollectionElementType; use Rv\Data\Cue; +use Rv\Data\FileProperties; +use Rv\Data\Graphics\EdgeInsets; use Rv\Data\Graphics\Element as GraphicsElement; use Rv\Data\Graphics\Feather; use Rv\Data\Graphics\Feather\Style as FeatherStyle; @@ -36,6 +40,7 @@ use Rv\Data\Graphics\Text\VerticalAlignment; use Rv\Data\Group; use Rv\Data\HotKey; use Rv\Data\Media; +use Rv\Data\Media\DrawingProperties; use Rv\Data\Media\ImageTypeProperties; use Rv\Data\Media\Metadata; use Rv\Data\Presentation; @@ -206,9 +211,23 @@ final class ProFileGenerator $actions = [self::buildSlideAction($slideType)]; if (isset($slideData['media'])) { + // Derive name from label OR filename without extension + $mediaName = $slideData['label'] ?? null; + if ($mediaName === null) { + $basename = basename((string) $slideData['media']); + // Strip query string and fragment + $basename = strtok($basename, '?#') ?: $basename; + // Remove extension + $dotPos = strrpos($basename, '.'); + $mediaName = $dotPos !== false ? substr($basename, 0, $dotPos) : $basename; + } + $actions[] = self::buildMediaAction( (string) $slideData['media'], (string) ($slideData['format'] ?? 'JPG'), + $mediaName, + (int) ($slideData['mediaWidth'] ?? 0), + (int) ($slideData['mediaHeight'] ?? 0), ); } @@ -262,7 +281,7 @@ final class ProFileGenerator return $action; } - private static function buildMediaAction(string $absoluteUrl, string $format): Action + private static function buildMediaAction(string $absoluteUrl, string $format, ?string $name = null, int $imageWidth = 0, int $imageHeight = 0): Action { $url = new URL(); $url->setAbsoluteString($absoluteUrl); @@ -272,15 +291,47 @@ final class ProFileGenerator $metadata = new Metadata(); $metadata->setFormat($format); + // Build the image type properties with drawing + file + $naturalSize = new Size(); + $naturalSize->setWidth($imageWidth); + $naturalSize->setHeight($imageHeight); + + $customBoundsOrigin = new Point(); + $customBoundsSize = new Size(); + $customImageBounds = new Rect(); + $customImageBounds->setOrigin($customBoundsOrigin); + $customImageBounds->setSize($customBoundsSize); + + $cropInsets = new EdgeInsets(); + + $drawing = new DrawingProperties(); + $drawing->setNaturalSize($naturalSize); + $drawing->setCustomImageBounds($customImageBounds); + $drawing->setCropInsets($cropInsets); + $drawing->setAlphaType(AlphaType::ALPHA_TYPE_STRAIGHT); + + $fileLocalUrl = new URL(); + $fileLocalUrl->setAbsoluteString($absoluteUrl); + $fileLocalUrl->setLocal(self::buildLocalRelativePath($absoluteUrl)); + $fileLocalUrl->setPlatform(UrlPlatform::PLATFORM_MACOS); + + $fileProperties = new FileProperties(); + $fileProperties->setLocalUrl($fileLocalUrl); + + $imageTypeProperties = new ImageTypeProperties(); + $imageTypeProperties->setDrawing($drawing); + $imageTypeProperties->setFile($fileProperties); + $mediaElement = new Media(); $mediaElement->setUuid(self::newUuid()); $mediaElement->setUrl($url); $mediaElement->setMetadata($metadata); - $mediaElement->setImage(new ImageTypeProperties()); + $mediaElement->setImage($imageTypeProperties); $mediaType = new MediaType(); $mediaType->setLayerType(LayerType::LAYER_TYPE_FOREGROUND); $mediaType->setElement($mediaElement); + $mediaType->setAudio(new Audio()); $action = new Action(); $action->setUuid(self::newUuid()); @@ -288,6 +339,10 @@ final class ProFileGenerator $action->setMedia($mediaType); $action->setIsEnabled(true); + if ($name !== null) { + $action->setName($name); + } + return $action; } diff --git a/php/tests/ProFileGeneratorTest.php b/php/tests/ProFileGeneratorTest.php index 641a1f7..111ade5 100644 --- a/php/tests/ProFileGeneratorTest.php +++ b/php/tests/ProFileGeneratorTest.php @@ -881,4 +881,137 @@ class ProFileGeneratorTest extends TestCase $this->assertSame(1, $url->getLocal()->getRoot()); // ROOT_BOOT_VOLUME = 1 $this->assertSame('tmp/test-image.jpg', $url->getLocal()->getPath()); } + + #[Test] + public function testMediaActionHasNameFromFilename(): void + { + $song = ProFileGenerator::generate( + 'Media Name Test', + [ + [ + 'name' => 'V1', + 'color' => [0, 0, 0, 1], + 'slides' => [ + [ + 'text' => 'x', + 'media' => 'file:///Users/test/AI/Media/test-image.png', + 'format' => 'png', + 'mediaWidth' => 200, + 'mediaHeight' => 150, + ], + ], + ], + ], + [ + ['name' => 'n', 'groupNames' => ['V1']], + ], + ); + + $mediaAction = $song->getPresentation()->getCues()[0]->getActions()[1]; + $this->assertSame('test-image', $mediaAction->getName()); + } + + #[Test] + public function testMediaActionHasNameFromLabel(): void + { + $song = ProFileGenerator::generate( + 'Media Label Name Test', + [ + [ + 'name' => 'V1', + 'color' => [0, 0, 0, 1], + 'slides' => [ + [ + 'text' => 'x', + 'media' => 'file:///Users/test/AI/Media/test-image.png', + 'format' => 'png', + 'label' => 'My Custom Label', + ], + ], + ], + ], + [ + ['name' => 'n', 'groupNames' => ['V1']], + ], + ); + + $mediaAction = $song->getPresentation()->getCues()[0]->getActions()[1]; + $this->assertSame('My Custom Label', $mediaAction->getName()); + } + + #[Test] + public function testMediaActionHasAudioOnMediaType(): void + { + $song = ProFileGenerator::generate( + 'Media Audio Test', + [ + [ + 'name' => 'V1', + 'color' => [0, 0, 0, 1], + 'slides' => [ + [ + 'text' => 'x', + 'media' => 'file:///Users/test/AI/Media/test-image.png', + 'format' => 'png', + ], + ], + ], + ], + [ + ['name' => 'n', 'groupNames' => ['V1']], + ], + ); + + $mediaAction = $song->getPresentation()->getCues()[0]->getActions()[1]; + $media = $mediaAction->getMedia(); + $this->assertNotNull($media->getAudio()); + + // Element should still be set (audio oneof doesn't clear element) + $this->assertNotNull($media->getElement()); + } + + #[Test] + public function testMediaActionHasImageDrawingAndFileProperties(): void + { + $song = ProFileGenerator::generate( + 'Media Image Props Test', + [ + [ + 'name' => 'V1', + 'color' => [0, 0, 0, 1], + 'slides' => [ + [ + 'text' => 'x', + 'media' => 'file:///Users/test/AI/Media/test-image.png', + 'format' => 'png', + 'mediaWidth' => 200, + 'mediaHeight' => 150, + ], + ], + ], + ], + [ + ['name' => 'n', 'groupNames' => ['V1']], + ], + ); + + $mediaAction = $song->getPresentation()->getCues()[0]->getActions()[1]; + $image = $mediaAction->getMedia()->getElement()->getImage(); + $this->assertNotNull($image); + + // Verify drawing properties + $this->assertNotNull($image->getDrawing()); + $this->assertSame(200.0, $image->getDrawing()->getNaturalSize()->getWidth()); + $this->assertSame(150.0, $image->getDrawing()->getNaturalSize()->getHeight()); + $this->assertNotNull($image->getDrawing()->getCustomImageBounds()); + $this->assertNotNull($image->getDrawing()->getCustomImageBounds()->getOrigin()); + $this->assertNotNull($image->getDrawing()->getCustomImageBounds()->getSize()); + $this->assertNotNull($image->getDrawing()->getCropInsets()); + $this->assertSame(1, $image->getDrawing()->getAlphaType()); // ALPHA_TYPE_STRAIGHT = 1 + + // Verify file properties + $this->assertNotNull($image->getFile()); + $this->assertNotNull($image->getFile()->getLocalUrl()); + $this->assertSame('file:///Users/test/AI/Media/test-image.png', $image->getFile()->getLocalUrl()->getAbsoluteString()); + } }