YouTube media tracking on web
This plugin enables the automatic tracking of an embedded YouTube iFrame video, using the YouTube Player API.
It uses the Snowplow Media plugin under the hood.
YouTube media events and entities are automatically tracked once configured.
Install plugin
- JavaScript (tag)
- Browser (npm)
| Tracker Distribution | Included |
|---|---|
sp.js | ❌ |
sp.lite.js | ❌ |
Download:
| Download from GitHub Releases (Recommended) | Github Releases (plugins.umd.zip) |
| Available on jsDelivr | jsDelivr (latest) |
| Available on unpkg | unpkg (latest) |
npm install @snowplow/browser-plugin-youtube-trackingyarn add @snowplow/browser-plugin-youtube-trackingpnpm add @snowplow/browser-plugin-youtube-tracking
Quick Start
The snippets below show how to get started with the plugin, after setting up your tracker.
Call startYouTubeTracking to begin tracking a YouTube video player, and endYouTubeTracking to stop tracking it.
- JavaScript (tag) - iFrame
- Browser (npm) - iFrame
- JavaScript (tag) - YT.Player
- Browser (npm) - YT.Player
<!DOCTYPE html>
<html>
<body>
<iframe
id="yt-player"
src="https://www.youtube.com/embed/zSM4ZyVe8xs"
></iframe>
<script>
window.snowplow(
'addPlugin',
'https://cdn.jsdelivr.net/npm/@snowplow/browser-plugin-youtube-tracking@latest/dist/index.umd.min.js',
['snowplowYouTubeTracking', 'YouTubeTrackingPlugin']
);
var mediaSessionId = crypto.randomUUID();
window.snowplow('startYouTubeTracking', {
id: mediaSessionId,
video: 'yt-player' // or: document.getElementById('yt-player')
});
window.snowplow('endYouTubeTracking', mediaSessionId);
</script>
</body>
</html>
<iframe
id="yt-player"
src="https://www.youtube.com/embed/zSM4ZyVe8xs"
></iframe>
import { newTracker, trackPageView } from '@snowplow/browser-tracker';
import { YouTubeTrackingPlugin, startYouTubeTracking } from '@snowplow/browser-plugin-youtube-tracking';
newTracker('sp1', '{{collector_url}}', {
appId: 'my-app-id',
plugins: [ YouTubeTrackingPlugin() ],
});
const mediaSessionId = startYouTubeTracking({
id: crypto.randomUUID(),
video: 'yt-player' // or: document.getElementById('yt-player')
})
endYouTubeTracking(mediaSessionId);
<!DOCTYPE html>
<html>
<body>
<div id="yt-player"></div>
<script>
window.snowplow(
'addPlugin',
'https://cdn.jsdelivr.net/npm/@snowplow/browser-plugin-youtube-tracking@latest/dist/index.umd.min.js',
['snowplowYouTubeTracking', 'YouTubeTrackingPlugin']
);
const player = new YT.Player('yt-player', {
videoId: 'zSM4ZyVe8xs'
});
const mediaSessionId = crypto.randomUUID();
window.snowplow('startYouTubeTracking', {
id: mediaSessionId,
video: player
});
window.snowplow('endYouTubeTracking', mediaSessionId);
</script>
</body>
</html>
<div id="yt-player"></div>
import { newTracker, trackPageView } from '@snowplow/browser-tracker';
import { YouTubeTrackingPlugin, startYouTubeTracking } from '@snowplow/browser-plugin-youtube-tracking';
newTracker('sp1', '{{collector_url}}', {
appId: 'my-app-id',
plugins: [ YouTubeTrackingPlugin() ],
});
const player = new YT.Player('yt-player', {
videoId: 'zSM4ZyVe8xs'
});
const mediaSessionId = startYouTubeTracking({
id: crypto.randomUUID(),
video: player
})
endYouTubeTracking(mediaSessionId);
Start tracking
The startYouTubeTracking function is used to begin auto-tracking a YouTube Player instance. You must provide a unique id for the media session, along with the video ID.
It installs the iFrame API if necessary, adds event listeners and any poll intervals, and then calls startMediaTracking from the underlying Snowplow Media plugin.
The function returns the mediaSessionId used for the events that will be generated.
| Parameter | Type | Description | Default / Required |
|---|---|---|---|
id | string | A unique session UUID for each media element, used to identify and end tracking. | Required |
video | VideoLocation | A DOM ID for the iFrame element hosting the player, an iFrame element, or a pre-existing YT.Player instance to track events for. | Required |
label | string | A custom human-readable label for the media element. | undefined |
captureEvents | CapturableEvents[] | A list of media events to track. | All events tracked by default |
boundaries | number[] | Percentage thresholds (0-100) to trigger progress events. | [10, 25, 50, 75] |
context | DynamicContext | Dynamic contexts attached to each tracking event. | undefined |
updatePageActivityWhilePlaying | boolean | Whether to update page activity while media is playing. | true |
filterOutRepeatedEvents | FilterOutRepeatedEvents | Whether to suppress consecutive identical events. | Enabled by default |
filterOutRepeatedEvents .seekEvents | boolean | Whether to filter out seek start and end events tracked after each other. | true |
filterOutRepeatedEvents .volumeChangeEvents | boolean | Whether to filter out volume change events tracked after each other. | true |
filterOutRepeatedEvents .flushTimeoutMs | number | Timeout in milliseconds after which to send the events that are queued for filtering. | 5000 (milliseconds) |
pings.pingInterval | number | Interval (in seconds) for sending ping events. | 30 (seconds) |
pings.maxPausedPings | number | Maximum number of ping events sent while playback is paused. | 1 |
session | boolean or object | Whether to track the media session entity for playback statistics, or set a custom start time. | true |
Stop tracking
The endYouTubeTracking function disables auto tracking for the player registered with the provided session ID.
It will remove any event listeners and poll intervals, and call endMediaTracking from the core plugin.
Events
Below is a table of all the core Snowplow Media plugin events that can be used in the captureEvents option:
| Name | Fire condition |
|---|---|
ready | The video player has loaded |
play | The video is played |
pause | The video is paused |
end | When playback stops at the end of the video |
seek_start | When seeking begins to jump to another position of the video |
seek_end | When seeking ends and another position of the video is jumped to |
playback_rate_change | Playback rate has changed |
volume_change | Volume has changed |
ping | Fires periodically during playback |
percent_progress | Fires at progress milestones defined by boundaries option |
buffer_start | Fires when playback pauses because content is not yet buffered |
buffer_end | Fires when playback resumes after content has been fetched |
quality_change | Playback quality has changed |
error | An error occurs in the player |
In addition, the following event names are also accepted for compatibility with earlier tracker versions, mapped to the equivalent event from above:
| Name | Fire condition |
|---|---|
seek | On seek |
volumechange | Volume has changed |
ended | When playback stops at the end of the video |
percentprogress | When a percentage boundary set in options.boundaries is reached |
playbackratechange | Playback rate has changed |
playbackqualitychange | Playback quality has changed |
You can also use a pre-made event group names in captureEvents:
| Name | Events |
|---|---|
DefaultEvents | ['ready', 'play', 'pause', 'ping', 'end', 'seek_start', 'seek_end', 'volume_change', 'percent_progress', 'playback_rate_change', 'quality_change'] |
AllEvents | Every supported event in the Events table |
It's possible to extend an event group with any event in the Events table above. This could be useful if you want, for example, all the events contained in the DefaultEvents group, along with the error event. This is expressed in the following way:
- JavaScript (tag)
- Browser (npm)
window.snowplow('startYouTubeTracking', {
id: crypto.randomUUID(),
video: "example-video",
captureEvents: ["DefaultEvents", "error"],
})
startYouTubeTracking({
id: crypto.randomUUID(),
video: 'example-video',
captureEvents: ['DefaultEvents', 'error'],
})
Unsupported events
The following events are defined by the core Snowplow Media plugin but aren't supported by the YouTube API, and so cannot be captured by this plugin:
| Name | Fire condition |
|---|---|
fullscreen_change | Full screen state toggled |
picture_in_picture_change | Picture-in-picture state toggled |
ad_break_start | Beginning of an ad break |
ad_break_end | End of an ad break |
ad_start | Beginning of an ad within an ad break |
ad_first_quartile | 25% progress through an ad |
ad_midpoint | 50% progress through an ad |
ad_third_quartile | 75% progress through an ad |
ad_complete | 100% progress through an ad |
ad_skip | User has opted to skip the ad before completion |
ad_click | User has clicked the playing ad |
ad_pause | User has paused the playing ad |
ad_resume | User has resumed the paused ad |
Add media entities to custom events
Use the trackYouTubeSelfDescribingEvent function to track custom event payloads that have the media-related entities attached as if they were generated by this or the core plugin directly.
- JavaScript (tag) - iFrame
- Browser (npm) - iFrame
<!DOCTYPE html>
<html>
<body>
<iframe
id="yt-player"
src="https://www.youtube.com/embed/zSM4ZyVe8xs"
></iframe>
<script>
window.snowplow(
'addPlugin',
'https://cdn.jsdelivr.net/npm/@snowplow/browser-plugin-youtube-tracking@latest/dist/index.umd.min.js',
['snowplowYouTubeTracking', 'YouTubeTrackingPlugin']
);
var mediaSessionId = crypto.randomUUID();
window.snowplow('startYouTubeTracking', {
id: mediaSessionId,
video: 'yt-player' // or: document.getElementById('yt-player')
});
window.snowplow('trackYouTubeSelfDescribingEvent', {event: {schema: 'iglu:com_example/my_event/jsonschema/1-0-0', data: { video: true }}});
window.snowplow('endYouTubeTracking', mediaSessionId);
</script>
</body>
</html>
<iframe
id="yt-player"
src="https://www.youtube.com/embed/zSM4ZyVe8xs"
></iframe>
import { newTracker, trackPageView } from '@snowplow/browser-tracker';
import { YouTubeTrackingPlugin, endYouTubeTracking, startYouTubeTracking } from '@snowplow/browser-plugin-youtube-tracking';
newTracker('sp1', '{{collector_url}}', {
appId: 'my-app-id',
plugins: [ YouTubeTrackingPlugin() ],
});
const mediaSessionId = startYouTubeTracking({
id: crypto.randomUUID(),
video: 'yt-player' // or: document.getElementById('yt-player')
});
trackYouTubeSelfDescribingEvent({event: {schema: 'iglu:com_example/my_event/jsonschema/1-0-0', data: { video: true }}});
endYouTubeTracking(mediaSessionId);
YouTube player entity
The event and entity schemas are the same as for the Snowplow Media plugin.
Along with the standard Snowplow media entities, the YouTube media plugin also adds a youtube entity to all media events.
If you've configured the Media Player dbt package to use the v1 schemas, or are using an older version of the Media Player model, it will use fields from the youtube entity. The Media Player model version 0.6+ doesn't use this entity.
youtube
EntityExample
{
"autoPlay": false,
"avaliablePlaybackRates": [
0.25,
0.5,
0.75,
1,
1.25,
1.5,
1.75,
2
],
"buffering": false,
"controls": true,
"cued": false,
"loaded": 17,
"playbackQuality": "hd1080",
"playerId": "example-id",
"unstarted": false,
"url": "https://www.youtube.com/watch?v=zSM4ZyVe8xs",
"yaw": 0,
"pitch": 0,
"roll": 0,
"fov": 100.00004285756798,
"avaliableQualityLevels": [
"hd2160",
"hd1440",
"hd1080",
"hd720",
"large",
"medium",
"small",
"tiny",
"auto"
]
}
Properties and schema
- Table
- JSON schema
| Property | Description |
|---|---|
avaliablePlaybackRatesarray | Required. An array of playback rates in which the current video is available |
avaliableQualityLevelsarray | Optional. An array of quality levels in which the current video is available |
cuedboolean | Required. If the video is cued |
playerIdstring | Required. The HTML id of the video element |
autoPlayboolean | Required. This specifies whether the initial video will automatically start to play when the player loads. |
bufferingboolean | Required. If the player is buffering |
controlsboolean | Required. Whether the video player controls are displayed |
errorstring | Optional. A string of the latest error to occur, or null if no errors Must be one of: INVALID_PARAMETER, HTML5_PLAYER_ERROR, NOT_FOUND, EMBED_DISALLOWED |
loadedinteger | Required. The percentage of the video that the player shows as buffered |
originstring | Optional. The origin domain of the embed |
playbackQualitystring | Required. The quality level of the current video |
playlistarray | Optional. An array of the video IDs in the playlist as they are currently ordered. |
playlistIndexnumber | Optional. The index of the playlist video that is currently playing |
unstartedboolean | Required. If the player hasn't started |
urlstring | Required. The YouTube embed URL of the media resource |
fovnumber | Optional. The field-of-view of the view in degrees, as measured along the longer edge of the viewport |
rollnumber | Optional. The clockwise or counterclockwise rotational angle of the view in degrees |
pitchnumber | Optional. The vertical angle of the view in degrees |
yawnumber | Optional. The horizontal angle of the view in degrees |
{
"$schema": "http://iglucentral.com/schemas/com.snowplowanalytics.self-desc/schema/jsonschema/1-0-0#",
"description": "Context Schema for a youtube player event",
"self": {
"vendor": "com.youtube",
"name": "youtube",
"format": "jsonschema",
"version": "1-0-0"
},
"type": "object",
"properties": {
"avaliablePlaybackRates": {
"type": "array",
"description": "An array of playback rates in which the current video is available",
"items": {
"type": "number",
"minimum": 0,
"maximum": 128
}
},
"avaliableQualityLevels": {
"type": [
"array",
"null"
],
"description": "An array of quality levels in which the current video is available",
"items": {
"type": "string",
"maxLength": 128
}
},
"cued": {
"type": "boolean",
"description": "If the video is cued"
},
"playerId": {
"type": "string",
"description": "The HTML id of the video element",
"maxLength": 65535
},
"autoPlay": {
"type": "boolean",
"description": "This specifies whether the initial video will automatically start to play when the player loads."
},
"buffering": {
"type": "boolean",
"description": "If the player is buffering"
},
"controls": {
"type": "boolean",
"description": "Whether the video player controls are displayed"
},
"error": {
"type": [
"string",
"null"
],
"description": "A string of the latest error to occur, or null if no errors",
"enum": [
"INVALID_PARAMETER",
"HTML5_PLAYER_ERROR",
"NOT_FOUND",
"EMBED_DISALLOWED"
]
},
"loaded": {
"type": "integer",
"description": "The percentage of the video that the player shows as buffered",
"minimum": 0,
"maximum": 100
},
"origin": {
"type": [
"string",
"null"
],
"description": "The origin domain of the embed",
"maxLength": 65535
},
"playbackQuality": {
"type": "string",
"description": "The quality level of the current video",
"maxLength": 128
},
"playlist": {
"type": [
"array",
"null"
],
"description": "An array of the video IDs in the playlist as they are currently ordered."
},
"playlistIndex": {
"type": [
"number",
"null"
],
"description": "The index of the playlist video that is currently playing",
"minimum": 0,
"maximum": 65535
},
"unstarted": {
"type": "boolean",
"description": "If the player hasn't started"
},
"url": {
"type": "string",
"description": "The YouTube embed URL of the media resource",
"maxLength": 65535,
"format": "uri"
},
"fov": {
"type": [
"number",
"null"
],
"description": "The field-of-view of the view in degrees, as measured along the longer edge of the viewport",
"minimum": 30,
"maximum": 120
},
"roll": {
"type": [
"number",
"null"
],
"description": "The clockwise or counterclockwise rotational angle of the view in degrees",
"minimum": -180,
"maximum": 180
},
"pitch": {
"type": [
"number",
"null"
],
"description": "The vertical angle of the view in degrees",
"minimum": -90,
"maximum": 90
},
"yaw": {
"type": [
"number",
"null"
],
"description": "The horizontal angle of the view in degrees",
"minimum": 0,
"maximum": 360
}
},
"additionalProperties": false,
"required": [
"avaliablePlaybackRates",
"autoPlay",
"buffering",
"controls",
"cued",
"loaded",
"playbackQuality",
"playerId",
"unstarted",
"url"
]
}
Legacy API
In addition to the above, the following methods are provided for compatibility with earlier versions of the plugin.
These APIs are deprecated in tracker version 4, and wrap the newer API methods above.
Enable tracking
The enableYouTubeTracking function is similar to startYouTubeTracking, except:
- An
idoption is used to identify the player instead ofvideo - The additional options to
startMediaTrackingare nested within anoptionsobject - The media session ID value will be generated automatically if not provided
id option is equivalent to video in the new API and will accept:- The
idof aniframeelement - An
iframeelement directly - An existing instance of
YT.Player, created with the YouTube Iframe API
The enableYouTubeTracking function takes the form:
- JavaScript (tag)
- Browser (npm)
window.snowplow("enableYouTubeTracking", { id, options?: { label?, captureEvents?, boundaries?, updateRate? } })
enableYouTubeTracking({ id, options?: { label?, captureEvents?, boundaries?, updateRate? } })
| Parameter | Type | Default | Description | Required |
|---|---|---|---|---|
id | string or YT.Player | - | The HTML id attribute of the media element | Yes |
options.label | string | - | An identifiable custom label sent with the event | No |
options.captureEvents | string[] | ['DefaultEvents'] | The events or Event Group to capture. For a full list of events and groups, check the section below | No |
options.boundaries | number[] | [10, 25, 50, 75] | The progress percentages to fire an event at (valid values 1 - 99 inclusive) | No |
options.* | any | Any other options are passed through to startMediaTracking | No |
Below is an example of the full enableYouTubeTracking function:
- JavaScript (tag)
- Browser (npm)
window.snowplow('enableYouTubeTracking', {
id: 'example-video',
options: {
label: 'My Custom Video Label',
captureEvents: ['play', 'pause', 'ended'],
boundaries: [20, 80],
updateRate: 500,
}
})
enableYouTubeTracking({
id: 'example-video',
options: {
label: 'My Custom Video Label',
captureEvents: ['play', 'pause', 'ended'],
boundaries: [20, 80],
updateRate: 200,
}
})
Disable tracking
The disableYouTubeTracking function is an equivalent counterpart to endYouTubeTracking for enableYouTubeTracking.
Instead of requiring the session ID to be provided, it will remove the oldest of any sessions started with enableYouTubeTracking so the session ID doesn't need to be known and passed explicitly.