@vouchfor/embeds 0.0.0-experiment.f80a91a → 0.0.0-experiment.f90d920

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. package/dist/es/embeds.js +1142 -9
  2. package/dist/es/embeds.js.map +1 -1
  3. package/dist/es/src/components/DialogEmbed/DialogOverlay.d.ts +2 -1
  4. package/dist/es/src/components/DialogEmbed/DialogPortal.d.ts +2 -1
  5. package/dist/es/src/components/DialogEmbed/index.d.ts +4 -1
  6. package/dist/es/src/components/PlayerEmbed/controllers/tracking/utils.d.ts +1 -1
  7. package/dist/es/src/components/PlayerEmbed/index.d.ts +75 -0
  8. package/dist/es/src/components/PlayerEmbed/tests/data.d.ts +3 -2
  9. package/dist/es/src/components/PlayerEmbed/tests/media-data.d.ts +19 -0
  10. package/dist/iife/dialog-embed/embed.iife.js +2732 -411
  11. package/dist/iife/dialog-embed/embed.iife.js.map +1 -1
  12. package/dist/iife/embeds.iife.js +2732 -411
  13. package/dist/iife/embeds.iife.js.map +1 -1
  14. package/dist/iife/player-embed/embed.iife.js +2706 -387
  15. package/dist/iife/player-embed/embed.iife.js.map +1 -1
  16. package/package.json +36 -31
  17. package/src/components/DialogEmbed/Dialog.stories.ts +12 -0
  18. package/src/components/DialogEmbed/DialogOverlay.ts +2 -1
  19. package/src/components/DialogEmbed/DialogPortal.ts +22 -10
  20. package/src/components/DialogEmbed/index.ts +5 -1
  21. package/src/components/PlayerEmbed/MultiEmbed.stories.ts +135 -0
  22. package/src/components/PlayerEmbed/PlayerEmbed.stories.ts +15 -1
  23. package/src/components/PlayerEmbed/controllers/tracking/index.ts +7 -7
  24. package/src/components/PlayerEmbed/controllers/tracking/utils.ts +1 -1
  25. package/src/components/PlayerEmbed/index.ts +120 -7
  26. package/src/components/PlayerEmbed/tests/PlayerEmbed.spec.ts +14 -7
  27. package/src/components/PlayerEmbed/tests/data.ts +121 -77
  28. package/src/components/PlayerEmbed/tests/media-data.ts +22 -0
  29. package/dist/es/browser-07a8b7dc.js +0 -433
  30. package/dist/es/browser-07a8b7dc.js.map +0 -1
  31. package/dist/es/index-0c298932.js +0 -10473
  32. package/dist/es/index-0c298932.js.map +0 -1
  33. package/dist/iife/dialog-embed/browser-450a324f.js +0 -433
  34. package/dist/iife/dialog-embed/browser-450a324f.js.map +0 -1
  35. package/dist/iife/dialog-embed/embed.js +0 -5
  36. package/dist/iife/dialog-embed/embed.js.map +0 -1
  37. package/dist/iife/dialog-embed/index-62750303.js +0 -32932
  38. package/dist/iife/dialog-embed/index-62750303.js.map +0 -1
  39. package/dist/iife/dialog-embed/src/components/DialogEmbed/DialogOverlay.d.ts +0 -19
  40. package/dist/iife/dialog-embed/src/components/DialogEmbed/DialogPortal.d.ts +0 -35
  41. package/dist/iife/dialog-embed/src/components/DialogEmbed/index.d.ts +0 -35
  42. package/dist/iife/dialog-embed/src/components/PlayerEmbed/controllers/event-forwarder.d.ts +0 -15
  43. package/dist/iife/dialog-embed/src/components/PlayerEmbed/controllers/fetcher.d.ts +0 -14
  44. package/dist/iife/dialog-embed/src/components/PlayerEmbed/controllers/tracking/index.d.ts +0 -36
  45. package/dist/iife/dialog-embed/src/components/PlayerEmbed/controllers/tracking/utils.d.ts +0 -17
  46. package/dist/iife/dialog-embed/src/components/PlayerEmbed/tests/data.d.ts +0 -3
  47. package/dist/iife/dialog-embed/src/index.d.ts +0 -2
  48. package/dist/iife/dialog-embed/src/utils/env.d.ts +0 -12
  49. package/dist/iife/dialog-embed/src/utils/events.d.ts +0 -2
  50. package/dist/iife/player-embed/browser-c51cdddb.js +0 -433
  51. package/dist/iife/player-embed/browser-c51cdddb.js.map +0 -1
  52. package/dist/iife/player-embed/embed.js +0 -5
  53. package/dist/iife/player-embed/embed.js.map +0 -1
  54. package/dist/iife/player-embed/index-0d71c577.js +0 -32489
  55. package/dist/iife/player-embed/index-0d71c577.js.map +0 -1
  56. package/dist/iife/player-embed/src/components/DialogEmbed/DialogOverlay.d.ts +0 -19
  57. package/dist/iife/player-embed/src/components/DialogEmbed/DialogPortal.d.ts +0 -35
  58. package/dist/iife/player-embed/src/components/DialogEmbed/index.d.ts +0 -35
  59. package/dist/iife/player-embed/src/components/PlayerEmbed/controllers/event-forwarder.d.ts +0 -15
  60. package/dist/iife/player-embed/src/components/PlayerEmbed/controllers/fetcher.d.ts +0 -14
  61. package/dist/iife/player-embed/src/components/PlayerEmbed/controllers/tracking/index.d.ts +0 -36
  62. package/dist/iife/player-embed/src/components/PlayerEmbed/controllers/tracking/utils.d.ts +0 -17
  63. package/dist/iife/player-embed/src/components/PlayerEmbed/tests/data.d.ts +0 -3
  64. package/dist/iife/player-embed/src/index.d.ts +0 -2
  65. package/dist/iife/player-embed/src/utils/env.d.ts +0 -12
  66. package/dist/iife/player-embed/src/utils/events.d.ts +0 -2
  67. package/src/mixins/media-player-proxy.ts +0 -116
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vouchfor/embeds",
3
- "version": "0.0.0-experiment.f80a91a",
3
+ "version": "0.0.0-experiment.f90d920",
4
4
  "license": "MIT",
