@growth-labs/video 0.1.0

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.
Files changed (66) hide show
  1. package/dist/access-registry.d.ts +16 -0
  2. package/dist/access-registry.d.ts.map +1 -0
  3. package/dist/access-registry.js +15 -0
  4. package/dist/access-registry.js.map +1 -0
  5. package/dist/index.d.ts +19 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +44 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/options.d.ts +136 -0
  10. package/dist/options.d.ts.map +1 -0
  11. package/dist/options.js +53 -0
  12. package/dist/options.js.map +1 -0
  13. package/dist/routes/playback.d.ts +3 -0
  14. package/dist/routes/playback.d.ts.map +1 -0
  15. package/dist/routes/playback.js +83 -0
  16. package/dist/routes/playback.js.map +1 -0
  17. package/dist/routes/session.d.ts +3 -0
  18. package/dist/routes/session.d.ts.map +1 -0
  19. package/dist/routes/session.js +69 -0
  20. package/dist/routes/session.js.map +1 -0
  21. package/dist/types.d.ts +31 -0
  22. package/dist/types.d.ts.map +1 -0
  23. package/dist/types.js +2 -0
  24. package/dist/types.js.map +1 -0
  25. package/dist/utils/captions.d.ts +7 -0
  26. package/dist/utils/captions.d.ts.map +1 -0
  27. package/dist/utils/captions.js +66 -0
  28. package/dist/utils/captions.js.map +1 -0
  29. package/dist/utils/chapters.d.ts +7 -0
  30. package/dist/utils/chapters.d.ts.map +1 -0
  31. package/dist/utils/chapters.js +40 -0
  32. package/dist/utils/chapters.js.map +1 -0
  33. package/dist/utils/index.d.ts +8 -0
  34. package/dist/utils/index.d.ts.map +1 -0
  35. package/dist/utils/index.js +8 -0
  36. package/dist/utils/index.js.map +1 -0
  37. package/dist/utils/jsonld.d.ts +3 -0
  38. package/dist/utils/jsonld.d.ts.map +1 -0
  39. package/dist/utils/jsonld.js +42 -0
  40. package/dist/utils/jsonld.js.map +1 -0
  41. package/dist/utils/keys.d.ts +6 -0
  42. package/dist/utils/keys.d.ts.map +1 -0
  43. package/dist/utils/keys.js +16 -0
  44. package/dist/utils/keys.js.map +1 -0
  45. package/dist/utils/session.d.ts +10 -0
  46. package/dist/utils/session.d.ts.map +1 -0
  47. package/dist/utils/session.js +70 -0
  48. package/dist/utils/session.js.map +1 -0
  49. package/dist/utils/url.d.ts +7 -0
  50. package/dist/utils/url.d.ts.map +1 -0
  51. package/dist/utils/url.js +25 -0
  52. package/dist/utils/url.js.map +1 -0
  53. package/dist/utils/youtube.d.ts +12 -0
  54. package/dist/utils/youtube.d.ts.map +1 -0
  55. package/dist/utils/youtube.js +21 -0
  56. package/dist/utils/youtube.js.map +1 -0
  57. package/dist/vite-plugin.d.ts +4 -0
  58. package/dist/vite-plugin.d.ts.map +1 -0
  59. package/dist/vite-plugin.js +18 -0
  60. package/dist/vite-plugin.js.map +1 -0
  61. package/package.json +65 -0
  62. package/src/components/Video.astro +164 -0
  63. package/src/components/VideoEmbed.astro +109 -0
  64. package/src/components/VideoGallery.astro +36 -0
  65. package/src/components/VideoPreview.astro +70 -0
  66. package/src/components/index.ts +4 -0
