├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json └── embedder ├── Embedder.php ├── fields └── EmbedderFieldType.php ├── icon.svg ├── migrations └── m180412_205549_update_embedder_fields.php ├── services └── EmbedderService.php ├── templates └── _fields │ └── input.twig └── variables └── EmbedderVariable.php /.gitignore: -------------------------------------------------------------------------------- 1 | # OS Files 2 | .DS_Store 3 | Thumbs.db 4 | 5 | # Dependencies 6 | /vendor 7 | *composer.lock 8 | 9 | # Log Files 10 | *.log 11 | 12 | # Editors 13 | /.idea 14 | /.vscode 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Embedder Changelog 2 | 3 | ## 4.0.0 - 2024-08-18 4 | ### Added 5 | - Add support for Craft 5 6 | - Drop support for Craft 4 7 | 8 | ## 3.0.0 - 2022-05-23 9 | 10 | ### Added 11 | 12 | - Add support for Craft 4 13 | - Drop support for Craft 3 14 | 15 | ## 2.0.3 - 2022-1-25 16 | 17 | ### Added 18 | 19 | - Add support for YouTube privacy-enhanced mode via `youtube_privacyEnhanced` parameter 20 | 21 | ## 2.0.2 - 2021-8-19 22 | 23 | ### Fixed 24 | 25 | - Fix code that handles graceful failure when a video is not found 26 | 27 | ### Changed 28 | 29 | - Add type hinting to EmbedderService 30 | 31 | ## 2.0.1 - 2020-12-15 32 | 33 | ### Fixed 34 | 35 | - Removed force_https setting as YouTube only accepts https now 36 | - Removed bug fix for YouTube videos over 612px as it doesn't work with the new API 37 | 38 | ## 2.0.0 - 2019-01-16 39 | 40 | ### Added 41 | 42 | - Added support for Craft 3 43 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Jonathan Sarmiento 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Embedder 2 | 3 | Embedder is a plugin for Craft CMS based on [Antenna](https://github.com/vector/Antenna) by Vector Media Group that will generate embed code for YouTube, Vimeo, Wistia, or Viddler. It also gives you access to the video’s title, its author, the author’s YouTube/Vimeo URL, and a thumbnail. All you have to do is pass it a single URL. 4 | 5 | You can also output various pieces of metadata about the video. 6 | 7 | ## Requirements 8 | 9 | This plugin requires Craft 5 or later. 10 | 11 | > For the Craft 2 version, see the [v1 branch](https://github.com/jdsdev/Embedder/tree/v1) 12 | 13 | ## Installation 14 | 15 | To install the plugin, follow these instructions. 16 | 17 | 1. Open your terminal and go to your Craft project: 18 | 19 | cd /path/to/project 20 | 21 | 2. Then tell Composer to load the plugin: 22 | 23 | composer require jdsdev/craft-embedder 24 | 25 | 3. In the Control Panel, go to Settings → Plugins and click the “Install” button for Embedder. 26 | 27 | --- 28 | 29 | ## Simple Usage 30 | 31 | If used as a single tag (embedder.embed), it returns the HTML embed/object code for the video. 32 | 33 | ```twig 34 | {{ craft.embedder.embed (entry.embedderVideo, {max_width:500, max_height:800}) }} 35 | ``` 36 | 37 | ## Full Usage and Variables 38 | 39 | If used by setting the video URL, you get access to several variables. 40 | 41 | ```twig 42 | {% set video = craft.embedder.url(entry.embedderVideo, {max_width:500, max_height:800}) %} 43 | 44 | {{ video.embed_code }} 45 | 50 | ``` 51 | 52 | There are three image sizes available for videos: `video_thumbnail`, `video_mediumres`, and `video_highres`. They are not consistent across services but they should fall into rough size brackets. `video_thumbnail` is going to be between 100-200px wide; `video_mediumres` will be around 400-500px wide; and `video_highres` will be at least the full size of your uploaded video and could be as wide as 1280px. 53 | 54 | ## Parameters 55 | 56 | ### Dimensions 57 | 58 | Set the `max_width` and/or `max_height` for whatever size your website requires. The video will be resized to be within those dimensions, and will stay at the correct proportions. 59 | 60 | - `max_width: 500` - Can be any number. Left unspecified by default. 61 | - `max_height: 800` - Can be any number. Left unspecified by default. 62 | 63 | ### YouTube 64 | 65 | If you're using YouTube, you can use any of the [supported embed parameters](https://developers.google.com/youtube/player_parameters#Parameters). Simply prefix the parameters with `youtube_`. Here are some common parameters: 66 | 67 | - `youtube_rel: 0` - Show related videos at the end of the video. Can be `0` or `1` (default). 68 | - `youtube_showinfo: 0` - Show the video title overlay. Can be `0` or `1` (default). 69 | - `youtube_controls: 0` - Show the video player controls. Can be `0` or `1` (default). 70 | - `youtube_autoplay: 1` - Automatically start playback of the video. Can be `0` (default) or `1`. 71 | - `youtube_enablejsapi: 1` - Enable the YouTube IFrame or JavaScript APIs. Can be `0` (default) or `1`. 72 | 73 | Additionally the following non-standard parameter is available when using YouTube: 74 | 75 | - `youtube_privacyEnhanced: 1` - Use [YouTube's privacy-enhanced mode](https://support.google.com/youtube/answer/171780#zippy=%2Cturn-on-privacy-enhanced-mode). Can be `0` (default) or `1`. 76 | 77 | ### Vimeo 78 | 79 | If you're using Vimeo, you can use any of the [supported embed parameters](https://github.com/vimeo/player.js#embed-options). Simply prefix the parameters with `vimeo_`. Here are some of the common parameters: 80 | 81 | - `vimeo_byline: 0` - Shows the byline on the video. Can be `0` or `1` (default). 82 | - `vimeo_title: 0` - Shows the title on the video. Can be `0` or `1` (default). 83 | - `vimeo_portrait: 0` - Shows the user's avatar on the video. Can be `0` or `1` (default). 84 | - `vimeo_loop: 1` - Loops the video playback. Can be `0` (default) or `1`. 85 | - `vimeo_autoplay: 1` - Automatically start playback of the video. Can be `0` (default) or `1`. 86 | - `vimeo_color: 'ff0000'` - Sets the theme color for the Vimeo player. Can be any hexidecimal color value (without the hash). Defaults to `'00adef'`. 87 | 88 | You can also use the following Vimeo parameter: 89 | 90 | - `vimeo_player_id: 'myVideoPlayer'` - Sets an ID on the player, which is useful if you want to control multiple videos on the same page in a different way. 91 | 92 | The following extra variable is available when using Vimeo: 93 | 94 | - `{{ video_description }}` - The description of the video, as set in Vimeo 95 | 96 | ### Viddler 97 | 98 | If you're using Viddler, you get access to two more parameters: 99 | 100 | - `viddler_type: 'simple'` - Specifies the player type. Can be `'simple'` or `'player'` (default). 101 | - `viddler_ratio: 'widescreen'` - Aspect ratio. Can be `'widescreen'`, `'fullscreen'`, or left unspecified for automatically determined aspect ratio. 102 | 103 | ### Wistia 104 | 105 | If you're using Wistia, you get access to two more parameters: 106 | 107 | - `wistia_type` - Sets the supported embed type. 108 | - `wistia_foam: true` - Makes the embedded video responsive using Wistia's Video Foam feature. 109 | 110 | ### HTML Output Control 111 | 112 | You can also control your output with the following parameters: 113 | 114 | - `id: 'myId'` - Gives the iFrame an `id=` attribute with the specified value. 115 | - `class: 'video player'` - Gives the iFrame a `class=` attribute with the specified value. 116 | - `attributes: 'data-video data-player'` - Gives the iFrame the specified HTML attribute(s). 117 | 118 | ### wmode (deprecated with most providers) 119 | 120 | The optional `wmode` parameter can be used if you're experiencing issues positioning HTML content in front of the embedded media. It accepts values of `transparent`, `opaque` and `window`. 121 | 122 | --- 123 | 124 | ## Contributions 125 | 126 | - [Adam Powell](https://github.com/A-P) - Original plugin author. 127 | 128 | - [Aaron Waldon](https://github.com/aaronwaldon) / @aaronwaldon - Reworked the logic to allow any provider parameters to be used. Added HTML output control parameters and updated the documentation. 129 | 130 | - [Jonathan Sarmiento](https://github.com/jdsdev) - Updated the plugin for Craft 3 and Craft 4. 131 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jdsdev/craft-embedder", 3 | "description": "Generate video embed code.", 4 | "version": "4.0.0", 5 | "type": "craft-plugin", 6 | "license": "MIT", 7 | "keywords": [ 8 | "craft", 9 | "cms", 10 | "craftcms", 11 | "craft-plugin", 12 | "embedder" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "jonathan@jdsdev.com", 17 | "homepage": "https://jdsdev.com/" 18 | } 19 | ], 20 | "require": { 21 | "craftcms/cms": "^5.0.0-beta.1", 22 | "php": "^8.2" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "jdsdev\\embedder\\": "embedder/" 27 | } 28 | }, 29 | "extra": { 30 | "name": "Embedder", 31 | "handle": "embedder", 32 | "changelogUrl": "https://raw.githubusercontent.com/jdsdev/Embedder/master/CHANGELOG.md", 33 | "components": { 34 | "embedder": "jdsdev\\embedder\\services\\EmbedderService" 35 | }, 36 | "class": "jdsdev\\embedder\\Embedder" 37 | }, 38 | "config": { 39 | "allow-plugins": { 40 | "yiisoft/yii2-composer": true, 41 | "craftcms/plugin-installer": true 42 | } 43 | }, 44 | "minimum-stability": "dev", 45 | "prefer-stable": true, 46 | "require-dev": { 47 | "craftcms/rector": "dev-main" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /embedder/Embedder.php: -------------------------------------------------------------------------------- 1 | types[] = EmbedderFieldType::class; 52 | } 53 | ); 54 | 55 | // Register variables 56 | Event::on( 57 | CraftVariable::class, 58 | CraftVariable::EVENT_INIT, 59 | function (Event $event) { 60 | $variable = $event->sender; 61 | $variable->set('embedder', EmbedderVariable::class); 62 | } 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /embedder/fields/EmbedderFieldType.php: -------------------------------------------------------------------------------- 1 | $this->handle, 35 | 'value' => $value, 36 | 'field' => $this, 37 | ]; 38 | 39 | return Craft::$app->getView()->renderTemplate( 40 | 'embedder/_fields/input', 41 | $variables 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /embedder/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /embedder/migrations/m180412_205549_update_embedder_fields.php: -------------------------------------------------------------------------------- 1 | update('{{%fields}}', [ 20 | 'type' => EmbedderFieldType::class 21 | ], [ 22 | 'type' => 'Embedder' 23 | ], [], false); 24 | } 25 | 26 | /** 27 | * @inheritdoc 28 | */ 29 | public function safeDown() 30 | { 31 | echo "m180412_205549_update_embedder_fields cannot be reverted.\n"; 32 | return false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /embedder/services/EmbedderService.php: -------------------------------------------------------------------------------- 1 | 'video_title', 33 | 'html' => 'embed_code', 34 | 'author_name' => 'video_author', 35 | 'author_url' => 'video_author_url', 36 | 'thumbnail_url' => 'video_thumbnail', 37 | 'medres_url' => 'video_mediumres', 38 | 'highres_url' => 'video_highres', 39 | 'description' => 'video_description', 40 | ]; 41 | 42 | $video_data = []; 43 | 44 | foreach ($plugin_vars as $var) 45 | { 46 | $video_data[$var] = false; 47 | } 48 | 49 | // if it's not YouTube, Vimeo, Wistia, or Viddler bail 50 | if ($isYouTube) 51 | { 52 | $url = 'https://www.youtube.com/oembed?format=xml&iframe=1&url='; 53 | } 54 | elseif ($isVimeo) 55 | { 56 | $url = 'https://vimeo.com/api/oembed.xml?url='; 57 | } 58 | elseif ($isWistia) 59 | { 60 | $url = 'http://app.wistia.com/embed/oembed.xml?url='; 61 | } 62 | elseif ($isViddler) 63 | { 64 | $url = 'http://www.viddler.com/oembed/?format=xml&url='; 65 | } 66 | else 67 | { 68 | return $output === 'simple' ? '' : $video_data; 69 | } 70 | $url .= urlencode($video_url); 71 | 72 | // set the semi-ubiquitous parameters 73 | $max_width = isset($params['max_width']) ? '&maxwidth=' . $params['max_width'] : ''; 74 | $max_height = isset($params['max_height']) ? '&maxheight=' . $params['max_height'] : ''; 75 | $wmode_param = isset($params['wmode']) ? '&wmode=' . $params['wmode'] : ''; 76 | $url .= $max_width . $max_height . $wmode_param; 77 | 78 | // cache can be disabled by setting 0 as the cache_minutes param 79 | if (isset($params['cache_minutes']) && $params['cache_minutes'] !== false && is_numeric($params['cache_minutes'])) 80 | { 81 | $cache_refresh_minutes = $params['cache_minutes']; 82 | } 83 | 84 | // optional provider prefixed parameters 85 | $providerExtraParams = []; 86 | if ($isVimeo) 87 | { 88 | $providerExtraParams = $this->getPrefixedParams($params, 'vimeo_'); 89 | } 90 | elseif ($isWistia) 91 | { 92 | $providerExtraParams = $this->getPrefixedParams($params, 'wistia_'); 93 | 94 | // handle legacy shortcuts 95 | if (isset($providerExtraParams['type'])) 96 | { 97 | $providerExtraParams['embedType'] = $providerExtraParams['type']; 98 | unset($providerExtraParams['type']); 99 | } 100 | if (isset($providerExtraParams['foam'])) 101 | { 102 | $providerExtraParams['videoFoam'] = $providerExtraParams['foam']; 103 | unset($providerExtraParams['foam']); 104 | } 105 | } 106 | elseif ($isViddler) 107 | { 108 | $providerExtraParams = $this->getPrefixedParams($params, 'viddler_'); 109 | } 110 | if (!empty($providerExtraParams)) 111 | { 112 | $url .= '&' . $this->makeUrlKeyValuePairsString($providerExtraParams); 113 | } 114 | 115 | // checking if url has been cached 116 | $cached_url = Craft::$app->cache->get($url); 117 | 118 | if (!$cache_refresh_minutes || $is_cache_expired || !$cached_url) 119 | { 120 | // create the info and header variables 121 | list($video_info, $video_header) = $this->getVideoInfo($url); 122 | 123 | // write the data to cache if caching hasn't been disabled 124 | if ($cache_refresh_minutes) 125 | { 126 | Craft::$app->cache->set($url, $video_info, $cache_refresh_minutes); 127 | } 128 | } 129 | else 130 | { 131 | $video_info = $cached_url; 132 | } 133 | 134 | // decode the cURL data 135 | libxml_use_internal_errors(true); 136 | 137 | $video_info = simplexml_load_string($video_info); 138 | 139 | // gracefully fail if the video is not found 140 | if ($video_info === false) 141 | { 142 | return $output === 'simple' ? 'Video not found' : $video_data; 143 | } 144 | 145 | // inject wmode transparent if required 146 | $wmode = $params['wmode'] ?? ''; 147 | if ($wmode === 'transparent' || $wmode === 'opaque' || $wmode === 'window') 148 | { 149 | $param_str = ''; 150 | $embed_str = ' wmode="' . $wmode . '" '; 151 | 152 | // determine whether we are dealing with iframe or embed and handle accordingly 153 | if (strpos($video_info->html, 'html, 'html = substr($video_info->html, 0, $param_pos) . $param_str . substr($video_info->html, $param_pos); 157 | $param_pos = strpos($video_info->html, 'html = substr($video_info->html, 0, $param_pos) . $embed_str . substr($video_info->html, $param_pos); 159 | } 160 | else 161 | { 162 | // determine whether to add question mark to query string 163 | preg_match('//i', $video_info->html, $matches); 164 | $append_query_marker = (strpos($matches[1], '?') !== false ? '' : '?'); 165 | 166 | $video_info->html = preg_replace('//i', '', $video_info->html); 167 | } 168 | } 169 | 170 | // add in the YouTube-specific params 171 | if ($isYouTube) 172 | { 173 | // Check for privacy-enhanced mode 174 | if (isset($params['youtube_privacyEnhanced'])) 175 | { 176 | unset($params['youtube_privacyEnhanced']); 177 | $video_info->html = str_replace('youtube.com', 'youtube-nocookie.com', $video_info->html); 178 | } 179 | 180 | $youTubeParams = $this->getPrefixedParams($params, 'youtube_'); 181 | if (!empty($youTubeParams)) 182 | { 183 | // handle any YouTube-specific param updates 184 | if (isset($youTubeParams['playlist'])) 185 | { 186 | // if the playlist is set to a url and not an id, then try to update it 187 | // regex from https://stackoverflow.com/a/26660288/1136822 188 | $value = $youTubeParams['playlist']; 189 | if (preg_match("#([\/|\?|&]vi?[\/|=]|youtu\.be\/|embed\/)(\w+)#", $value, $matches)) 190 | { 191 | $youTubeParams['playlist'] = $matches[2]; 192 | } 193 | } 194 | 195 | // work the params into the embed URL 196 | preg_match('/.*?src="(.*?)".*?/', $video_info->html, $matches); 197 | if (!empty($matches[1])) 198 | { 199 | $video_info->html = str_replace($matches[1], $matches[1] . '&' . $this->makeUrlKeyValuePairsString($youTubeParams), $video_info->html); 200 | } 201 | } 202 | } 203 | 204 | // add the vimeo_player_id or id param value to the iFrame HTML if set 205 | $id = ''; 206 | if (!empty($params['vimeo_player_id'])) 207 | { 208 | $id = $params['vimeo_player_id']; 209 | } 210 | elseif (!empty($params['id'])) 211 | { 212 | $id = $params['id']; 213 | } 214 | if (!empty($id)) 215 | { 216 | $video_info->html = preg_replace('/