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 @@ 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, + ]; + } +}