From 7201d4bfba4aaedf5ff68ab9ccfa035554fa88be Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Sat, 6 Apr 2024 15:58:22 +0100
Subject: [PATCH] Issue #3420997 by sorlov, quietone, DanielVeza, smustgrave,
 alexpott, mstrelan: Convert MediaSource plugin discovery to attributes

---
 .../media/src/Attribute/MediaSource.php       | 87 +++++++++++++++++++
 .../media/src/Attribute/OEmbedMediaSource.php | 86 ++++++++++++++++++
 core/modules/media/src/MediaSourceManager.php |  4 +-
 .../src/Plugin/media/Source/AudioFile.php     | 17 ++--
 .../media/src/Plugin/media/Source/File.php    | 16 ++--
 .../media/src/Plugin/media/Source/Image.php   | 19 ++--
 .../media/src/Plugin/media/Source/OEmbed.php  | 20 ++---
 .../src/Plugin/media/Source/VideoFile.php     | 17 ++--
 .../src/Plugin/media/Source/Test.php          | 15 ++--
 .../media/Source/TestDifferentDisplays.php    | 15 ++--
 .../Plugin/media/Source/TestTranslation.php   | 17 ++--
 .../media/Source/TestWithConstraints.php      | 15 ++--
 .../Source/TestWithHiddenSourceField.php      | 15 ++--
 .../media_library/media_library.api.php       | 19 ++--
 14 files changed, 271 insertions(+), 91 deletions(-)
 create mode 100644 core/modules/media/src/Attribute/MediaSource.php
 create mode 100644 core/modules/media/src/Attribute/OEmbedMediaSource.php

