New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More →

@segment/analytics.js-video-plugins

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@segment/analytics.js-video-plugins - npm Package Compare versions

Comparing version

to
0.0.3

@@ -93,7 +93,7 @@ (function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap

\***********************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return VideoPlugin; });\nclass VideoPlugin {\n constructor(name, version) {\n this.pluginName = this.name\n this.packageName = name\n this.version = version\n }\n \n track(event, properties) {\n window.analytics.track(event, properties, {\n integration: {\n name: this.packageName,\n version: this.version\n }\n })\n }\n}\n\n//# sourceURL=webpack:///./lib/plugin.js?");
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar VideoPlugin = function () {\n function VideoPlugin(name, version) {\n _classCallCheck(this, VideoPlugin);\n\n this.pluginName = this.name;\n this.packageName = name;\n this.version = version;\n }\n\n _createClass(VideoPlugin, [{\n key: \"track\",\n value: function track(event, properties) {\n window.analytics.track(event, properties, {\n integration: {\n name: this.packageName,\n version: this.version\n }\n });\n }\n }]);\n\n return VideoPlugin;\n}();\n\nexports.default = VideoPlugin;\n\n//# sourceURL=webpack:///./lib/plugin.js?");

@@ -118,7 +118,7 @@ /***/ }),

\**************************/
/*! exports provided: VimeoAnalytics, YouTubeAnalytics */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _vimeo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./vimeo */ \"./plugins/vimeo/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"VimeoAnalytics\", function() { return _vimeo__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _youtube__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./youtube */ \"./plugins/youtube/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"YouTubeAnalytics\", function() { return _youtube__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n\n\n\n\n\n//# sourceURL=webpack:///./plugins/index.js?");
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.YouTubeAnalytics = exports.VimeoAnalytics = undefined;\n\nvar _vimeo = __webpack_require__(/*! ./vimeo */ \"./plugins/vimeo/index.js\");\n\nvar _vimeo2 = _interopRequireDefault(_vimeo);\n\nvar _youtube = __webpack_require__(/*! ./youtube */ \"./plugins/youtube/index.js\");\n\nvar _youtube2 = _interopRequireDefault(_youtube);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nexports.VimeoAnalytics = _vimeo2.default;\nexports.YouTubeAnalytics = _youtube2.default;\n\n//# sourceURL=webpack:///./plugins/index.js?");

@@ -131,7 +131,7 @@ /***/ }),