@@ -0,0 +1,109 @@
1
+ ---
2
+ import { config } from 'virtual:growth-labs/video/config'
3
+ import { youtubeEmbedUrl, youtubeThumbnailUrl } from '@growth-labs/video/utils'
4
+
5
+ interface Props {
6
+ videoId: string
7
+ title: string
8
+ start?: number
9
+ end?: number
10
+ autoplay?: boolean
11
+ muted?: boolean
12
+ privacyMode?: boolean
13
+ lazyLoad?: boolean
14
+ class?: string
15
+ }
16
+
17
+ const {
18
+ videoId,
19
+ title,
20
+ start,
21
+ end,
22
+ autoplay,
23
+ muted,
24
+ class: className,
25
+ } = Astro.props
26
+ const privacyMode = Astro.props.privacyMode ?? config.youtube.privacyMode
27
+ const lazyLoad = Astro.props.lazyLoad ?? config.youtube.lazyLoad
28
+
29
+ const embedSrc = youtubeEmbedUrl(videoId, { start, end, autoplay, muted, privacyMode })
30
+ const thumbnail = youtubeThumbnailUrl(videoId, 'maxres')
31
+ ---
32
+
33
+ {lazyLoad ? (
34
+ <div class={`gl-youtube-embed ${className ?? ''}`.trim()} data-youtube-embed data-src={embedSrc}>
35
+ <button type="button" class="gl-youtube-play" aria-label={`Play ${title}`}>
36
+ <img src={thumbnail} alt="" loading="lazy" decoding="async" />
37
+ <span aria-hidden="true" class="gl-youtube-play-icon">▶</span>
38
+ </button>
39
+ </div>
40
+ ) : (
41
+ <iframe
42
+ class={`gl-youtube-embed ${className ?? ''}`.trim()}
43
+ src={embedSrc}
44
+ title={title}
45
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
46
+ allowfullscreen
47
+ loading="lazy"
48
+ ></iframe>
49
+ )}
50
+
51
+ <script>
52
+ document.querySelectorAll<HTMLElement>('[data-youtube-embed]').forEach((wrapper) => {
53
+ const button = wrapper.querySelector<HTMLButtonElement>('.gl-youtube-play')
54
+ const src = wrapper.dataset.src
55
+ if (!button || !src) return
56
+ button.addEventListener(
57
+ 'click',
58
+ () => {
59
+ const iframe = document.createElement('iframe')
60
+ iframe.src = `${src}${src.includes('?') ? '&' : '?'}autoplay=1`
61
+ iframe.title = button.getAttribute('aria-label')?.replace(/^Play /, '') ?? ''
62
+ iframe.allow =
63
+ 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture'
64
+ iframe.setAttribute('allowfullscreen', '')
65
+ iframe.className = wrapper.className
66
+ wrapper.replaceWith(iframe)
67
+ },
68
+ { once: true },
69
+ )
70
+ })
71
+ </script>
72
+
73
+ <style>
74
+ .gl-youtube-embed {
75
+ width: 100%;
76
+ aspect-ratio: 16 / 9;
77
+ display: block;
78
+ position: relative;
79
+ border: 0;
80
+ background: #000;
81
+ }
82
+ .gl-youtube-play {
83
+ all: unset;
84
+ display: block;
85
+ cursor: pointer;
86
+ width: 100%;
87
+ height: 100%;
88
+ position: relative;
89
+ }
90
+ .gl-youtube-play img {
91
+ width: 100%;
92
+ height: 100%;
93
+ object-fit: cover;
94
+ display: block;
95
+ }
96
+ .gl-youtube-play-icon {
97
+ position: absolute;
98
+ top: 50%;
99
+ left: 50%;
100
+ transform: translate(-50%, -50%);
101
+ font-size: 3rem;
102
+ color: #fff;
103
+ text-shadow: 0 0 12px rgba(0, 0, 0, 0.7);
104
+ }
105
+ .gl-youtube-play:focus-visible {
106
+ outline: 2px solid currentColor;
107
+ outline-offset: 2px;
108
+ }
109
+ </style>
@@ -0,0 +1,36 @@
1
+ ---
2
+ import VideoPreview from './VideoPreview.astro'
3
+
4
+ interface Item {
5
+ videoId: string
6
+ title: string
7
+ href: string
8
+ durationSeconds?: number
9
+ }
10
+
11
+ interface Props {
12
+ items: Item[]
13
+ class?: string
14
+ }
15
+
16
+ const { items, class: className } = Astro.props
17
+ ---
18
+
19
+ <div class={`gl-video-gallery ${className ?? ''}`.trim()}>
20
+ {items.map((item) => (
21
+ <VideoPreview
22
+ videoId={item.videoId}
23
+ title={item.title}
24
+ href={item.href}
25
+ durationSeconds={item.durationSeconds}
26
+ />
27
+ ))}
28
+ </div>
29
+
30
+ <style>
31
+ .gl-video-gallery {
32
+ display: grid;
33
+ grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
34
+ gap: 1rem;
35
+ }
36
+ </style>
@@ -0,0 +1,70 @@
1
+ ---
2
+ import { config } from 'virtual:growth-labs/video/config'
3
+ import { posterUrl } from '@growth-labs/video/utils'
4
+
5
+ interface Props {
6
+ videoId: string
7
+ title: string
8
+ href: string
9
+ durationSeconds?: number
10
+ class?: string
11
+ }
12
+
13
+ const { videoId, title, href, durationSeconds, class: className } = Astro.props
14
+ const poster = posterUrl(config.publicDomain, videoId)
15
+ const durationLabel = durationSeconds
16
+ ? `${Math.floor(durationSeconds / 60)}:${String(durationSeconds % 60).padStart(2, '0')}`
17
+ : null
18
+ ---
19
+
20
+ <a class={`gl-video-preview ${className ?? ''}`.trim()} href={href} aria-label={title}>
21
+ <img src={poster} alt="" loading="lazy" decoding="async" />
22
+ <span aria-hidden="true" class="gl-video-preview-play">▶</span>
23
+ <span class="gl-video-preview-title">{title}</span>
24
+ {durationLabel ? <span class="gl-video-preview-duration">{durationLabel}</span> : null}
25
+ </a>
26
+
27
+ <style>
28
+ .gl-video-preview {
29
+ display: block;
30
+ position: relative;
31
+ aspect-ratio: 16 / 9;
32
+ text-decoration: none;
33
+ color: inherit;
34
+ }
35
+ .gl-video-preview img {
36
+ width: 100%;
37
+ height: 100%;
38
+ object-fit: cover;
39
+ }
40
+ .gl-video-preview-play {
41
+ position: absolute;
42
+ inset: 0;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ font-size: 2.5rem;
47
+ color: #fff;
48
+ text-shadow: 0 0 12px rgba(0, 0, 0, 0.7);
49
+ }
50
+ .gl-video-preview-title {
51
+ position: absolute;
52
+ left: 0.75rem;
53
+ bottom: 0.75rem;
54
+ right: 0.75rem;
55
+ color: #fff;
56
+ font-weight: 600;
57
+ line-height: 1.2;
58
+ text-shadow: 0 0 8px rgba(0, 0, 0, 0.8);
59
+ }
60
+ .gl-video-preview-duration {
61
+ position: absolute;
62
+ right: 0.5rem;
63
+ top: 0.5rem;
64
+ background: rgba(0, 0, 0, 0.75);
65
+ color: #fff;
66
+ padding: 0.125rem 0.5rem;
67
+ border-radius: 0.25rem;
68
+ font-size: 0.875rem;
69
+ }
70
+ </style>
@@ -0,0 +1,4 @@
1
+ export { default as Video } from './Video.astro'
2
+ export { default as VideoEmbed } from './VideoEmbed.astro'
3
+ export { default as VideoGallery } from './VideoGallery.astro'
4
+ export { default as VideoPreview } from './VideoPreview.astro'