diff --git a/composer.json b/composer.json
index 4168578..689e154 100644
--- a/composer.json
+++ b/composer.json
@@ -16,10 +16,5 @@
     "source": "http://cgit.drupalcode.org/media_entity_carto"
   },
   "license": "GPL-2.0+",
-  "minimum-stability": "dev",
-  "require": {
-    "drupal/media_entity": "~1.6",
-    "league/csv": "~8.0"
-  }
-
+  "minimum-stability": "dev"
 }
diff --git a/config/schema/media_entity_carto.schema.yml b/config/schema/media_entity_carto.schema.yml
index 1a1f94c..27ff5e6 100644
--- a/config/schema/media_entity_carto.schema.yml
+++ b/config/schema/media_entity_carto.schema.yml
@@ -1,7 +1,3 @@
-media_entity.bundle.type.carto:
-  type: mapping
-  label: 'CARTO type configuration'
-  mapping:
-    source_field:
-      type: string
-      label: 'Field with embed code/URL'
\ No newline at end of file
+media.source.carto:
+  type: media.source.field_aware
+  label: '"CARTO" media source configuration'
diff --git a/media_entity_carto.info.yml b/media_entity_carto.info.yml
index 5fe450d..82091cf 100644
--- a/media_entity_carto.info.yml
+++ b/media_entity_carto.info.yml
@@ -4,4 +4,4 @@ type: module
 package: Media
 core: 8.x
 dependencies:
-  - media_entity
+  - drupal:media (>= 8.4)
diff --git a/media_entity_carto.install b/media_entity_carto.install
index e8e7aa5..4d98893 100644
--- a/media_entity_carto.install
+++ b/media_entity_carto.install
@@ -10,6 +10,47 @@
  */
 function media_entity_carto_install() {
   $source = drupal_get_path('module', 'media_entity_carto') . '/images/icons';
-  $destination = \Drupal::config('media_entity.settings')->get('icon_base');
-  media_entity_copy_icons($source, $destination);
+  $destination = \Drupal::config('media.settings')->get('icon_base_uri');
+  file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+
+  $files = file_scan_directory($source, '/.*\.(svg|png|jpg|jpeg|gif)$/');
+  foreach ($files as $file) {
+    // When reinstalling the media module we don't want to copy the icons when
+    // they already exist. The icons could be replaced (by a contrib module or
+    // manually), so we don't want to replace the existing files. Removing the
+    // files when we uninstall could also be a problem if the files are
+    // referenced somewhere else. Since showing an error that it was not
+    // possible to copy the files is also confusing, we silently do nothing.
+    if (!file_exists($destination . DIRECTORY_SEPARATOR . $file->filename)) {
+      file_unmanaged_copy($file->uri, $destination, FILE_EXISTS_ERROR);
+    }
+  }
+}
+
+/**
+ * Implements hook_requirements().
+ */
+function media_entity_carto_requirements($phase) {
+  $requirements = [];
+  if ($phase == 'install') {
+    $destination = \Drupal::config('media.settings')->get('icon_base_uri');
+    file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+    $is_writable = is_writable($destination);
+    $is_directory = is_dir($destination);
+    if (!$is_writable || !$is_directory) {
+      if (!$is_directory) {
+        $error = t('The directory %directory does not exist.', ['%directory' => $destination]);
+      }
+      else {
+        $error = t('The directory %directory is not writable.', ['%directory' => $destination]);
+      }
+      $description = t('An automated attempt to create this directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see INSTALL.txt or the <a href=":handbook_url">online handbook</a>.', [':handbook_url' => 'https://www.drupal.org/server-permissions']);
+      if (!empty($error)) {
+        $description = $error . ' ' . $description;
+        $requirements['media_entity_carto']['description'] = $description;
+        $requirements['media_entity_carto']['severity'] = REQUIREMENT_ERROR;
+      }
+    }
+  }
+  return $requirements;
 }