5
5
  "author": "Aaron Williams",
6
6
  "main": "dist/es/embeds.js",
@@ -15,6 +15,9 @@
15
15
  "tag": "experiment",
16
16
  "access": "public"
17
17
  },
18
+ "engines": {
19
+ "node": ">=18.18.0"
20
+ },
18
21
  "scripts": {
19
22
  "build": "rm -rf dist && tsc && yarn build:self",
20
23
  "build:deps": "yarn --cwd ../media-player build",
@@ -30,7 +33,8 @@
30
33
  "storybook": "yarn prebuild && storybook dev -p 6007",
31
34
  "prebuild": "yarn build:deps && yarn generate:manifest",
32
35
  "test": "rm -rf test/lib && yarn prebuild && vite build --mode test && web-test-runner",
33
- "test:ci": "yarn test --config web-test-runner.ci.config.js"
36
+ "test:ci": "yarn test --config web-test-runner.ci.config.js",
37
+ "test:watch": "yarn test --watch"
34
38
  },
35
39
  "lint-staged": {
36
40
  "**/*.{ts,tsx,js}": "eslint --fix --quiet",
@@ -39,45 +43,46 @@
39
43
  "dependencies": {
40
44
  "@a11y/focus-trap": "^1.0.5",
41
45
  "@lit/task": "^1.0.0",
42
- "@vouchfor/media-player": "0.0.0-experiment.f80a91a",
46
+ "@vouchfor/canvas-video": "0.0.0-experiment.f90d920",
47
+ "@vouchfor/media-player": "0.0.0-experiment.f90d920",
43
48
  "uuid": "^9.0.1"
44
49
  },
45
50
  "peerDependencies": {
46
- "lit": "^3.1.0"
51
+ "lit": "^3.1.2"
47
52
  },
48
53
  "devDependencies": {
49
54
  "@esm-bundle/chai": "^4.3.4-fix.0",
50
- "@open-wc/testing": "^3.2.0",
51
- "@storybook/addon-essentials": "^7.4.5",
52
- "@storybook/addon-links": "^7.4.5",
53
- "@storybook/blocks": "^7.4.5",
54
- "@storybook/web-components": "^7.4.5",
55
- "@storybook/web-components-vite": "^7.4.5",
56
- "@types/mocha": "^10.0.1",
57
- "@vouchfor/eslint-config": "^1.0.0",
58
- "@vouchfor/prettier-config": "^1.0.0",
59
- "@web/dev-server-esbuild": "^0.4.1",
60
- "@web/test-runner": "^0.17.1",
61
- "@web/test-runner-browserstack": "^0.6.1",
62
- "@web/test-runner-mocha": "^0.8.1",
63
- "@web/test-runner-playwright": "^0.10.1",
64
- "dotenv": "^16.3.1",
65
- "eslint": "^8.50.0",
66
- "eslint-plugin-import": "^2.28.1",
55
+ "@open-wc/testing": "^4.0.0",
56
+ "@storybook/addon-essentials": "^8.0.4",
57
+ "@storybook/addon-links": "^8.0.4",
58
+ "@storybook/blocks": "^8.0.4",
59
+ "@storybook/web-components": "^8.0.4",
60
+ "@storybook/web-components-vite": "^8.0.4",
61
+ "@svgr/core": "^8.1.0",
62
+ "@types/flat": "^5.0.5",
63
+ "@types/mocha": "^10.0.6",
64
+ "@vouchfor/eslint-config": "^1.0.1",
65
+ "@vouchfor/prettier-config": "^1.0.1",
66
+ "@vouchfor/video-utils": "0.0.0-experiment.f90d920",
67
+ "@web/dev-server-esbuild": "^1.0.2",
68
+ "@web/test-runner": "^0.18.1",
69
+ "@web/test-runner-browserstack": "^0.7.1",
70
+ "@web/test-runner-mocha": "^0.9.0",
71
+ "@web/test-runner-playwright": "^0.11.0",
67
72
  "glob": "^10.3.10",
68
- "lint-staged": "^14.0.1",
69
- "lit": "^3.1.0",
73
+ "lint-staged": "^15.2.2",
74
+ "lit": "^3.1.2",
70
75
  "lodash": "^4.17.21",
71
- "prettier": "^3.0.3",
72
76
  "react": "^18.2.0",
73
77
  "react-dom": "^18.2.0",
74
- "rollup-plugin-internal": "^1.0.4",
78
+ "rollup-plugin-tla": "^0.0.2",
75
79
  "sinon": "^17.0.1",
76
- "storybook": "^7.4.5",
77
- "typescript": "^5.1.3",
78
- "vite": "^4.4.9",
79
- "vite-plugin-commonjs": "^0.10.0",
80
- "vite-plugin-dts": "^3.6.0",
81
- "web-component-analyzer": "^1.1.7"
80
+ "storybook": "^8.0.4",
81
+ "svgson": "^5.3.1",
82
+ "typescript": "^5.4.3",
83
+ "vite": "^5.2.2",
84
+ "vite-plugin-commonjs": "^0.10.1",
85
+ "vite-plugin-dts": "^3.7.3",
86
+ "web-component-analyzer": "^2.0.0"
82
87
  }
83
88
  }
