sanity-plugin-mux-input 2.14.0 → 2.15.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 (48) hide show
  1. package/README.md +25 -24
  2. package/dist/index.d.mts +13 -1
  3. package/dist/index.d.ts +13 -1
  4. package/dist/index.js +771 -351
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +773 -353
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +1 -1
  9. package/src/_exports/index.ts +1 -0
  10. package/src/actions/secrets.ts +6 -1
  11. package/src/actions/upload.ts +1 -1
  12. package/src/components/ConfigureApi.tsx +51 -5
  13. package/src/components/EditCaptionDialog.tsx +2 -2
  14. package/src/components/InputBrowser.tsx +8 -2
  15. package/src/components/PageSelector.tsx +4 -7
  16. package/src/components/Player.styled.tsx +7 -2
  17. package/src/components/PlayerActionsMenu.tsx +1 -1
  18. package/src/components/SelectAsset.tsx +9 -3
  19. package/src/components/StudioTool.tsx +2 -2
  20. package/src/components/UploadConfiguration.tsx +104 -343
  21. package/src/components/Uploader.tsx +18 -7
  22. package/src/components/VideoDetails/VideoDetails.tsx +28 -8
  23. package/src/components/VideoInBrowser.tsx +53 -6
  24. package/src/components/VideoPlayer.tsx +120 -47
  25. package/src/components/VideoThumbnail.tsx +84 -72
  26. package/src/components/VideosBrowser.tsx +7 -5
  27. package/src/components/uploadConfiguration/PlaybackPolicy.tsx +95 -6
  28. package/src/components/uploadConfiguration/PlaybackPolicyOption.tsx +26 -10
  29. package/src/components/uploadConfiguration/ResolutionTierSelector.tsx +71 -0
  30. package/src/components/uploadConfiguration/StaticRenditionSelector.tsx +179 -0
  31. package/src/context/DrmPlaybackWarningContext.tsx +93 -0
  32. package/src/hooks/useFetchFileSize.ts +54 -0
  33. package/src/hooks/useMediaMetadata.ts +100 -0
  34. package/src/hooks/useSaveSecrets.ts +10 -3
  35. package/src/hooks/useSecretsDocumentValues.ts +9 -1
  36. package/src/hooks/useSecretsFormState.ts +6 -3
  37. package/src/util/asserters.ts +14 -0
  38. package/src/util/createUrlParamsObject.ts +7 -3
  39. package/src/util/generateJwt.ts +11 -2
  40. package/src/util/getPlaybackPolicy.ts +63 -4
  41. package/src/util/getStoryboardSrc.ts +7 -3
  42. package/src/util/getVideoMetadata.ts +1 -0
  43. package/src/util/getVideoSrc.ts +9 -9
  44. package/src/util/readSecrets.ts +3 -1
  45. package/src/util/textTracks.ts +6 -3
  46. package/src/util/tryWithSuspend.ts +22 -0
  47. package/src/util/types.ts +27 -2
  48. package/src/util/getPlaybackId.ts +0 -9