\********************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return VimeoAnalytics; });\n/* harmony import */ var unfetch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! unfetch */ \"./node_modules/unfetch/dist/unfetch.es.js\");\n/* harmony import */ var _lib_plugin__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../lib/plugin */ \"./lib/plugin.js\");\n\n\n\nclass VimeoAnalytics extends _lib_plugin__WEBPACK_IMPORTED_MODULE_1__[\"default\"] {\n constructor(player, authToken) {\n super('VimeoAnalytics', 1)\n this.authToken = authToken\n this.player = player\n this.metadata = {\n content: {},\n playback: { videoPlayer: 'Vimeo' }\n }\n this.mostRecentHeartbeat = 0\n this.isPaused = false\n }\n\n initialize() {\n const events = {\n loaded: this.retrieveMetadata,\n play: this.trackPlay,\n pause: this.trackPause,\n ended: this.trackEnded,\n timeupdate: this.trackHeartbeat\n }\n\n for (event in events) {\n this.registerHandler(event, events[event])\n }\n }\n\n // This is a helper function used to facilitate easier testing.\n registerHandler(event, handler) {\n this.player.on(event, data => {\n this.updateMetadata(data)\n handler.call(this, data)\n })\n }\n\n trackPlay() {\n if (this.isPaused) {\n this.track('Video Playback Resumed', this.metadata.playback)\n this.isPaused = false\n } else {\n this.track('Video Playback Started', this.metadata.playback)\n this.track('Video Content Started', this.metadata.content)\n }\n }\n\n trackEnded() {\n this.track('Video Playback Completed', this.metadata.playback)\n this.track('Video Content Completed', this.metadata.content)\n }\n\n // Vimeo provides time updates every 250ms while Segment documents sending heartbeats every 10s.\n // Therefore, we need to do some math to ensure we are not sending 4 heartbeat events when we reach\n // 10 second intervals.\n trackHeartbeat() {\n const mostRecentHeartbeat = this.mostRecentHeartbeat\n const currentPosition = this.metadata.playback.position\n if (\n currentPosition !== mostRecentHeartbeat &&\n currentPosition - mostRecentHeartbeat >= 10\n ) {\n this.track('Video Content Playing', this.metadata.content)\n this.mostRecentHeartbeat = Math.floor(currentPosition)\n }\n }\n\n trackPause() {\n this.isPaused = true\n this.track('Video Playback Paused', this.metadata.playback)\n }\n\n // retrieve static video metadata from vimeo API\n retrieveMetadata(data) {\n return new Promise((resolve, reject) => {\n const videoId = data.id\n Object(unfetch__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(`https://api.vimeo.com/videos/${videoId}`, {\n headers: {\n Authorization: `Bearer ${this.authToken}`\n }\n })\n .then(res => {\n return res.json()\n })\n .then(json => {\n this.metadata.content.title = json.name\n this.metadata.content.description = json.description\n this.metadata.content.publisher = json.user.name\n\n this.metadata.playback.position = 0\n this.metadata.playback.totalLength = json.duration\n })\n .catch(err => {\n console.error('Request to Vimeo API failed with: ', err)\n return reject(err)\n })\n })\n\n }\n\n // update dynamic video information\n updateMetadata(data) {\n return new Promise((resolve, reject) => {\n this.player.getVolume().then(volume => {\n if (volume) this.metadata.playback.sound = volume * 100\n this.metadata.playback.position = data.seconds\n resolve()\n }).catch(reject)\n })\n }\n}\n\n//# sourceURL=webpack:///./plugins/vimeo/index.js?");
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _unfetch = __webpack_require__(/*! unfetch */ \"./node_modules/unfetch/dist/unfetch.es.js\");\n\nvar _unfetch2 = _interopRequireDefault(_unfetch);\n\nvar _plugin = __webpack_require__(/*! ../../lib/plugin */ \"./lib/plugin.js\");\n\nvar _plugin2 = _interopRequireDefault(_plugin);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar VimeoAnalytics = function (_Plugin) {\n _inherits(VimeoAnalytics, _Plugin);\n\n function VimeoAnalytics(player, authToken) {\n _classCallCheck(this, VimeoAnalytics);\n\n var _this = _possibleConstructorReturn(this, (VimeoAnalytics.__proto__ || Object.getPrototypeOf(VimeoAnalytics)).call(this, 'VimeoAnalytics', 1));\n\n _this.authToken = authToken;\n _this.player = player;\n _this.metadata = {\n content: {},\n playback: { videoPlayer: 'Vimeo' }\n };\n _this.mostRecentHeartbeat = 0;\n _this.isPaused = false;\n return _this;\n }\n\n _createClass(VimeoAnalytics, [{\n key: 'initialize',\n value: function initialize() {\n var events = {\n loaded: this.retrieveMetadata,\n play: this.trackPlay,\n pause: this.trackPause,\n ended: this.trackEnded,\n timeupdate: this.trackHeartbeat\n };\n\n for (event in events) {\n this.registerHandler(event, events[event]);\n }\n }\n\n // This is a helper function used to facilitate easier testing.\n\n }, {\n key: 'registerHandler',\n value: function registerHandler(event, handler) {\n var _this2 = this;\n\n this.player.on(event, function (data) {\n _this2.updateMetadata(data);\n handler.call(_this2, data);\n });\n }\n }, {\n key: 'trackPlay',\n value: function trackPlay() {\n if (this.isPaused) {\n this.track('Video Playback Resumed', this.metadata.playback);\n this.isPaused = false;\n } else {\n this.track('Video Playback Started', this.metadata.playback);\n this.track('Video Content Started', this.metadata.content);\n }\n }\n }, {\n key: 'trackEnded',\n value: function trackEnded() {\n this.track('Video Playback Completed', this.metadata.playback);\n this.track('Video Content Completed', this.metadata.content);\n }\n\n // Vimeo provides time updates every 250ms while Segment documents sending heartbeats every 10s.\n // Therefore, we need to do some math to ensure we are not sending 4 heartbeat events when we reach\n // 10 second intervals.\n\n }, {\n key: 'trackHeartbeat',\n value: function trackHeartbeat() {\n var mostRecentHeartbeat = this.mostRecentHeartbeat;\n var currentPosition = this.metadata.playback.position;\n if (currentPosition !== mostRecentHeartbeat && currentPosition - mostRecentHeartbeat >= 10) {\n this.track('Video Content Playing', this.metadata.content);\n this.mostRecentHeartbeat = Math.floor(currentPosition);\n }\n }\n }, {\n key: 'trackPause',\n value: function trackPause() {\n this.isPaused = true;\n this.track('Video Playback Paused', this.metadata.playback);\n }\n\n // retrieve static video metadata from vimeo API\n\n }, {\n key: 'retrieveMetadata',\n value: function retrieveMetadata(data) {\n var _this3 = this;\n\n return new Promise(function (resolve, reject) {\n var videoId = data.id;\n (0, _unfetch2.default)('https://api.vimeo.com/videos/' + videoId, {\n headers: {\n Authorization: 'Bearer ' + _this3.authToken\n }\n }).then(function (res) {\n return res.json();\n }).then(function (json) {\n _this3.metadata.content.title = json.name;\n _this3.metadata.content.description = json.description;\n _this3.metadata.content.publisher = json.user.name;\n\n _this3.metadata.playback.position = 0;\n _this3.metadata.playback.totalLength = json.duration;\n }).catch(function (err) {\n console.error('Request to Vimeo API failed with: ', err);\n return reject(err);\n });\n });\n }\n\n // update dynamic video information\n\n }, {\n key: 'updateMetadata',\n value: function updateMetadata(data) {\n var _this4 = this;\n\n return new Promise(function (resolve, reject) {\n _this4.player.getVolume().then(function (volume) {\n if (volume) _this4.metadata.playback.sound = volume * 100;\n _this4.metadata.playback.position = data.seconds;\n resolve();\n }).catch(reject);\n });\n }\n }]);\n\n return VimeoAnalytics;\n}(_plugin2.default);\n\nexports.default = VimeoAnalytics;\n\n//# sourceURL=webpack:///./plugins/vimeo/index.js?");

