Usage
nebula()->video_meta($provider, $id)
Parameters
$provider
(Required) (String) Which video service to check
Default: None
$id
(Required) (String) The ID of the video to get metadata for
Default: None
Examples
Trackable Youtube video iframe
<?php $youtube_data = nebula()->video_meta('youtube', 'fjh61K3hyY0'); ?> <div class="embed-responsive embed-responsive-16by9"> <iframe id="<?php echo $youtube_data['id']; ?>" class="youtube embed-responsive-item" width="560" height="315" title="<?php echo $youtube_data['title']; ?>" src="//www.youtube.com/embed/<?php echo $youtube_data['id']; ?>?wmode=transparent&enablejsapi=1&rel=0" allow="autoplay"></iframe> </div>
Trackable Vimeo video iframe
<?php $vimeo_data = nebula()->video_meta('vimeo', '208432684'); ?> <div class="embed-responsive embed-responsive-16by9"> <iframe id="<?php echo $vimeo_data['id']; ?>" class="vimeo embed-responsive-item" src="https://player.vimeo.com/video/<?php echo $vimeo_data['id']; ?>" width="560" height="315"></iframe> </div>
Demo
Youtube
See nebulaYoutubeTracking()
for available Nebula data for Youtube videos.
Note: the enablejsapi=1
query parameter is needed for tracking to work. Nebula adds it automatically, but it is significantly faster to include it manually.
Note: it is recommended to use a title
attribute on the iframe to enhance analytics reports with a video title (rather than a video ID).
Note: iframes can be lazy loaded with either hard-coded HTML (nebula-lazy class on noscript tag) or using the lazy_load()
function (or one of its aliases).
Regular Youtube Video
It’s amazing to see all the creative, inspiring ways people are coming together right now. This is for our hometown.
Lazy-Loaded Youtube Video
This video was lazy-loaded.
Pinckney Hugo Group is a fully integrated ad agency. We take our work seriously, but not ourselves. We work ridiculous hours. We play well with others. We're good listeners. We pride ourselves on accomplishing what others can't. And we love what we do.
Youtube Video w/o JS API
Available Youtube Data
{ "kind": "youtube#video", "etag": "TqNxjBeaiQMm31JPUFJL7jzvQfw", "id": "KfJ3XcAiOJY", "snippet": { "publishedAt": "2021-08-31T13:48:17Z", "channelId": "UCrO9BZ4TaJ4taT91y9l1oCg", "title": "Hi Rochester.", "description": "We\u2019ve been doing work in the Rochester area since the start of PHG\u2014so this only made sense.\n\nTo learn more, visit: https:\/\/www.pinckneyhugogroup.com\/pinckney-hugo-group-expands-syracuse-headquarters-opens-rochester-office\/", "thumbnails": { "default": { "url": "https:\/\/i.ytimg.com\/vi\/KfJ3XcAiOJY\/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https:\/\/i.ytimg.com\/vi\/KfJ3XcAiOJY\/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https:\/\/i.ytimg.com\/vi\/KfJ3XcAiOJY\/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https:\/\/i.ytimg.com\/vi\/KfJ3XcAiOJY\/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https:\/\/i.ytimg.com\/vi\/KfJ3XcAiOJY\/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "Pinckney Hugo Group", "categoryId": "22", "liveBroadcastContent": "none", "localized": { "title": "Hi Rochester.", "description": "We\u2019ve been doing work in the Rochester area since the start of PHG\u2014so this only made sense.\n\nTo learn more, visit: https:\/\/www.pinckneyhugogroup.com\/pinckney-hugo-group-expands-syracuse-headquarters-opens-rochester-office\/" }, "defaultAudioLanguage": "en-US" }, "contentDetails": { "duration": "PT41S", "dimension": "2d", "definition": "hd", "caption": "false", "licensedContent": false, "contentRating": {}, "projection": "rectangular" }, "statistics": { "viewCount": "595", "favoriteCount": "0" } }
Vimeo
See nebulaVimeoTracking()
for available Nebula data for Vimeo videos.
Note: Nebula tracking prefers the Vimeo ID to be entered as a data attribute data-vimeo-id
to match the Vimeo video ID (which will be a number). It also accepts this as the iframe ID and ultimately modifies the ID itself. If needing to select the iframe with CSS, you'll need to escape the ID selector.
Note: the query parameters api
and player_id
are no longer needed. In fact, they will prevent video tracking from working, so they must be removed!
Note: iframes can be lazy loaded with either hard-coded HTML (nebula-lazy class on noscript tag) or using the lazy_load()
function (or one of its aliases).
Produced by: Gabe Langlois, Tanner McCarty, Jake Blauvelt
Directed and Edited by: Gabe Langlois
Principal Cinematography: Gabe Langlois, Paul Watt, Brandon Kelly
Aerial Cinematography: Gabe Langlois
Camera Assistant / Production Assistant: Christian Bertrand
Colour: David Tomiak /Silver Lining Post
Sound Mix: Keith White / Keith White Audio
Graphic Design: Javas Lehn
Featuring: jake Blauvelt, Shayne Pospisil, Jake Welch
A very special thanks to Tanner McCarty - Ride Snowboards, Andrew Goggins - Ten Barrel Brewing, Mike Last - Sigma Canada
A special thanks:
Leah Langlois
Kristen Blauvelt
The Langlois family
The Blauvelt family
Shin Campos
Stuart Andrews
Abby Andrew
Paul Watt
Christian Bertrand
Cholo Burns
Jenna Burns-Low
Adidas Snowboarding
Smith Optics
The North Face
Purl Wax
Salt and Stone
Rube and Sam Goldburg
Brandon Kelly
TBird
Dave Seaton
Lorne Lapham
Cole Brash
Shayne Pospisil
Jake Welch
Leanne Pelosi
Robin Van Gyn
Austin Sweetin
Skye Sheeley
WCP
TL City Locals
The Mighty Selkirks
This video was lazy-loaded.
Animation by Drew Tyndell
Music by Shannon Ferguson
Sound Production by Mooj Zadie
Special Thanks to Kevin Ferguson
California Inspires Me is a collaboration between Google Play and California Sunday Magazine.
Available Vimeo Data
{ "id": 132454664, "title": "California Inspires Me: Reggie Watts", "description": "Animation by Drew Tyndell<br \/>\r\nMusic by Shannon Ferguson<br \/>\r\nSound Production by Mooj Zadie<br \/>\r\nSpecial Thanks to Kevin Ferguson<br \/>\r\n<br \/>\r\nCalifornia Inspires Me is a collaboration between Google Play and California Sunday Magazine.", "url": "https:\/\/vimeo.com\/132454664", "upload_date": "2015-07-02 12:46:22", "thumbnail_small": "https:\/\/i.vimeocdn.com\/video\/525124557-b80ce9983768edc012c970c11750c09a2bd9b629c28180c4569eaa2b94558d9e-d_100x75", "thumbnail_medium": "https:\/\/i.vimeocdn.com\/video\/525124557-b80ce9983768edc012c970c11750c09a2bd9b629c28180c4569eaa2b94558d9e-d_200x150", "thumbnail_large": "https:\/\/i.vimeocdn.com\/video\/525124557-b80ce9983768edc012c970c11750c09a2bd9b629c28180c4569eaa2b94558d9e-d_640", "user_id": 2624480, "user_name": "Drew Tyndell", "user_url": "https:\/\/vimeo.com\/drewtyndell", "user_portrait_small": "https:\/\/i.vimeocdn.com\/portrait\/20098251_30x30?sig=81e88db8cd96eef4fee1c450aa8d7a56c3e4d414cc3d6d8c1dead98bad462d89&v=1", "user_portrait_medium": "https:\/\/i.vimeocdn.com\/portrait\/20098251_75x75?sig=81e88db8cd96eef4fee1c450aa8d7a56c3e4d414cc3d6d8c1dead98bad462d89&v=1", "user_portrait_large": "https:\/\/i.vimeocdn.com\/portrait\/20098251_100x100?sig=81e88db8cd96eef4fee1c450aa8d7a56c3e4d414cc3d6d8c1dead98bad462d89&v=1", "user_portrait_huge": "https:\/\/i.vimeocdn.com\/portrait\/20098251_300x300?sig=81e88db8cd96eef4fee1c450aa8d7a56c3e4d414cc3d6d8c1dead98bad462d89&v=1", "stats_number_of_likes": 3899, "stats_number_of_plays": 107859, "stats_number_of_comments": 41, "duration": 225, "width": 1920, "height": 1080, "tags": "Computer Team, Drew Tyndell, Kevin Ferguson, Mooj Zadie, Shannon Ferguson, Google Play, Reggie Watts, California Sunday Magazine", "embed_privacy": "anywhere" }
HTML5
video_meta() is not used with HTML5 videos, but for demo purposes, Google Analytics tracking, and MediaSession API it is shown here for reference. See nebulaHTML5VideoTracking()
for available Nebula data for HTML5 videos.
iOS requires videos to be muted for autoplay
to work. Nebula will not send events to Google Analytics for videos that have both autoplay
and loop
attributes.
This video was lazy-loaded.
Additional Notes
This function returns an array that includes data such as “raw”, “title”, “safetitle”, “description”, “thumbnail”, “author”, “date”, “url”, “duration” (which is an array of “time” and “seconds”).
This function includes two aliases: vimeo_meta()
and youtube_meta()
.
Sidenote: Remember that Youtube and Vimeo have their own APIs for JavaScript integration. It is recommended to read through these as there are many intricate nuances in each.
Source File
Located in /libs/Functions.php on line 1348.
1 Hook
Find these filters and actions in the source code below to hook into them. Use do_action() and add_filter() in your functions file or plugin.
Filters
"pre_video_meta"Need a new filter hook? Request one here.
Actions
This function has no action hooks available. Request one?public function video_meta($provider, $id){ $override = apply_filters('pre_video_meta', null, $provider, $id); if ( isset($override) ){return $override;} $timer_name = $this->timer('Video Meta (' . $id . ')', 'start', 'Video Meta'); $video_metadata = array( 'origin' => $this->url_components('basedomain'), 'id' => $id, 'error' => false ); if ( !empty($provider) ){ $provider = strtolower($provider); } else { $video_metadata['error'] = 'Video provider is required.'; $this->timer($timer_name, 'end'); return $video_metadata; } //Get Transients $video_json = nebula()->transient('nebula_' . $provider . '_' . $id, function($data){ if ( $data['provider'] === 'youtube' ){ if ( !$this->get_option('google_server_api_key') && $this->is_staff() ){ trigger_error('No Google Youtube Iframe API key. Youtube videos may not be tracked!', E_USER_WARNING); echo '<script>console.warn("No Google Youtube Iframe API key. Youtube videos may not be tracked!");</script>'; $video_metadata['error'] = 'No Google Youtube Iframe API key.'; } $response = $this->remote_get('https://www.googleapis.com/youtube/v3/videos?id=' . $data['id'] . '&part=snippet,contentDetails,statistics&key=' . $this->get_option('google_server_api_key')); if ( is_wp_error($response) ){ trigger_error('Youtube video is unavailable.', E_USER_WARNING); $video_metadata['error'] = 'Youtube video is unavailable.'; $this->timer($data['timer_name'], 'end'); return $video_metadata; } $video_json = $response['body']; } elseif ( $data['provider'] === 'vimeo' ){ $response = $this->remote_get('http://vimeo.com/api/v2/video/' . $data['id'] . '.json'); if ( is_wp_error($response) ){ trigger_error('Vimeo video is unavailable.', E_USER_WARNING); $video_metadata['error'] = 'Vimeo video is unavailable.'; $this->timer($data['timer_name'], 'end'); return $video_metadata; } $video_json = $response['body']; } return $video_json; }, array('provider' => $provider, 'id' => $id, 'timer_name' => $timer_name), HOUR_IN_SECONDS*12); if ( !is_array($video_json) ){ //If it is not already an array, decode it from the JSON string $video_json = json_decode($video_json); } //Check for errors if ( empty($video_json) ){ if ( current_user_can('manage_options') || $this->is_dev() ){ if ( $provider === 'youtube' ){ $video_metadata['error'] = 'A Youtube Data API error occurred. Make sure the Youtube Data API is enabled in the Google Developer Console and the server key is saved in Nebula Options.'; } else { $video_metadata['error'] = 'A Vimeo API error occurred (A video with ID ' . $id . ' may not exist). Tracking will not be possible.'; } } $this->timer($timer_name, 'end'); return $video_metadata; } elseif ( $provider === 'youtube' && !empty($video_json->error) ){ if ( current_user_can('manage_options') || $this->is_dev() ){ $video_metadata['error'] = 'Youtube API Error: ' . $video_json->error->message; } $this->timer($timer_name, 'end'); return $video_metadata; } elseif ( $provider === 'youtube' && empty($video_json->items) ){ if ( current_user_can('manage_options') || $this->is_dev() ){ $video_metadata['error'] = 'A Youtube video with ID ' . $id . ' does not exist.'; } $this->timer($timer_name, 'end'); return $video_metadata; } elseif ( $provider === 'vimeo' && is_array($video_json) && empty($video_json[0]) ){ $video_metadata['error'] = 'A Vimeo video with ID ' . $id . ' does not exist.'; } //Build Data if ( $provider === 'youtube' ){ $video_metadata['raw'] = $video_json->items[0]; $video_metadata['title'] = $video_json->items[0]->snippet->title; $video_metadata['safetitle'] = preg_replace('/(\W)/i', '', $video_json->items[0]->snippet->title); $video_metadata['description'] = $video_json->items[0]->snippet->description; $video_metadata['thumbnail'] = $video_json->items[0]->snippet->thumbnails->high->url; $video_metadata['author'] = $video_json->items[0]->snippet->channelTitle; $video_metadata['date'] = $video_json->items[0]->snippet->publishedAt; $video_metadata['url'] = 'https://www.youtube.com/watch?v=' . $id; $start = new DateTime('@0'); //Unix epoch $start->add(new DateInterval($video_json->items[0]->contentDetails->duration)); $duration_seconds = intval($start->format('H'))*60*60 + intval($start->format('i'))*60 + intval($start->format('s')); } elseif ( $provider === 'vimeo' ){ $video_metadata['raw'] = $video_json[0]; $video_metadata['title'] = $video_json[0]->title; $video_metadata['safetitle'] = preg_replace('/(\W)/i', '', $video_json[0]->title); $video_metadata['description'] = $video_json[0]->description; $video_metadata['thumbnail'] = $video_json[0]->thumbnail_large; $video_metadata['author'] = $video_json[0]->user_name; $video_metadata['date'] = $video_json[0]->upload_date; $video_metadata['url'] = $video_json[0]->url; $duration_seconds = strval($video_json[0]->duration); } $video_metadata['duration'] = array( 'time' => intval(gmdate("i", $duration_seconds)) . gmdate(":s", $duration_seconds), 'seconds' => $duration_seconds ); $this->timer($timer_name, 'end'); return $video_metadata; }
Override
To override this PHP function, use this hook in your child theme or plugin ("my_custom" can be changed):
add_filter('pre_video_meta', 'my_custom_video_meta', 10, 3); //The last integer must be 1 more than the actual parameters function my_custom_video_meta($null, $provider, $id){ //$null is required, but can be ignored //Write your own code here return true; //Return true to prevent the original function from running afterwords }
You can completely disable this PHP function with a single line actions:
add_filter('pre_video_meta', '__return_false');