diff --git a/src/Plugin/Field/FieldFormatter/CartoEmbedFormatter.php b/src/Plugin/Field/FieldFormatter/CartoEmbedFormatter.php
index 6ddc28b..93a8e31 100644
--- a/src/Plugin/Field/FieldFormatter/CartoEmbedFormatter.php
+++ b/src/Plugin/Field/FieldFormatter/CartoEmbedFormatter.php
@@ -5,7 +5,7 @@
 use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\FormatterBase;
-use Drupal\media_entity_carto\Plugin\MediaEntity\Type\Carto;
+use Drupal\media_entity_carto\Plugin\media\Source\Carto;
 
 /**
  * Plugin implementation of the 'carto_embed' formatter.
@@ -47,7 +47,7 @@ protected function getEmbedCode(FieldItemInterface $item) {
    * {@inheritdoc}
    */
   public function viewElements(FieldItemListInterface $items, $langcode) {
-    $element = array();
+    $element = [];
     foreach ($items as $delta => $item) {
       $matches = [];
 
diff --git a/src/Plugin/MediaEntity/Type/Carto.php b/src/Plugin/MediaEntity/Type/Carto.php
deleted file mode 100644
index f97cf80..0000000
--- a/src/Plugin/MediaEntity/Type/Carto.php
+++ /dev/null
@@ -1,168 +0,0 @@
-<?php
-
-namespace Drupal\media_entity_carto\Plugin\MediaEntity\Type;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\media_entity\MediaInterface;
-use Drupal\media_entity\MediaTypeBase;
-
-/**
- * Provides media type plugin for CARTO.
- *
- * @MediaType(
- *   id = "carto",
- *   label = @Translation("CARTO"),
- *   description = @Translation("Provides business logic and metadata for CARTO.")
- * )
- */
-class Carto extends MediaTypeBase {
-
-  /**
-   * List of validation regular expressions.
-   *
-   * @var array
-   */
-  public static $validationRegexp = array(
-    '@((http|https):){0,1}//(www\.){0,1}(?<user>[a-z0-9_-]+)\.carto\.com/builder/(?<id>[a-z0-9_-]+)/embed@i' => 'id',
-  );
-
-  /**
-   * {@inheritdoc}
-   */
-  public function providedFields() {
-    $fields = array(
-      'id' => $this->t('Map ID'),
-      'user' => $this->t('CARTO user information'),
-    );
-
-    return $fields;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getField(MediaInterface $media, $name) {
-    $matches = $this->matchRegexp($media);
-
-    if (!$matches['id']) {
-      return FALSE;
-    }
-
-    // First we return the fields that are available from regex.
-    switch ($name) {
-      case 'id':
-        return $matches['id'];
-
-      case 'user':
-        if ($matches['user']) {
-          return $matches['user'];
-        }
-        return FALSE;
-    }
-
-    return FALSE;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
-    $options = [];
-    $allowed_field_types = ['string', 'string_long', 'link'];
-    /** @var \Drupal\media_entity\MediaBundleInterface $bundle */
-    $bundle = $form_state->getFormObject()->getEntity();
-    foreach ($this->entityFieldManager->getFieldDefinitions('media', $bundle->id()) as $field_name => $field) {
-      if (in_array($field->getType(), $allowed_field_types) && !$field->getFieldStorageDefinition()->isBaseField()) {
-        $options[$field_name] = $field->getLabel();
-      }
-    }
-
-    $form['source_field'] = array(
-      '#type' => 'select',
-      '#title' => $this->t('Field with source information'),
-      '#description' => $this->t('Field on media entity that stores CARTO embed code or URL. You can create a bundle without selecting a value for this dropdown initially. This dropdown can be populated after adding fields to the bundle.'),
-      '#default_value' => empty($this->configuration['source_field']) ? NULL : $this->configuration['source_field'],
-      '#options' => $options,
-    );
-
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getDefaultThumbnail() {
-    return $this->config->get('icon_base') . '/carto.png';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function thumbnail(MediaInterface $media) {
-    return $this->getDefaultThumbnail();
-  }
-
-  /**
-   * Runs preg_match on embed code/URL.
-   *
-   * @param MediaInterface $media
-   *   Media object.
-   *
-   * @return array|bool
-   *   Array of preg matches or FALSE if no match.
-   *
-   * @see preg_match()
-   */
-  protected function matchRegexp(MediaInterface $media) {
-    $matches = array();
-
-    if (isset($this->configuration['source_field'])) {
-      $source_field = $this->configuration['source_field'];
-      if ($media->hasField($source_field)) {
-        $property_name = $media->{$source_field}->first()->mainPropertyName();
-        foreach (static::$validationRegexp as $pattern => $key) {
-          if (preg_match($pattern, $media->{$source_field}->{$property_name}, $matches)) {
-            return $matches;
-          }
-        }
-      }
-    }
-
-    return FALSE;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function attachConstraints(MediaInterface $media) {
-    parent::attachConstraints($media);
-
-    if (isset($this->configuration['source_field'])) {
-      $source_field_name = $this->configuration['source_field'];
-      if ($media->hasField($source_field_name)) {
-        foreach ($media->get($source_field_name) as &$embed_code) {
-          /** @var \Drupal\Core\TypedData\DataDefinitionInterface $typed_data */
-          $typed_data = $embed_code->getDataDefinition();
-          $typed_data->addConstraint('CartoEmbedCode');
-        }
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getDefaultName(MediaInterface $media) {
-    // The default name will be the CARTO username of the author + the
-    // map ID.
-    $user = $this->getField($media, 'user');
-    $id = $this->getField($media, 'id');
-    if (!empty($user) && !empty($id)) {
-      return $user . ' - ' . $id;
-    }
-
-    return parent::getDefaultName($media);
-  }
-
-
-}
diff --git a/src/Plugin/Validation/Constraint/CartoEmbedCodeConstraintValidator.php b/src/Plugin/Validation/Constraint/CartoEmbedCodeConstraintValidator.php
index 60b6c50..32e1634 100644
--- a/src/Plugin/Validation/Constraint/CartoEmbedCodeConstraintValidator.php
+++ b/src/Plugin/Validation/Constraint/CartoEmbedCodeConstraintValidator.php
@@ -2,8 +2,9 @@
 
 namespace Drupal\media_entity_carto\Plugin\Validation\Constraint;
 
-use Drupal\media_entity\EmbedCodeValueTrait;
-use Drupal\media_entity_carto\Plugin\MediaEntity\Type\Carto;
+use Drupal\Core\Field\FieldItemInterface;
+use Drupal\Core\Field\FieldItemList;
+use Drupal\media_entity_carto\Plugin\media\Source\Carto;
 use Symfony\Component\Validator\Constraint;
 use Symfony\Component\Validator\ConstraintValidator;
 
@@ -12,24 +13,42 @@
  */
 class CartoEmbedCodeConstraintValidator extends ConstraintValidator {
 
-  use EmbedCodeValueTrait;
-
   /**
    * {@inheritdoc}
    */
   public function validate($value, Constraint $constraint) {
-    $value = $this->getEmbedCode($value);
-    if (!isset($value)) {
-      return;
+    $data = '';
+    if (is_string($value)) {
+      $data = $value;
     }
-
-    foreach (Carto::$validationRegexp as $pattern => $key) {
-      if (preg_match($pattern, $value)) {
-        return;
+    elseif ($value instanceof FieldItemList) {
+      $fieldtype = $value->getFieldDefinition()->getType();
+      $field_value = $value->getValue();
+      if ($fieldtype == 'link') {
+        $data = empty($field_value[0]['uri']) ? "" : $field_value[0]['uri'];
+      }
+      else {
+        $data = empty($field_value[0]['value']) ? "" : $field_value[0]['value'];
+      }
+    }
+    elseif ($value instanceof FieldItemInterface) {
+      $class = get_class($value);
+      $property = $class::mainPropertyName();
+      if ($property) {
+        $data = $value->{$property};
+      }
+    }
+    if ($data) {
+      $matches = [];
+      foreach (Carto::$validationRegexp as $pattern => $key) {
+        if (preg_match($pattern, $data, $item_matches)) {
+          $matches[] = $item_matches;
+        }
+      }
+      if (empty($matches)) {
+        $this->context->addViolation($constraint->message);
       }
     }
-
-    $this->context->addViolation($constraint->message);
   }
 
 }