@@ -144,7 +144,7 @@ /***/ }),

\**********************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return YoutubeAnalytics; });\n/* harmony import */ var unfetch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! unfetch */ \"./node_modules/unfetch/dist/unfetch.es.js\");\n/* harmony import */ var _lib_plugin__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../lib/plugin */ \"./lib/plugin.js\");\n\n\nconst SEEK_THRESHOLD = 2000\n\nclass YoutubeAnalytics extends _lib_plugin__WEBPACK_IMPORTED_MODULE_1__[\"default\"] {\n constructor(player, apiKey) {\n super('YouTubeAnalytics', 1)\n this.player = player\n this.apiKey = apiKey\n this.playerLoaded = false\n this.playbackStarted = false\n this.contentStarted = false\n this.isPaused = false\n this.isBuffering = false\n this.isSeeking = false\n this.lastRecordedTime = { // updated every event\n timeReported: Date.now(),\n timeElapsed: 0.0\n }\n this.metadata = []\n this.playlistIndex = 0\n }\n \n initialize() {\n // Youtube API requires listeners to exist as top-level props on global object\n global.segmentYoutubeOnStateChange = this.onPlayerStateChange.bind(this)\n global.segmentYoutubeOnReady = this.onPlayerReady.bind(this)\n\n const { player } = this\n\n player.addEventListener('onReady', 'segmentYoutubeOnReady')\n player.addEventListener('onStateChange', 'segmentYoutubeOnStateChange')\n }\n\n onPlayerReady(event) { // this fires when the player html element loads\n const self = this\n\n if (this.player.getPlaylistId()) {\n this.player.cuePlaylist({\n listType: 'playlist',\n list: this.player.getPlaylistId()\n })\n } else {\n const videoData = this.player.getVideoData() \n this.player.cueVideoById(videoData.video_id)\n }\n }\n\n // yt reports events via state changes in the player rather than explicitly EVENTS like 'play', 'pause', etc\n onPlayerStateChange(event) {\n var self = this\n const currentTime = this.player.getCurrentTime()\n if (this.metadata[this.playlistIndex]) {\n this.metadata[this.playlistIndex].playback.position = this.metadata[this.playlistIndex].content.position = currentTime\n this.metadata[this.playlistIndex].playback.quality = this.player.getPlaybackQuality()\n this.metadata[this.playlistIndex].playback.sound = this.player.getVolume()\n }\n\n switch (event.data) {\n case -1: // this fires when a video or playlist has loaded\n if (this.playerLoaded) break\n this.retrieveMetadata()\n this.playerLoaded = true\n break\n\n case YT.PlayerState.BUFFERING:\n this.handleBuffer()\n break\n\n case YT.PlayerState.PLAYING:\n this.handlePlay()\n break\n\n case YT.PlayerState.PAUSED:\n this.handlePause()\n break\n\n case YT.PlayerState.ENDED:\n this.handleEnd()\n break\n }\n\n this.lastRecordedTime = {\n timeReported: Date.now(),\n timeElapsed: this.player.getCurrentTime()*1000.0\n }\n }\n\n // retrieve metadata from Youtube Data API using user's API Key\n retrieveMetadata() {\n return new Promise((resolve, reject) => {\n let videoData = this.player.getVideoData()\n let playlist = this.player.getPlaylist() || [videoData.video_id]\n \n const videoIds = playlist.join()\n \n Object(unfetch__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(`https://www.googleapis.com/youtube/v3/videos?id=${videoIds}&part=snippet,contentDetails&key=${this.apiKey}`)\n .then(res => res.json())\n .then(res => {\n let total_length = 0\n for (var i=0; i<playlist.length; i++) {\n const videoInfo = json.items[i]\n this.metadata.push({\n content: {\n title: videoInfo.snippet.title,\n description: videoInfo.snippet.description,\n keywords: videoInfo.snippet.tags,\n channel: videoInfo.snippet.channelTitle,\n airdate: videoInfo.snippet.publishedAt,\n }\n })\n total_length += YTDurationToSeconds(videoInfo.contentDetails.duration) \n }\n \n for (var i=0; i<playlist.length; i++) {\n this.metadata[i].playback = {\n total_length,\n video_player: 'youtube'\n }\n }\n })\n .catch(err => {\n console.error('Segment request to Youtube API failed (likely due to a bad API Key. Events will still be sent but will not contain video metadata ')\n this.metadata = playlist.map((item) => { return { playback: { video_player: 'youtube' }, content: {} } })\n reject()\n })\n })\n }\n\n handleBuffer() {\n var seekDetected = this.determineSeek()\n\n if (!this.playbackStarted) {\n this.playbackStarted = true\n this.track('Video Playback Started', this.metadata[this.playlistIndex].playback)\n }\n\n // user used keyboard to seek or seeked while video was paused\n if (seekDetected && !this.isSeeking) {\n this.isSeeking = true\n this.track('Video Playback Seek Started', this.metadata[this.playlistIndex].playback)\n }\n\n // state changing to BUFFERING denotes seeking has completed\n if (this.isSeeking) {\n this.track('Video Playback Seek Completed', this.metadata[this.playlistIndex].playback)\n this.isSeeking = false\n }\n\n // user clicked next video button (not possible in single video playback)\n const playlist = this.player.getPlaylist()\n if (playlist && this.player.getCurrentTime() === 0 && this.player.getPlaylistIndex() !== this.playlistIndex) {\n this.contentStarted = false\n if (this.playlistIndex === playlist.length - 1 && this.player.getPlaylistIndex() === 0) { // user skipped to end of last video in playlist\n this.track('Video Playback Completed', this.metadata[this.player.getPlaylistIndex()].playback)\n this.track('Video Playback Started', this.metadata[this.player.getPlaylistIndex()].playback) // playlist automatically starts from beginning\n }\n }\n\n this.track('Video Playback Buffer Started', this.metadata[this.playlistIndex].playback)\n this.isBuffering = true\n }\n\n handlePlay() {\n if (!this.contentStarted) {\n this.playlistIndex = this.player.getPlaylistIndex() // will return -1 if the player has a singular video instead of a playlist\n if (this.playlistIndex === -1) this.playlistIndex = 0\n this.track('Video Content Started', this.metadata[this.playlistIndex].content)\n this.contentStarted = true\n }\n\n if (this.isBuffering) {\n this.track('Video Playback Buffer Completed', this.metadata[this.playlistIndex].playback)\n this.isBuffering = false\n }\n \n if (this.isPaused) {\n this.track('Video Playback Resumed', this.metadata[this.playlistIndex].playback)\n this.isPaused = false\n }\n }\n\n handlePause() {\n const seekDetected = this.determineSeek()\n // user seeked while video was paused, it buffered, and then finished buffering\n if (this.isBuffering) {\n this.track('Video Playback Buffer Completed', this.metadata[this.playlistIndex].playback) \n this.isBuffering = false\n }\n\n if (!this.isPaused) {\n // user seeked while video was playing\n if (seekDetected) {\n this.track('Video Playback Seek Started', this.metadata[this.playlistIndex].playback)\n this.isSeeking = true\n }\n // user clicked pause while video was playing\n else {\n this.track('Video Playback Paused', this.metadata[this.playlistIndex].playback)\n this.isPaused = true\n }\n }\n }\n\n handleEnd() {\n this.track('Video Content Completed', this.metadata[this.playlistIndex].content)\n this.contentStarted = false\n\n const playlistIndex = this.player.getPlaylistIndex()\n const playlist = this.player.getPlaylist()\n \n if ((playlist && playlistIndex === playlist.length-1) || (playlistIndex === -1)) {\n this.track('Video Playback Completed', this.metadata[this.playlistIndex].playback)\n this.playbackStarted = false\n }\n }\n\n // yt doesn't natively track seeking so we have to manually calculate whether a seek has occurred based on expected vs actual event timestamps\n determineSeek() {\n const expectedTimeElapsed = (this.isPaused || this.isBuffering) ? 0 : Date.now() - this.lastRecordedTime.timeReported\n const actualTimeElapsed = this.player.getCurrentTime()*1000.0 - this.lastRecordedTime.timeElapsed\n\n return Math.abs(expectedTimeElapsed - actualTimeElapsed) > SEEK_THRESHOLD // if the diff btwn the 2 is > the threshold we can reasonably assume a seek has occurred\n }\n}\n\n// convert from ISO 8601 to seconds\nfunction YTDurationToSeconds(duration) {\n var match = duration.match(/PT(\\d+H)?(\\d+M)?(\\d+S)?/);\n\n match = match.slice(1).map(function(x) {\n if (x != null) {\n return x.replace(/\\D/, '');\n }\n });\n\n var hours = (parseInt(match[0]) || 0);\n var minutes = (parseInt(match[1]) || 0);\n var seconds = (parseInt(match[2]) || 0);\n\n return hours * 3600 + minutes * 60 + seconds;\n}\n\n\n//# sourceURL=webpack:///./plugins/youtube/index.js?");
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _unfetch = __webpack_require__(/*! unfetch */ \"./node_modules/unfetch/dist/unfetch.es.js\");\n\nvar _unfetch2 = _interopRequireDefault(_unfetch);\n\nvar _plugin = __webpack_require__(/*! ../../lib/plugin */ \"./lib/plugin.js\");\n\nvar _plugin2 = _interopRequireDefault(_plugin);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar SEEK_THRESHOLD = 2000;\n\nvar YoutubeAnalytics = function (_VideoPlugin) {\n _inherits(YoutubeAnalytics, _VideoPlugin);\n\n function YoutubeAnalytics(player, apiKey) {\n _classCallCheck(this, YoutubeAnalytics);\n\n var _this = _possibleConstructorReturn(this, (YoutubeAnalytics.__proto__ || Object.getPrototypeOf(YoutubeAnalytics)).call(this, 'YouTubeAnalytics', 1));\n\n _this.player = player;\n _this.apiKey = apiKey;\n _this.playerLoaded = false;\n _this.playbackStarted = false;\n _this.contentStarted = false;\n _this.isPaused = false;\n _this.isBuffering = false;\n _this.isSeeking = false;\n _this.lastRecordedTime = { // updated every event\n timeReported: Date.now(),\n timeElapsed: 0.0\n };\n _this.metadata = [];\n _this.playlistIndex = 0;\n return _this;\n }\n\n _createClass(YoutubeAnalytics, [{\n key: 'initialize',\n value: function initialize() {\n // Youtube API requires listeners to exist as top-level props on global object\n global.segmentYoutubeOnStateChange = this.onPlayerStateChange.bind(this);\n global.segmentYoutubeOnReady = this.onPlayerReady.bind(this);\n\n var player = this.player;\n\n\n player.addEventListener('onReady', 'segmentYoutubeOnReady');\n player.addEventListener('onStateChange', 'segmentYoutubeOnStateChange');\n }\n }, {\n key: 'onPlayerReady',\n value: function onPlayerReady(event) {\n // this fires when the player html element loads\n var self = this;\n\n if (this.player.getPlaylistId()) {\n this.player.cuePlaylist({\n listType: 'playlist',\n list: this.player.getPlaylistId()\n });\n } else {\n var videoData = this.player.getVideoData();\n this.player.cueVideoById(videoData.video_id);\n }\n }\n\n // yt reports events via state changes in the player rather than explicitly EVENTS like 'play', 'pause', etc\n\n }, {\n key: 'onPlayerStateChange',\n value: function onPlayerStateChange(event) {\n var self = this;\n var currentTime = this.player.getCurrentTime();\n if (this.metadata[this.playlistIndex]) {\n this.metadata[this.playlistIndex].playback.position = this.metadata[this.playlistIndex].content.position = currentTime;\n this.metadata[this.playlistIndex].playback.quality = this.player.getPlaybackQuality();\n this.metadata[this.playlistIndex].playback.sound = this.player.getVolume();\n }\n\n switch (event.data) {\n case -1:\n // this fires when a video or playlist has loaded\n if (this.playerLoaded) break;\n this.retrieveMetadata();\n this.playerLoaded = true;\n break;\n\n case YT.PlayerState.BUFFERING:\n this.handleBuffer();\n break;\n\n case YT.PlayerState.PLAYING:\n this.handlePlay();\n break;\n\n case YT.PlayerState.PAUSED:\n this.handlePause();\n break;\n\n case YT.PlayerState.ENDED:\n this.handleEnd();\n break;\n }\n\n this.lastRecordedTime = {\n timeReported: Date.now(),\n timeElapsed: this.player.getCurrentTime() * 1000.0\n };\n }\n\n // retrieve metadata from Youtube Data API using user's API Key\n\n }, {\n key: 'retrieveMetadata',\n value: function retrieveMetadata() {\n var _this2 = this;\n\n return new Promise(function (resolve, reject) {\n var videoData = _this2.player.getVideoData();\n var playlist = _this2.player.getPlaylist() || [videoData.video_id];\n\n var videoIds = playlist.join();\n\n (0, _unfetch2.default)('https://www.googleapis.com/youtube/v3/videos?id=' + videoIds + '&part=snippet,contentDetails&key=' + _this2.apiKey).then(function (res) {\n return res.json();\n }).then(function (res) {\n var total_length = 0;\n for (var i = 0; i < playlist.length; i++) {\n var videoInfo = json.items[i];\n _this2.metadata.push({\n content: {\n title: videoInfo.snippet.title,\n description: videoInfo.snippet.description,\n keywords: videoInfo.snippet.tags,\n channel: videoInfo.snippet.channelTitle,\n airdate: videoInfo.snippet.publishedAt\n }\n });\n total_length += YTDurationToSeconds(videoInfo.contentDetails.duration);\n }\n\n for (var i = 0; i < playlist.length; i++) {\n _this2.metadata[i].playback = {\n total_length: total_length,\n video_player: 'youtube'\n };\n }\n }).catch(function (err) {\n console.error('Segment request to Youtube API failed (likely due to a bad API Key. Events will still be sent but will not contain video metadata ');\n _this2.metadata = playlist.map(function (item) {\n return { playback: { video_player: 'youtube' }, content: {} };\n });\n reject();\n });\n });\n }\n }, {\n key: 'handleBuffer',\n value: function handleBuffer() {\n var seekDetected = this.determineSeek();\n\n if (!this.playbackStarted) {\n this.playbackStarted = true;\n this.track('Video Playback Started', this.metadata[this.playlistIndex].playback);\n }\n\n // user used keyboard to seek or seeked while video was paused\n if (seekDetected && !this.isSeeking) {\n this.isSeeking = true;\n this.track('Video Playback Seek Started', this.metadata[this.playlistIndex].playback);\n }\n\n // state changing to BUFFERING denotes seeking has completed\n if (this.isSeeking) {\n this.track('Video Playback Seek Completed', this.metadata[this.playlistIndex].playback);\n this.isSeeking = false;\n }\n\n // user clicked next video button (not possible in single video playback)\n var playlist = this.player.getPlaylist();\n if (playlist && this.player.getCurrentTime() === 0 && this.player.getPlaylistIndex() !== this.playlistIndex) {\n this.contentStarted = false;\n if (this.playlistIndex === playlist.length - 1 && this.player.getPlaylistIndex() === 0) {\n // user skipped to end of last video in playlist\n this.track('Video Playback Completed', this.metadata[this.player.getPlaylistIndex()].playback);\n this.track('Video Playback Started', this.metadata[this.player.getPlaylistIndex()].playback); // playlist automatically starts from beginning\n }\n }\n\n this.track('Video Playback Buffer Started', this.metadata[this.playlistIndex].playback);\n this.isBuffering = true;\n }\n }, {\n key: 'handlePlay',\n value: function handlePlay() {\n if (!this.contentStarted) {\n this.playlistIndex = this.player.getPlaylistIndex(); // will return -1 if the player has a singular video instead of a playlist\n if (this.playlistIndex === -1) this.playlistIndex = 0;\n this.track('Video Content Started', this.metadata[this.playlistIndex].content);\n this.contentStarted = true;\n }\n\n if (this.isBuffering) {\n this.track('Video Playback Buffer Completed', this.metadata[this.playlistIndex].playback);\n this.isBuffering = false;\n }\n\n if (this.isPaused) {\n this.track('Video Playback Resumed', this.metadata[this.playlistIndex].playback);\n this.isPaused = false;\n }\n }\n }, {\n key: 'handlePause',\n value: function handlePause() {\n var seekDetected = this.determineSeek();\n // user seeked while video was paused, it buffered, and then finished buffering\n if (this.isBuffering) {\n this.track('Video Playback Buffer Completed', this.metadata[this.playlistIndex].playback);\n this.isBuffering = false;\n }\n\n if (!this.isPaused) {\n // user seeked while video was playing\n if (seekDetected) {\n this.track('Video Playback Seek Started', this.metadata[this.playlistIndex].playback);\n this.isSeeking = true;\n }\n // user clicked pause while video was playing\n else {\n this.track('Video Playback Paused', this.metadata[this.playlistIndex].playback);\n this.isPaused = true;\n }\n }\n }\n }, {\n key: 'handleEnd',\n value: function handleEnd() {\n this.track('Video Content Completed', this.metadata[this.playlistIndex].content);\n this.contentStarted = false;\n\n var playlistIndex = this.player.getPlaylistIndex();\n var playlist = this.player.getPlaylist();\n\n if (playlist && playlistIndex === playlist.length - 1 || playlistIndex === -1) {\n this.track('Video Playback Completed', this.metadata[this.playlistIndex].playback);\n this.playbackStarted = false;\n }\n }\n\n // yt doesn't natively track seeking so we have to manually calculate whether a seek has occurred based on expected vs actual event timestamps\n\n }, {\n key: 'determineSeek',\n value: function determineSeek() {\n var expectedTimeElapsed = this.isPaused || this.isBuffering ? 0 : Date.now() - this.lastRecordedTime.timeReported;\n var actualTimeElapsed = this.player.getCurrentTime() * 1000.0 - this.lastRecordedTime.timeElapsed;\n\n return Math.abs(expectedTimeElapsed - actualTimeElapsed) > SEEK_THRESHOLD; // if the diff btwn the 2 is > the threshold we can reasonably assume a seek has occurred\n }\n }]);\n\n return YoutubeAnalytics;\n}(_plugin2.default);\n\n// convert from ISO 8601 to seconds\n\n\nexports.default = YoutubeAnalytics;\nfunction YTDurationToSeconds(duration) {\n var match = duration.match(/PT(\\d+H)?(\\d+M)?(\\d+S)?/);\n\n match = match.slice(1).map(function (x) {\n if (x != null) {\n return x.replace(/\\D/, '');\n }\n });\n\n var hours = parseInt(match[0]) || 0;\n var minutes = parseInt(match[1]) || 0;\n var seconds = parseInt(match[2]) || 0;\n\n return hours * 3600 + minutes * 60 + seconds;\n}\n\n//# sourceURL=webpack:///./plugins/youtube/index.js?");

@@ -151,0 +151,0 @@ /***/ })

{
"name": "@segment/analytics.js-video-plugins",
"version": "0.0.2",
"version": "0.0.3",
"description": "",

@@ -5,0 +5,0 @@ "scripts": {

@@ -9,3 +9,17 @@ module.exports = {

filename: 'index.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['babel-preset-env']
}
}
}
]
}
};