diff --git a/core/modules/media/src/Attribute/MediaSource.php b/core/modules/media/src/Attribute/MediaSource.php
new file mode 100644
index 000000000000..13289419f1f3
--- /dev/null
+++ b/core/modules/media/src/Attribute/MediaSource.php
@@ -0,0 +1,87 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\media\Attribute;
+
+use Drupal\Component\Plugin\Attribute\Plugin;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
+/**
+ * Defines a MediaSource attribute.
+ *
+ * Media sources are responsible for implementing all the logic for dealing
+ * with a particular type of media. They provide various universal and
+ * type-specific metadata about media of the type they handle.
+ *
+ * Plugin namespace: Plugin\media\Source
+ *
+ * For a working example, see \Drupal\media\Plugin\media\Source\File.
+ *
+ * @see \Drupal\media\MediaSourceInterface
+ * @see \Drupal\media\MediaSourceBase
+ * @see \Drupal\media\MediaSourceManager
+ * @see hook_media_source_info_alter()
+ * @see plugin_api
+ */
+#[\Attribute(\Attribute::TARGET_CLASS)]
+class MediaSource extends Plugin {
+
+  /**
+   * Constructs a new MediaSource attribute.
+   *
+   * @param string $id
+   *   The attribute class ID.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup $label
+   *   The human-readable name of the media source.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $description
+   *   (optional) A brief description of the media source.
+   * @param string[] $allowed_field_types
+   *   (optional) The field types that can be used as a source field for this
+   *   media source.
+   * @param class-string[] $forms
+   *   (optional) The classes used to define media source-specific forms. An
+   *   array of form class names, keyed by ID. The ID represents the operation
+   *   the form is used for, for example, 'media_library_add'.
+   * @param string $default_thumbnail_filename
+   *   (optional) A filename for the default thumbnail.
+   *   The thumbnails are placed in the directory defined by the config setting
+   *   'media.settings.icon_base_uri'. When using custom icons, make sure the
+   *   module provides a hook_install() implementation to copy the custom icons
+   *   to this directory. The media_install() function provides a clear example
+   *   of how to do this.
+   * @param string $thumbnail_uri_metadata_attribute
+   *   (optional) The metadata attribute name to provide the thumbnail URI.
+   * @param string $thumbnail_width_metadata_attribute
+   *   (optional) The metadata attribute name to provide the thumbnail width.
+   * @param string $thumbnail_height_metadata_attribute
+   *   (optional) The metadata attribute name to provide the thumbnail height.
+   * @param string|null $thumbnail_alt_metadata_attribute
+   *   (optional) The metadata attribute name to provide the thumbnail alt.
+   *   "Thumbnail" will be used if the attribute name is not provided.
+   * @param string|null $thumbnail_title_metadata_attribute
+   *   (optional) The metadata attribute name to provide the thumbnail title.
+   *   The name of the media item will be used if the attribute name is not
+   *   provided.
+   * @param string $default_name_metadata_attribute
+   *   (optional) The metadata attribute name to provide the default name.
+   * @param class-string|null $deriver
+   *   (optional) The deriver class.
+   */
+  public function __construct(
+    public readonly string $id,
+    public readonly TranslatableMarkup $label,
+    public readonly ?TranslatableMarkup $description = NULL,
+    public readonly array $allowed_field_types = [],
+    public readonly array $forms = [],
+    public readonly string $default_thumbnail_filename = 'generic.png',
+    public readonly string $thumbnail_uri_metadata_attribute = 'thumbnail_uri',
+    public readonly string $thumbnail_width_metadata_attribute = 'thumbnail_width',
+    public readonly string $thumbnail_height_metadata_attribute = 'thumbnail_height',
+    public readonly ?string $thumbnail_alt_metadata_attribute = NULL,
+    public readonly ?string $thumbnail_title_metadata_attribute = NULL,
+    public readonly string $default_name_metadata_attribute = 'default_name',
+    public readonly ?string $deriver = NULL
+  ) {}
+
+}
diff --git a/core/modules/media/src/Attribute/OEmbedMediaSource.php b/core/modules/media/src/Attribute/OEmbedMediaSource.php
new file mode 100644
index 000000000000..3de756bb9d7c
--- /dev/null
+++ b/core/modules/media/src/Attribute/OEmbedMediaSource.php
@@ -0,0 +1,86 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\media\Attribute;
+
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
+/**
+ * Defines a OEmbedMediaSource attribute.
+ *
+ * Plugin namespace: Plugin\media\Source
+ *
+ * For a working example, see \Drupal\media\Plugin\media\Source\OEmbed.
+ *
+ * @see \Drupal\media\MediaSourceInterface
+ * @see \Drupal\media\MediaSourceBase
+ * @see \Drupal\media\MediaSourceManager
+ * @see hook_media_source_info_alter()
+ * @see plugin_api
+ */
+#[\Attribute(\Attribute::TARGET_CLASS)]
+class OEmbedMediaSource extends MediaSource {
+
+  /**
+   * Constructs a new OEmbedMediaSource attribute.
+   *
+   * @param string $id
+   *   The attribute class ID.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup $label
+   *   The human-readable name of the media source.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $description
+   *   (optional) A brief description of the media source.
+   * @param string[] $allowed_field_types
+   *   (optional) The field types that can be used as a source field for this
+   *   media source.
+   * @param class-string[] $forms
+   *   (optional) The classes used to define media source-specific forms. An
+   *   array of form class names, keyed by ID. The ID represents the operation
+   *   the form is used for, for example, 'media_library_add'.
+   * @param string[] $providers
+   *   (optional) A set of provider names, exactly as they appear in the
+   *   canonical oEmbed provider database at https://oembed.com/providers.json.
+   * @param string $default_thumbnail_filename
+   *   (optional) A filename for the default thumbnail.
+   *   The thumbnails are placed in the directory defined by the config setting
+   *   'media.settings.icon_base_uri'. When using custom icons, make sure the
+   *   module provides a hook_install() implementation to copy the custom icons
+   *   to this directory. The media_install() function provides a clear example
+   *   of how to do this.
+   * @param string $thumbnail_uri_metadata_attribute
+   *   (optional) The metadata attribute name to provide the thumbnail URI.
+   * @param string $thumbnail_width_metadata_attribute
+   *   (optional) The metadata attribute name to provide the thumbnail width.
+   * @param string $thumbnail_height_metadata_attribute
+   *   (optional) The metadata attribute name to provide the thumbnail height.
+   * @param string|null $thumbnail_alt_metadata_attribute
+   *   (optional) The metadata attribute name to provide the thumbnail alt.
+   *   "Thumbnail" will be used if the attribute name is not provided.
+   * @param string|null $thumbnail_title_metadata_attribute
+   *   (optional) The metadata attribute name to provide the thumbnail title.
+   *   The name of the media item will be used if the attribute name is not
+   *   provided.
+   * @param string $default_name_metadata_attribute
+   *   (optional) The metadata attribute name to provide the default name.
+   * @param class-string|null $deriver
+   *   (optional) The deriver class.
+   */
+  public function __construct(
+    public readonly string $id,
+    public readonly TranslatableMarkup $label,
+    public readonly ?TranslatableMarkup $description = NULL,
+    public readonly array $allowed_field_types = [],
+    public readonly array $forms = [],
+    public readonly array $providers = [],
+    public readonly string $default_thumbnail_filename = 'generic.png',
+    public readonly string $thumbnail_uri_metadata_attribute = 'thumbnail_uri',
+    public readonly string $thumbnail_width_metadata_attribute = 'thumbnail_width',
+    public readonly string $thumbnail_height_metadata_attribute = 'thumbnail_height',
+    public readonly ?string $thumbnail_alt_metadata_attribute = NULL,
+    public readonly ?string $thumbnail_title_metadata_attribute = NULL,
+    public readonly string $default_name_metadata_attribute = 'default_name',
+    public readonly ?string $deriver = NULL
+  ) {}
+
+}
diff --git a/core/modules/media/src/MediaSourceManager.php b/core/modules/media/src/MediaSourceManager.php
index 5b8778721c76..302a5ef55306 100644
--- a/core/modules/media/src/MediaSourceManager.php
+++ b/core/modules/media/src/MediaSourceManager.php
@@ -5,7 +5,7 @@
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\DefaultPluginManager;
-use Drupal\media\Annotation\MediaSource;
+use Drupal\media\Attribute\MediaSource;
 
 /**
  * Manages media source plugins.
@@ -24,7 +24,7 @@ class MediaSourceManager extends DefaultPluginManager {
    *   The module handler.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/media/Source', $namespaces, $module_handler, MediaSourceInterface::class, MediaSource::class);
+    parent::__construct('Plugin/media/Source', $namespaces, $module_handler, MediaSourceInterface::class, MediaSource::class, '\Drupal\media\Annotation\MediaSource');
 
     $this->alterInfo('media_source_info');
     $this->setCacheBackend($cache_backend, 'media_source_plugins');
diff --git a/core/modules/media/src/Plugin/media/Source/AudioFile.php b/core/modules/media/src/Plugin/media/Source/AudioFile.php
index 1e215930851d..d2f2035fc607 100644
--- a/core/modules/media/src/Plugin/media/Source/AudioFile.php
+++ b/core/modules/media/src/Plugin/media/Source/AudioFile.php
@@ -3,21 +3,22 @@
 namespace Drupal\media\Plugin\media\Source;
 
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\media\Attribute\MediaSource;
 use Drupal\media\MediaTypeInterface;
 
 /**
  * Media source wrapping around an audio file.
  *
  * @see \Drupal\file\FileInterface
- *
- * @MediaSource(
- *   id = "audio_file",
- *   label = @Translation("Audio file"),
- *   description = @Translation("Use audio files for reusable media."),
- *   allowed_field_types = {"file"},
- *   default_thumbnail_filename = "audio.png"
- * )
  */