@@ -35,6 +35,18 @@ const _DialogEmbed = ({
35
35
  aspectRatio=${ifDefined(aspectRatio)}
36
36
  @error=${console.log}
37
37
  ></vouch-embed-dialog>
38
+ <vouch-embed-dialog
39
+ env=${ifDefined(env)}
40
+ apiKey=${ifDefined(apiKey)}
41
+ vouchId=${ifDefined(vouchId)}
42
+ templateId=${ifDefined(templateId)}
43
+ .questions=${questions}
44
+ .controls=${controls}
45
+ ?autoplay=${autoplay}
46
+ preload=${ifDefined(preload)}
47
+ aspectRatio=${ifDefined(aspectRatio)}
48
+ @error=${console.log}
49
+ ></vouch-embed-dialog>
38
50
  </div>
39
51
  `;
40
52
  };
@@ -21,10 +21,11 @@ class DialogOverlay extends LitElement {
21
21
  }
22
22
 
23
23
  .container {
24
- position: absolute;
24
+ position: fixed;
25
25
  display: flex;
26
26
  inset: 0;
27
27
  opacity: 1;
28
+ z-index: 2147483647;
28
29
  align-items: center;
29
30
  justify-content: center;
30
31
  transition: opacity 100ms ease-in;
@@ -29,19 +29,25 @@ class DialogPortal extends LitElement {
29
29
 
30
30
  @state() open = false;
31
31
 
32
- private _handleToggle = () => {
33
- this.open = !this.open;
34
-
35
- if (this.open) {
36
- if (!this.disableAutoplay && this._mediaPlayerRef?.value) {
37
- this._mediaPlayerRef.value.muted = false;
38
- this._mediaPlayerRef.value.play();
32
+ private _handleToggle = ({ detail }: CustomEvent<string>) => {
33
+ // Because we have to attach this listener to the document since this element is portalled outside of the button,
34
+ // we also have to make sure that this player is actually the one we want to open and play by passing in an ID
35
+ // from the button wrapper parent and checking against that same ID we pass as the event detail
36
+ if (this.id === detail) {
37
+ this.open = !this.open;
38
+
39
+ if (this.open) {
40
+ if (!this.disableAutoplay && this._mediaPlayerRef?.value) {
41
+ this._mediaPlayerRef.value.muted = false;
42
+ this._mediaPlayerRef.value.play();
43
+ }
44
+ } else {
45
+ this._mediaPlayerRef?.value?.pause();
39
46
  }
40
- } else {
41
- this._mediaPlayerRef?.value?.pause();
42
47
  }
43
48
  };
44
49
 
50
+ // We could do the same thing on close and check for the correct ID but it doesn't really matter
45
51
  private _handleClose = () => {
46
52
  this.open = false;
47
53
  this._mediaPlayerRef?.value?.pause();
@@ -70,7 +76,13 @@ class DialogPortal extends LitElement {
70
76
  }
71
77
 
72
78
  protected createRenderRoot(): HTMLElement | DocumentFragment {
73
- return document.body;
79
+ // We must create a new node here because portalling into the same node (document.body) causes the second
80
+ // element to overwrite the first for some reason (not behaviour really stated in the docs)
81
+ // I am fairly certain this function is only run once as the default behaviour creates the open shadow root
82
+ // and returns that shadow root in this function: https://lit.dev/docs/components/shadow-dom/#implementing-createrenderroot
83
+ const newNode = document.createElement('div');
84
+ document.body.appendChild(newNode);
85
+ return newNode;
74
86
  }
75
87
 
76
88
  render() {
@@ -1,6 +1,7 @@
1
1
  import { css, html, LitElement } from 'lit';
2
2
  import { customElement, property } from 'lit/decorators.js';
3
3
  import { ifDefined } from 'lit/directives/if-defined.js';
4
+ import { v4 as uuidv4 } from 'uuid';
4
5
 
5
6
  import type { PlayerEmbedProps } from '../PlayerEmbed';
6
7
 
@@ -41,8 +42,10 @@ class DialogEmbed extends LitElement {
41
42
  @property({ type: Boolean }) disableAutoplay: DialogEmbedProps['disableAutoplay'] = false;
42
43
  @property({ type: Number }) aspectRatio: DialogEmbedProps['aspectRatio'] = 0;
43
44
 
45
+ private _id = uuidv4();
46
+
44
47
  private _handleRootClick = () => {
45
- this.dispatchEvent(new CustomEvent('dialogembed:click', { bubbles: true, composed: true }));
48
+ this.dispatchEvent(new CustomEvent('dialogembed:click', { detail: this._id, bubbles: true, composed: true }));
46
49
  };
47
50
 
48
51
  connectedCallback(): void {
@@ -61,6 +64,7 @@ class DialogEmbed extends LitElement {
61
64
  <vmp-button size="large">Play</vmp-button>
62
65
  </slot>
63
66
  <vouch-embed-dialog-portal
67
+ id=${this._id}
64
68
  ?autoplay=${false}
65
69
  vouchId=${ifDefined(this.vouchId)}
66
70
  templateId=${ifDefined(this.templateId)}
@@ -0,0 +1,135 @@
1
+ import { html } from 'lit';
2
+ import { ifDefined } from 'lit/directives/if-defined.js';
3
+
4
+ import type { PlayerEmbedProps } from '.';
5
+ import type { Meta, StoryObj } from '@storybook/web-components';
6
+
7
+ import '.';
8
+
9
+ type MultiEmbedArgs = PlayerEmbedProps & {
10
+ apiKey1?: string;
11
+ apiKey2?: string;
12
+ apiKey3?: string;
13
+ vouchId1?: string;
14
+ vouchId2?: string;
15
+ vouchId3?: string;
16
+ showVouch?: boolean;
17
+ };
18
+
19
+ const _MultiEmbed = ({
20
+ vouchId1,
21
+ vouchId2,
22
+ vouchId3,
23
+ templateId,
24
+ questions,
25
+ preload,
26
+ autoplay,
27
+ env,
28
+ apiKey1,
29
+ apiKey2,
30
+ apiKey3,
31
+ controls,
32
+ aspectRatio
33
+ }: MultiEmbedArgs) => {
34
+ return html`
35
+ <div style="height: 33vh">
36
+ <vouch-embed-player
37
+ env=${ifDefined(env)}
38
+ apiKey=${ifDefined(apiKey1)}
39
+ vouchId=${ifDefined(vouchId1)}
40
+ templateId=${ifDefined(templateId)}
41
+ .questions=${questions}
42
+ .controls=${controls}
43
+ ?autoplay=${autoplay}
44
+ preload=${ifDefined(preload)}
45
+ aspectRatio=${ifDefined(aspectRatio)}
46
+ @error=${console.log}
47
+ ></vouch-embed-player>
48
+ </div>
49
+ <div style="height: 33vh">
50
+ <vouch-embed-player
51
+ env=${ifDefined(env)}
52
+ apiKey=${ifDefined(apiKey2)}
53
+ vouchId=${ifDefined(vouchId2)}
54
+ templateId=${ifDefined(templateId)}
55
+ .questions=${questions}
56
+ .controls=${controls}
57
+ ?autoplay=${autoplay}
58
+ preload=${ifDefined(preload)}
59
+ aspectRatio=${ifDefined(aspectRatio)}
60
+ @error=${console.log}
61
+ ></vouch-embed-player>
62
+ </div>
63
+ <div style="height: 33vh">
64
+ <vouch-embed-player
65
+ env=${ifDefined(env)}
66
+ apiKey=${ifDefined(apiKey3)}
67
+ vouchId=${ifDefined(vouchId3)}
68
+ templateId=${ifDefined(templateId)}
69
+ .questions=${questions}
70
+ .controls=${controls}
71
+ ?autoplay=${autoplay}
72
+ preload=${ifDefined(preload)}
73
+ aspectRatio=${ifDefined(aspectRatio)}
74
+ @error=${console.log}
75
+ ></vouch-embed-player>
76
+ </div>
77
+ `;
78
+ };
79
+
80
+ // More on how to set up stories at: https://storybook.js.org/docs/web-components/writing-stories/introduction
81
+ const meta = {
82
+ title: 'Embeds',
83
+ tags: ['autodocs'],
84
+ render: (args) => _MultiEmbed(args),
85
+ component: 'vouch-embed-player'
86
+ } satisfies Meta<PlayerEmbedProps>;
87
+
88
+ type Story = StoryObj<MultiEmbedArgs>;
89
+
90
+ const MultiPlayer: Story = {
91
+ args: {
92
+ env: 'dev',
93
+ apiKey1: 'TVik9uTMgE-PD25UTHIS6gyl0hMBWC7AT4dkpdlLBT4VIfDWZJrQiCk6Ak7m1',
94
+ vouchId1: '6JQEIPeStt',
95
+ apiKey2: 'TVik9uTMgE-PD25UTHIS6gyl0hMBWC7AT4dkpdlLBT4VIfDWZJrQiCk6Ak7m1',
96
+ vouchId2: '6JQEIPeStt',
97
+ apiKey3: 'TVik9uTMgE-PD25UTHIS6gyl0hMBWC7AT4dkpdlLBT4VIfDWZJrQiCk6Ak7m1',
98
+ vouchId3: '6JQEIPeStt',
99
+ templateId: '357fc118-e179-4171-9446-ff2b8e9d1b29',
100
+ questions: [],
101
+ aspectRatio: 0,
102
+ preload: 'none',
103
+ autoplay: false,
104
+ controls: [
105
+ 'progress',
106
+ 'play-large',
107
+ 'navigation',
108
+ 'play',
109
+ 'volume',
110
+ 'current-time',
111
+ 'duration',
112
+ 'speed',
113
+ 'captions',
114
+ 'fullscreen',
115
+ 'preview',
116
+ 'languages'
117
+ ]
118
+ },
119
+ argTypes: {
120
+ env: {
121
+ control: 'radio',
122
+ options: ['local', 'dev', 'staging', 'prod']
123
+ },
124
+ preload: {
125
+ control: 'radio',
126
+ options: ['auto', 'none']
127
+ }
128
+ },
129
+ parameters: {
130
+ layout: 'fullscreen'
131
+ }
132
+ };
133
+
134
+ export default meta;
135
+ export { MultiPlayer };
@@ -58,7 +58,21 @@ const Player: Story = {
58
58
  questions: [],
59
59
  aspectRatio: 0,
60
60
  preload: 'none',
61
- autoplay: false
61
+ autoplay: false,
62
+ controls: [
63
+ 'progress',
64
+ 'play-large',
65
+ 'navigation',
66
+ 'play',
67
+ 'volume',
68
+ 'current-time',
69
+ 'duration',
70
+ 'speed',
71
+ 'captions',
72
+ 'fullscreen',
73
+ 'preview',
74
+ 'languages'
75
+ ]
62
76
  },
63
77
  argTypes: {
64
78
  env: {
@@ -1,5 +1,5 @@
1
1
  import type { PlayerEmbed } from '../..';
2
- import type { VideoEventDetail } from '@vouchfor/media-player';
2
+ import type { MediaEventDetail } from '@vouchfor/media-player';
3
3
  import type { ReactiveController, ReactiveControllerHost } from 'lit';
4
4
 
5
5
  import { findVouchId, getReportingMetadata, getUids } from './utils';
@@ -41,7 +41,7 @@ class TrackingController implements ReactiveController {
41
41
  private _answersViewed: BooleanMap = {};
42
42
  private _streamStartTime: TimeMap = {};
43
43
  private _streamLatestTime: TimeMap = {};
44
- private _currentlyPlayingVideo: VideoEventDetail | null = null;
44
+ private _currentlyPlayingVideo: MediaEventDetail | null = null;
45
45
 
46
46
  constructor(host: PlayerEmbedHost) {
47
47
  this.host = host;
@@ -134,7 +134,7 @@ class TrackingController implements ReactiveController {
134
134
  }
135
135
  };
136
136
 
137
- private _handleVideoPlay = ({ detail: { id, key } }: CustomEvent<VideoEventDetail>) => {
137
+ private _handleVideoPlay = ({ detail: { id, key } }: CustomEvent<MediaEventDetail>) => {
138
138
  // Only increment play count once per session
139
139
  if (!this._answersViewed[key]) {
140
140
  this._createTrackingEvent('VOUCH_RESPONSE_VIEWED', {
@@ -144,12 +144,12 @@ class TrackingController implements ReactiveController {
144
144
  }
145
145
  };
146
146
 
147
- private _handleVideoTimeUpdate = ({ detail: { id, key, node } }: CustomEvent<VideoEventDetail>) => {
147
+ private _handleVideoTimeUpdate = ({ detail: { id, key, node } }: CustomEvent<MediaEventDetail>) => {
148
148
  if (
149
149
  // We only want to count any time that the video is actually playing
150
- !this.host.paused &&
150
+ !this.host.paused
151
151
  // Only update the latest time if this event fires for the currently active video
152
- id === this.host.scene?.video?.id
152
+ // id === this.host.scene?.video?.id
153
153
  ) {
154
154
  this._currentlyPlayingVideo = { id, key, node };
155
155
  this._streamLatestTime[key] = node.currentTime;
@@ -161,7 +161,7 @@ class TrackingController implements ReactiveController {
161
161
  }
162
162
  };
163
163
 
164
- private _handleVideoPause = ({ detail: { id, key } }: CustomEvent<VideoEventDetail>) => {
164
+ private _handleVideoPause = ({ detail: { id, key } }: CustomEvent<MediaEventDetail>) => {
165
165
  if (this._streamLatestTime[key] > this._streamStartTime[key] + MINIMUM_SEND_THRESHOLD) {
166
166
  this._createTrackingEvent('VIDEO_STREAMED', {
167
167
  answerId: id,
@@ -2,7 +2,7 @@ import { TEMPLATE_VERSION } from '@vouchfor/canvas-video';
2
2
  import { v4 as uuidv4 } from 'uuid';
3
3
 
4
4
  import type { TrackingPayload } from '.';
5
- import type { Vouch } from '@vouchfor/media-player';
5
+ import type { Vouch } from '@vouchfor/video-utils';
6
6
  import type { Environment } from '~/utils/env';
7
7
 
8
8
  import packageJson from '~/../package.json';
@@ -1,20 +1,22 @@
1
1
  import { css, html, LitElement } from 'lit';
2
2
  import { customElement, property, state } from 'lit/decorators.js';
3
3
  import { ifDefined } from 'lit/directives/if-defined.js';
4
- import { ref } from 'lit/directives/ref.js';
4
+ import { createRef, ref } from 'lit/directives/ref.js';
5
5
 
6
6
  import type { TemplateInstance } from '@vouchfor/canvas-video';
7
- import type { MediaPlayerProps } from '@vouchfor/media-player';
7
+ import type { MediaPlayer, MediaPlayerProps } from '@vouchfor/media-player';
8
+ import type { Vouch } from '@vouchfor/video-utils';
9
+ import type { PropertyValueMap } from 'lit';
8
10
  import type { Environment } from '~/utils/env';
9
11
 
10
12
  import { EventForwardController } from './controllers/event-forwarder';
11
13
  import { FetcherController } from './controllers/fetcher';
12
14
  import { TrackingController } from './controllers/tracking';
13
- import { MediaPlayerProxy } from '~/mixins/media-player-proxy';
14
15
 
15
16
  import '@vouchfor/media-player';
16
17
 
17
- type PlayerEmbedProps = Pick<MediaPlayerProps, 'data' | 'aspectRatio' | 'preload' | 'autoplay' | 'controls'> & {
18
+ type PlayerEmbedProps = Pick<MediaPlayerProps, 'aspectRatio' | 'language' | 'preload' | 'autoplay' | 'controls'> & {
19
+ data?: Vouch;
18
20
  env: Environment;
19
21
  apiKey: string;
20
22
  disableTracking?: boolean;
@@ -26,7 +28,7 @@ type PlayerEmbedProps = Pick<MediaPlayerProps, 'data' | 'aspectRatio' | 'preload
26
28
  };
27
29
 
28
30
  @customElement('vouch-embed-player')
29
- class PlayerEmbed extends MediaPlayerProxy(LitElement) {
31
+ class PlayerEmbed extends LitElement {
30
32
  static styles = [
31
33
  css`
32
34
  :host {
@@ -49,6 +51,7 @@ class PlayerEmbed extends MediaPlayerProxy(LitElement) {
49
51
  @property({ type: String }) preload: PlayerEmbedProps['preload'] = 'auto';
50
52
  @property({ type: Boolean }) autoplay: PlayerEmbedProps['autoplay'] = false;
51
53
  @property({ type: Number }) aspectRatio: PlayerEmbedProps['aspectRatio'] = 0;
54
+ @property({ type: String }) language?: MediaPlayerProps['language'];
52
55
 
53
56
  private eventController = new EventForwardController(this, [
54
57
  'durationchange',
@@ -61,10 +64,12 @@ class PlayerEmbed extends MediaPlayerProxy(LitElement) {
61
64
  'playing',
62
65
  'ratechange',
63
66
  'scenechange',
67
+ 'scenesupdate',
64
68
  'seeking',
65
69
  'seeked',
66
70
  'timeupdate',
67
71
  'volumechange',
72
+ 'processing',
68
73
  'waiting',
69
74
 
70
75
  'video:loadeddata',
@@ -90,6 +95,108 @@ class PlayerEmbed extends MediaPlayerProxy(LitElement) {
90
95
  return this._fetcherController.fetching;
91
96
  }
92
97
 
98
+ private _mediaPlayerRef = createRef<MediaPlayer>();
99
+
100
+ get waiting() {
101
+ return this._mediaPlayerRef.value?.waiting;
102
+ }
103
+
104
+ get initialised() {
105
+ return this._mediaPlayerRef.value?.initialised;
106
+ }
107
+
108
+ get seeking() {
109
+ return this._mediaPlayerRef.value?.seeking;
110
+ }
111
+
112
+ get paused() {
113
+ return this._mediaPlayerRef.value?.paused;
114
+ }
115
+
116
+ get captions() {
117
+ return this._mediaPlayerRef.value?.captions;
118
+ }
119
+
120
+ get fullscreen() {
121
+ return this._mediaPlayerRef.value?.fullscreen;
122
+ }
123
+
124
+ get duration() {
125
+ return this._mediaPlayerRef.value?.duration;
126
+ }
127
+
128
+ set currentTime(value: number) {
129
+ if (this._mediaPlayerRef.value) {
130
+ this._mediaPlayerRef.value.currentTime = value;
131
+ }
132
+ }
133
+ get currentTime() {
134
+ return this._mediaPlayerRef.value?.currentTime ?? 0;
135
+ }
136
+
137
+ set playbackRate(value: number) {
138
+ if (this._mediaPlayerRef.value) {
139
+ this._mediaPlayerRef.value.playbackRate = value;
140
+ }
141
+ }
142
+ get playbackRate() {
143
+ return this._mediaPlayerRef.value?.playbackRate ?? 1;
144
+ }
145
+
146
+ set volume(value: number) {
147
+ if (this._mediaPlayerRef.value) {
148
+ this._mediaPlayerRef.value.volume = value;
149
+ }
150
+ }
151
+ get volume() {
152
+ return this._mediaPlayerRef.value?.volume ?? 1;
153
+ }
154
+
155
+ set muted(value: boolean) {
156
+ if (this._mediaPlayerRef.value) {
157
+ this._mediaPlayerRef.value.muted = value;
158
+ }
159
+ }
160
+ get muted() {
161
+ return this._mediaPlayerRef.value?.muted ?? false;
162
+ }
163
+
164
+ // get scene(): Scene | null {
165
+ // return this._mediaPlayerRef.value?.scene ?? null;
166
+ // }
167
+
168
+ // get scenes(): Scene[] {
169
+ // return this._mediaPlayerRef.value?.scenes ?? [];
170
+ // }
171
+
172
+ // get sceneConfig(): Scenes | null {
173
+ // return this._mediaPlayerRef.value?.sceneConfig ?? null;
174
+ // }
175
+
176
+ get mediaState() {
177
+ return this._mediaPlayerRef.value?.mediaState;
178
+ }
179
+
180
+ get mediaPlayer() {
181
+ return this._mediaPlayerRef.value;
182
+ }
183
+
184
+ play() {
185
+ this._mediaPlayerRef.value?.play();
186
+ }
187
+
188
+ pause() {
189
+ this._mediaPlayerRef.value?.pause();
190
+ }
191
+
192
+ reset(time = 0, play = false) {
193
+ this._mediaPlayerRef.value?.reset(time, play);
194
+ }
195
+
196
+ // setScene(index: number) {
197
+ // this._mediaPlayerRef.value?.setScene(index);
198
+ // }
199
+
93
200
  private _renderStyles() {
94
201
  if (!this.aspectRatio) {
95
202
  return html`
@@ -115,6 +222,13 @@ class PlayerEmbed extends MediaPlayerProxy(LitElement) {
115
222
  return null;
116
223
  }
117
224
 
225
+ protected willUpdate(changedProperties: PropertyValueMap<PlayerEmbedProps>) {
226
+ // If the vouch this embed is pointing to changes then reset the player
227
+ if (changedProperties.has('vouchId') && this.vouchId !== changedProperties.get('vouchId')) {
228
+ this.reset(0, false);
229
+ }
230
+ }
231
+
118
232
  render() {
119
233
  return html`
120
234
  ${this._renderStyles()}
@@ -123,10 +237,9 @@ class PlayerEmbed extends MediaPlayerProxy(LitElement) {
123
237
  ${this.eventController.register()}
124
238
  ?autoplay=${this.autoplay}
125
239
  ?loading=${this.fetching}
126
- .data=${this.vouch}
127
- .template=${this.template}
128
240
  aspectRatio=${ifDefined(this.aspectRatio)}
129
241
  preload=${ifDefined(this.preload)}
242
+ language=${ifDefined(this.language)}
130
243
  .controls=${this.controls}
131
244
  ></vmp-media-player>
132
245
  `;
@@ -4,7 +4,7 @@ import { html } from 'lit';
4
4
  import sinon from 'sinon';
5
5
 
6
6
  import type { PlayerEmbed } from '../index.js';
7
- import type { VideoMap } from '@vouchfor/media-player';
7
+ import type { MediaMap } from '@vouchfor/media-player';
8
8
 
9
9
  import { data } from './data.js';
10
10
 
@@ -13,7 +13,7 @@ import { data } from './data.js';
13
13
  // https://modern-web.dev/guides/test-runner/typescript/
14
14
  import '../../../test/lib/embeds.js';
15
15
 
16
- function getVideo(videos: VideoMap) {
16
+ function getVideo(videos: MediaMap) {
17
17
  return Object.values(videos)[0];
18
18
  }
19
19
 
@@ -27,8 +27,8 @@ function playerLoaded(player: PlayerEmbed) {
27
27
  );
28
28
  }
29
29
 
30
- describe('Embeds', () => {
31
- it('passes', async () => {
30
+ describe.skip('Embeds', () => {
31
+ it('Sends correct tracking events', async () => {
32
32
  const player = await fixture<PlayerEmbed>(
33
33
  html`<vouch-embed-player env="dev" .data=${data} aspectratio=${1}></vouch-embed-player>`
34
34
  );
@@ -45,17 +45,24 @@ describe('Embeds', () => {
45
45
  await waitUntil(
46
46
  () => {
47
47
  // Video plays for 3 seconds
48
- return (getVideo(player.mediaPlayer!.videos)?.node?.currentTime ?? 0) > 3;
48
+ return (getVideo(player.mediaPlayer!.media)?.node?.currentTime ?? 0) > 3;
49
49
  },
50
50
  'Video did not play for 3 seconds',
51
51
  { timeout: 20000 }
52
52
  );
53
- expect(getVideo(player.mediaPlayer!.videos)?.node?.paused).eq(false);
53
+ expect(getVideo(player.mediaPlayer!.media)?.node?.paused).eq(false);
54
54
  player.pause();
55
- expect(getVideo(player.mediaPlayer!.videos)?.node?.paused).eq(true);
55
+ expect(getVideo(player.mediaPlayer!.media)?.node?.paused).eq(true);
56
56
  expect(sendTrackingSpy.callCount).to.be.eq(0);
57
57
  // Destroy node because events are sent when node is removed from the document
58
58
  player.remove();
59
+ await waitUntil(
60
+ () => {
61
+ return createTrackingSpy.args[2];
62
+ },
63
+ 'Cleanup event has not fired',
64
+ { timeout: 5000 }
65
+ );
59
66
  expect(sendTrackingSpy.callCount).to.be.eq(1);
60
67
  expect(createTrackingSpy.args[0]).to.eql([
61
68
  'VIDEO_PLAYED',