sanity-plugin-mux-input 2.2.4 → 2.3.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/README.md +148 -16
- package/lib/index.cjs +3996 -3677
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +210 -0
- package/lib/index.d.ts +109 -25
- package/lib/index.esm.js +4390 -0
- package/lib/index.esm.js.map +1 -0
- package/lib/index.js +3964 -3626
- package/lib/index.js.map +1 -1
- package/package.json +48 -52
- package/src/_exports/index.ts +32 -0
- package/src/actions/upload.ts +35 -40
- package/src/clients/upChunkObservable.ts +5 -1
- package/src/components/ConfigureApi.tsx +0 -1
- package/src/components/FileInputArea.tsx +92 -0
- package/src/components/FileInputButton.tsx +3 -2
- package/src/components/FileInputMenuItem.styled.tsx +2 -2
- package/src/components/FileInputMenuItem.tsx +2 -10
- package/src/components/ImportVideosFromMux.tsx +317 -0
- package/src/components/Input.tsx +3 -3
- package/src/components/PlayerActionsMenu.tsx +14 -12
- package/src/components/SelectAsset.tsx +1 -1
- package/src/components/StudioTool.tsx +11 -6
- package/src/components/TextTracksEditor.tsx +214 -0
- package/src/components/UploadConfiguration.tsx +390 -0
- package/src/components/UploadPlaceholder.tsx +41 -55
- package/src/components/Uploader.styled.tsx +0 -1
- package/src/components/Uploader.tsx +384 -0
- package/src/components/VideoDetails/DeleteDialog.tsx +20 -24
- package/src/components/VideoPlayer.tsx +33 -5
- package/src/components/VideoThumbnail.tsx +21 -7
- package/src/components/VideosBrowser.tsx +6 -3
- package/src/components/withFocusRing/withFocusRing.ts +20 -22
- package/src/hooks/useClient.ts +1 -1
- package/src/hooks/useImportMuxAssets.ts +127 -0
- package/src/hooks/useMuxAssets.ts +168 -0
- package/src/plugin.tsx +5 -5
- package/src/util/asserters.ts +9 -0
- package/src/util/createSearchFilter.ts +1 -1
- package/src/util/formatBytes.ts +32 -0
- package/src/util/generateJwt.ts +1 -0
- package/src/util/getAnimatedPosterSrc.ts +1 -1
- package/src/util/getPlaybackId.ts +1 -1
- package/src/util/getPlaybackPolicy.ts +1 -1
- package/src/util/parsers.ts +5 -0
- package/src/util/types.ts +195 -12
- package/lib/index.cjs.js +0 -5
- package/src/components/__legacy__Uploader.tsx +0 -280
- package/src/index.ts +0 -29
|
@@ -1,32 +1,30 @@
|
|
|
1
|
-
import {rem
|
|
1
|
+
import {rem} from '@sanity/ui'
|
|
2
2
|
import {type ComponentType} from 'react'
|
|
3
3
|
import styled, {css} from 'styled-components'
|
|
4
4
|
|
|
5
5
|
import {focusRingBorderStyle, focusRingStyle} from './helpers'
|
|
6
6
|
|
|
7
7
|
export function withFocusRing<Props>(component: ComponentType<Props>) {
|
|
8
|
-
return styled(component as unknown as any)<Props & {$border?: boolean}>(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
8
|
+
return styled(component as unknown as any)<Props & {$border?: boolean}>((props) => {
|
|
9
|
+
const border = {
|
|
10
|
+
width: props.$border ? 1 : 0,
|
|
11
|
+
color: 'var(--card-border-color)',
|
|
12
|
+
}
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
return css`
|
|
15
|
+
--card-focus-box-shadow: ${focusRingBorderStyle(border)};
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
border-radius: ${rem(props.theme.sanity.radius[1])};
|
|
18
|
+
outline: none;
|
|
19
|
+
box-shadow: var(--card-focus-box-shadow);
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
)
|
|
21
|
+
&:focus {
|
|
22
|
+
--card-focus-box-shadow: ${focusRingStyle({
|
|
23
|
+
base: props.theme.sanity.color.base,
|
|
24
|
+
border,
|
|
25
|
+
focusRing: props.theme.sanity.focusRing,
|
|
26
|
+
})};
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
})
|
|
32
30
|
}
|
package/src/hooks/useClient.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// As it's required to specify the API Version this custom hook ensures it's all using the same version
|
|
2
2
|
import {useClient as useSanityClient} from 'sanity'
|
|
3
3
|
|
|
4
|
-
export const SANITY_API_VERSION = '
|
|
4
|
+
export const SANITY_API_VERSION = '2024-03-05'
|
|
5
5
|
|
|
6
6
|
export function useClient() {
|
|
7
7
|
return useSanityClient({apiVersion: SANITY_API_VERSION})
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import {uuid} from '@sanity/uuid'
|
|
2
|
+
import {useMemo, useState} from 'react'
|
|
3
|
+
import {
|
|
4
|
+
createHookFromObservableFactory,
|
|
5
|
+
truncateString,
|
|
6
|
+
useClient,
|
|
7
|
+
useDocumentStore,
|
|
8
|
+
type DocumentStore,
|
|
9
|
+
} from 'sanity'
|
|
10
|
+
import {parseMuxDate} from '../util/parsers'
|
|
11
|
+
import type {MuxAsset, VideoAssetDocument} from '../util/types'
|
|
12
|
+
import {SANITY_API_VERSION} from './useClient'
|
|
13
|
+
import useMuxAssets from './useMuxAssets'
|
|
14
|
+
import {useSecretsDocumentValues} from './useSecretsDocumentValues'
|
|
15
|
+
|
|
16
|
+
type ImportState = 'closed' | 'idle' | 'importing' | 'done' | 'error'
|
|
17
|
+
|
|
18
|
+
export type AssetInSanity = {
|
|
19
|
+
uploadId: string
|
|
20
|
+
assetId: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default function useImportMuxAssets() {
|
|
24
|
+
const documentStore = useDocumentStore()
|
|
25
|
+
const client = useClient({
|
|
26
|
+
apiVersion: SANITY_API_VERSION,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const [assetsInSanity, assetsInSanityLoading] = useAssetsInSanity(documentStore)
|
|
30
|
+
|
|
31
|
+
const secretDocumentValues = useSecretsDocumentValues()
|
|
32
|
+
const hasSecrets = !!secretDocumentValues.value.secrets?.secretKey
|
|
33
|
+
|
|
34
|
+
const [importError, setImportError] = useState<unknown>()
|
|
35
|
+
const [importState, setImportState] = useState<ImportState>('closed')
|
|
36
|
+
const dialogOpen = importState !== 'closed'
|
|
37
|
+
|
|
38
|
+
const muxAssets = useMuxAssets({
|
|
39
|
+
secrets: secretDocumentValues.value.secrets,
|
|
40
|
+
enabled: hasSecrets && dialogOpen,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const missingAssets = useMemo(() => {
|
|
44
|
+
return assetsInSanity && muxAssets.data
|
|
45
|
+
? muxAssets.data.filter((a) => !assetExistsInSanity(a, assetsInSanity))
|
|
46
|
+
: undefined
|
|
47
|
+
}, [assetsInSanity, muxAssets.data])
|
|
48
|
+
|
|
49
|
+
const [selectedAssets, setSelectedAssets] = useState<MuxAsset[]>([])
|
|
50
|
+
|
|
51
|
+
const closeDialog = () => {
|
|
52
|
+
if (importState !== 'importing') setImportState('closed')
|
|
53
|
+
}
|
|
54
|
+
const openDialog = () => {
|
|
55
|
+
if (importState === 'closed') setImportState('idle')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function importAssets() {
|
|
59
|
+
setImportState('importing')
|
|
60
|
+
const documents = selectedAssets.map(muxAssetToSanityDocument)
|
|
61
|
+
|
|
62
|
+
const tx = client.transaction()
|
|
63
|
+
documents.forEach((doc) => tx.create(doc))
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
await tx.commit({returnDocuments: false})
|
|
67
|
+
setSelectedAssets([])
|
|
68
|
+
setImportState('done')
|
|
69
|
+
} catch (error) {
|
|
70
|
+
setImportState('error')
|
|
71
|
+
setImportError(error)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
assetsInSanityLoading,
|
|
77
|
+
closeDialog,
|
|
78
|
+
dialogOpen,
|
|
79
|
+
importState,
|
|
80
|
+
importError,
|
|
81
|
+
hasSecrets,
|
|
82
|
+
importAssets,
|
|
83
|
+
missingAssets,
|
|
84
|
+
muxAssets,
|
|
85
|
+
openDialog,
|
|
86
|
+
selectedAssets,
|
|
87
|
+
setSelectedAssets,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function muxAssetToSanityDocument(asset: MuxAsset): VideoAssetDocument {
|
|
92
|
+
return {
|
|
93
|
+
_id: uuid(),
|
|
94
|
+
_type: 'mux.videoAsset',
|
|
95
|
+
_updatedAt: new Date().toISOString(),
|
|
96
|
+
_createdAt: parseMuxDate(asset.created_at).toISOString(),
|
|
97
|
+
assetId: asset.id,
|
|
98
|
+
playbackId: asset.playback_ids.find((p) => p.id)?.id,
|
|
99
|
+
filename: `Asset #${truncateString(asset.id, 15)}`,
|
|
100
|
+
status: asset.status,
|
|
101
|
+
data: asset,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const useAssetsInSanity = createHookFromObservableFactory<AssetInSanity[], DocumentStore>(
|
|
106
|
+
(documentStore) => {
|
|
107
|
+
return documentStore.listenQuery(
|
|
108
|
+
/* groq */ `*[_type == "mux.videoAsset"] {
|
|
109
|
+
"uploadId": coalesce(uploadId, data.upload_id),
|
|
110
|
+
"assetId": coalesce(assetId, data.id),
|
|
111
|
+
}`,
|
|
112
|
+
{},
|
|
113
|
+
{
|
|
114
|
+
apiVersion: SANITY_API_VERSION,
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
function assetExistsInSanity(asset: MuxAsset, existingAssets: AssetInSanity[]) {
|
|
121
|
+
// Don't allow importing assets that are not ready
|
|
122
|
+
if (asset.status !== 'ready') return false
|
|
123
|
+
|
|
124
|
+
return existingAssets.some(
|
|
125
|
+
(existing) => existing.assetId === asset.id || existing.uploadId === asset.upload_id
|
|
126
|
+
)
|
|
127
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {useEffect, useState} from 'react'
|
|
2
|
+
import {defer, of, timer} from 'rxjs'
|
|
3
|
+
import {concatMap, expand, tap} from 'rxjs/operators'
|
|
4
|
+
|
|
5
|
+
import type {MuxAsset, Secrets} from '../util/types'
|
|
6
|
+
|
|
7
|
+
const FIRST_PAGE = 1
|
|
8
|
+
const ASSETS_PER_PAGE = 100
|
|
9
|
+
|
|
10
|
+
type MuxAssetsState = {
|
|
11
|
+
pageNum: number
|
|
12
|
+
loading: boolean
|
|
13
|
+
data?: MuxAsset[]
|
|
14
|
+
error?: FetchError
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type FetchError =
|
|
18
|
+
| {
|
|
19
|
+
_tag: 'FetchError'
|
|
20
|
+
}
|
|
21
|
+
| {_tag: 'MuxError'; error: unknown}
|
|
22
|
+
|
|
23
|
+
type PageResult = (
|
|
24
|
+
| {
|
|
25
|
+
data: MuxAsset[]
|
|
26
|
+
}
|
|
27
|
+
| {
|
|
28
|
+
error: FetchError
|
|
29
|
+
}
|
|
30
|
+
) & {
|
|
31
|
+
pageNum: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @docs {@link https://docs.mux.com/api-reference#video/operation/list-assets}
|
|
36
|
+
*/
|
|
37
|
+
async function fetchMuxAssetsPage(
|
|
38
|
+
{secretKey, token}: Secrets,
|
|
39
|
+
pageNum: number
|
|
40
|
+
): Promise<PageResult> {
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(
|
|
43
|
+
`https://api.mux.com/video/v1/assets?limit=${ASSETS_PER_PAGE}&page=${pageNum}`,
|
|
44
|
+
{
|
|
45
|
+
headers: {
|
|
46
|
+
Authorization: `Basic ${btoa(`${token}:${secretKey}`)}`,
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
const json = await res.json()
|
|
51
|
+
|
|
52
|
+
if (json.error) {
|
|
53
|
+
return {
|
|
54
|
+
pageNum,
|
|
55
|
+
error: {
|
|
56
|
+
_tag: 'MuxError',
|
|
57
|
+
error: json.error,
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
pageNum,
|
|
64
|
+
data: json.data as MuxAsset[],
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return {
|
|
68
|
+
pageNum,
|
|
69
|
+
error: {_tag: 'FetchError'},
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function accumulateIntermediateState(
|
|
75
|
+
currentState: MuxAssetsState,
|
|
76
|
+
pageResult: PageResult
|
|
77
|
+
): MuxAssetsState {
|
|
78
|
+
const currentData = ('data' in currentState && currentState.data) || []
|
|
79
|
+
return {
|
|
80
|
+
...currentState,
|
|
81
|
+
data: [
|
|
82
|
+
...currentData,
|
|
83
|
+
...(('data' in pageResult && pageResult.data) || []).filter(
|
|
84
|
+
// De-duplicate assets for safety
|
|
85
|
+
(asset) => !currentData.some((a) => a.id === asset.id)
|
|
86
|
+
),
|
|
87
|
+
],
|
|
88
|
+
error:
|
|
89
|
+
'error' in pageResult
|
|
90
|
+
? pageResult.error
|
|
91
|
+
: // Reset error if current page is successful
|
|
92
|
+
undefined,
|
|
93
|
+
pageNum: pageResult.pageNum,
|
|
94
|
+
loading: true,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function hasMorePages(pageResult: PageResult) {
|
|
99
|
+
return (
|
|
100
|
+
typeof pageResult === 'object' &&
|
|
101
|
+
'data' in pageResult &&
|
|
102
|
+
Array.isArray(pageResult.data) &&
|
|
103
|
+
pageResult.data.length > 0
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Fetches all assets from a Mux environment. Rules:
|
|
109
|
+
* - One page at a time
|
|
110
|
+
* - Mux has no information on pagination
|
|
111
|
+
* - We've finished fetching if a page returns `data.length === 0`
|
|
112
|
+
* - Rate limiting to one request per 2 seconds
|
|
113
|
+
* - Update state while still fetching to give feedback to users
|
|
114
|
+
*/
|
|
115
|
+
export default function useMuxAssets({secrets, enabled}: {enabled: boolean; secrets: Secrets}) {
|
|
116
|
+
const [state, setState] = useState<MuxAssetsState>({loading: true, pageNum: FIRST_PAGE})
|
|
117
|
+
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (!enabled) return
|
|
120
|
+
|
|
121
|
+
const subscription = defer(() =>
|
|
122
|
+
fetchMuxAssetsPage(
|
|
123
|
+
secrets,
|
|
124
|
+
// When we've already successfully loaded before (fully or partially), we start from the following page to avoid re-fetching
|
|
125
|
+
'data' in state && state.data && state.data.length > 0 && !state.error
|
|
126
|
+
? state.pageNum + 1
|
|
127
|
+
: state.pageNum
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
.pipe(
|
|
131
|
+
// Here we replace "concatMap" with "expand" to recursively fetch next pages
|
|
132
|
+
expand((pageResult) => {
|
|
133
|
+
// if fetched page has data, we continue emitting, requesting the next page
|
|
134
|
+
// after 2s to avoid rate limiting
|
|
135
|
+
if (hasMorePages(pageResult)) {
|
|
136
|
+
return timer(2000).pipe(
|
|
137
|
+
// eslint-disable-next-line max-nested-callbacks
|
|
138
|
+
concatMap(() => defer(() => fetchMuxAssetsPage(secrets, pageResult.pageNum + 1)))
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Else, we stop emitting
|
|
143
|
+
return of()
|
|
144
|
+
}),
|
|
145
|
+
|
|
146
|
+
// On each iteration, persist intermediate states to give feedback to users
|
|
147
|
+
tap((pageResult) =>
|
|
148
|
+
setState((prevState) => accumulateIntermediateState(prevState, pageResult))
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
.subscribe({
|
|
152
|
+
// Once done, let the user know we've stopped loading
|
|
153
|
+
complete: () => {
|
|
154
|
+
setState((prev) => ({
|
|
155
|
+
...prev,
|
|
156
|
+
loading: false,
|
|
157
|
+
}))
|
|
158
|
+
},
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// Unsubscribe on component unmount to prevent memory leaks or fetching unnecessarily
|
|
162
|
+
// eslint-disable-next-line consistent-return
|
|
163
|
+
return () => subscription.unsubscribe()
|
|
164
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
165
|
+
}, [enabled])
|
|
166
|
+
|
|
167
|
+
return state
|
|
168
|
+
}
|
package/src/plugin.tsx
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
1
|
import Input from './components/Input'
|
|
4
2
|
import VideoThumbnail from './components/VideoThumbnail'
|
|
5
|
-
import type {
|
|
3
|
+
import type {MuxInputProps, PluginConfig, VideoAssetDocument} from './util/types'
|
|
6
4
|
|
|
7
|
-
export function muxVideoCustomRendering(config:
|
|
5
|
+
export function muxVideoCustomRendering(config: PluginConfig) {
|
|
8
6
|
return {
|
|
9
7
|
components: {
|
|
10
|
-
input: (props: MuxInputProps) =>
|
|
8
|
+
input: (props: MuxInputProps) => (
|
|
9
|
+
<Input config={{...config, ...props.schemaType.options}} {...props} />
|
|
10
|
+
),
|
|
11
11
|
},
|
|
12
12
|
preview: {
|
|
13
13
|
select: {
|
package/src/util/asserters.ts
CHANGED
|
@@ -11,3 +11,12 @@ export function isMuxInputPreviewProps(
|
|
|
11
11
|
): props is MuxInputPreviewProps {
|
|
12
12
|
return props.schemaType?.type?.name === 'mux.video'
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
export function isValidUrl(url: string): boolean {
|
|
16
|
+
try {
|
|
17
|
+
const parsed = new URL(url)
|
|
18
|
+
return parsed && !!parsed.protocol.match(/http:|https:/)
|
|
19
|
+
} catch {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -55,7 +55,7 @@ function extractTermsFromQuery(query: string): string[] {
|
|
|
55
55
|
* Create GROQ constraints, given search terms and the full spec of available document types and fields.
|
|
56
56
|
* Essentially a large list of all possible fields (joined by logical OR) to match our search terms against.
|
|
57
57
|
*/
|
|
58
|
-
function createConstraints(terms: string[], includeAssetId:
|
|
58
|
+
function createConstraints(terms: string[], includeAssetId: boolean) {
|
|
59
59
|
const searchPaths = includeAssetId ? ['filename', 'assetId'] : ['filename']
|
|
60
60
|
const constraints = terms
|
|
61
61
|
.map((_term, i) => searchPaths.map((joinedPath) => `${joinedPath} match $t${i}`))
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// From: https://stackoverflow.com/questions/10420352/converting-file-size-in-bytes-to-human-readable-string
|
|
3
|
+
/**
|
|
4
|
+
* Format bytes as human-readable text.
|
|
5
|
+
*
|
|
6
|
+
* @param bytes Number of bytes.
|
|
7
|
+
* @param si True to use metric (SI) units, aka powers of 1000. False to use
|
|
8
|
+
* binary (IEC), aka powers of 1024.
|
|
9
|
+
* @param dp Number of decimal places to display.
|
|
10
|
+
*
|
|
11
|
+
* @return Formatted string.
|
|
12
|
+
*/
|
|
13
|
+
export default function formatBytes(bytes: number, si = false, dp = 1) {
|
|
14
|
+
const thresh = si ? 1000 : 1024
|
|
15
|
+
|
|
16
|
+
if (Math.abs(bytes) < thresh) {
|
|
17
|
+
return bytes + ' B'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const units = si
|
|
21
|
+
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
|
22
|
+
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
|
|
23
|
+
let u = -1
|
|
24
|
+
const r = 10 ** dp
|
|
25
|
+
|
|
26
|
+
do {
|
|
27
|
+
bytes /= thresh
|
|
28
|
+
++u
|
|
29
|
+
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1)
|
|
30
|
+
|
|
31
|
+
return bytes.toFixed(dp) + ' ' + units[u]
|
|
32
|
+
}
|
package/src/util/generateJwt.ts
CHANGED
|
@@ -30,6 +30,7 @@ export function generateJwt<T extends Audience>(
|
|
|
30
30
|
throw new TypeError('Missing signingKeyPrivate')
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// @ts-expect-error - handle missing typings for this package
|
|
33
34
|
const {default: sign} = suspend(() => import('jsonwebtoken-esm/sign'), ['jsonwebtoken-esm/sign'])
|
|
34
35
|
|
|
35
36
|
return sign(
|
|
@@ -6,7 +6,7 @@ import {getPlaybackPolicy} from './getPlaybackPolicy'
|
|
|
6
6
|
import type {AnimatedThumbnailOptions, MuxAnimatedThumbnailUrl, VideoAssetDocument} from './types'
|
|
7
7
|
|
|
8
8
|
export interface AnimatedPosterSrcOptions extends AnimatedThumbnailOptions {
|
|
9
|
-
asset:
|
|
9
|
+
asset: Pick<VideoAssetDocument, 'playbackId' | 'data' | 'thumbTime'>
|
|
10
10
|
client: SanityClient
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type {VideoAssetDocument} from './types'
|
|
2
2
|
|
|
3
|
-
export function getPlaybackId(asset:
|
|
3
|
+
export function getPlaybackId(asset: Pick<VideoAssetDocument, 'playbackId'>): string {
|
|
4
4
|
if (!asset?.playbackId) {
|
|
5
5
|
console.error('Asset is missing a playbackId', {asset})
|
|
6
6
|
throw new TypeError(`Missing playbackId`)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {PlaybackPolicy, VideoAssetDocument} from './types'
|
|
2
2
|
|
|
3
|
-
export function getPlaybackPolicy(asset:
|
|
3
|
+
export function getPlaybackPolicy(asset: Pick<VideoAssetDocument, 'data'>): PlaybackPolicy {
|
|
4
4
|
return asset.data?.playback_ids?.[0]?.policy ?? 'public'
|
|
5
5
|
}
|