sanity-plugin-mux-input 3.0.4 → 3.0.5
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/README.md +0 -2
- package/dist/index.cjs +2876 -4400
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +134 -193
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +134 -193
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2877 -4401
- package/dist/index.js.map +1 -1
- package/package.json +36 -36
- package/src/_exports/index.ts +1 -1
- package/src/actions/assets.ts +5 -5
- package/src/actions/secrets.ts +5 -6
- package/src/actions/upload.ts +32 -34
- package/src/components/AddCaptionDialog.tsx +3 -3
- package/src/components/ConfigureApi.tsx +7 -7
- package/src/components/DraggableWatermark.tsx +15 -7
- package/src/components/EditCaptionDialog.tsx +6 -5
- package/src/components/EditThumbnailDialog.tsx +4 -5
- package/src/components/ErrorBoundaryCard.tsx +1 -0
- package/src/components/FileInputButton.tsx +1 -1
- package/src/components/FileInputMenuItem.tsx +10 -10
- package/src/components/ImportVideosFromMux.tsx +0 -3
- package/src/components/MuxLogo.tsx +1 -1
- package/src/components/Onboard.tsx +1 -1
- package/src/components/PageSelector.tsx +1 -1
- package/src/components/Player.styled.tsx +0 -44
- package/src/components/Player.tsx +1 -1
- package/src/components/PlayerActionsMenu.tsx +3 -2
- package/src/components/ResyncMetadata.tsx +4 -6
- package/src/components/SelectAsset.tsx +2 -2
- package/src/components/SelectSortOptions.tsx +3 -3
- package/src/components/TextTracksEditor.tsx +2 -2
- package/src/components/TextTracksManager.tsx +5 -4
- package/src/components/UploadConfiguration.tsx +17 -15
- package/src/components/UploadPlaceholder.tsx +1 -1
- package/src/components/UploadProgress.tsx +4 -4
- package/src/components/Uploader.styled.tsx +1 -2
- package/src/components/Uploader.tsx +15 -14
- package/src/components/VideoDetails/DeleteDialog.tsx +2 -1
- package/src/components/VideoDetails/VideoDetails.tsx +3 -3
- package/src/components/VideoDetails/useVideoDetails.ts +2 -2
- package/src/components/VideoInBrowser.tsx +1 -1
- package/src/components/VideoMetadata.tsx +1 -1
- package/src/components/VideoPlayer.tsx +12 -6
- package/src/components/VideoThumbnail.tsx +4 -3
- package/src/components/VideosBrowser.tsx +1 -1
- package/src/components/documentPreview/DocumentPreview.tsx +4 -3
- package/src/components/documentPreview/PaneItemPreview.tsx +5 -12
- package/src/components/uploadConfiguration/PlaybackPolicy.tsx +3 -3
- package/src/components/uploadConfiguration/PlaybackPolicyOption.tsx +2 -2
- package/src/components/uploadConfiguration/PlaybackPolicyWarning.tsx +1 -1
- package/src/components/uploadConfiguration/ResolutionTierSelector.tsx +2 -2
- package/src/components/uploadConfiguration/StaticRenditionSelector.tsx +4 -4
- package/src/components/withFocusRing/withFocusRing.ts +1 -1
- package/src/context/DialogStateContext.tsx +3 -6
- package/src/context/DrmPlaybackWarningContext.tsx +14 -10
- package/src/hooks/useAccessControl.ts +1 -1
- package/src/hooks/useAssetDocumentValues.ts +2 -2
- package/src/hooks/useAssets.ts +11 -6
- package/src/hooks/useCancelUpload.ts +2 -2
- package/src/hooks/useDocReferences.ts +2 -2
- package/src/hooks/useFetchFileSize.ts +4 -3
- package/src/hooks/useImportMuxAssets.ts +3 -3
- package/src/hooks/useInView.ts +3 -4
- package/src/hooks/useMediaMetadata.ts +5 -4
- package/src/hooks/useMuxAssets.ts +15 -15
- package/src/hooks/useMuxPolling.ts +6 -3
- package/src/hooks/useResyncAsset.ts +1 -1
- package/src/hooks/useResyncMuxMetadata.ts +4 -11
- package/src/hooks/useSaveSecrets.ts +4 -4
- package/src/hooks/useSecretsDocumentValues.ts +1 -1
- package/src/util/asserters.ts +0 -13
- package/src/util/convertWatermarkToMux.ts +4 -4
- package/src/util/createUrlParamsObject.ts +3 -3
- package/src/util/extractFiles.ts +3 -3
- package/src/util/formatBytes.ts +0 -1
- package/src/util/formatSeconds.ts +0 -1
- package/src/util/generateJwt.ts +3 -3
- package/src/util/getAnimatedPosterSrc.ts +1 -1
- package/src/util/getPlaybackPolicy.ts +6 -6
- package/src/util/getPosterSrc.ts +1 -1
- package/src/util/pluginVersion.ts +5 -1
- package/src/util/readSecrets.ts +1 -1
- package/src/util/textTracks.ts +5 -5
- package/src/util/tryWithSuspend.ts +1 -1
- package/src/util/types.ts +2 -32
- package/src/components/InputError.tsx +0 -17
- package/src/components/documentPreview/paneItemTypes.ts +0 -7
- package/src/util/areSecretsSignable.ts +0 -5
- package/src/util/getStoryboardSrc.ts +0 -27
- package/src/util/isSigned.ts +0 -20
|
@@ -117,9 +117,9 @@ const useAssetsInSanity = createHookFromObservableFactory<AssetInSanity[], Docum
|
|
|
117
117
|
{},
|
|
118
118
|
{
|
|
119
119
|
apiVersion: SANITY_API_VERSION,
|
|
120
|
-
}
|
|
120
|
+
},
|
|
121
121
|
)
|
|
122
|
-
}
|
|
122
|
+
},
|
|
123
123
|
)
|
|
124
124
|
|
|
125
125
|
function assetExistsInSanity(asset: MuxAsset, existingAssets: AssetInSanity[]) {
|
|
@@ -127,6 +127,6 @@ function assetExistsInSanity(asset: MuxAsset, existingAssets: AssetInSanity[]) {
|
|
|
127
127
|
if (asset.status !== 'ready') return false
|
|
128
128
|
|
|
129
129
|
return existingAssets.some(
|
|
130
|
-
(existing) => existing.assetId === asset.id || existing.uploadId === asset.upload_id
|
|
130
|
+
(existing) => existing.assetId === asset.id || existing.uploadId === asset.upload_id,
|
|
131
131
|
)
|
|
132
132
|
}
|
package/src/hooks/useInView.ts
CHANGED
|
@@ -9,7 +9,7 @@ type IntersectionOptions = {
|
|
|
9
9
|
|
|
10
10
|
export function useInView(
|
|
11
11
|
ref: React.RefObject<HTMLDivElement | null>,
|
|
12
|
-
options: IntersectionOptions = {}
|
|
12
|
+
options: IntersectionOptions = {},
|
|
13
13
|
) {
|
|
14
14
|
const [inView, setInView] = useState(false)
|
|
15
15
|
|
|
@@ -21,8 +21,8 @@ export function useInView(
|
|
|
21
21
|
// While it would be nice if you could just look at isIntersecting to determine if the component is inside the viewport, browsers can't agree on how to use it.
|
|
22
22
|
// -Firefox ignores `threshold` when considering `isIntersecting`, so it will never be false again if `threshold` is > 0
|
|
23
23
|
const nowInView =
|
|
24
|
-
entry
|
|
25
|
-
obs.thresholds.some((threshold) => entry
|
|
24
|
+
entry!.isIntersecting &&
|
|
25
|
+
obs.thresholds.some((threshold) => entry!.intersectionRatio >= threshold)
|
|
26
26
|
|
|
27
27
|
// Update our state when observer callback fires
|
|
28
28
|
setInView(nowInView)
|
|
@@ -32,7 +32,6 @@ export function useInView(
|
|
|
32
32
|
const toObserve = ref.current
|
|
33
33
|
observer.observe(toObserve)
|
|
34
34
|
|
|
35
|
-
// eslint-disable-next-line
|
|
36
35
|
return () => {
|
|
37
36
|
if (toObserve) observer.unobserve(toObserve)
|
|
38
37
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {useEffect, useState} from 'react'
|
|
2
2
|
|
|
3
|
-
import {StagedUpload} from '../components/Uploader'
|
|
3
|
+
import {type StagedUpload} from '../components/Uploader'
|
|
4
4
|
|
|
5
5
|
export interface VideoAssetMetadata {
|
|
6
6
|
width?: number
|
|
@@ -19,7 +19,7 @@ export function useMediaMetadata(stagedUpload: StagedUpload) {
|
|
|
19
19
|
// Validate file uploads
|
|
20
20
|
if (stagedUpload.type === 'file') {
|
|
21
21
|
const file = stagedUpload.files[0]
|
|
22
|
-
videoSrc = URL.createObjectURL(file)
|
|
22
|
+
videoSrc = URL.createObjectURL(file!)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// Validate URL uploads
|
|
@@ -27,6 +27,7 @@ export function useMediaMetadata(stagedUpload: StagedUpload) {
|
|
|
27
27
|
videoSrc = stagedUpload.url
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// oxlint-disable-next-line react/react-compiler
|
|
30
31
|
setVideoAssetMetadata((old) => ({
|
|
31
32
|
...old,
|
|
32
33
|
duration: undefined,
|
|
@@ -67,7 +68,7 @@ export function useMediaMetadata(stagedUpload: StagedUpload) {
|
|
|
67
68
|
const currentVideoSrc = videoEl?.src
|
|
68
69
|
if (videoEl) {
|
|
69
70
|
metadataListeners.forEach((listener) =>
|
|
70
|
-
videoEl.removeEventListener('loadedmetadata', listener)
|
|
71
|
+
videoEl.removeEventListener('loadedmetadata', listener),
|
|
71
72
|
)
|
|
72
73
|
videoEl.onerror = null
|
|
73
74
|
videoEl.src = ''
|
|
@@ -86,7 +87,7 @@ export function useMediaMetadata(stagedUpload: StagedUpload) {
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
metadataListeners.forEach((listener) =>
|
|
89
|
-
videoElement.addEventListener('loadedmetadata', listener)
|
|
90
|
+
videoElement.addEventListener('loadedmetadata', listener),
|
|
90
91
|
)
|
|
91
92
|
videoElement.src = videoSrc
|
|
92
93
|
|
|
@@ -39,7 +39,7 @@ type PageResult = (
|
|
|
39
39
|
*/
|
|
40
40
|
async function fetchMuxAssetsPage(
|
|
41
41
|
client: SanityClient,
|
|
42
|
-
cursor: string | null
|
|
42
|
+
cursor: string | null,
|
|
43
43
|
): Promise<PageResult> {
|
|
44
44
|
try {
|
|
45
45
|
const response = await listAssets(client, {
|
|
@@ -49,10 +49,10 @@ async function fetchMuxAssetsPage(
|
|
|
49
49
|
|
|
50
50
|
return {
|
|
51
51
|
cursor,
|
|
52
|
-
data: response.data
|
|
52
|
+
data: response.data,
|
|
53
53
|
next_cursor: response.next_cursor || null,
|
|
54
54
|
}
|
|
55
|
-
} catch
|
|
55
|
+
} catch {
|
|
56
56
|
return {
|
|
57
57
|
cursor,
|
|
58
58
|
error: {_tag: 'FetchError'},
|
|
@@ -62,7 +62,7 @@ async function fetchMuxAssetsPage(
|
|
|
62
62
|
|
|
63
63
|
function accumulateIntermediateState(
|
|
64
64
|
currentState: MuxAssetsState,
|
|
65
|
-
pageResult: PageResult
|
|
65
|
+
pageResult: PageResult,
|
|
66
66
|
): MuxAssetsState {
|
|
67
67
|
const currentData = ('data' in currentState && currentState.data) || []
|
|
68
68
|
const newAssets = ('data' in pageResult && pageResult.data) || []
|
|
@@ -86,7 +86,7 @@ function accumulateIntermediateState(
|
|
|
86
86
|
|
|
87
87
|
return acc
|
|
88
88
|
},
|
|
89
|
-
{validAssets: [], skippedInThisPage: false}
|
|
89
|
+
{validAssets: [], skippedInThisPage: false},
|
|
90
90
|
)
|
|
91
91
|
|
|
92
92
|
return {
|
|
@@ -128,8 +128,10 @@ export default function useMuxAssets({client, enabled}: {client: SanityClient; e
|
|
|
128
128
|
fetchMuxAssetsPage(
|
|
129
129
|
client,
|
|
130
130
|
// When we've already successfully loaded before (fully or partially), we start from the next cursor to avoid re-fetching
|
|
131
|
-
'data' in state && state.data && state.data.length > 0 && !state.error
|
|
132
|
-
|
|
131
|
+
'data' in state && state.data && state.data.length > 0 && !state.error
|
|
132
|
+
? state.cursor
|
|
133
|
+
: null,
|
|
134
|
+
),
|
|
133
135
|
)
|
|
134
136
|
.pipe(
|
|
135
137
|
// Here we use "expand" to recursively fetch next pages
|
|
@@ -139,14 +141,13 @@ export default function useMuxAssets({client, enabled}: {client: SanityClient; e
|
|
|
139
141
|
if (hasMorePages(pageResult)) {
|
|
140
142
|
return timer(2000).pipe(
|
|
141
143
|
concatMap(() =>
|
|
142
|
-
// eslint-disable-next-line max-nested-callbacks
|
|
143
144
|
defer(() =>
|
|
144
145
|
fetchMuxAssetsPage(
|
|
145
146
|
client,
|
|
146
|
-
'next_cursor' in pageResult ? pageResult.next_cursor : null
|
|
147
|
-
)
|
|
148
|
-
)
|
|
149
|
-
)
|
|
147
|
+
'next_cursor' in pageResult ? pageResult.next_cursor : null,
|
|
148
|
+
),
|
|
149
|
+
),
|
|
150
|
+
),
|
|
150
151
|
)
|
|
151
152
|
}
|
|
152
153
|
|
|
@@ -156,8 +157,8 @@ export default function useMuxAssets({client, enabled}: {client: SanityClient; e
|
|
|
156
157
|
|
|
157
158
|
// On each iteration, persist intermediate states to give feedback to users
|
|
158
159
|
tap((pageResult) =>
|
|
159
|
-
setState((prevState) => accumulateIntermediateState(prevState, pageResult))
|
|
160
|
-
)
|
|
160
|
+
setState((prevState) => accumulateIntermediateState(prevState, pageResult)),
|
|
161
|
+
),
|
|
161
162
|
)
|
|
162
163
|
.subscribe({
|
|
163
164
|
// Once done, let the user know we've stopped loading
|
|
@@ -170,7 +171,6 @@ export default function useMuxAssets({client, enabled}: {client: SanityClient; e
|
|
|
170
171
|
})
|
|
171
172
|
|
|
172
173
|
// Unsubscribe on component unmount to prevent memory leaks or fetching unnecessarily
|
|
173
|
-
// eslint-disable-next-line consistent-return
|
|
174
174
|
return () => subscription.unsubscribe()
|
|
175
175
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
176
176
|
}, [enabled])
|
|
@@ -31,7 +31,7 @@ export const useMuxPolling = (asset?: VideoAssetDocument) => {
|
|
|
31
31
|
|
|
32
32
|
const shouldFetch = useMemo(
|
|
33
33
|
() => !!asset?.assetId && (asset?.status === 'preparing' || isPreparingStaticRenditions),
|
|
34
|
-
[asset?.assetId, asset?.status, isPreparingStaticRenditions]
|
|
34
|
+
[asset?.assetId, asset?.status, isPreparingStaticRenditions],
|
|
35
35
|
)
|
|
36
36
|
return useSWR(
|
|
37
37
|
shouldFetch ? `/${projectId}/addons/mux/assets/${dataset}/data/${asset?.assetId}` : null,
|
|
@@ -42,8 +42,11 @@ export const useMuxPolling = (asset?: VideoAssetDocument) => {
|
|
|
42
42
|
method: 'GET',
|
|
43
43
|
query: PLUGIN_VERSION_QUERY,
|
|
44
44
|
})
|
|
45
|
-
client
|
|
45
|
+
await client
|
|
46
|
+
.patch(asset!._id)
|
|
47
|
+
.set({status: data.status, data})
|
|
48
|
+
.commit({returnDocuments: false})
|
|
46
49
|
},
|
|
47
|
-
{refreshInterval: 2000, refreshWhenHidden: true, dedupingInterval: 1000}
|
|
50
|
+
{refreshInterval: 2000, refreshWhenHidden: true, dedupingInterval: 1000},
|
|
48
51
|
)
|
|
49
52
|
}
|
|
@@ -8,20 +8,13 @@ import {
|
|
|
8
8
|
|
|
9
9
|
import {addKeysToMuxData} from '../util/addKeysToMuxData'
|
|
10
10
|
import {isEmptyOrPlaceholderTitle} from '../util/assetTitlePlaceholder'
|
|
11
|
-
import type {
|
|
11
|
+
import type {VideoAssetDocument} from '../util/types'
|
|
12
12
|
import {SANITY_API_VERSION} from './useClient'
|
|
13
13
|
import useMuxAssets from './useMuxAssets'
|
|
14
14
|
import {useSecretsDocumentValues} from './useSecretsDocumentValues'
|
|
15
15
|
|
|
16
16
|
type ResyncState = 'closed' | 'idle' | 'syncing' | 'done' | 'error'
|
|
17
17
|
|
|
18
|
-
export type MatchedAsset = {
|
|
19
|
-
sanityDoc: VideoAssetDocument
|
|
20
|
-
muxAsset: MuxAsset | undefined
|
|
21
|
-
muxTitle: string | undefined
|
|
22
|
-
currentTitle: string | undefined
|
|
23
|
-
}
|
|
24
|
-
|
|
25
18
|
export default function useResyncMuxMetadata() {
|
|
26
19
|
const documentStore = useDocumentStore()
|
|
27
20
|
const client = useClient({
|
|
@@ -46,7 +39,7 @@ export default function useResyncMuxMetadata() {
|
|
|
46
39
|
return sanityAssets && muxAssets.data
|
|
47
40
|
? sanityAssets.map((sanityDoc) => {
|
|
48
41
|
const muxAsset = muxAssets.data?.find(
|
|
49
|
-
(m) => m.id === sanityDoc.assetId || m.id === sanityDoc.data?.id
|
|
42
|
+
(m) => m.id === sanityDoc.assetId || m.id === sanityDoc.data?.id,
|
|
50
43
|
)
|
|
51
44
|
return {
|
|
52
45
|
sanityDoc,
|
|
@@ -170,7 +163,7 @@ const useSanityAssets = createHookFromObservableFactory<VideoAssetDocument[], Do
|
|
|
170
163
|
{},
|
|
171
164
|
{
|
|
172
165
|
apiVersion: SANITY_API_VERSION,
|
|
173
|
-
}
|
|
166
|
+
},
|
|
174
167
|
)
|
|
175
|
-
}
|
|
168
|
+
},
|
|
176
169
|
)
|
|
@@ -25,7 +25,7 @@ export const useSaveSecrets = (client: SanityClient, secrets: Secrets) => {
|
|
|
25
25
|
enableSignedUrls,
|
|
26
26
|
signingKeyId!,
|
|
27
27
|
signingKeyPrivate!,
|
|
28
|
-
drmConfigId
|
|
28
|
+
drmConfigId!,
|
|
29
29
|
)
|
|
30
30
|
const valid = await testSecrets(client)
|
|
31
31
|
if (!valid?.status && token && secretKey) {
|
|
@@ -40,7 +40,7 @@ export const useSaveSecrets = (client: SanityClient, secrets: Secrets) => {
|
|
|
40
40
|
const hasValidSigningKeys = await haveValidSigningKeys(
|
|
41
41
|
client,
|
|
42
42
|
signingKeyId!,
|
|
43
|
-
signingKeyPrivate
|
|
43
|
+
signingKeyPrivate!,
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
if (!hasValidSigningKeys) {
|
|
@@ -55,7 +55,7 @@ export const useSaveSecrets = (client: SanityClient, secrets: Secrets) => {
|
|
|
55
55
|
enableSignedUrls,
|
|
56
56
|
signingKeyId,
|
|
57
57
|
signingKeyPrivate,
|
|
58
|
-
drmConfigId ?? ''
|
|
58
|
+
drmConfigId ?? '',
|
|
59
59
|
)
|
|
60
60
|
} catch (err: any) {
|
|
61
61
|
// eslint-disable-next-line no-console
|
|
@@ -73,6 +73,6 @@ export const useSaveSecrets = (client: SanityClient, secrets: Secrets) => {
|
|
|
73
73
|
drmConfigId,
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
|
-
[client, secrets]
|
|
76
|
+
[client, secrets],
|
|
77
77
|
)
|
|
78
78
|
}
|
|
@@ -15,7 +15,7 @@ const path = [
|
|
|
15
15
|
export const useSecretsDocumentValues = () => {
|
|
16
16
|
const {error, isLoading, value} = useDocumentValues<Partial<Secrets> | null | undefined>(
|
|
17
17
|
muxSecretsDocumentId,
|
|
18
|
-
path
|
|
18
|
+
path,
|
|
19
19
|
)
|
|
20
20
|
const cache = useMemo(() => {
|
|
21
21
|
const exists = Boolean(value)
|
package/src/util/asserters.ts
CHANGED
|
@@ -1,17 +1,4 @@
|
|
|
1
1
|
import {ServerError} from '@sanity/client'
|
|
2
|
-
import {type InputProps, isObjectInputProps, type PreviewLayoutKey, type PreviewProps} from 'sanity'
|
|
3
|
-
|
|
4
|
-
import type {MuxInputPreviewProps, MuxInputProps} from './types'
|
|
5
|
-
|
|
6
|
-
export function isMuxInputProps(props: InputProps): props is MuxInputProps {
|
|
7
|
-
return isObjectInputProps(props) && props.schemaType.type?.name === 'mux.video'
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function isMuxInputPreviewProps(
|
|
11
|
-
props: PreviewProps<PreviewLayoutKey>
|
|
12
|
-
): props is MuxInputPreviewProps {
|
|
13
|
-
return props.schemaType?.type?.name === 'mux.video'
|
|
14
|
-
}
|
|
15
2
|
|
|
16
3
|
export function isValidUrl(url: string): boolean {
|
|
17
4
|
try {
|
|
@@ -24,7 +24,7 @@ export function convertWatermarkToMuxOverlay(
|
|
|
24
24
|
* - '%' preserves existing behavior.
|
|
25
25
|
*/
|
|
26
26
|
units?: '%' | 'px'
|
|
27
|
-
}
|
|
27
|
+
},
|
|
28
28
|
): MuxOverlaySettings | null {
|
|
29
29
|
if (!watermark.enabled || !watermark.imageUrl) {
|
|
30
30
|
return null
|
|
@@ -129,17 +129,17 @@ export function convertWatermarkToMuxOverlay(
|
|
|
129
129
|
const imageAspectRatio = watermark.imageAspectRatio ?? 1
|
|
130
130
|
const watermarkHeightPercentOfVideoHeight = Math.max(
|
|
131
131
|
0,
|
|
132
|
-
Math.min(100, (size * videoAspectRatio) / imageAspectRatio)
|
|
132
|
+
Math.min(100, (size * videoAspectRatio) / imageAspectRatio),
|
|
133
133
|
)
|
|
134
134
|
|
|
135
135
|
const halfWidth = watermarkWidthPercentOfVideoWidth / 2
|
|
136
136
|
const halfHeight = watermarkHeightPercentOfVideoHeight / 2
|
|
137
137
|
|
|
138
138
|
const leftMargin = clampPercent(
|
|
139
|
-
Math.min(position.x - halfWidth, 100 - watermarkWidthPercentOfVideoWidth)
|
|
139
|
+
Math.min(position.x - halfWidth, 100 - watermarkWidthPercentOfVideoWidth),
|
|
140
140
|
)
|
|
141
141
|
const topMargin = clampPercent(
|
|
142
|
-
Math.min(position.y - halfHeight, 100 - watermarkHeightPercentOfVideoHeight)
|
|
142
|
+
Math.min(position.y - halfHeight, 100 - watermarkHeightPercentOfVideoHeight),
|
|
143
143
|
)
|
|
144
144
|
|
|
145
145
|
const units = options?.units ?? '%'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {SanityClient} from 'sanity'
|
|
2
2
|
|
|
3
3
|
import {getPlaybackId} from '../util/getPlaybackPolicy'
|
|
4
|
-
import {Audience, generateJwt} from './generateJwt'
|
|
4
|
+
import {type Audience, generateJwt} from './generateJwt'
|
|
5
5
|
import {getPlaybackPolicyById} from './getPlaybackPolicy'
|
|
6
6
|
import type {AssetThumbnailOptions} from './types'
|
|
7
7
|
|
|
@@ -12,12 +12,12 @@ export function createUrlParamsObject(
|
|
|
12
12
|
client: SanityClient,
|
|
13
13
|
asset: AssetThumbnailOptions['asset'],
|
|
14
14
|
params: object,
|
|
15
|
-
audience: Audience
|
|
15
|
+
audience: Audience,
|
|
16
16
|
) {
|
|
17
17
|
const playbackId = getPlaybackId(asset)
|
|
18
18
|
|
|
19
19
|
let searchParams = new URLSearchParams(
|
|
20
|
-
JSON.parse(JSON.stringify(params, (_, v) => v ?? undefined))
|
|
20
|
+
JSON.parse(JSON.stringify(params, (_, v) => v ?? undefined)),
|
|
21
21
|
)
|
|
22
22
|
const playbackPolicy = getPlaybackPolicyById(asset, playbackId)?.policy
|
|
23
23
|
if (playbackPolicy === 'signed' || playbackPolicy === 'drm') {
|
package/src/util/extractFiles.ts
CHANGED
|
@@ -22,7 +22,7 @@ function normalizeItems(items: DataTransferItem[]) {
|
|
|
22
22
|
// Edge throws
|
|
23
23
|
try {
|
|
24
24
|
entry = item.webkitGetAsEntry()
|
|
25
|
-
} catch
|
|
25
|
+
} catch {
|
|
26
26
|
return [item.getAsFile()]
|
|
27
27
|
}
|
|
28
28
|
if (!entry) {
|
|
@@ -39,9 +39,9 @@ function normalizeItems(items: DataTransferItem[]) {
|
|
|
39
39
|
|
|
40
40
|
// others
|
|
41
41
|
return new Promise((resolve) => item.getAsString(resolve)).then((str?: any) =>
|
|
42
|
-
str ? [new File([str], 'unknown.txt', {type: item.type})] : []
|
|
42
|
+
str ? [new File([str], 'unknown.txt', {type: item.type})] : [],
|
|
43
43
|
)
|
|
44
|
-
})
|
|
44
|
+
}),
|
|
45
45
|
)
|
|
46
46
|
}
|
|
47
47
|
|
package/src/util/formatBytes.ts
CHANGED
package/src/util/generateJwt.ts
CHANGED
|
@@ -25,7 +25,7 @@ export function generateJwt<T extends Audience>(
|
|
|
25
25
|
client: SanityClient,
|
|
26
26
|
playbackId: string,
|
|
27
27
|
aud: T,
|
|
28
|
-
payload?: Payload<T
|
|
28
|
+
payload?: Payload<T>,
|
|
29
29
|
): string {
|
|
30
30
|
const {signingKeyId, signingKeyPrivate} = readSecrets(client)
|
|
31
31
|
if (!signingKeyId) {
|
|
@@ -35,7 +35,7 @@ export function generateJwt<T extends Audience>(
|
|
|
35
35
|
throw new TypeError("Missing `signingKeyPrivate`.\n Check your plugin's configuration")
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
/* Using suspend means we need to use Suspense on parent components.
|
|
38
|
+
/* Using suspend means we need to use Suspense on parent components.
|
|
39
39
|
Also, this will throw a Promise under the hood (apparently common in React),
|
|
40
40
|
so if we want to catch errors we have to take this into account in catch blocks
|
|
41
41
|
and rethrow promises. */
|
|
@@ -52,6 +52,6 @@ export function generateJwt<T extends Audience>(
|
|
|
52
52
|
subject: playbackId,
|
|
53
53
|
noTimestamp: true,
|
|
54
54
|
expiresIn: '12h',
|
|
55
|
-
}
|
|
55
|
+
},
|
|
56
56
|
)
|
|
57
57
|
}
|
|
@@ -2,7 +2,7 @@ import type {SanityClient} from 'sanity'
|
|
|
2
2
|
|
|
3
3
|
import {createUrlParamsObject} from './createUrlParamsObject'
|
|
4
4
|
import type {AnimatedThumbnailOptions, MuxAnimatedThumbnailUrl} from './types'
|
|
5
|
-
import {AssetThumbnailOptions} from './types'
|
|
5
|
+
import {type AssetThumbnailOptions} from './types'
|
|
6
6
|
|
|
7
7
|
export interface AnimatedPosterSrcOptions extends AnimatedThumbnailOptions {
|
|
8
8
|
asset: AssetThumbnailOptions['asset']
|
|
@@ -11,7 +11,7 @@ import type {
|
|
|
11
11
|
*/
|
|
12
12
|
export function getPlaybackId(
|
|
13
13
|
asset: Pick<VideoAssetDocument, 'data'>,
|
|
14
|
-
priority: string[] = ['drm', 'signed', 'public']
|
|
14
|
+
priority: string[] = ['drm', 'signed', 'public'],
|
|
15
15
|
): string {
|
|
16
16
|
try {
|
|
17
17
|
if (!asset) {
|
|
@@ -27,7 +27,7 @@ export function getPlaybackId(
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
return playbackIds[0]
|
|
30
|
+
return playbackIds[0]!.id
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
throw new TypeError('Missing playbackId')
|
|
@@ -38,18 +38,18 @@ export function getPlaybackId(
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export function getPlaybackPolicy(
|
|
41
|
-
asset: Pick<VideoAssetDocument, 'data' | 'playbackId'
|
|
41
|
+
asset: Pick<VideoAssetDocument, 'data' | 'playbackId'>,
|
|
42
42
|
): MuxPlaybackId | undefined {
|
|
43
43
|
return (
|
|
44
44
|
asset.data?.playback_ids?.find(
|
|
45
|
-
(playbackId) => getPlaybackId(asset, ['drm', 'signed', 'public']) === playbackId.id
|
|
45
|
+
(playbackId) => getPlaybackId(asset, ['drm', 'signed', 'public']) === playbackId.id,
|
|
46
46
|
) ?? {id: '', policy: 'public'}
|
|
47
47
|
)
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export function getPlaybackPolicyById(
|
|
51
51
|
asset: Pick<VideoAssetDocument, 'data'>,
|
|
52
|
-
playbackId: string
|
|
52
|
+
playbackId: string,
|
|
53
53
|
): MuxPlaybackId | undefined {
|
|
54
54
|
return asset.data?.playback_ids?.find((entry) => playbackId === entry.id)
|
|
55
55
|
}
|
|
@@ -59,7 +59,7 @@ export function hasPlaybackPolicy(
|
|
|
59
59
|
playback_policy?: PlaybackPolicy[]
|
|
60
60
|
advanced_playback_policies: AdvancedPlaybackPolicy[]
|
|
61
61
|
}>,
|
|
62
|
-
policy: PlaybackPolicy
|
|
62
|
+
policy: PlaybackPolicy,
|
|
63
63
|
) {
|
|
64
64
|
return (
|
|
65
65
|
(data.advanced_playback_policies &&
|
package/src/util/getPosterSrc.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type {SanityClient} from 'sanity'
|
|
|
2
2
|
|
|
3
3
|
import {createUrlParamsObject} from './createUrlParamsObject'
|
|
4
4
|
import type {MuxThumbnailUrl, ThumbnailOptions} from './types'
|
|
5
|
-
import {AssetThumbnailOptions} from './types'
|
|
5
|
+
import {type AssetThumbnailOptions} from './types'
|
|
6
6
|
|
|
7
7
|
export interface PosterSrcOptions extends ThumbnailOptions {
|
|
8
8
|
asset: AssetThumbnailOptions['asset']
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
export const PLUGIN_VERSION_QUERY = {
|
|
1
|
+
export const PLUGIN_VERSION_QUERY = {
|
|
2
|
+
sanityVersion:
|
|
3
|
+
// @ts-expect-error - this constant is search/replaced so must be exact, not accessed with an index signature
|
|
4
|
+
process.env.PKG_VERSION!,
|
|
5
|
+
}
|
package/src/util/readSecrets.ts
CHANGED
package/src/util/textTracks.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type {MuxTextTrack, VideoAssetDocument} from './types'
|
|
|
8
8
|
|
|
9
9
|
export function extractErrorMessage(
|
|
10
10
|
error: unknown,
|
|
11
|
-
defaultMessage = 'Failed to process request'
|
|
11
|
+
defaultMessage = 'Failed to process request',
|
|
12
12
|
): string {
|
|
13
13
|
let message = ''
|
|
14
14
|
|
|
@@ -31,7 +31,7 @@ export function extractErrorMessage(
|
|
|
31
31
|
if (message.includes('responded with')) {
|
|
32
32
|
const parts = message.split('(')
|
|
33
33
|
if (parts.length > 1) {
|
|
34
|
-
return parts[parts.length - 1]
|
|
34
|
+
return parts[parts.length - 1]!.replace(')', '').trim()
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -63,7 +63,7 @@ export interface PollTrackStatusResult {
|
|
|
63
63
|
* @returns Promise resolving to the poll result
|
|
64
64
|
*/
|
|
65
65
|
export async function pollTrackStatus(
|
|
66
|
-
options: PollTrackStatusOptions
|
|
66
|
+
options: PollTrackStatusOptions,
|
|
67
67
|
): Promise<PollTrackStatusResult> {
|
|
68
68
|
const {
|
|
69
69
|
client,
|
|
@@ -84,7 +84,7 @@ export async function pollTrackStatus(
|
|
|
84
84
|
|
|
85
85
|
const findTrack = (textTracks: MuxTextTrack[]): MuxTextTrack | undefined => {
|
|
86
86
|
let foundTrack = textTracks.find(
|
|
87
|
-
(track) => track.name === trimmedName && track.language_code === trimmedLanguageCode
|
|
87
|
+
(track) => track.name === trimmedName && track.language_code === trimmedLanguageCode,
|
|
88
88
|
)
|
|
89
89
|
|
|
90
90
|
if (!foundTrack) {
|
|
@@ -175,7 +175,7 @@ export async function pollTrackStatus(
|
|
|
175
175
|
export async function downloadVttFile(
|
|
176
176
|
client: SanityClient,
|
|
177
177
|
asset: VideoAssetDocument,
|
|
178
|
-
track: MuxTextTrack
|
|
178
|
+
track: MuxTextTrack,
|
|
179
179
|
): Promise<void> {
|
|
180
180
|
if (!track.id) {
|
|
181
181
|
throw new Error('Track ID is missing')
|
package/src/util/types.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import type MuxPlayerElement from '@mux/mux-player'
|
|
2
|
-
import type {ObjectInputProps
|
|
2
|
+
import type {ObjectInputProps} from 'sanity'
|
|
3
3
|
import type {PartialDeep} from 'type-fest'
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Standard static rendition options available for plugin configuration defaults
|
|
7
|
-
*/
|
|
8
|
-
export type StandardRendition = 'highest' | 'audio-only'
|
|
9
|
-
|
|
10
5
|
/**
|
|
11
6
|
* All static rendition resolution options supported by Mux
|
|
12
7
|
*/
|
|
@@ -231,12 +226,6 @@ export const SUPPORTED_MUX_LANGUAGES = [
|
|
|
231
226
|
{label: 'Bulgarian', code: 'bg', state: 'Beta'},
|
|
232
227
|
] as const
|
|
233
228
|
|
|
234
|
-
export const VIDEO_QUALITY_LEVELS = [
|
|
235
|
-
{label: 'Basic', value: 'basic'},
|
|
236
|
-
{label: 'Plus', value: 'plus'},
|
|
237
|
-
{label: 'Premium', value: 'premium'},
|
|
238
|
-
] as const
|
|
239
|
-
|
|
240
229
|
export const SUPPORTED_MUX_LANGUAGES_VALUES = SUPPORTED_MUX_LANGUAGES.map((l) => l.code)
|
|
241
230
|
|
|
242
231
|
export type SupportedMuxLanguage = (typeof SUPPORTED_MUX_LANGUAGES_VALUES)[number]
|
|
@@ -267,7 +256,7 @@ export function isCustomTextTrack(track: Partial<UploadTextTrack>): track is Cus
|
|
|
267
256
|
}
|
|
268
257
|
|
|
269
258
|
export function isAutogeneratedTrack(
|
|
270
|
-
track: Partial<UploadTextTrack
|
|
259
|
+
track: Partial<UploadTextTrack>,
|
|
271
260
|
): track is AutogeneratedTextTrack {
|
|
272
261
|
return track.type === 'autogenerated'
|
|
273
262
|
}
|
|
@@ -393,20 +382,11 @@ export interface Secrets {
|
|
|
393
382
|
drmConfigId: string | null
|
|
394
383
|
}
|
|
395
384
|
|
|
396
|
-
// This narrowed type indicates that there may be assets that are signed, and we have the secrets to access them
|
|
397
|
-
// enabledSignedUrls might be false but that's only relevant for future uploads and their playback policy
|
|
398
|
-
export interface SignableSecrets extends Omit<Secrets, 'signingKeyId' | 'signingKeyPrivate'> {
|
|
399
|
-
signingKeyId: string
|
|
400
|
-
signingKeyPrivate: string
|
|
401
|
-
}
|
|
402
|
-
|
|
403
385
|
export type MuxImageOrigin = `https://image.mux.com`
|
|
404
386
|
export type MuxThumbnailUrl = `${MuxImageOrigin}/${string}/thumbnail.png?${string}`
|
|
405
387
|
export type MuxAnimatedThumbnailUrl = `${MuxImageOrigin}/${string}/animated.gif?${string}`
|
|
406
|
-
export type MuxStoryboardUrl = `${MuxImageOrigin}/${string}/storyboard.vtt?${string}`
|
|
407
388
|
export type MuxVideoOrigin = `https://stream.mux.com`
|
|
408
389
|
export type MuxVideoUrl = `${MuxVideoOrigin}/${string}.m3u8?${string}`
|
|
409
|
-
export type MuxApiUrl = MuxThumbnailUrl | MuxAnimatedThumbnailUrl | MuxStoryboardUrl | MuxVideoUrl
|
|
410
390
|
|
|
411
391
|
// 'preserve' by default
|
|
412
392
|
// @url: https://docs.mux.com/guides/video/get-images-from-a-video#thumbnail-query-string-parameters
|
|
@@ -584,13 +564,3 @@ export type Reference = {_type: 'reference'; _ref: string}
|
|
|
584
564
|
export type MuxInputProps = ObjectInputProps<{
|
|
585
565
|
asset?: Reference
|
|
586
566
|
}>
|
|
587
|
-
|
|
588
|
-
export interface MuxInputPreviewProps extends Omit<PreviewProps<PreviewLayoutKey>, 'value'> {
|
|
589
|
-
schemaType: SchemaType
|
|
590
|
-
value?: {
|
|
591
|
-
asset?: Reference
|
|
592
|
-
} | null
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/** Whether the VideosBrowser was opened from a field in a document, or from the standalone studio tool */
|
|
596
|
-
export type PluginPlacement = 'input' | 'tool'
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import {Box, Dialog, Text} from '@sanity/ui'
|
|
2
|
-
import {useId} from 'react'
|
|
3
|
-
|
|
4
|
-
interface Props {
|
|
5
|
-
error: Error
|
|
6
|
-
onClose: () => void
|
|
7
|
-
}
|
|
8
|
-
export default function InputError({onClose, error}: Props) {
|
|
9
|
-
const id = `InputError${useId()}`
|
|
10
|
-
return (
|
|
11
|
-
<Dialog animate header={error.name} id={id} onClose={onClose}>
|
|
12
|
-
<Box padding={4}>
|
|
13
|
-
<Text>{error.message}</Text>
|
|
14
|
-
</Box>
|
|
15
|
-
</Dialog>
|
|
16
|
-
)
|
|
17
|
-
}
|