@@ -27,7 +27,7 @@ import {
27
27
  import React, {useEffect, useState} from 'react'
28
28
 
29
29
  import {DIALOGS_Z_INDEX} from '../../util/constants'
30
- import type {MuxTextTrack} from '../../util/types'
30
+ import {MuxPlaybackId, MuxTextTrack, PlaybackPolicy} from '../../util/types'
31
31
  import FormField from '../FormField'
32
32
  import IconInfo from '../IconInfo'
33
33
  import {ResolutionIcon} from '../icons/Resolution'
@@ -295,13 +295,7 @@ const VideoDetails: React.FC<VideoDetailsProps> = (props) => {
295
295
  size={2}
296
296
  />
297
297
  <IconInfo text={`Mux ID: \n${displayInfo.id}`} icon={TagIcon} size={2} />
298
- {displayInfo?.playbackId && (
299
- <IconInfo
300
- text={`Playback ID: ${displayInfo.playbackId}`}
301
- icon={TagIcon}
302
- size={2}
303
- />
304
- )}
298
+ <PlaybackIds playback_ids={displayInfo.playback_ids} />
305
299
  </Stack>
306
300
  </Stack>
307
301
  </TabPanel>
@@ -319,4 +313,30 @@ const VideoDetails: React.FC<VideoDetailsProps> = (props) => {
319
313
  )
320
314
  }
321
315
 
316
+ const PlaybackIds = ({playback_ids}: {playback_ids?: MuxPlaybackId[]}) => {
317
+ if (playback_ids) {
318
+ return playback_ids.map((entry) => (
319
+ <IconInfo
320
+ key={entry.id}
321
+ text={`Playback ID [${policyToText(entry.policy)}]: ${entry.id}`}
322
+ icon={TagIcon}
323
+ size={2}
324
+ />
325
+ ))
326
+ }
327
+ return <IconInfo text={'No Playback ID'} icon={TagIcon} size={2} />
328
+ }
329
+
330
+ const policyToText = (policy: PlaybackPolicy) => {
331
+ switch (policy) {
332
+ case 'drm':
333
+ return 'DRM'
334
+ case 'signed':
335
+ return 'Signed'
336
+ case 'public':
337
+ return 'Public'
338
+ default:
339
+ return policy
340
+ }
341
+ }
322
342
  export default VideoDetails
@@ -3,6 +3,7 @@ import {Button, Card, Stack, Text, Tooltip} from '@sanity/ui'
3
3
  import React, {useState} from 'react'
4
4
  import {styled} from 'styled-components'
5
5
 
6
+ import {DRMWarningDialog, useDrmPlaybackWarningContext} from '../context/DrmPlaybackWarningContext'
6
7
  import {THUMBNAIL_ASPECT_RATIO} from '../util/constants'
7
8
  import {getPlaybackPolicy} from '../util/getPlaybackPolicy'
8
9
  import {VideoAssetDocument} from '../util/types'
@@ -71,6 +72,8 @@ const PlayButton = styled.button`
71
72
  }
72
73
  `
73
74
 
75
+ type RenderState = 'render-video' | 'pre-render-warn' | false
76
+
74
77
  export default function VideoInBrowser({
75
78
  onSelect,
76
79
  onEdit,
@@ -80,16 +83,23 @@ export default function VideoInBrowser({
80
83
  onEdit?: (asset: VideoAssetDocument) => void
81
84
  asset: VideoAssetDocument
82
85
  }) {
83
- const [renderVideo, setRenderVideo] = useState(false)
86
+ const [renderVideo, setRenderVideo] = useState<RenderState>(false)
84
87
  const select = React.useCallback(() => onSelect?.(asset), [onSelect, asset])
85
88
  const edit = React.useCallback(() => onEdit?.(asset), [onEdit, asset])
89
+ const {hasShownWarning} = useDrmPlaybackWarningContext()
86
90
 
87
91
  if (!asset) {
88
92
  return null
89
93
  }
90
94
 
91
95
  const playbackPolicy = getPlaybackPolicy(asset)
92
-
96
+ const onClickPlay = () => {
97
+ if (playbackPolicy?.policy === 'drm' && !hasShownWarning) {
98
+ setRenderVideo('pre-render-warn')
99
+ } else {
100
+ setRenderVideo('render-video')
101
+ }
102
+ }
93
103
  return (
94
104
  <Card
95
105
  border
@@ -100,7 +110,7 @@ export default function VideoInBrowser({
100
110
  position: 'relative',
101
111
  }}
102
112
  >
103
- {playbackPolicy === 'signed' && (
113
+ {playbackPolicy?.policy === 'signed' && (
104
114
  <Tooltip
105
115
  animate
106
116
  content={
@@ -119,7 +129,7 @@ export default function VideoInBrowser({
119
129
  position: 'absolute',
120
130
  left: '1em',
121
131
  top: '1em',
122
- zIndex: 10,
132
+ zIndex: 11,
123
133
  }}
124
134
  padding={2}
125
135
  border
@@ -130,6 +140,36 @@ export default function VideoInBrowser({
130
140
  </Card>
131
141
  </Tooltip>
132
142
  )}
143
+ {playbackPolicy?.policy === 'drm' && (
144
+ <Tooltip
145
+ animate
146
+ content={
147
+ <Card padding={2} radius={2}>
148
+ <IconInfo icon={LockIcon} text="DRM playback policy" size={2} />
149
+ </Card>
150
+ }
151
+ placement="right"
152
+ fallbackPlacements={['top', 'bottom']}
153
+ portal
154
+ >
155
+ <Card
156
+ tone="caution"
157
+ style={{
158
+ borderRadius: '0.25rem',
159
+ position: 'absolute',
160
+ left: '1em',
161
+ top: '1em',
162
+ zIndex: 11,
163
+ }}
164
+ padding={2}
165
+ border
166
+ >
167
+ <Text muted size={1} weight="semibold" style={{color: 'var(--card-icon-color)'}}>
168
+ DRM
169
+ </Text>
170
+ </Card>
171
+ </Tooltip>
172
+ )}
133
173
  <Stack
134
174
  space={3}
135
175
  height="fill"
@@ -137,10 +177,17 @@ export default function VideoInBrowser({
137
177
  gridTemplateRows: 'min-content min-content 1fr',
138
178
  }}
139
179
  >
140
- {renderVideo ? (
180
+ {renderVideo === 'pre-render-warn' && (
181
+ <DRMWarningDialog
182
+ onClose={() => {
183
+ setRenderVideo('render-video')
184
+ }}
185
+ />
186
+ )}
187
+ {renderVideo === 'render-video' ? (
141
188
  <VideoPlayer asset={asset} autoPlay forceAspectRatio={THUMBNAIL_ASPECT_RATIO} />
142
189
  ) : (
143
- <PlayButton onClick={() => setRenderVideo(true)}>
190
+ <PlayButton onClick={onClickPlay}>
144
191
  <div data-play>
145
192
  <PlayIcon />
146
193
  </div>
@@ -2,13 +2,17 @@ import {type MuxPlayerProps, type MuxPlayerRefAttributes} from '@mux/mux-player-
2
2
  import MuxPlayer from '@mux/mux-player-react/lazy'
3
3
  import {ErrorOutlineIcon} from '@sanity/icons'
4
4
  import {Card, Text} from '@sanity/ui'
5
- import {type PropsWithChildren, useMemo, useRef} from 'react'
5
+ import {type PropsWithChildren, Suspense, useMemo, useRef, useState} from 'react'
6
6
 
7
7
  import {useDialogStateContext} from '../context/DialogStateContext'
8
8
  import {useClient} from '../hooks/useClient'
9
9
  import {AUDIO_ASPECT_RATIO, MIN_ASPECT_RATIO} from '../util/constants'
10
+ import {generateJwt} from '../util/generateJwt'
11
+ import {getPlaybackId} from '../util/getPlaybackPolicy'
12
+ import {getPlaybackPolicyById} from '../util/getPlaybackPolicy'
10
13
  import {getPosterSrc} from '../util/getPosterSrc'
11
14
  import {getVideoSrc} from '../util/getVideoSrc'
15
+ import {tryWithSuspend} from '../util/tryWithSuspend'
12
16
  import type {VideoAssetDocument} from '../util/types'
13
17
  import CaptionsDialog from './CaptionsDialog'
14
18
  import EditThumbnailDialog from './EditThumbnailDialog'
@@ -33,32 +37,101 @@ export default function VideoPlayer({
33
37
 
34
38
  const isAudio = assetIsAudio(asset)
35
39
  const muxPlayer = useRef<MuxPlayerRefAttributes>(null)
40
+ const [error, setError] = useState<Error>()
36
41
 
37
- const {
38
- src: videoSrc,
39
- thumbnail: thumbnailSrc,
40
- error,
41
- } = useMemo(() => {
42
+ /* Playback ID that will be used to play the video */
43
+ const playbackId = useMemo(() => {
42
44
  try {
43
- const thumbnail = getPosterSrc({asset, client, width: thumbnailWidth})
44
- const src = asset?.playbackId && getVideoSrc({client, asset})
45
- if (src) return {src: src, thumbnail}
46
-
47
- return {error: new TypeError('Asset has no playback ID')}
48
- // eslint-disable-next-line @typescript-eslint/no-shadow
49
- } catch (error) {
50
- return {error}
45
+ return getPlaybackId(asset, ['public', 'signed', 'drm'])
46
+ } catch (e) {
47
+ setError(new TypeError('Asset has no playback ID'))
48
+ return undefined
51
49
  }
50
+ }, [asset])
51
+
52
+ const muxPlaybackId = useMemo(() => {
53
+ if (!playbackId) return undefined
54
+ return getPlaybackPolicyById(asset, playbackId)
55
+ }, [asset, playbackId])
56
+
57
+ const src = useMemo(() => {
58
+ if (!playbackId) return undefined
59
+ if (!muxPlaybackId) return undefined
60
+ return tryWithSuspend(
61
+ () => getVideoSrc({muxPlaybackId, client}),
62
+ (e: Error) => {
63
+ setError(e)
64
+ return undefined
65
+ }
66
+ )
67
+ }, [muxPlaybackId, playbackId, client])
68
+
69
+ const poster = useMemo(() => {
70
+ return tryWithSuspend(
71
+ () => getPosterSrc({asset, client, width: thumbnailWidth}),
72
+ (e: Error) => {
73
+ setError(e)
74
+ return undefined
75
+ }
76
+ )
52
77
  }, [asset, client, thumbnailWidth])
53
78
 
54
79
  const signedToken = useMemo(() => {
55
80
  try {
56
- const url = new URL(videoSrc!)
81
+ const url = new URL(src!)
57
82
  return url.searchParams.get('token')
58
83
  } catch {
59
- return false
84
+ return undefined
85
+ }
86
+ }, [src])
87
+ const drmToken = useMemo(() => {
88
+ if (!playbackId) return undefined
89
+ if (muxPlaybackId?.policy !== 'drm') return undefined
90
+
91
+ return tryWithSuspend(
92
+ () => generateJwt(client, playbackId, 'd'),
93
+ (e: Error) => {
94
+ setError(e)
95
+ return undefined
96
+ }
97
+ )
98
+ }, [client, muxPlaybackId?.policy, playbackId])
99
+ const tokens:
100
+ | Partial<{
101
+ playback?: string
102
+ thumbnail?: string
103
+ storyboard?: string
104
+ drm?: string
105
+ }>
106
+ | undefined = useMemo(() => {
107
+ try {
108
+ const partialTokens: {
109
+ playback?: string
110
+ thumbnail?: string
111
+ storyboard?: string
112
+ drm?: string
113
+ } = {
114
+ playback: undefined,
115
+ thumbnail: undefined,
116
+ storyboard: undefined,
117
+ drm: undefined,
118
+ }
119
+
120
+ if (signedToken) {
121
+ partialTokens.playback = signedToken
122
+ partialTokens.thumbnail = signedToken
123
+ partialTokens.storyboard = signedToken
124
+ }
125
+
126
+ if (drmToken) {
127
+ partialTokens.drm = drmToken
128
+ }
129
+
130
+ return {...partialTokens}
131
+ } catch {
132
+ return undefined
60
133
  }
61
- }, [videoSrc])
134
+ }, [signedToken, drmToken])
62
135
 
63
136
  const [width, height] = (asset?.data?.aspect_ratio ?? '16:9').split(':').map(Number)
64
137
  const targetAspectRatio =
@@ -71,6 +144,8 @@ export default function VideoPlayer({
71
144
  : AUDIO_ASPECT_RATIO
72
145
  }
73
146
 
147
+ /* We use Suspense here because `generateJwt` and related functions use suspend()
148
+ under the hood */
74
149
  return (
75
150
  <>
76
151
  <Card
@@ -81,7 +156,7 @@ export default function VideoPlayer({
81
156
  ...(isAudio && {display: 'flex', alignItems: 'flex-end'}),
82
157
  }}
83
158
  >
84
- {videoSrc && (
159
+ {src && poster && (
85
160
  <>
86
161
  {isAudio && (
87
162
  <AudioIcon
@@ -96,35 +171,33 @@ export default function VideoPlayer({
96
171
  }}
97
172
  />
98
173
  )}
99
- <MuxPlayer
100
- poster={isAudio ? undefined : thumbnailSrc}
101
- ref={muxPlayer}
102
- {...props}
103
- playsInline
104
- playbackId={asset.playbackId}
105
- tokens={
106
- signedToken
107
- ? {playback: signedToken, thumbnail: signedToken, storyboard: signedToken}
108
- : undefined
109
- }
110
- preload="metadata"
111
- crossOrigin="anonymous"
112
- metadata={{
113
- player_name: 'Sanity Admin Dashboard',
114
- player_version: process.env.PKG_VERSION,
115
- page_type: 'Preview Player',
116
- }}
117
- audio={isAudio}
118
- _hlsConfig={hlsConfig}
119
- style={{
120
- ...(!isAudio && {height: '100%'}),
121
- width: '100%',
122
- display: 'block',
123
- objectFit: 'contain',
124
- ...(isAudio && {alignSelf: 'end'}),
125
- }}
126
- />
127
- {children}
174
+ <Suspense fallback={null}>
175
+ <MuxPlayer
176
+ poster={isAudio ? undefined : poster}
177
+ ref={muxPlayer}
178
+ {...props}
179
+ playsInline
180
+ playbackId={playbackId}
181
+ tokens={tokens}
182
+ preload="metadata"
183
+ crossOrigin="anonymous"
184
+ metadata={{
185
+ player_name: 'Sanity Admin Dashboard',
186
+ player_version: process.env.PKG_VERSION,
187
+ page_type: 'Preview Player',
188
+ }}
189
+ audio={isAudio}
190
+ _hlsConfig={hlsConfig}
191
+ style={{
192
+ ...(!isAudio && {height: '100%'}),
193
+ width: '100%',
194
+ display: 'block',
195
+ objectFit: 'contain',
196
+ ...(isAudio && {alignSelf: 'end'}),
197
+ }}
198
+ />
199
+ {children}
200
+ </Suspense>
128
201
  </>
129
202
  )}
130
203
  {error ? (
@@ -1,6 +1,6 @@
1
1
  import {ErrorOutlineIcon} from '@sanity/icons'
2
2
  import {Box, Card, CardTone, Spinner, Stack, Text} from '@sanity/ui'
3
- import {useMemo, useRef, useState} from 'react'
3
+ import {Suspense, useMemo, useRef, useState} from 'react'
4
4
  import {styled} from 'styled-components'
5
5
 
6
6
  import {useClient} from '../hooks/useClient'
@@ -8,6 +8,7 @@ import {useInView} from '../hooks/useInView'
8
8
  import {THUMBNAIL_ASPECT_RATIO} from '../util/constants'
9
9
  import {getAnimatedPosterSrc} from '../util/getAnimatedPosterSrc'
10
10
  import {getPosterSrc} from '../util/getPosterSrc'
11
+ import {tryWithSuspend} from '../util/tryWithSuspend'
11
12
  import {AssetThumbnailOptions, MuxAnimatedThumbnailUrl, MuxThumbnailUrl} from '../util/types'
12
13
 
13
14
  const Image = styled.img`
@@ -36,91 +37,102 @@ export default function VideoThumbnail({
36
37
  width?: number
37
38
  staticImage?: boolean
38
39
  }) {
40
+ const posterWidth = width || 250
41
+ const client = useClient()
39
42
  const ref = useRef<HTMLDivElement | null>(null)
40
43
  const inView = useInView(ref)
41
- const posterWidth = width || 250
42
44
 
43
45
  const [status, setStatus] = useState<ImageStatus>('loading')
44
- const client = useClient()
46
+ const [error, setError] = useState<string | null>(null)
45
47
 
46
- const src = useMemo(() => {
47
- try {
48
- let thumbnail: MuxAnimatedThumbnailUrl | MuxThumbnailUrl
49
- if (staticImage) thumbnail = getPosterSrc({asset, client, width: posterWidth})
50
- else thumbnail = getAnimatedPosterSrc({asset, client, width: posterWidth})
48
+ const thumbnailSrc = useMemo(() => {
49
+ return tryWithSuspend(
50
+ () => {
51
+ let thumbnail: MuxAnimatedThumbnailUrl | MuxThumbnailUrl | undefined
51
52
 
52
- return thumbnail
53
- } catch {
54
- if (status !== 'error') setStatus('error')
55
- return undefined
56
- }
57
- }, [asset, client, posterWidth, status, staticImage])
53
+ if (staticImage) thumbnail = getPosterSrc({asset, client, width: posterWidth})
54
+ else thumbnail = getAnimatedPosterSrc({asset, client, width: posterWidth})
55
+ return thumbnail
56
+ },
57
+ (err: Error) => {
58
+ handleError(err.message)
59
+ return undefined
60
+ }
61
+ )
62
+ }, [asset, client, posterWidth, staticImage])
58
63
 
59
64
  function handleLoad() {
60
65
  setStatus('loaded')
61
66
  }
62
67
 
63
- function handleError() {
68
+ function handleError(err?: string) {
64
69
  setStatus('error')
70
+ if (err) {
71
+ setError(err)
72
+ } else {
73
+ setError('Failed loading thumbnail')
74
+ }
65
75
  }
66
76
 
67
77
  return (
68
- <Card
69
- style={{
70
- aspectRatio: THUMBNAIL_ASPECT_RATIO,
71
- position: 'relative',
72
- maxWidth: width ? `${width}px` : undefined,
73
- width: '100%',
74
- flex: 1,
75
- }}
76
- border
77
- radius={2}
78
- ref={ref}
79
- tone={STATUS_TO_TONE[status]}
80
- >
81
- {inView ? (
82
- <>
83
- {status === 'loading' && (
84
- <Box
85
- style={{
86
- position: 'absolute',
87
- left: '50%',
88
- top: '50%',
89
- transform: 'translate(-50%, -50%)',
90
- }}
91
- >
92
- <Spinner />
93
- </Box>
94
- )}
95
- {status === 'error' && (
96
- <Stack
97
- space={4}
98
- style={{
99
- position: 'absolute',
100
- width: '100%',
101
- left: 0,
102
- top: '50%',
103
- transform: 'translateY(-50%)',
104
- justifyItems: 'center',
105
- }}
106
- >
107
- <Text size={4} muted>
108
- <ErrorOutlineIcon style={{fontSize: '1.75em'}} />
109
- </Text>
110
- <Text muted align="center">
111
- Failed loading thumbnail
112
- </Text>
113
- </Stack>
114
- )}
115
- <Image
116
- src={src}
117
- alt={`Preview for ${staticImage ? 'image' : 'video'} ${asset.filename || asset.assetId}`}
118
- onLoad={handleLoad}
119
- onError={handleError}
120
- style={{opacity: status === 'loaded' ? 1 : 0}}
121
- />
122
- </>
123
- ) : null}
124
- </Card>
78
+ <Suspense fallback={<span>Preparing thumbnail</span>}>
79
+ <Card
80
+ style={{
81
+ aspectRatio: THUMBNAIL_ASPECT_RATIO,
82
+ position: 'relative',
83
+ maxWidth: width ? `${width}px` : undefined,
84
+ width: '100%',
85
+ flex: 1,
86
+ }}
87
+ border
88
+ radius={2}
89
+ ref={ref}
90
+ tone={STATUS_TO_TONE[status]}
91
+ >
92
+ {inView ? (
93
+ <>
94
+ {status === 'loading' && (
95
+ <Box
96
+ style={{
97
+ position: 'absolute',
98
+ left: '50%',
99
+ top: '50%',
100
+ transform: 'translate(-50%, -50%)',
101
+ }}
102
+ >
103
+ <Spinner />
104
+ </Box>
105
+ )}
106
+ {status === 'error' && (
107
+ <Stack
108
+ space={4}
109
+ style={{
110
+ position: 'absolute',
111
+ width: '100%',
112
+ left: 0,
113
+ top: '50%',
114
+ transform: 'translateY(-50%)',
115
+ justifyItems: 'center',
116
+ }}
117
+ >
118
+ <Text size={4} muted>
119
+ <ErrorOutlineIcon style={{fontSize: '1.75em'}} />
120
+ </Text>
121
+ <Text muted align="center">
122
+ {error}
123
+ </Text>
124
+ </Stack>
125
+ )}
126
+ <Image
127
+ src={thumbnailSrc ?? undefined}
128
+ alt={`Preview for ${staticImage ? 'image' : 'video'} ${asset.filename || asset.assetId}`}
129
+ onLoad={handleLoad}
130
+ onError={() => handleError()}
131
+ style={{opacity: status === 'loaded' ? 1 : 0}}
132
+ />
133
+ </>
134
+ ) : null}
135
+ </Card>
136
+ </Suspense>
125
137
  )
126
138
  }
@@ -2,8 +2,9 @@ import {SearchIcon} from '@sanity/icons'
2
2
  import {Card, Flex, Grid, Inline, Label, Stack, Text, TextInput} from '@sanity/ui'
3
3
  import {useMemo, useState} from 'react'
4
4
 
5
+ import {DrmPlaybackWarningContextProvider} from '../context/DrmPlaybackWarningContext'
5
6
  import useAssets from '../hooks/useAssets'
6
- import type {VideoAssetDocument} from '../util/types'
7
+ import type {PluginConfig, VideoAssetDocument} from '../util/types'
7
8
  import ConfigureApi from './ConfigureApi'
8
9
  import ImportVideosFromMux from './ImportVideosFromMux'
9
10
  import PageSelector from './PageSelector'
@@ -15,10 +16,11 @@ import VideoDetails from './VideoDetails/VideoDetails'
15
16
  import VideoInBrowser from './VideoInBrowser'
16
17
 
17
18
  export interface VideosBrowserProps {
19
+ config: PluginConfig
18
20
  onSelect?: (asset: VideoAssetDocument) => void
19
21
  }
20
22
 
21
- export default function VideosBrowser({onSelect}: VideosBrowserProps) {
23
+ export default function VideosBrowser({onSelect, config}: VideosBrowserProps) {
22
24
  const {assets, isLoading, searchQuery, setSearchQuery, setSort, sort} = useAssets()
23
25
  const [page, setPage] = useState<number>(0)
24
26
  const pageLimit = 20
@@ -34,7 +36,7 @@ export default function VideosBrowser({onSelect}: VideosBrowserProps) {
34
36
 
35
37
  const placement = onSelect ? 'input' : 'tool'
36
38
  return (
37
- <>
39
+ <DrmPlaybackWarningContextProvider config={config}>
38
40
  <Stack padding={4} space={4} style={{minHeight: '50vh'}}>
39
41
  <Flex justify="space-between" align="center">
40
42
  <Flex align="center" gap={3}>
@@ -47,7 +49,7 @@ export default function VideosBrowser({onSelect}: VideosBrowserProps) {
47
49
  placeholder="Search videos"
48
50
  />
49
51
  <SelectSortOptions setSort={setSort} sort={sort} />
50
- <PageSelector page={page} setPage={setPage} total={pageTotal} limit={pageLimit} />
52
+ <PageSelector page={page} setPage={setPage} total={pageTotal} />
51
53
  </Flex>
52
54
  {placement === 'tool' && (
53
55
  <Inline space={2}>
@@ -93,6 +95,6 @@ export default function VideosBrowser({onSelect}: VideosBrowserProps) {
93
95
  {freshEditedAsset && (
94
96
  <VideoDetails closeDialog={() => setEditedAsset(null)} asset={freshEditedAsset} />
95
97
  )}
96
- </>
98
+ </DrmPlaybackWarningContextProvider>
97
99
  )
98
100
  }