diff --git a/src/Asset/Controller/Video/ThumbnailStatusController.php b/src/Asset/Controller/Video/ThumbnailStatusController.php
new file mode 100644
index 000000000..35a91fff5
--- /dev/null
+++ b/src/Asset/Controller/Video/ThumbnailStatusController.php
@@ -0,0 +1,97 @@
+value)]
+ #[Get(
+ path: self::PREFIX . '/assets/{id}/video/thumbnail/{thumbnailName}/status',
+ operationId: 'asset_video_thumbnail_status',
+ description: 'asset_video_thumbnail_status_description',
+ summary: 'asset_video_thumbnail_status_summary',
+ tags: [Tags::Assets->name]
+ )]
+ #[IdParameter(type: 'video')]
+ #[ThumbnailNameParameter]
+ #[SuccessResponse(
+ description: 'asset_video_thumbnail_status_success_response',
+ content: new JsonContent(ref: VideoThumbnailStatus::class)
+ )]
+ #[DefaultResponses([
+ HttpResponseCodes::BAD_REQUEST,
+ HttpResponseCodes::FORBIDDEN,
+ HttpResponseCodes::UNAUTHORIZED,
+ HttpResponseCodes::NOT_FOUND,
+ ])]
+ public function getVideoThumbnailStatus(int $id, string $thumbnailName): JsonResponse
+ {
+ $asset = $this->assetService->getAssetElement(
+ $this->securityService->getCurrentUser(),
+ $id
+ );
+
+ return $this->jsonResponse(
+ $this->videoService->getThumbnailStatus($asset, $thumbnailName)
+ );
+ }
+}
diff --git a/src/Asset/Schema/Type/Video/VideoThumbnailStatus.php b/src/Asset/Schema/Type/Video/VideoThumbnailStatus.php
new file mode 100644
index 000000000..ee4235d99
--- /dev/null
+++ b/src/Asset/Schema/Type/Video/VideoThumbnailStatus.php
@@ -0,0 +1,59 @@
+status;
+ }
+}
diff --git a/src/Asset/Service/VideoService.php b/src/Asset/Service/VideoService.php
index f0ee374ca..1c28c1028 100644
--- a/src/Asset/Service/VideoService.php
+++ b/src/Asset/Service/VideoService.php
@@ -14,7 +14,12 @@
namespace Pimcore\Bundle\StudioBackendBundle\Asset\Service;
use Pimcore\Bundle\StudioBackendBundle\Asset\Event\PreResponse\VideoTypeEvent;
+use Pimcore\Bundle\StudioBackendBundle\Asset\Schema\Type\Video\VideoThumbnailStatus;
use Pimcore\Bundle\StudioBackendBundle\Asset\Schema\Type\Video\VideoType;
+use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidElementTypeException;
+use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;
+use Pimcore\Model\Asset;
+use Pimcore\Model\Asset\Video as VideoAsset;
use Pimcore\Model\DataObject\ClassDefinition\Data\Video;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -25,6 +30,7 @@
{
public function __construct(
private EventDispatcherInterface $eventDispatcher,
+ private ThumbnailServiceInterface $thumbnailService,
) {
}
@@ -40,4 +46,23 @@ public function getVideoTypes(): array
return $types;
}
+
+ public function getThumbnailStatus(Asset $video, string $thumbnailName): VideoThumbnailStatus
+ {
+ if (!$video instanceof VideoAsset) {
+ throw new InvalidElementTypeException($video->getType(), ElementTypes::TYPE_ASSET);
+ }
+
+ $configuration = $this->thumbnailService->getVideoThumbnailConfig($thumbnailName);
+
+ // Read the status from the custom setting directly: Asset\Video::getThumbnail() would
+ // start a conversion as a side effect and returns null for errored conversions,
+ // which would make the error status unreachable for polling clients.
+ $customSetting = $video->getCustomSetting('thumbnails');
+ $status = is_array($customSetting)
+ ? ($customSetting[$configuration->getName()]['status'] ?? VideoThumbnailStatus::STATUS_NOT_STARTED)
+ : VideoThumbnailStatus::STATUS_NOT_STARTED;
+
+ return new VideoThumbnailStatus((string) $status);
+ }
}
diff --git a/src/Asset/Service/VideoServiceInterface.php b/src/Asset/Service/VideoServiceInterface.php
index 33f78e9a3..8940c9986 100644
--- a/src/Asset/Service/VideoServiceInterface.php
+++ b/src/Asset/Service/VideoServiceInterface.php
@@ -13,7 +13,11 @@
namespace Pimcore\Bundle\StudioBackendBundle\Asset\Service;
+use Pimcore\Bundle\StudioBackendBundle\Asset\Schema\Type\Video\VideoThumbnailStatus;
use Pimcore\Bundle\StudioBackendBundle\Asset\Schema\Type\Video\VideoType;
+use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidElementTypeException;
+use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidThumbnailException;
+use Pimcore\Model\Asset;
/**
* @internal
@@ -24,4 +28,10 @@ interface VideoServiceInterface
* @return VideoType[]
*/
public function getVideoTypes(): array;
+
+ /**
+ * @throws InvalidElementTypeException
+ * @throws InvalidThumbnailException
+ */
+ public function getThumbnailStatus(Asset $video, string $thumbnailName): VideoThumbnailStatus;
}
diff --git a/tests/Unit/Asset/Service/VideoServiceTest.php b/tests/Unit/Asset/Service/VideoServiceTest.php
new file mode 100644
index 000000000..08914499d
--- /dev/null
+++ b/tests/Unit/Asset/Service/VideoServiceTest.php
@@ -0,0 +1,104 @@
+expectException(InvalidElementTypeException::class);
+
+ $this->getVideoService()->getThumbnailStatus(new Document(), self::THUMBNAIL_NAME);
+ }
+
+ /**
+ * @throws Exception
+ */
+ public function testGetThumbnailStatusReturnsStatusFromCustomSetting(): void
+ {
+ $video = $this->makeEmpty(Video::class, [
+ 'getCustomSetting' => [
+ self::THUMBNAIL_NAME => [
+ 'status' => VideoThumbnailStatus::STATUS_ERROR,
+ ],
+ ],
+ ]);
+
+ $status = $this->getVideoService()->getThumbnailStatus($video, self::THUMBNAIL_NAME);
+
+ $this->assertSame(VideoThumbnailStatus::STATUS_ERROR, $status->getStatus());
+ }
+
+ /**
+ * @throws Exception
+ */
+ public function testGetThumbnailStatusReturnsNotStartedWithoutCustomSetting(): void
+ {
+ $video = $this->makeEmpty(Video::class, [
+ 'getCustomSetting' => null,
+ ]);
+
+ $status = $this->getVideoService()->getThumbnailStatus($video, self::THUMBNAIL_NAME);
+
+ $this->assertSame(VideoThumbnailStatus::STATUS_NOT_STARTED, $status->getStatus());
+ }
+
+ /**
+ * @throws Exception
+ */
+ public function testGetThumbnailStatusReturnsNotStartedForUnknownThumbnail(): void
+ {
+ $video = $this->makeEmpty(Video::class, [
+ 'getCustomSetting' => [
+ 'other-thumbnail' => [
+ 'status' => VideoThumbnailStatus::STATUS_FINISHED,
+ ],
+ ],
+ ]);
+
+ $status = $this->getVideoService()->getThumbnailStatus($video, self::THUMBNAIL_NAME);
+
+ $this->assertSame(VideoThumbnailStatus::STATUS_NOT_STARTED, $status->getStatus());
+ }
+
+ private function getVideoService(): VideoServiceInterface
+ {
+ $config = new Config();
+ $config->setName(self::THUMBNAIL_NAME);
+
+ return new VideoService(
+ $this->makeEmpty(EventDispatcherInterface::class),
+ $this->makeEmpty(ThumbnailServiceInterface::class, [
+ 'getVideoThumbnailConfig' => $config,
+ ]),
+ );
+ }
+}
diff --git a/translations/studio_api_docs.en.yaml b/translations/studio_api_docs.en.yaml
index 43b5f4d0e..c1ccdefb9 100644
--- a/translations/studio_api_docs.en.yaml
+++ b/translations/studio_api_docs.en.yaml
@@ -212,6 +212,12 @@ asset_video_stream_by_thumbnail_description: |
List of thumbnail names can be obtained via the thumbnail video collection endpoint
asset_video_stream_by_thumbnail_success_response: Video stream based on thumbnail name
asset_video_stream_by_thumbnail_summary: Stream video asset by ID and thumbnail name
+asset_video_thumbnail_status_description: |
+ Get the conversion status of a video thumbnail based on the provided {id} and {thumbnailName}.
+ The {id} must be an ID of existing asset video
+ List of thumbnail names can be obtained via the thumbnail video collection endpoint
+asset_video_thumbnail_status_success_response: Conversion status of the video thumbnail
+asset_video_thumbnail_status_summary: Get video thumbnail conversion status by asset ID and thumbnail name
asset_get_search_configuration: Get asset search configuration
asset_get_search_configuration_description: Get asset configuration
asset_get_search_configuration_summary: Get asset search configuration