diff --git a/composer.json b/composer.json
index dda634e45c7..5fe6cdc168c 100644
--- a/composer.json
+++ b/composer.json
@@ -22,6 +22,7 @@
"league/glide": "^3.0",
"maennchen/zipstream-php": "^3.1",
"michelf/php-smartypants": "^1.8.1",
+ "mpratt/embera": "~2.0",
"nesbot/carbon": "^3.0",
"pixelfear/composer-dist-plugin": "^0.1.4",
"pragmarx/google2fa": "^8.0",
diff --git a/resources/js/components/fieldtypes/VideoFieldtype.vue b/resources/js/components/fieldtypes/VideoFieldtype.vue
index cd38e653e2e..f3b755c67e6 100644
--- a/resources/js/components/fieldtypes/VideoFieldtype.vue
+++ b/resources/js/components/fieldtypes/VideoFieldtype.vue
@@ -1,89 +1,93 @@
-
-
-
-
-
-
{{ __('statamic::validation.url') }}
-
diff --git a/routes/cp.php b/routes/cp.php
index b820ef22c55..bbd80acf297 100644
--- a/routes/cp.php
+++ b/routes/cp.php
@@ -57,6 +57,7 @@
use Statamic\Http\Controllers\CP\Fieldtypes\IconFieldtypeController;
use Statamic\Http\Controllers\CP\Fieldtypes\MarkdownFieldtypeController;
use Statamic\Http\Controllers\CP\Fieldtypes\RelationshipFieldtypeController;
+use Statamic\Http\Controllers\CP\Fieldtypes\VideoFieldtypeController;
use Statamic\Http\Controllers\CP\Forms\ActionController as FormActionController;
use Statamic\Http\Controllers\CP\Forms\FormBlueprintController;
use Statamic\Http\Controllers\CP\Forms\FormExportController;
@@ -343,6 +344,7 @@
Route::post('files/upload', [FilesFieldtypeController::class, 'upload'])->name('files.upload');
Route::get('dictionaries/{dictionary}', DictionaryFieldtypeController::class)->name('dictionary-fieldtype');
Route::post('icons', IconFieldtypeController::class)->name('icon-fieldtype');
+ Route::get('video/details', [VideoFieldtypeController::class, 'details'])->name('video.details');
});
Route::group(['prefix' => 'field-action-modal'], function () {
diff --git a/src/Fieldtypes/Video.php b/src/Fieldtypes/Video.php
index c395e5f6882..590f046a6a9 100644
--- a/src/Fieldtypes/Video.php
+++ b/src/Fieldtypes/Video.php
@@ -2,12 +2,38 @@
namespace Statamic\Fieldtypes;
+use Embera\ProviderCollection\SlimProviderCollection;
use Statamic\Fields\Fieldtype;
class Video extends Fieldtype
{
protected $categories = ['media'];
+ public function augment($value)
+ {
+ if (is_null($value)) {
+ return null;
+ }
+
+ if (str($value)->isUrl()) {
+ return $value;
+ }
+
+ //otherwise assume it's a Cloudflare ID
+ return str($value)->afterLast('::')->value();
+ }
+
+ public function preload()
+ {
+ $providers = new Providers();
+
+ /** @todo Fetch these from some repository so folks can add their own */
+ return [
+ 'providers' => $providers->get(),
+ 'url' => cp_route('video.details'),
+ ];
+ }
+
protected function configFieldItems(): array
{
return [
@@ -29,3 +55,19 @@ protected function configFieldItems(): array
];
}
}
+
+class Providers extends SlimProviderCollection
+{
+ public function get(): array
+ {
+ return collect($this->providers)
+ ->unique()
+ ->values()
+ ->map(fn (string $class) => ['provider' => class_basename($class)])
+ ->add(['provider' => 'Cloudflare'])
+ ->sortBy('provider')
+ ->add(['provider' => 'Not Supported'])
+ ->values()
+ ->all();
+ }
+}
diff --git a/src/Http/Controllers/CP/Fieldtypes/VideoFieldtypeController.php b/src/Http/Controllers/CP/Fieldtypes/VideoFieldtypeController.php
new file mode 100644
index 00000000000..562632c5c36
--- /dev/null
+++ b/src/Http/Controllers/CP/Fieldtypes/VideoFieldtypeController.php
@@ -0,0 +1,74 @@
+query('url'))) {
+ return Video::fromUrl($url);
+ }
+
+ if ($this->isCloudflareStream($request)) {
+ $id = $request->query('id');
+ $embedUrl = "https://iframe.cloudflarestream.com/{$id}";
+ $iframe = "";
+
+ return new Video(id: $id, provider: 'Cloudflare', embedUrl: $iframe);
+ }
+
+ return Video::notSupported();
+ }
+
+ private function isCloudflareStream(Request $request): bool
+ {
+ return $request->has('id') && $request->query('type') === 'Cloudflare';
+ }
+}
+
+class Video implements Arrayable
+{
+ public static function fromUrl(string $url): self
+ {
+ if (empty($details = (new Embera(['responsive' => true]))->getUrlData($url))) {
+ return static::notSupported();
+ }
+
+ $data = new Fluent(Arr::first($details));
+
+ return new self(
+ id: $data->video_id,
+ provider: $data->embera_provider_name,
+ embedUrl: $data->html
+ );
+ }
+
+ public static function notSupported(): self
+ {
+ return new self(provider: 'Not Supported');
+ }
+
+ public function __construct(
+ public string $provider,
+ public ?string $id = null,
+ public ?string $embedUrl = null,
+ ) {
+ }
+
+ public function toArray(): array
+ {
+ return [
+ 'embed_url' => $this->embedUrl,
+ 'id' => $this->id,
+ 'provider' => $this->provider,
+ ];
+ }
+}