+#[MediaSource(
+  id: "audio_file",
+  label: new TranslatableMarkup("Audio file"),
+  description: new TranslatableMarkup("Use audio files for reusable media."),
+  allowed_field_types: ["file"],
+  default_thumbnail_filename: "audio.png"
+)]
 class AudioFile extends File {
 
   /**
diff --git a/core/modules/media/src/Plugin/media/Source/File.php b/core/modules/media/src/Plugin/media/Source/File.php
index 1c963ef9636b..ba42d9a49686 100644
--- a/core/modules/media/src/Plugin/media/Source/File.php
+++ b/core/modules/media/src/Plugin/media/Source/File.php
@@ -2,7 +2,9 @@
 
 namespace Drupal\media\Plugin\media\Source;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\file\FileInterface;
+use Drupal\media\Attribute\MediaSource;
 use Drupal\media\MediaInterface;
 use Drupal\media\MediaTypeInterface;
 use Drupal\media\MediaSourceBase;
@@ -11,15 +13,13 @@
  * File entity media source.
  *
  * @see \Drupal\file\FileInterface
- *
- * @MediaSource(
- *   id = "file",
- *   label = @Translation("File"),
- *   description = @Translation("Use local files for reusable media."),
- *   allowed_field_types = {"file"},
- *   default_thumbnail_filename = "generic.png"
- * )
  */
+#[MediaSource(
+  id: "file",
+  label: new TranslatableMarkup("File"),
+  description: new TranslatableMarkup("Use local files for reusable media."),
+  allowed_field_types: ["file"],
+)]
 class File extends MediaSourceBase {
 
   /**
diff --git a/core/modules/media/src/Plugin/media/Source/Image.php b/core/modules/media/src/Plugin/media/Source/Image.php
index 4b6d48bd01cb..e896f3b41a22 100644
--- a/core/modules/media/src/Plugin/media/Source/Image.php
+++ b/core/modules/media/src/Plugin/media/Source/Image.php
@@ -9,6 +9,8 @@
 use Drupal\Core\Field\FieldTypePluginManagerInterface;
 use Drupal\Core\File\FileSystemInterface;
 use Drupal\Core\Image\ImageFactory;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\media\Attribute\MediaSource;
 use Drupal\media\MediaInterface;
 use Drupal\media\MediaTypeInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -17,16 +19,15 @@
  * Image entity media source.
  *
  * @see \Drupal\Core\Image\ImageInterface
- *
- * @MediaSource(
- *   id = "image",
- *   label = @Translation("Image"),
- *   description = @Translation("Use local images for reusable media."),
- *   allowed_field_types = {"image"},
- *   default_thumbnail_filename = "no-thumbnail.png",
- *   thumbnail_alt_metadata_attribute = "thumbnail_alt_value"
- * )
  */
+#[MediaSource(
+  id: "image",
+  label: new TranslatableMarkup("Image"),
+  description: new TranslatableMarkup("Use local images for reusable media."),
+  allowed_field_types: ["image"],
+  default_thumbnail_filename: "no-thumbnail.png",
+  thumbnail_alt_metadata_attribute: "thumbnail_alt_value"
+)]
 class Image extends File {
 
   /**
diff --git a/core/modules/media/src/Plugin/media/Source/OEmbed.php b/core/modules/media/src/Plugin/media/Source/OEmbed.php
index 34b8581a1df2..671e1800c7f6 100644
--- a/core/modules/media/src/Plugin/media/Source/OEmbed.php
+++ b/core/modules/media/src/Plugin/media/Source/OEmbed.php
@@ -14,8 +14,10 @@
 use Drupal\Core\File\FileSystemInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Url;
 use Drupal\Core\Utility\Token;
+use Drupal\media\Attribute\OEmbedMediaSource;
 use Drupal\media\IFrameUrlHelper;
 use Drupal\media\OEmbed\Resource;
 use Drupal\media\OEmbed\ResourceException;
@@ -65,17 +67,15 @@
  * define a new class which extends it. With the code above, you will able to
  * create media types which use the "Artwork" source plugin, and use those media
  * types to link to assets on Deviantart and Flickr.
- *
- * @MediaSource(
- *   id = "oembed",
- *   label = @Translation("oEmbed source"),
- *   description = @Translation("Use oEmbed URL for reusable media."),
- *   allowed_field_types = {"string"},
- *   default_thumbnail_filename = "no-thumbnail.png",
- *   deriver = "Drupal\media\Plugin\media\Source\OEmbedDeriver",
- *   providers = {},
- * )
  */
+#[OEmbedMediaSource(
+  id: "oembed",
+  label: new TranslatableMarkup("oEmbed source"),
+  description: new TranslatableMarkup("Use oEmbed URL for reusable media."),
+  allowed_field_types: ["string"],
+  default_thumbnail_filename: "no-thumbnail.png",
+  deriver: OEmbedDeriver::class,
+)]
 class OEmbed extends MediaSourceBase implements OEmbedInterface {
 
   /**
diff --git a/core/modules/media/src/Plugin/media/Source/VideoFile.php b/core/modules/media/src/Plugin/media/Source/VideoFile.php
index ebcf2f474a38..6c796e58ee37 100644
--- a/core/modules/media/src/Plugin/media/Source/VideoFile.php
+++ b/core/modules/media/src/Plugin/media/Source/VideoFile.php
@@ -3,21 +3,22 @@
 namespace Drupal\media\Plugin\media\Source;
 
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\media\Attribute\MediaSource;
 use Drupal\media\MediaTypeInterface;
 
 /**
  * Media source wrapping around a video file.
  *
  * @see \Drupal\file\FileInterface
- *
- * @MediaSource(
- *   id = "video_file",
- *   label = @Translation("Video file"),
- *   description = @Translation("Use video files for reusable media."),
- *   allowed_field_types = {"file"},
- *   default_thumbnail_filename = "video.png"
- * )
  */
+#[MediaSource(
+  id: "video_file",
+  label: new TranslatableMarkup("Video file"),
+  description: new TranslatableMarkup("Use video files for reusable media."),
+  allowed_field_types: ["file"],
+  default_thumbnail_filename: "video.png"
+)]
 class VideoFile extends File {
 
   /**
diff --git a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/Test.php b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/Test.php
index b7baaa555781..3cf8a3e0e36c 100644
--- a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/Test.php
+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/Test.php
@@ -4,19 +4,20 @@
 
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\media\Attribute\MediaSource;
 use Drupal\media\MediaInterface;
 use Drupal\media\MediaSourceBase;
 
 /**
  * Provides test media source.
- *
- * @MediaSource(
- *   id = "test",
- *   label = @Translation("Test source"),
- *   description = @Translation("Test media source."),
- *   allowed_field_types = {"string"},
- * )
  */
+#[MediaSource(
+  id: "test",
+  label: new TranslatableMarkup("Test source"),
+  description: new TranslatableMarkup("Test media source."),
+  allowed_field_types: ["string"]
+)]
 class Test extends MediaSourceBase {
 
   /**
diff --git a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php
index e2389f5301d4..61007c31c2ea 100644
--- a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php
+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php
@@ -4,18 +4,19 @@
 
 use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\media\Attribute\MediaSource;
 use Drupal\media\MediaTypeInterface;
 
 /**
  * Provides test media source.
- *
- * @MediaSource(
- *   id = "test_different_displays",
- *   label = @Translation("Test source with different displays"),
- *   description = @Translation("Test source with different displays."),
- *   allowed_field_types = {"entity_reference"},
- * )
  */
+#[MediaSource(
+  id: "test_different_displays",
+  label: new TranslatableMarkup("Test source with different displays"),
+  description: new TranslatableMarkup("Test source with different displays."),
+  allowed_field_types: ["entity_reference"]
+)]
 class TestDifferentDisplays extends Test {
 
   /**
diff --git a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestTranslation.php b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestTranslation.php
index 947f06733b65..9565b724494b 100644
--- a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestTranslation.php
+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestTranslation.php
@@ -2,19 +2,20 @@
 
 namespace Drupal\media_test_source\Plugin\media\Source;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\media\Attribute\MediaSource;
 use Drupal\media\MediaInterface;
 
 /**
  * Provides test media source.
- *
- * @MediaSource(
- *   id = "test_translation",
- *   label = @Translation("Test source with translations"),
- *   description = @Translation("Test media source with translations."),
- *   allowed_field_types = {"string"},
- *   thumbnail_alt_metadata_attribute = "test_thumbnail_alt",
- * )
  */
+#[MediaSource(
+  id: "test_translation",
+  label: new TranslatableMarkup("Test source with translations"),
+  description: new TranslatableMarkup("Test media source with translations."),
+  allowed_field_types: ["string"],
+  thumbnail_alt_metadata_attribute: "test_thumbnail_alt"
+)]
 class TestTranslation extends Test {
 
   /**
diff --git a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithConstraints.php b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithConstraints.php
index 98039ca2d2a2..beb9b023e78d 100644
--- a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithConstraints.php
+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithConstraints.php
@@ -2,19 +2,20 @@
 
 namespace Drupal\media_test_source\Plugin\media\Source;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\media\Attribute\MediaSource;
 use Drupal\media\MediaSourceEntityConstraintsInterface;
 use Drupal\media\MediaSourceFieldConstraintsInterface;
 
 /**
  * Provides generic media type.
- *
- * @MediaSource(
- *   id = "test_constraints",
- *   label = @Translation("Test source with constraints"),
- *   description = @Translation("Test media source that provides constraints."),
- *   allowed_field_types = {"string_long"},
- * )
  */
+#[MediaSource(
+  id: "test_constraints",
+  label: new TranslatableMarkup("Test source with constraints"),
+  description: new TranslatableMarkup("Test media source that provides constraints."),
+  allowed_field_types: ["string_long"],
+)]
 class TestWithConstraints extends Test implements MediaSourceEntityConstraintsInterface, MediaSourceFieldConstraintsInterface {
 
   /**
diff --git a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithHiddenSourceField.php b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithHiddenSourceField.php
index e5ac525eb0e6..ac4754f2818f 100644
--- a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithHiddenSourceField.php
+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithHiddenSourceField.php
@@ -4,18 +4,19 @@
 
 use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\media\Attribute\MediaSource;
 use Drupal\media\MediaTypeInterface;
 
 /**
  * Provides test media source.
- *
- * @MediaSource(
- *   id = "test_hidden_source_field",
- *   label = @Translation("Test source with hidden source field"),
- *   description = @Translation("Test media source with hidden source field."),
- *   allowed_field_types = {"string"},
- * )
  */
+#[MediaSource(
+  id: "test_hidden_source_field",
+  label: new TranslatableMarkup("Test source with hidden source field"),
+  description: new TranslatableMarkup("Test media source with hidden source field."),
+  allowed_field_types: ["string"],
+)]
 class TestWithHiddenSourceField extends Test {
 
   /**
diff --git a/core/modules/media_library/media_library.api.php b/core/modules/media_library/media_library.api.php
index 5c33600ff53f..4979fd1ae2e2 100644
--- a/core/modules/media_library/media_library.api.php
+++ b/core/modules/media_library/media_library.api.php
@@ -60,16 +60,15 @@
  * source plugin definition which provides an add form for the media library:
  *
  * @code
- * @MediaSource(
- *   id = "file",
- *   label = @Translation("File"),
- *   description = @Translation("Use local files for reusable media."),
- *   allowed_field_types = {"file"},
- *   default_thumbnail_filename = "generic.png",
- *   forms = {
- *     "media_library_add" = "\Drupal\media_library\Form\FileUploadForm",
- *   },
- * )
+ * #[MediaSource(
+ *   id: "file",
+ *   label: new TranslatableMarkup("File"),
+ *   description: new TranslatableMarkup("Use local files for reusable media."),
+ *   allowed_field_types: ["file"],
+ *   forms = [
+ *     "media_library_add" => "\Drupal\media_library\Form\FileUploadForm",
+ *   ]
+ * )]
  * @endcode
  *
  * This can also be done in hook_media_source_info_alter(). For example:
-- 
GitLab