@playpilot/tpi 8.32.0 → 8.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "8.32.0",
3
+ "version": "8.33.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -66,3 +66,16 @@
66
66
  }
67
67
  }
68
68
  }
69
+
70
+ @mixin safari() {
71
+ @media not all and (min-resolution: 0.001dpcm) {
72
+ @supports (-webkit-appearance: none) {
73
+ @content;
74
+ }
75
+ }
76
+
77
+ @supports (hanging-punctuation: first) and (font: -apple-system-body) and
78
+ (-webkit-appearance: none) {
79
+ @content;
80
+ }
81
+ }
@@ -0,0 +1,3 @@
1
+ <svg width="32px" height="32px" viewBox="0 -960 960 960">
2
+ <path fill="currentColor" d="M120-120v-200h80v120h120v80H120Zm520 0v-80h120v-120h80v200H640ZM120-640v-200h200v80H200v120h-80Zm640 0v-120H640v-80h200v200h-80Z" />
3
+ </svg>
@@ -188,7 +188,7 @@
188
188
  <div class="video-overlay" title="" {onclick}></div>
189
189
 
190
190
  {#if !!title.embeddable_url}
191
- <YouTubeEmbed bind:this={embed} embeddable_url={title.embeddable_url!} muted loop captions showMuteControls />
191
+ <YouTubeEmbed bind:this={embed} embeddable_url={title.embeddable_url!} muted loop captions showActions />
192
192
  {:else}
193
193
  <img class="video-fallback" src={title.medium_poster} alt="" />
194
194
  {/if}
@@ -1,7 +1,9 @@
1
1
  <script lang="ts">
2
- import { getVideoId } from '$lib/trailer'
2
+ import { getVideoId, openTrailerOverlay } from '$lib/trailer'
3
3
  import { onMount } from 'svelte'
4
4
  import IconMute from './Icons/IconMute.svelte'
5
+ import IconFullscreen from './Icons/IconFullscreen.svelte'
6
+ import type { TitleData } from '$lib/types/title'
5
7
 
6
8
  interface Props {
7
9
  embeddable_url: string
@@ -10,17 +12,19 @@
10
12
  loop?: boolean
11
13
  captions?: boolean
12
14
  autoplay?: boolean
13
- showMuteControls?: boolean
15
+ showActions?: boolean
14
16
  fallbackStartTime?: number
17
+ clickToPlay?: boolean
15
18
  }
16
19
 
17
- let { embeddable_url = '', controls = [], muted = false, loop = false, captions = false, autoplay = true, showMuteControls = false, fallbackStartTime = 0 }: Props = $props()
20
+ let { embeddable_url = '', controls = [], muted = false, loop = false, captions = false, autoplay = true, showActions = false, fallbackStartTime = 0, clickToPlay = true }: Props = $props()
18
21
 
19
22
  const videoId = $derived(getVideoId(embeddable_url))
20
23
  const startTime = $derived((window?.PlayPilotLinkInjections?.video_playtimes?.[videoId || ''] || (fallbackStartTime + 1)) - 1)
21
24
  const color = window?.getComputedStyle(document.body).getPropertyValue('--playpilot-primary')?.replace('#', '') || 'fa548a'
22
25
 
23
26
  let element: HTMLElement | null = $state(null)
27
+ let fullscreen: boolean = $state(false)
24
28
  let player: any
25
29
 
26
30
  onMount(() => {
@@ -45,11 +49,12 @@
45
49
  element.style.setProperty('--plyr-color-main', '#' + color)
46
50
 
47
51
  player = new window.Plyr(element, {
48
- controls,
52
+ controls: controls.length === 0 ? ['play', 'mute', 'fullscreen', 'progress', 'current-time'] : controls,
49
53
  playsinline: true,
50
54
  autoplay,
51
55
  muted,
52
- clickToPlay: false,
56
+ clickToPlay,
57
+ storage: { enabled: false },
53
58
  youtube: {
54
59
  noCookie: true,
55
60
  cc_load_policy: captions ? 1 : 0,
@@ -62,6 +67,16 @@
62
67
  toggleMute(muted)
63
68
  })
64
69
 
70
+ player.on('enterfullscreen', () => {
71
+ fullscreen = true
72
+ toggleMute(false)
73
+ })
74
+
75
+ player.on('exitfullscreen', () => {
76
+ fullscreen = false
77
+ toggleMute(true)
78
+ })
79
+
65
80
  if (!loop) return
66
81
 
67
82
  player.on('statechange', (event: { detail: { code: number } }) => {
@@ -75,10 +90,19 @@
75
90
  }
76
91
 
77
92
  export function toggleMute(state = !muted): void {
93
+ muted = state
78
94
  player.muted = state
79
- player.volume = muted ? 0 : 1
95
+ player.volume = state ? 0 : 1
96
+ }
80
97
 
81
- muted = state
98
+ export function enterFullscreen(): void {
99
+ if (/iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) {
100
+ openTrailerOverlay({ embeddable_url } as TitleData)
101
+ toggleMute(true)
102
+ return
103
+ }
104
+
105
+ player.fullscreen.enter()
82
106
  }
83
107
 
84
108
  export function play(event: CustomEvent): void {
@@ -100,13 +124,19 @@
100
124
  </svelte:head>
101
125
 
102
126
  {#if videoId}
103
- <div bind:this={element} class="player" data-plyr-provider="youtube" data-plyr-embed-id data-testid={embeddable_url}></div>
104
-
105
- {#if showMuteControls}
106
- <button class="mute" onclick={() => toggleMute()} aria-label="Mute">
107
- <IconMute {muted} />
108
- </button>
109
- {/if}
127
+ <div class:fullscreen class:hide-controls={controls.length === 0 && !fullscreen}>
128
+ <div bind:this={element} class="player" data-plyr-provider="youtube" data-plyr-embed-id data-testid={embeddable_url}></div>
129
+
130
+ {#if showActions}
131
+ <button class="action" onclick={() => toggleMute()} aria-label="Mute">
132
+ <IconMute {muted} />
133
+ </button>
134
+
135
+ <button class="action secondary" onclick={enterFullscreen} aria-label="Fullscreen">
136
+ <IconFullscreen />
137
+ </button>
138
+ {/if}
139
+ </div>
110
140
  {:else}
111
141
  Something went wrong
112
142
  {/if}
@@ -123,7 +153,20 @@
123
153
  height: 100%;
124
154
  }
125
155
 
126
- .mute {
156
+ .fullscreen :global(iframe) {
157
+ height: 100%;
158
+ width: 100%;
159
+ top: 0;
160
+ left: 0;
161
+ transform: none;
162
+ }
163
+
164
+ .hide-controls :global(.plyr__controls) {
165
+ display: none;
166
+ opacity: 0;
167
+ }
168
+
169
+ .action {
127
170
  z-index: 10;
128
171
  display: flex;
129
172
  align-items: center;
@@ -148,5 +191,9 @@
148
191
  &:active {
149
192
  transform: scale(0.95);
150
193
  }
194
+
195
+ &.secondary {
196
+ top: calc(margin(3) + var(--mute-top, 0px));
197
+ }
151
198
  }
152
199
  </style>
@@ -15,7 +15,7 @@
15
15
  </script>
16
16
 
17
17
  <div class="video-background" style="--height: {height}px; --width: {clientWidth}px;" bind:clientWidth bind:clientHeight data-testid="video-background">
18
- <YouTubeEmbed {embeddable_url} {autoplay} fallbackStartTime={5} muted loop showMuteControls />
18
+ <YouTubeEmbed {embeddable_url} {autoplay} fallbackStartTime={5} muted loop showActions clickToPlay={false} />
19
19
  </div>
20
20
 
21
21
  <style lang="scss">
@@ -48,7 +48,7 @@
48
48
  <h2>Youtube Embed</h2>
49
49
 
50
50
  <div class="video">
51
- <YouTubeEmbed embeddable_url="https://www.youtube.com/watch?v=bBMajXwi6Cs" controls={['progress', 'play']} loop showMuteControls muted />
51
+ <YouTubeEmbed embeddable_url="https://www.youtube.com/watch?v=bBMajXwi6Cs" controls={['progress', 'play']} loop showActions muted />
52
52
  </div>
53
53
 
54
54
  <h2>Title</h2>
@@ -54,10 +54,10 @@ describe('YouTubeEmbed.svelte', () => {
54
54
  expect(Plyr).toHaveBeenCalledWith(
55
55
  playerElement,
56
56
  expect.objectContaining({
57
- controls: [],
57
+ controls: ['play', 'mute', 'fullscreen', 'progress', 'current-time'],
58
58
  autoplay: true,
59
59
  muted: false,
60
- clickToPlay: false,
60
+ clickToPlay: true,
61
61
  playsinline: true,
62
62
  youtube: expect.objectContaining({ noCookie: true, cc_load_policy: 0, cc_lang_pref: 'auto' }),
63
63
  }),
@@ -145,4 +145,11 @@ describe('YouTubeEmbed.svelte', () => {
145
145
  expect(container.querySelector('.player')).not.toBeTruthy()
146
146
  expect(getByText('Something went wrong')).toBeTruthy()
147
147
  })
148
+
149
+ it('Should render actions when showActions is true', () => {
150
+ const { getByLabelText } = render(YouTubeEmbed, { embeddable_url: 'youtube.com/watch?v=abc', showActions: true })
151
+
152
+ expect(getByLabelText('Mute')).toBeTruthy()
153
+ expect(getByLabelText('Fullscreen')).toBeTruthy()
154
+ })
148
155
  })