@cliff-studio/sanity-plugin-bunny-input 2.0.3 → 2.0.4
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/dist/index.js +3 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/BunnyInput.jsx +7 -12
package/dist/index.js
CHANGED
|
@@ -145,17 +145,6 @@ async function listVideos(config, { collectionId, search = "", page = 1, itemsPe
|
|
|
145
145
|
itemsPerPage: payload.itemsPerPage ?? itemsPerPage
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
|
-
async function deleteVideo(config, videoId) {
|
|
149
|
-
const url = config.proxyEndpoint ? `${config.proxyEndpoint}/video/${videoId}` : `${BUNNY_API_BASE}/library/${config.libraryId}/videos/${videoId}`, headers = createHeaders(config), response = await fetch(url, {
|
|
150
|
-
method: "DELETE",
|
|
151
|
-
headers
|
|
152
|
-
});
|
|
153
|
-
if (!response.ok) {
|
|
154
|
-
const error = await response.text();
|
|
155
|
-
throw new Error(`Failed to delete video: ${error}`);
|
|
156
|
-
}
|
|
157
|
-
return response.json();
|
|
158
|
-
}
|
|
159
148
|
const ITEMS_PER_PAGE = 48;
|
|
160
149
|
function BunnyBrowserModal({ config, collectionId: collectionIdProp, onSelect, onClose }) {
|
|
161
150
|
const [videos, setVideos] = useState([]), [search, setSearch] = useState(""), [page, setPage] = useState(1), [totalItems, setTotalItems] = useState(0), [isLoading, setIsLoading] = useState(!1), [error, setError] = useState(null), [resolvedCollectionId, setResolvedCollectionId] = useState(collectionIdProp || null), searchTimeoutRef = useRef(null), fetchVideos = useCallback(
|
|
@@ -438,15 +427,9 @@ function BunnyInput(props) {
|
|
|
438
427
|
setIsBrowserOpen(!1), onChange(set(videoData));
|
|
439
428
|
},
|
|
440
429
|
[onChange]
|
|
441
|
-
), handleRemove = useCallback(
|
|
442
|
-
if (value?.videoId)
|
|
443
|
-
try {
|
|
444
|
-
await deleteVideo(config, value.videoId);
|
|
445
|
-
} catch (err) {
|
|
446
|
-
console.error("Error deleting from Bunny:", err);
|
|
447
|
-
}
|
|
430
|
+
), handleRemove = useCallback(() => {
|
|
448
431
|
onChange(unset());
|
|
449
|
-
}, [
|
|
432
|
+
}, [onChange]);
|
|
450
433
|
return value?.videoId && value?.status === "ready" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
451
434
|
/* @__PURE__ */ jsx(Card, { padding: 3, radius: 2, shadow: 1, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
452
435
|
/* @__PURE__ */ jsx(
|
|
@@ -494,7 +477,7 @@ function BunnyInput(props) {
|
|
|
494
477
|
onClick: () => setIsBrowserOpen(!0)
|
|
495
478
|
}
|
|
496
479
|
),
|
|
497
|
-
/* @__PURE__ */ jsx(Button, { text: "
|
|
480
|
+
/* @__PURE__ */ jsx(Button, { text: "Clear", tone: "critical", mode: "ghost", onClick: handleRemove })
|
|
498
481
|
] })
|
|
499
482
|
] })
|
|
500
483
|
] }) }),
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/components/icons.jsx","../src/api.js","../src/components/BunnyBrowserModal.jsx","../src/components/BunnyInput.jsx","../src/components/BunnyPreview.jsx","../src/schema.js","../src/index.js"],"sourcesContent":["import {Icon} from '@sanity/icons'\n\n// @sanity/icons has changed its per-icon import style across major versions:\n// v3.x only exports icons from the root barrel, v4.1+ adds per-icon subpaths\n// (e.g. '@sanity/icons/Upload'), and v5.0+ removes the root barrel entirely,\n// requiring subpath imports. Consuming apps can end up with any of these\n// versions hard-pinned somewhere in their dependency tree (outside our\n// control), so no single import style works everywhere.\n//\n// The one API that has stayed stable across all of these versions is the\n// dynamic `Icon` component with a `symbol` prop, exported from the root\n// entry point. These thin wrappers let the rest of the plugin use icons as\n// normal components (e.g. for @sanity/ui's `icon` prop) while staying\n// compatible with whatever @sanity/icons version ends up resolved.\nexport const UploadIcon = (props) => <Icon symbol=\"upload\" {...props} />\nexport const SearchIcon = (props) => <Icon symbol=\"search\" {...props} />\nexport const VideoIcon = (props) => <Icon symbol=\"video\" {...props} />\n","const BUNNY_API_BASE = 'https://video.bunnycdn.com'\n\n/**\n * Maps Bunny's numeric status to our string status\n * @param {number} status\n * @returns {'uploading' | 'processing' | 'ready' | 'error'}\n */\nexport function mapBunnyStatus(status) {\n switch (status) {\n case 0: // created\n case 1: // uploaded\n return 'uploading'\n case 2: // processing\n case 3: // transcoding\n return 'processing'\n case 4: // finished\n return 'ready'\n case 5: // error\n default:\n return 'error'\n }\n}\n\n/**\n * Get the thumbnail URL for a video\n * @param {string} cdnHostname - The CDN hostname (e.g., 'vz-fbd37838-a6a.b-cdn.net')\n * @param {string} videoId\n * @returns {string}\n */\nexport function getThumbnailUrl(cdnHostname, videoId) {\n return `https://${cdnHostname}/${videoId}/thumbnail.jpg`\n}\n\n/**\n * Get the HLS playback URL for a video\n * @param {string} cdnHostname - The CDN hostname (e.g., 'vz-fbd37838-a6a.b-cdn.net')\n * @param {string} videoId\n * @returns {string}\n */\nexport function getPlaybackUrl(cdnHostname, videoId) {\n return `https://${cdnHostname}/${videoId}/playlist.m3u8`\n}\n\n/**\n * Get the direct MP4 URL (if available)\n * @param {string} cdnHostname - The CDN hostname (e.g., 'vz-fbd37838-a6a.b-cdn.net')\n * @param {string} videoId\n * @param {string} availableResolutions - Comma-separated string (e.g. \"360p,480p,240p\")\n * @returns {string}\n */\nexport function getMp4Url(cdnHostname, videoId, availableResolutions = '') {\n // Parse to numbers so we can pick the highest; URL format requires \"480p\" etc.\n const parsedResolutions = (availableResolutions || '')\n .split(',')\n .map((s) => Number.parseInt(String(s.trim()).replace(/p$/i, ''), 10))\n .filter((n) => Number.isFinite(n))\n\n const bestResolution = parsedResolutions.length ? Math.max(...parsedResolutions) : 720\n return `https://${cdnHostname}/${videoId}/play_${bestResolution}p.mp4`\n}\n\nfunction createHeaders(config, includeJsonContentType = false, useDirectApi = false) {\n const headers = {\n Accept: 'application/json',\n }\n\n if (includeJsonContentType) {\n headers['Content-Type'] = 'application/json'\n }\n\n if (config.apiKey && (useDirectApi || !config.proxyEndpoint)) {\n headers.AccessKey = config.apiKey\n }\n\n return headers\n}\n\nfunction getCollectionListUrl(config, searchTerm) {\n if (config.proxyEndpoint && !config.apiKey) {\n return `${config.proxyEndpoint}/collections?search=${encodeURIComponent(searchTerm)}`\n }\n\n return `${BUNNY_API_BASE}/library/${config.libraryId}/collections?search=${encodeURIComponent(\n searchTerm\n )}&page=1&itemsPerPage=100`\n}\n\nfunction getCreateCollectionUrl(config) {\n if (config.proxyEndpoint && !config.apiKey) {\n return `${config.proxyEndpoint}/collections`\n }\n\n return `${BUNNY_API_BASE}/library/${config.libraryId}/collections`\n}\n\nasync function getCollectionList(config, searchTerm) {\n const url = getCollectionListUrl(config, searchTerm)\n\n const response = await fetch(url, {\n headers: createHeaders(config, false, !config.proxyEndpoint || Boolean(config.apiKey)),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to list collections: ${error}`)\n }\n\n const payload = await response.json()\n return payload.items || []\n}\n\n/**\n * Get or create a Bunny Stream collection by name\n * @param {Object} config\n * @param {string} collectionName\n * @returns {Promise<{guid: string, name: string}>}\n */\nexport async function getOrCreateCollection(config, collectionName) {\n const normalizedCollectionName = collectionName?.trim()\n\n if (!normalizedCollectionName) {\n throw new Error('Collection name cannot be empty')\n }\n\n const collections = await getCollectionList(config, normalizedCollectionName)\n const existingCollection = collections.find(\n (collection) => collection.name?.toLowerCase() === normalizedCollectionName.toLowerCase()\n )\n\n if (existingCollection) {\n return existingCollection\n }\n\n const response = await fetch(getCreateCollectionUrl(config), {\n method: 'POST',\n headers: createHeaders(config, true, !config.proxyEndpoint || Boolean(config.apiKey)),\n body: JSON.stringify({name: normalizedCollectionName}),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to create collection: ${error}`)\n }\n\n return response.json()\n}\n\n/**\n * Create a new video object in Bunny Stream\n * @param {Object} config\n * @param {string} title\n * @param {string} [collectionId]\n * @returns {Promise<Object>}\n */\nexport async function createVideo(config, title, collectionId) {\n const url = config.proxyEndpoint\n ? `${config.proxyEndpoint}/create`\n : `${BUNNY_API_BASE}/library/${config.libraryId}/videos`\n\n const headers = createHeaders(config, true)\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n title,\n ...(collectionId ? {collectionId} : {}),\n }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to create video: ${error}`)\n }\n\n return response.json()\n}\n\n/**\n * Upload video file to Bunny Stream\n * @param {Object} config\n * @param {string} videoId\n * @param {File} file\n * @param {Function} onProgress\n * @returns {Promise<Object>}\n */\nexport async function uploadVideo(config, videoId, file, onProgress) {\n const url = config.proxyEndpoint\n ? `${config.proxyEndpoint}/upload/${videoId}`\n : `${BUNNY_API_BASE}/library/${config.libraryId}/videos/${videoId}`\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest()\n\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable && onProgress) {\n const progress = Math.round((event.loaded / event.total) * 100)\n onProgress(progress)\n }\n })\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n const response = JSON.parse(xhr.responseText)\n resolve(response)\n } catch {\n resolve({success: true, message: 'OK'})\n }\n } else {\n reject(new Error(`Upload failed: ${xhr.statusText}`))\n }\n })\n\n xhr.addEventListener('error', () => {\n reject(new Error('Upload failed: Network error'))\n })\n\n xhr.open('PUT', url)\n xhr.setRequestHeader('Accept', 'application/json')\n\n if (!config.proxyEndpoint && config.apiKey) {\n xhr.setRequestHeader('AccessKey', config.apiKey)\n }\n\n xhr.send(file)\n })\n}\n\n/**\n * Get video details from Bunny Stream\n * @param {Object} config\n * @param {string} videoId\n * @returns {Promise<Object>}\n */\nexport async function getVideo(config, videoId) {\n const url = config.proxyEndpoint\n ? `${config.proxyEndpoint}/video/${videoId}`\n : `${BUNNY_API_BASE}/library/${config.libraryId}/videos/${videoId}`\n\n const headers = createHeaders(config)\n\n const response = await fetch(url, {headers})\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to get video: ${error}`)\n }\n\n return response.json()\n}\n\n/**\n * List videos in a collection (or entire library if no collectionId)\n * @param {Object} config\n * @param {Object} options\n * @param {string} [options.collectionId]\n * @param {string} [options.search]\n * @param {number} [options.page]\n * @param {number} [options.itemsPerPage]\n * @returns {Promise<{items: Array, totalItems: number, currentPage: number, itemsPerPage: number}>}\n */\nexport async function listVideos(config, {collectionId, search = '', page = 1, itemsPerPage = 50} = {}) {\n const params = new URLSearchParams({\n page: String(page),\n itemsPerPage: String(itemsPerPage),\n orderBy: 'date',\n })\n\n if (collectionId) params.set('collection', collectionId)\n if (search) params.set('search', search)\n\n const url = config.proxyEndpoint\n ? `${config.proxyEndpoint}/videos?${params}`\n : `${BUNNY_API_BASE}/library/${config.libraryId}/videos?${params}`\n\n const response = await fetch(url, {\n headers: createHeaders(config, false, !config.proxyEndpoint || Boolean(config.apiKey)),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to list videos: ${error}`)\n }\n\n const payload = await response.json()\n return {\n items: payload.items || [],\n totalItems: payload.totalItems ?? 0,\n currentPage: payload.currentPage ?? page,\n itemsPerPage: payload.itemsPerPage ?? itemsPerPage,\n }\n}\n\n/**\n * Delete a video from Bunny Stream\n * @param {Object} config\n * @param {string} videoId\n * @returns {Promise<Object>}\n */\nexport async function deleteVideo(config, videoId) {\n const url = config.proxyEndpoint\n ? `${config.proxyEndpoint}/video/${videoId}`\n : `${BUNNY_API_BASE}/library/${config.libraryId}/videos/${videoId}`\n\n const headers = createHeaders(config)\n\n const response = await fetch(url, {\n method: 'DELETE',\n headers,\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to delete video: ${error}`)\n }\n\n return response.json()\n}\n","import {useCallback, useEffect, useRef, useState} from 'react'\nimport {Box, Button, Card, Dialog, Flex, Grid, Spinner, Stack, Text, TextInput} from '@sanity/ui'\nimport {SearchIcon} from './icons'\nimport {listVideos, getThumbnailUrl, getPlaybackUrl, getMp4Url, mapBunnyStatus, getOrCreateCollection} from '../api'\n\nconst ITEMS_PER_PAGE = 48\n\nexport function BunnyBrowserModal({config, collectionId: collectionIdProp, onSelect, onClose}) {\n const [videos, setVideos] = useState([])\n const [search, setSearch] = useState('')\n const [page, setPage] = useState(1)\n const [totalItems, setTotalItems] = useState(0)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState(null)\n // Resolved collection ID (may come from prop or be looked up from collectionName)\n const [resolvedCollectionId, setResolvedCollectionId] = useState(collectionIdProp || null)\n const searchTimeoutRef = useRef(null)\n\n const fetchVideos = useCallback(\n async (searchTerm, pageNum, collId) => {\n setIsLoading(true)\n setError(null)\n try {\n const result = await listVideos(config, {\n collectionId: collId,\n search: searchTerm,\n page: pageNum,\n itemsPerPage: ITEMS_PER_PAGE,\n })\n setVideos(result.items)\n setTotalItems(result.totalItems)\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load videos')\n } finally {\n setIsLoading(false)\n }\n },\n [config]\n )\n\n // Resolve collectionId from collectionName if needed, then fetch\n useEffect(() => {\n async function init() {\n let collId = collectionIdProp || null\n\n if (!collId && config.collectionName) {\n try {\n const collection = await getOrCreateCollection(config, config.collectionName)\n collId = collection.guid\n setResolvedCollectionId(collId)\n } catch (err) {\n // Fall back to browsing entire library if collection lookup fails\n console.warn('Could not resolve collection, browsing full library:', err)\n }\n }\n\n fetchVideos('', 1, collId)\n }\n init()\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleSearchChange = useCallback(\n (e) => {\n const value = e.target.value\n setSearch(value)\n setPage(1)\n if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current)\n searchTimeoutRef.current = setTimeout(() => {\n fetchVideos(value, 1, resolvedCollectionId)\n }, 400)\n },\n [fetchVideos, resolvedCollectionId]\n )\n\n const handlePageChange = useCallback(\n (newPage) => {\n setPage(newPage)\n fetchVideos(search, newPage, resolvedCollectionId)\n },\n [fetchVideos, search, resolvedCollectionId]\n )\n\n const handleSelect = useCallback(\n (video) => {\n const videoStatus = mapBunnyStatus(video.status)\n if (videoStatus !== 'ready') return\n\n const width = Number(video.width)\n const height = Number(video.height)\n const hasDims = Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0\n const ratio = hasDims ? Number((width / height).toFixed(6)) : null\n const orientation = hasDims\n ? width > height\n ? 'landscape'\n : width < height\n ? 'portrait'\n : 'square'\n : null\n\n onSelect({\n _type: 'bunnyVideo',\n videoId: video.guid,\n libraryId: String(video.videoLibraryId),\n title: video.title,\n status: 'ready',\n duration: video.length,\n width: hasDims ? width : null,\n height: hasDims ? height : null,\n ratio,\n orientation,\n thumbnailUrl: getThumbnailUrl(config.cdnHostname, video.guid),\n playbackUrl: getPlaybackUrl(config.cdnHostname, video.guid),\n mp4Url: getMp4Url(config.cdnHostname, video.guid, video.availableResolutions),\n ...(resolvedCollectionId ? {collectionId: resolvedCollectionId} : {}),\n ...(video.collectionId ? {collectionId: video.collectionId} : {}),\n })\n },\n [config, resolvedCollectionId, onSelect]\n )\n\n const totalPages = Math.ceil(totalItems / ITEMS_PER_PAGE)\n\n return (\n <Dialog\n header=\"Browse videos\"\n id=\"bunny-browser-modal\"\n onClose={onClose}\n width={3}\n >\n <Box padding={4}>\n <Box marginBottom={4}>\n <TextInput\n icon={SearchIcon}\n placeholder=\"Search videos…\"\n value={search}\n onChange={handleSearchChange}\n />\n </Box>\n\n {error && (\n <Card padding={3} radius={2} tone=\"critical\" marginBottom={4}>\n <Text size={1}>{error}</Text>\n </Card>\n )}\n\n {isLoading ? (\n <Flex align=\"center\" justify=\"center\" padding={6}>\n <Spinner />\n </Flex>\n ) : videos.length === 0 ? (\n <Flex align=\"center\" justify=\"center\" padding={6}>\n <Text muted size={1}>\n {search ? 'No videos found matching your search' : 'No videos in this collection'}\n </Text>\n </Flex>\n ) : (\n <>\n <Grid columns={[2, 2, 3, 4]} gap={3}>\n {videos.map((video) => {\n const status = mapBunnyStatus(video.status)\n const isReady = status === 'ready'\n const thumbUrl = getThumbnailUrl(config.cdnHostname, video.guid)\n\n return (\n <Card\n key={video.guid}\n radius={2}\n shadow={1}\n style={{\n cursor: isReady ? 'pointer' : 'not-allowed',\n opacity: isReady ? 1 : 0.5,\n }}\n onClick={() => isReady && handleSelect(video)}\n >\n <Box\n style={{\n position: 'relative',\n paddingBottom: '56.25%',\n backgroundColor: '#111',\n borderRadius: '2px',\n overflow: 'hidden',\n }}\n >\n <img\n src={thumbUrl}\n alt={video.title || 'Video thumbnail'}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n }}\n />\n {!isReady && (\n <Flex\n align=\"center\"\n justify=\"center\"\n style={{\n position: 'absolute',\n inset: 0,\n backgroundColor: 'rgba(0,0,0,0.5)',\n }}\n >\n <Text size={0} style={{color: '#fff'}}>\n {status === 'processing' ? 'Processing…' : status}\n </Text>\n </Flex>\n )}\n </Box>\n <Box padding={3}>\n <Stack space={2}>\n <Text size={1} weight=\"semibold\">\n {video.title || 'Untitled'}\n </Text>\n {video.length > 0 && (\n <Text size={1} muted>\n {Math.floor(video.length / 60)}:\n {String(Math.floor(video.length % 60)).padStart(2, '0')}\n </Text>\n )}\n </Stack>\n </Box>\n </Card>\n )\n })}\n </Grid>\n\n {totalPages > 1 && (\n <Flex align=\"center\" justify=\"center\" gap={2} marginTop={4}>\n <Button\n text=\"Previous\"\n mode=\"ghost\"\n disabled={page <= 1}\n onClick={() => handlePageChange(page - 1)}\n />\n <Text size={1} muted>\n Page {page} of {totalPages}\n </Text>\n <Button\n text=\"Next\"\n mode=\"ghost\"\n disabled={page >= totalPages}\n onClick={() => handlePageChange(page + 1)}\n />\n </Flex>\n )}\n </>\n )}\n </Box>\n </Dialog>\n )\n}\n","import {useCallback, useEffect, useRef, useState} from 'react'\nimport {set, unset} from 'sanity'\nimport {Box, Button, Card, Flex, Spinner, Stack, Text} from '@sanity/ui'\nimport {UploadIcon, SearchIcon, VideoIcon} from './icons'\nimport {\n createVideo,\n uploadVideo,\n getVideo,\n deleteVideo,\n getOrCreateCollection,\n mapBunnyStatus,\n getThumbnailUrl,\n getPlaybackUrl,\n getMp4Url,\n} from '../api'\nimport {BunnyBrowserModal} from './BunnyBrowserModal'\n\nexport function BunnyInput(props) {\n const {value, onChange, config} = props\n const [uploadProgress, setUploadProgress] = useState(0)\n const [isUploading, setIsUploading] = useState(false)\n const [error, setError] = useState(null)\n const [isBrowserOpen, setIsBrowserOpen] = useState(false)\n const fileInputRef = useRef(null)\n const pollIntervalRef = useRef(null)\n\n // Poll for video status when processing\n useEffect(() => {\n if (value?.videoId && value?.status === 'processing') {\n pollIntervalRef.current = window.setInterval(async () => {\n try {\n const videoData = await getVideo(config, value.videoId)\n const status = mapBunnyStatus(videoData.status)\n\n if (status === 'ready' || status === 'error') {\n // Clear interval when done\n if (pollIntervalRef.current) {\n clearInterval(pollIntervalRef.current)\n pollIntervalRef.current = null\n }\n\n const width = Number(videoData.width)\n const height = Number(videoData.height)\n\n const hasDims = Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0\n const ratio = hasDims ? Number((width / height).toFixed(6)) : null\n const orientation = hasDims\n ? width > height\n ? 'landscape'\n : width < height\n ? 'portrait'\n : 'square'\n : null\n\n onChange(\n set({\n ...value,\n status,\n duration: videoData.length,\n height: hasDims ? height : null,\n width: hasDims ? width : null,\n ratio: ratio,\n orientation: orientation,\n thumbnailUrl: getThumbnailUrl(config.cdnHostname, value.videoId),\n playbackUrl: getPlaybackUrl(config.cdnHostname, value.videoId),\n mp4Url: getMp4Url(\n config.cdnHostname,\n value.videoId,\n videoData.availableResolutions\n ),\n })\n )\n }\n } catch (err) {\n console.error('Error polling video status:', err)\n }\n }, 5000) // Poll every 5 seconds\n }\n\n return () => {\n if (pollIntervalRef.current) {\n clearInterval(pollIntervalRef.current)\n }\n }\n }, [value?.videoId, value?.status, config, onChange, value])\n\n const handleFileSelect = useCallback(\n async (file) => {\n if (!file.type.startsWith('video/')) {\n setError('Please select a video file')\n return\n }\n\n setError(null)\n setIsUploading(true)\n setUploadProgress(0)\n\n try {\n // Step 1: Create video object in Bunny\n const title = file.name.replace(/\\.[^/.]+$/, '') // Remove extension\n let collectionId = config.collectionId\n let collectionName = config.collectionName\n\n if (!collectionId && collectionName) {\n const collection = await getOrCreateCollection(config, collectionName)\n collectionId = collection.guid\n collectionName = collection.name\n }\n\n const created = await createVideo(config, title, collectionId)\n\n // Update Sanity with initial data\n onChange(\n set({\n _type: 'bunnyVideo',\n videoId: created.guid,\n libraryId: String(created.videoLibraryId),\n title,\n status: 'uploading',\n ...(collectionId ? {collectionId} : {}),\n ...(collectionName ? {collectionName} : {}),\n })\n )\n\n // Step 2: Upload the actual file\n await uploadVideo(config, created.guid, file, setUploadProgress)\n\n // Step 3: Update status to processing\n onChange(\n set({\n _type: 'bunnyVideo',\n videoId: created.guid,\n libraryId: String(created.videoLibraryId),\n title,\n status: 'processing',\n ...(collectionId ? {collectionId} : {}),\n ...(collectionName ? {collectionName} : {}),\n })\n )\n } catch (err) {\n console.error('Upload error:', err)\n setError(err instanceof Error ? err.message : 'Upload failed')\n onChange(\n set({\n ...value,\n status: 'error',\n errorMessage: err instanceof Error ? err.message : 'Upload failed',\n })\n )\n } finally {\n setIsUploading(false)\n setUploadProgress(0)\n }\n },\n [config, onChange, value]\n )\n\n const handleInputChange = useCallback(\n (e) => {\n const file = e.target.files?.[0]\n if (file) {\n handleFileSelect(file)\n }\n },\n [handleFileSelect]\n )\n\n const handleBrowseSelect = useCallback(\n (videoData) => {\n setIsBrowserOpen(false)\n onChange(set(videoData))\n },\n [onChange]\n )\n\n const handleRemove = useCallback(async () => {\n if (value?.videoId) {\n try {\n await deleteVideo(config, value.videoId)\n } catch (err) {\n // Log but don't block removal from Sanity\n console.error('Error deleting from Bunny:', err)\n }\n }\n onChange(unset())\n }, [config, value, onChange])\n\n // Render video preview if we have one\n if (value?.videoId && value?.status === 'ready') {\n return (\n <>\n <Card padding={3} radius={2} shadow={1}>\n <Stack space={3}>\n <Box\n style={{\n position: 'relative',\n paddingBottom: '56.25%',\n backgroundColor: '#000',\n borderRadius: '4px',\n overflow: 'hidden',\n }}\n >\n {value.thumbnailUrl && (\n <img\n src={value.thumbnailUrl}\n alt={value.title || 'Video thumbnail'}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n objectFit: 'contain',\n }}\n />\n )}\n </Box>\n <Flex justify=\"space-between\" align=\"center\">\n <Stack space={2}>\n <Text size={1} weight=\"semibold\">\n {value.title}\n </Text>\n {value.duration && (\n <Text size={1} muted>\n {Math.floor(value.duration / 60)}:\n {String(Math.floor(value.duration % 60)).padStart(2, '0')}\n </Text>\n )}\n </Stack>\n <Flex gap={2}>\n <Button\n text=\"Browse\"\n mode=\"ghost\"\n onClick={() => setIsBrowserOpen(true)}\n />\n <Button text=\"Remove\" tone=\"critical\" mode=\"ghost\" onClick={handleRemove} />\n </Flex>\n </Flex>\n </Stack>\n </Card>\n {isBrowserOpen && (\n <BunnyBrowserModal\n config={config}\n collectionId={value.collectionId || config.collectionId}\n onSelect={handleBrowseSelect}\n onClose={() => setIsBrowserOpen(false)}\n />\n )}\n </>\n )\n }\n\n // Render processing state\n if (value?.status === 'processing') {\n return (\n <Card padding={4} radius={2} shadow={1}>\n <Flex direction=\"column\" align=\"center\" justify=\"center\" gap={3}>\n <Spinner />\n <Text size={1}>Processing video...</Text>\n <Text size={0} muted>\n This may take a few minutes\n </Text>\n </Flex>\n </Card>\n )\n }\n\n // Render uploading state\n if (isUploading) {\n return (\n <Card padding={4} radius={2} shadow={1}>\n <Stack space={3}>\n <Flex direction=\"column\" align=\"center\" justify=\"center\" gap={2}>\n <Spinner />\n <Text size={1}>Uploading... {uploadProgress}%</Text>\n </Flex>\n <Box\n style={{\n height: '4px',\n backgroundColor: '#e5e5e5',\n borderRadius: '2px',\n overflow: 'hidden',\n }}\n >\n <Box\n style={{\n height: '100%',\n width: `${uploadProgress}%`,\n backgroundColor: '#2563eb',\n transition: 'width 0.3s ease',\n }}\n />\n </Box>\n </Stack>\n </Card>\n )\n }\n\n // Render error state\n if (error || value?.status === 'error') {\n return (\n <Stack space={3}>\n <Card padding={3} radius={2} tone=\"critical\">\n <Text size={1}>Error: {error || value?.errorMessage}</Text>\n </Card>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"video/*\"\n onChange={handleInputChange}\n />\n </Stack>\n )\n }\n\n // Render file input + browse option\n return (\n <>\n <div style={{padding: 1}}>\n <Card tone=\"inherit\" border paddingX={3} paddingY={2} radius={2}>\n <Flex align=\"center\" gap={4} justify=\"space-between\">\n <Flex flex={1} align=\"center\" gap={3}>\n <Text size={1} muted><VideoIcon /></Text>\n <Text size={1} muted>Drag or paste video here</Text>\n </Flex>\n <Flex align=\"center\" gap={1}>\n <label style={{display: 'contents'}}>\n <Button\n as=\"span\"\n icon={UploadIcon}\n text=\"Upload\"\n mode=\"bleed\"\n padding={2}\n style={{cursor: 'pointer'}}\n />\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"video/*\"\n onChange={handleInputChange}\n style={{\n position: 'absolute',\n width: '1px',\n height: '1px',\n overflow: 'hidden',\n opacity: 0,\n }}\n />\n </label>\n <Button\n icon={SearchIcon}\n text=\"Select\"\n mode=\"bleed\"\n padding={2}\n onClick={() => setIsBrowserOpen(true)}\n />\n </Flex>\n </Flex>\n </Card>\n </div>\n {isBrowserOpen && (\n <BunnyBrowserModal\n config={config}\n collectionId={config.collectionId}\n onSelect={handleBrowseSelect}\n onClose={() => setIsBrowserOpen(false)}\n />\n )}\n </>\n )\n}\n","import {Box, Flex, Text} from '@sanity/ui'\n\nexport function BunnyPreview(props) {\n const {title, status, thumbnailUrl} = props\n\n const statusColors = {\n uploading: '#f59e0b',\n processing: '#3b82f6',\n ready: '#22c55e',\n error: '#ef4444',\n }\n\n const statusLabels = {\n uploading: 'Uploading',\n processing: 'Processing',\n ready: 'Ready',\n error: 'Error',\n }\n\n return (\n <Flex align=\"center\" gap={3} padding={2}>\n {thumbnailUrl ? (\n <Box\n style={{\n width: 80,\n height: 45,\n borderRadius: 4,\n overflow: 'hidden',\n backgroundColor: '#000',\n flexShrink: 0,\n }}\n >\n <img\n src={thumbnailUrl}\n alt={title || 'Video thumbnail'}\n style={{\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n }}\n />\n </Box>\n ) : (\n <Box\n style={{\n width: 80,\n height: 45,\n borderRadius: 4,\n backgroundColor: '#e5e5e5',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n }}\n >\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{opacity: 0.4}}\n >\n <polygon points=\"5 3 19 12 5 21 5 3\" />\n </svg>\n </Box>\n )}\n <Flex direction=\"column\" gap={1} style={{minWidth: 0}}>\n <Text\n size={1}\n weight=\"semibold\"\n style={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {title || 'Untitled video'}\n </Text>\n {status && (\n <Flex align=\"center\" gap={2}>\n <Box\n style={{\n width: 8,\n height: 8,\n borderRadius: '50%',\n backgroundColor: statusColors[status] || '#9ca3af',\n }}\n />\n <Text size={0} muted>\n {statusLabels[status] || status}\n </Text>\n </Flex>\n )}\n </Flex>\n </Flex>\n )\n}\n","import { defineType, defineField } from 'sanity'\nimport { BunnyInput } from './components/BunnyInput'\nimport { BunnyPreview } from './components/BunnyPreview'\n\nexport const createBunnyVideoSchema = (config) => {\n\treturn defineType({\n\t\ttype: 'object',\n\t\ttitle: 'Bunny Video',\n\t\tname: 'bunnyVideo',\n\t\tfields: [\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Video ID',\n\t\t\t\tname: 'videoId',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Library ID',\n\t\t\t\tname: 'libraryId',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Collection ID',\n\t\t\t\tname: 'collectionId',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Collection name',\n\t\t\t\tname: 'collectionName',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Title',\n\t\t\t\tname: 'title',\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Status',\n\t\t\t\tname: 'status',\n\t\t\t\toptions: {\n\t\t\t\t\tlist: [\n\t\t\t\t\t\t{title: 'Uploading', value: 'uploading'},\n\t\t\t\t\t\t{title: 'Processing', value: 'processing'},\n\t\t\t\t\t\t{title: 'Ready', value: 'ready'},\n\t\t\t\t\t\t{title: 'Error', value: 'error'},\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'number',\n\t\t\t\ttitle: 'Video width',\n\t\t\t\tname: 'width',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'number',\n\t\t\t\ttitle: 'Video height',\n\t\t\t\tname: 'height',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'number',\n\t\t\t\ttitle: 'Video ratio',\n\t\t\t\tname: 'ratio',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Orientation',\n\t\t\t\tname: 'orientation',\n\t\t\t\toptions: {\n\t\t\t\t\tlist: [\n\t\t\t\t\t\t{title: 'Landscape', value: 'landscape'},\n\t\t\t\t\t\t{title: 'Portrait', value: 'portrait'},\n\t\t\t\t\t\t{title: 'Square', value: 'square'},\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Thumbnail URL',\n\t\t\t\tname: 'thumbnailUrl',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Playback URL',\n\t\t\t\tname: 'playbackUrl',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'MP4 URL',\n\t\t\t\tname: 'mp4Url',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'number',\n\t\t\t\ttitle: 'Duration (seconds)',\n\t\t\t\tname: 'duration',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Error Message',\n\t\t\t\tname: 'errorMessage',\n\t\t\t\treadOnly: true,\n\t\t\t\thidden: true,\n\t\t\t}),\n\t\t],\n\t\tcomponents: {\n\t\t\tinput: (props) => BunnyInput({...props, config}),\n\t\t\tpreview: BunnyPreview,\n\t\t},\n\t\tpreview: {\n\t\t\tselect: {\n\t\t\t\ttitle: 'title',\n\t\t\t\tstatus: 'status',\n\t\t\t\tthumbnailUrl: 'thumbnailUrl',\n\t\t\t},\n\t\t},\n\t})\n}","import {definePlugin} from 'sanity'\nimport {createBunnyVideoSchema} from './schema'\n\nexport {getThumbnailUrl, getPlaybackUrl, getMp4Url} from './api'\n\n/**\n * Sanity plugin for Bunny Stream video uploads\n *\n * @example\n * ```js\n * // sanity.config.js\n * import { bunnyInput } from 'sanity-plugin-bunny-input'\n *\n * export default defineConfig({\n * plugins: [\n * bunnyInput({\n * libraryId: 'your-library-id',\n * cdnHostname: 'vz-abc123-xyz.b-cdn.net', // From Bunny dashboard\n * collectionName: 'my-project-videos', // Optional: auto-create/use collection by name\n * // collectionId: 'existing-collection-guid', // Optional: use existing collection directly\n * apiKey: 'your-api-key', // Or use proxyEndpoint for security\n * })\n * ]\n * })\n * ```\n *\n * Then use in your schemas:\n * ```js\n * defineField({\n * name: 'video',\n * title: 'Video',\n * type: 'bunnyVideo'\n * })\n * ```\n */\nexport const bunnyInput = definePlugin((config) => {\n if (!config.libraryId) {\n throw new Error('sanity-plugin-bunny-input: libraryId is required')\n }\n\n if (!config.cdnHostname) {\n throw new Error('sanity-plugin-bunny-input: cdnHostname is required (e.g., vz-abc123-xyz.b-cdn.net)')\n }\n\n if (!config.apiKey && !config.proxyEndpoint) {\n throw new Error('sanity-plugin-bunny-input: Either apiKey or proxyEndpoint is required')\n }\n\n const normalizedCollectionName =\n typeof config.collectionName === 'string' ? config.collectionName.trim() : undefined\n\n const pluginConfig = {\n ...config,\n ...(normalizedCollectionName ? {collectionName: normalizedCollectionName} : {}),\n }\n\n return {\n name: 'sanity-plugin-bunny-input',\n schema: {\n types: [createBunnyVideoSchema(pluginConfig)],\n },\n }\n})\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/components/icons.jsx","../src/api.js","../src/components/BunnyBrowserModal.jsx","../src/components/BunnyInput.jsx","../src/components/BunnyPreview.jsx","../src/schema.js","../src/index.js"],"sourcesContent":["import {Icon} from '@sanity/icons'\n\n// @sanity/icons has changed its per-icon import style across major versions:\n// v3.x only exports icons from the root barrel, v4.1+ adds per-icon subpaths\n// (e.g. '@sanity/icons/Upload'), and v5.0+ removes the root barrel entirely,\n// requiring subpath imports. Consuming apps can end up with any of these\n// versions hard-pinned somewhere in their dependency tree (outside our\n// control), so no single import style works everywhere.\n//\n// The one API that has stayed stable across all of these versions is the\n// dynamic `Icon` component with a `symbol` prop, exported from the root\n// entry point. These thin wrappers let the rest of the plugin use icons as\n// normal components (e.g. for @sanity/ui's `icon` prop) while staying\n// compatible with whatever @sanity/icons version ends up resolved.\nexport const UploadIcon = (props) => <Icon symbol=\"upload\" {...props} />\nexport const SearchIcon = (props) => <Icon symbol=\"search\" {...props} />\nexport const VideoIcon = (props) => <Icon symbol=\"video\" {...props} />\n","const BUNNY_API_BASE = 'https://video.bunnycdn.com'\n\n/**\n * Maps Bunny's numeric status to our string status\n * @param {number} status\n * @returns {'uploading' | 'processing' | 'ready' | 'error'}\n */\nexport function mapBunnyStatus(status) {\n switch (status) {\n case 0: // created\n case 1: // uploaded\n return 'uploading'\n case 2: // processing\n case 3: // transcoding\n return 'processing'\n case 4: // finished\n return 'ready'\n case 5: // error\n default:\n return 'error'\n }\n}\n\n/**\n * Get the thumbnail URL for a video\n * @param {string} cdnHostname - The CDN hostname (e.g., 'vz-fbd37838-a6a.b-cdn.net')\n * @param {string} videoId\n * @returns {string}\n */\nexport function getThumbnailUrl(cdnHostname, videoId) {\n return `https://${cdnHostname}/${videoId}/thumbnail.jpg`\n}\n\n/**\n * Get the HLS playback URL for a video\n * @param {string} cdnHostname - The CDN hostname (e.g., 'vz-fbd37838-a6a.b-cdn.net')\n * @param {string} videoId\n * @returns {string}\n */\nexport function getPlaybackUrl(cdnHostname, videoId) {\n return `https://${cdnHostname}/${videoId}/playlist.m3u8`\n}\n\n/**\n * Get the direct MP4 URL (if available)\n * @param {string} cdnHostname - The CDN hostname (e.g., 'vz-fbd37838-a6a.b-cdn.net')\n * @param {string} videoId\n * @param {string} availableResolutions - Comma-separated string (e.g. \"360p,480p,240p\")\n * @returns {string}\n */\nexport function getMp4Url(cdnHostname, videoId, availableResolutions = '') {\n // Parse to numbers so we can pick the highest; URL format requires \"480p\" etc.\n const parsedResolutions = (availableResolutions || '')\n .split(',')\n .map((s) => Number.parseInt(String(s.trim()).replace(/p$/i, ''), 10))\n .filter((n) => Number.isFinite(n))\n\n const bestResolution = parsedResolutions.length ? Math.max(...parsedResolutions) : 720\n return `https://${cdnHostname}/${videoId}/play_${bestResolution}p.mp4`\n}\n\nfunction createHeaders(config, includeJsonContentType = false, useDirectApi = false) {\n const headers = {\n Accept: 'application/json',\n }\n\n if (includeJsonContentType) {\n headers['Content-Type'] = 'application/json'\n }\n\n if (config.apiKey && (useDirectApi || !config.proxyEndpoint)) {\n headers.AccessKey = config.apiKey\n }\n\n return headers\n}\n\nfunction getCollectionListUrl(config, searchTerm) {\n if (config.proxyEndpoint && !config.apiKey) {\n return `${config.proxyEndpoint}/collections?search=${encodeURIComponent(searchTerm)}`\n }\n\n return `${BUNNY_API_BASE}/library/${config.libraryId}/collections?search=${encodeURIComponent(\n searchTerm\n )}&page=1&itemsPerPage=100`\n}\n\nfunction getCreateCollectionUrl(config) {\n if (config.proxyEndpoint && !config.apiKey) {\n return `${config.proxyEndpoint}/collections`\n }\n\n return `${BUNNY_API_BASE}/library/${config.libraryId}/collections`\n}\n\nasync function getCollectionList(config, searchTerm) {\n const url = getCollectionListUrl(config, searchTerm)\n\n const response = await fetch(url, {\n headers: createHeaders(config, false, !config.proxyEndpoint || Boolean(config.apiKey)),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to list collections: ${error}`)\n }\n\n const payload = await response.json()\n return payload.items || []\n}\n\n/**\n * Get or create a Bunny Stream collection by name\n * @param {Object} config\n * @param {string} collectionName\n * @returns {Promise<{guid: string, name: string}>}\n */\nexport async function getOrCreateCollection(config, collectionName) {\n const normalizedCollectionName = collectionName?.trim()\n\n if (!normalizedCollectionName) {\n throw new Error('Collection name cannot be empty')\n }\n\n const collections = await getCollectionList(config, normalizedCollectionName)\n const existingCollection = collections.find(\n (collection) => collection.name?.toLowerCase() === normalizedCollectionName.toLowerCase()\n )\n\n if (existingCollection) {\n return existingCollection\n }\n\n const response = await fetch(getCreateCollectionUrl(config), {\n method: 'POST',\n headers: createHeaders(config, true, !config.proxyEndpoint || Boolean(config.apiKey)),\n body: JSON.stringify({name: normalizedCollectionName}),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to create collection: ${error}`)\n }\n\n return response.json()\n}\n\n/**\n * Create a new video object in Bunny Stream\n * @param {Object} config\n * @param {string} title\n * @param {string} [collectionId]\n * @returns {Promise<Object>}\n */\nexport async function createVideo(config, title, collectionId) {\n const url = config.proxyEndpoint\n ? `${config.proxyEndpoint}/create`\n : `${BUNNY_API_BASE}/library/${config.libraryId}/videos`\n\n const headers = createHeaders(config, true)\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n title,\n ...(collectionId ? {collectionId} : {}),\n }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to create video: ${error}`)\n }\n\n return response.json()\n}\n\n/**\n * Upload video file to Bunny Stream\n * @param {Object} config\n * @param {string} videoId\n * @param {File} file\n * @param {Function} onProgress\n * @returns {Promise<Object>}\n */\nexport async function uploadVideo(config, videoId, file, onProgress) {\n const url = config.proxyEndpoint\n ? `${config.proxyEndpoint}/upload/${videoId}`\n : `${BUNNY_API_BASE}/library/${config.libraryId}/videos/${videoId}`\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest()\n\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable && onProgress) {\n const progress = Math.round((event.loaded / event.total) * 100)\n onProgress(progress)\n }\n })\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n const response = JSON.parse(xhr.responseText)\n resolve(response)\n } catch {\n resolve({success: true, message: 'OK'})\n }\n } else {\n reject(new Error(`Upload failed: ${xhr.statusText}`))\n }\n })\n\n xhr.addEventListener('error', () => {\n reject(new Error('Upload failed: Network error'))\n })\n\n xhr.open('PUT', url)\n xhr.setRequestHeader('Accept', 'application/json')\n\n if (!config.proxyEndpoint && config.apiKey) {\n xhr.setRequestHeader('AccessKey', config.apiKey)\n }\n\n xhr.send(file)\n })\n}\n\n/**\n * Get video details from Bunny Stream\n * @param {Object} config\n * @param {string} videoId\n * @returns {Promise<Object>}\n */\nexport async function getVideo(config, videoId) {\n const url = config.proxyEndpoint\n ? `${config.proxyEndpoint}/video/${videoId}`\n : `${BUNNY_API_BASE}/library/${config.libraryId}/videos/${videoId}`\n\n const headers = createHeaders(config)\n\n const response = await fetch(url, {headers})\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to get video: ${error}`)\n }\n\n return response.json()\n}\n\n/**\n * List videos in a collection (or entire library if no collectionId)\n * @param {Object} config\n * @param {Object} options\n * @param {string} [options.collectionId]\n * @param {string} [options.search]\n * @param {number} [options.page]\n * @param {number} [options.itemsPerPage]\n * @returns {Promise<{items: Array, totalItems: number, currentPage: number, itemsPerPage: number}>}\n */\nexport async function listVideos(config, {collectionId, search = '', page = 1, itemsPerPage = 50} = {}) {\n const params = new URLSearchParams({\n page: String(page),\n itemsPerPage: String(itemsPerPage),\n orderBy: 'date',\n })\n\n if (collectionId) params.set('collection', collectionId)\n if (search) params.set('search', search)\n\n const url = config.proxyEndpoint\n ? `${config.proxyEndpoint}/videos?${params}`\n : `${BUNNY_API_BASE}/library/${config.libraryId}/videos?${params}`\n\n const response = await fetch(url, {\n headers: createHeaders(config, false, !config.proxyEndpoint || Boolean(config.apiKey)),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to list videos: ${error}`)\n }\n\n const payload = await response.json()\n return {\n items: payload.items || [],\n totalItems: payload.totalItems ?? 0,\n currentPage: payload.currentPage ?? page,\n itemsPerPage: payload.itemsPerPage ?? itemsPerPage,\n }\n}\n\n/**\n * Delete a video from Bunny Stream\n * @param {Object} config\n * @param {string} videoId\n * @returns {Promise<Object>}\n */\nexport async function deleteVideo(config, videoId) {\n const url = config.proxyEndpoint\n ? `${config.proxyEndpoint}/video/${videoId}`\n : `${BUNNY_API_BASE}/library/${config.libraryId}/videos/${videoId}`\n\n const headers = createHeaders(config)\n\n const response = await fetch(url, {\n method: 'DELETE',\n headers,\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to delete video: ${error}`)\n }\n\n return response.json()\n}\n","import {useCallback, useEffect, useRef, useState} from 'react'\nimport {Box, Button, Card, Dialog, Flex, Grid, Spinner, Stack, Text, TextInput} from '@sanity/ui'\nimport {SearchIcon} from './icons'\nimport {listVideos, getThumbnailUrl, getPlaybackUrl, getMp4Url, mapBunnyStatus, getOrCreateCollection} from '../api'\n\nconst ITEMS_PER_PAGE = 48\n\nexport function BunnyBrowserModal({config, collectionId: collectionIdProp, onSelect, onClose}) {\n const [videos, setVideos] = useState([])\n const [search, setSearch] = useState('')\n const [page, setPage] = useState(1)\n const [totalItems, setTotalItems] = useState(0)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState(null)\n // Resolved collection ID (may come from prop or be looked up from collectionName)\n const [resolvedCollectionId, setResolvedCollectionId] = useState(collectionIdProp || null)\n const searchTimeoutRef = useRef(null)\n\n const fetchVideos = useCallback(\n async (searchTerm, pageNum, collId) => {\n setIsLoading(true)\n setError(null)\n try {\n const result = await listVideos(config, {\n collectionId: collId,\n search: searchTerm,\n page: pageNum,\n itemsPerPage: ITEMS_PER_PAGE,\n })\n setVideos(result.items)\n setTotalItems(result.totalItems)\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load videos')\n } finally {\n setIsLoading(false)\n }\n },\n [config]\n )\n\n // Resolve collectionId from collectionName if needed, then fetch\n useEffect(() => {\n async function init() {\n let collId = collectionIdProp || null\n\n if (!collId && config.collectionName) {\n try {\n const collection = await getOrCreateCollection(config, config.collectionName)\n collId = collection.guid\n setResolvedCollectionId(collId)\n } catch (err) {\n // Fall back to browsing entire library if collection lookup fails\n console.warn('Could not resolve collection, browsing full library:', err)\n }\n }\n\n fetchVideos('', 1, collId)\n }\n init()\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleSearchChange = useCallback(\n (e) => {\n const value = e.target.value\n setSearch(value)\n setPage(1)\n if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current)\n searchTimeoutRef.current = setTimeout(() => {\n fetchVideos(value, 1, resolvedCollectionId)\n }, 400)\n },\n [fetchVideos, resolvedCollectionId]\n )\n\n const handlePageChange = useCallback(\n (newPage) => {\n setPage(newPage)\n fetchVideos(search, newPage, resolvedCollectionId)\n },\n [fetchVideos, search, resolvedCollectionId]\n )\n\n const handleSelect = useCallback(\n (video) => {\n const videoStatus = mapBunnyStatus(video.status)\n if (videoStatus !== 'ready') return\n\n const width = Number(video.width)\n const height = Number(video.height)\n const hasDims = Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0\n const ratio = hasDims ? Number((width / height).toFixed(6)) : null\n const orientation = hasDims\n ? width > height\n ? 'landscape'\n : width < height\n ? 'portrait'\n : 'square'\n : null\n\n onSelect({\n _type: 'bunnyVideo',\n videoId: video.guid,\n libraryId: String(video.videoLibraryId),\n title: video.title,\n status: 'ready',\n duration: video.length,\n width: hasDims ? width : null,\n height: hasDims ? height : null,\n ratio,\n orientation,\n thumbnailUrl: getThumbnailUrl(config.cdnHostname, video.guid),\n playbackUrl: getPlaybackUrl(config.cdnHostname, video.guid),\n mp4Url: getMp4Url(config.cdnHostname, video.guid, video.availableResolutions),\n ...(resolvedCollectionId ? {collectionId: resolvedCollectionId} : {}),\n ...(video.collectionId ? {collectionId: video.collectionId} : {}),\n })\n },\n [config, resolvedCollectionId, onSelect]\n )\n\n const totalPages = Math.ceil(totalItems / ITEMS_PER_PAGE)\n\n return (\n <Dialog\n header=\"Browse videos\"\n id=\"bunny-browser-modal\"\n onClose={onClose}\n width={3}\n >\n <Box padding={4}>\n <Box marginBottom={4}>\n <TextInput\n icon={SearchIcon}\n placeholder=\"Search videos…\"\n value={search}\n onChange={handleSearchChange}\n />\n </Box>\n\n {error && (\n <Card padding={3} radius={2} tone=\"critical\" marginBottom={4}>\n <Text size={1}>{error}</Text>\n </Card>\n )}\n\n {isLoading ? (\n <Flex align=\"center\" justify=\"center\" padding={6}>\n <Spinner />\n </Flex>\n ) : videos.length === 0 ? (\n <Flex align=\"center\" justify=\"center\" padding={6}>\n <Text muted size={1}>\n {search ? 'No videos found matching your search' : 'No videos in this collection'}\n </Text>\n </Flex>\n ) : (\n <>\n <Grid columns={[2, 2, 3, 4]} gap={3}>\n {videos.map((video) => {\n const status = mapBunnyStatus(video.status)\n const isReady = status === 'ready'\n const thumbUrl = getThumbnailUrl(config.cdnHostname, video.guid)\n\n return (\n <Card\n key={video.guid}\n radius={2}\n shadow={1}\n style={{\n cursor: isReady ? 'pointer' : 'not-allowed',\n opacity: isReady ? 1 : 0.5,\n }}\n onClick={() => isReady && handleSelect(video)}\n >\n <Box\n style={{\n position: 'relative',\n paddingBottom: '56.25%',\n backgroundColor: '#111',\n borderRadius: '2px',\n overflow: 'hidden',\n }}\n >\n <img\n src={thumbUrl}\n alt={video.title || 'Video thumbnail'}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n }}\n />\n {!isReady && (\n <Flex\n align=\"center\"\n justify=\"center\"\n style={{\n position: 'absolute',\n inset: 0,\n backgroundColor: 'rgba(0,0,0,0.5)',\n }}\n >\n <Text size={0} style={{color: '#fff'}}>\n {status === 'processing' ? 'Processing…' : status}\n </Text>\n </Flex>\n )}\n </Box>\n <Box padding={3}>\n <Stack space={2}>\n <Text size={1} weight=\"semibold\">\n {video.title || 'Untitled'}\n </Text>\n {video.length > 0 && (\n <Text size={1} muted>\n {Math.floor(video.length / 60)}:\n {String(Math.floor(video.length % 60)).padStart(2, '0')}\n </Text>\n )}\n </Stack>\n </Box>\n </Card>\n )\n })}\n </Grid>\n\n {totalPages > 1 && (\n <Flex align=\"center\" justify=\"center\" gap={2} marginTop={4}>\n <Button\n text=\"Previous\"\n mode=\"ghost\"\n disabled={page <= 1}\n onClick={() => handlePageChange(page - 1)}\n />\n <Text size={1} muted>\n Page {page} of {totalPages}\n </Text>\n <Button\n text=\"Next\"\n mode=\"ghost\"\n disabled={page >= totalPages}\n onClick={() => handlePageChange(page + 1)}\n />\n </Flex>\n )}\n </>\n )}\n </Box>\n </Dialog>\n )\n}\n","import {useCallback, useEffect, useRef, useState} from 'react'\nimport {set, unset} from 'sanity'\nimport {Box, Button, Card, Flex, Spinner, Stack, Text} from '@sanity/ui'\nimport {UploadIcon, SearchIcon, VideoIcon} from './icons'\nimport {\n createVideo,\n uploadVideo,\n getVideo,\n getOrCreateCollection,\n mapBunnyStatus,\n getThumbnailUrl,\n getPlaybackUrl,\n getMp4Url,\n} from '../api'\nimport {BunnyBrowserModal} from './BunnyBrowserModal'\n\nexport function BunnyInput(props) {\n const {value, onChange, config} = props\n const [uploadProgress, setUploadProgress] = useState(0)\n const [isUploading, setIsUploading] = useState(false)\n const [error, setError] = useState(null)\n const [isBrowserOpen, setIsBrowserOpen] = useState(false)\n const fileInputRef = useRef(null)\n const pollIntervalRef = useRef(null)\n\n // Poll for video status when processing\n useEffect(() => {\n if (value?.videoId && value?.status === 'processing') {\n pollIntervalRef.current = window.setInterval(async () => {\n try {\n const videoData = await getVideo(config, value.videoId)\n const status = mapBunnyStatus(videoData.status)\n\n if (status === 'ready' || status === 'error') {\n // Clear interval when done\n if (pollIntervalRef.current) {\n clearInterval(pollIntervalRef.current)\n pollIntervalRef.current = null\n }\n\n const width = Number(videoData.width)\n const height = Number(videoData.height)\n\n const hasDims = Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0\n const ratio = hasDims ? Number((width / height).toFixed(6)) : null\n const orientation = hasDims\n ? width > height\n ? 'landscape'\n : width < height\n ? 'portrait'\n : 'square'\n : null\n\n onChange(\n set({\n ...value,\n status,\n duration: videoData.length,\n height: hasDims ? height : null,\n width: hasDims ? width : null,\n ratio: ratio,\n orientation: orientation,\n thumbnailUrl: getThumbnailUrl(config.cdnHostname, value.videoId),\n playbackUrl: getPlaybackUrl(config.cdnHostname, value.videoId),\n mp4Url: getMp4Url(\n config.cdnHostname,\n value.videoId,\n videoData.availableResolutions\n ),\n })\n )\n }\n } catch (err) {\n console.error('Error polling video status:', err)\n }\n }, 5000) // Poll every 5 seconds\n }\n\n return () => {\n if (pollIntervalRef.current) {\n clearInterval(pollIntervalRef.current)\n }\n }\n }, [value?.videoId, value?.status, config, onChange, value])\n\n const handleFileSelect = useCallback(\n async (file) => {\n if (!file.type.startsWith('video/')) {\n setError('Please select a video file')\n return\n }\n\n setError(null)\n setIsUploading(true)\n setUploadProgress(0)\n\n try {\n // Step 1: Create video object in Bunny\n const title = file.name.replace(/\\.[^/.]+$/, '') // Remove extension\n let collectionId = config.collectionId\n let collectionName = config.collectionName\n\n if (!collectionId && collectionName) {\n const collection = await getOrCreateCollection(config, collectionName)\n collectionId = collection.guid\n collectionName = collection.name\n }\n\n const created = await createVideo(config, title, collectionId)\n\n // Update Sanity with initial data\n onChange(\n set({\n _type: 'bunnyVideo',\n videoId: created.guid,\n libraryId: String(created.videoLibraryId),\n title,\n status: 'uploading',\n ...(collectionId ? {collectionId} : {}),\n ...(collectionName ? {collectionName} : {}),\n })\n )\n\n // Step 2: Upload the actual file\n await uploadVideo(config, created.guid, file, setUploadProgress)\n\n // Step 3: Update status to processing\n onChange(\n set({\n _type: 'bunnyVideo',\n videoId: created.guid,\n libraryId: String(created.videoLibraryId),\n title,\n status: 'processing',\n ...(collectionId ? {collectionId} : {}),\n ...(collectionName ? {collectionName} : {}),\n })\n )\n } catch (err) {\n console.error('Upload error:', err)\n setError(err instanceof Error ? err.message : 'Upload failed')\n onChange(\n set({\n ...value,\n status: 'error',\n errorMessage: err instanceof Error ? err.message : 'Upload failed',\n })\n )\n } finally {\n setIsUploading(false)\n setUploadProgress(0)\n }\n },\n [config, onChange, value]\n )\n\n const handleInputChange = useCallback(\n (e) => {\n const file = e.target.files?.[0]\n if (file) {\n handleFileSelect(file)\n }\n },\n [handleFileSelect]\n )\n\n const handleBrowseSelect = useCallback(\n (videoData) => {\n setIsBrowserOpen(false)\n onChange(set(videoData))\n },\n [onChange]\n )\n\n const handleRemove = useCallback(() => {\n // Clears the field's reference to the video only - it does not delete\n // the video from the Bunny library, matching how the Image field's\n // \"Clear\" behaves. Delete the video from Bunny itself (e.g. via the\n // Bunny dashboard) if you actually want it gone from the library.\n onChange(unset())\n }, [onChange])\n\n // Render video preview if we have one\n if (value?.videoId && value?.status === 'ready') {\n return (\n <>\n <Card padding={3} radius={2} shadow={1}>\n <Stack space={3}>\n <Box\n style={{\n position: 'relative',\n paddingBottom: '56.25%',\n backgroundColor: '#000',\n borderRadius: '4px',\n overflow: 'hidden',\n }}\n >\n {value.thumbnailUrl && (\n <img\n src={value.thumbnailUrl}\n alt={value.title || 'Video thumbnail'}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n objectFit: 'contain',\n }}\n />\n )}\n </Box>\n <Flex justify=\"space-between\" align=\"center\">\n <Stack space={2}>\n <Text size={1} weight=\"semibold\">\n {value.title}\n </Text>\n {value.duration && (\n <Text size={1} muted>\n {Math.floor(value.duration / 60)}:\n {String(Math.floor(value.duration % 60)).padStart(2, '0')}\n </Text>\n )}\n </Stack>\n <Flex gap={2}>\n <Button\n text=\"Browse\"\n mode=\"ghost\"\n onClick={() => setIsBrowserOpen(true)}\n />\n <Button text=\"Clear\" tone=\"critical\" mode=\"ghost\" onClick={handleRemove} />\n </Flex>\n </Flex>\n </Stack>\n </Card>\n {isBrowserOpen && (\n <BunnyBrowserModal\n config={config}\n collectionId={value.collectionId || config.collectionId}\n onSelect={handleBrowseSelect}\n onClose={() => setIsBrowserOpen(false)}\n />\n )}\n </>\n )\n }\n\n // Render processing state\n if (value?.status === 'processing') {\n return (\n <Card padding={4} radius={2} shadow={1}>\n <Flex direction=\"column\" align=\"center\" justify=\"center\" gap={3}>\n <Spinner />\n <Text size={1}>Processing video...</Text>\n <Text size={0} muted>\n This may take a few minutes\n </Text>\n </Flex>\n </Card>\n )\n }\n\n // Render uploading state\n if (isUploading) {\n return (\n <Card padding={4} radius={2} shadow={1}>\n <Stack space={3}>\n <Flex direction=\"column\" align=\"center\" justify=\"center\" gap={2}>\n <Spinner />\n <Text size={1}>Uploading... {uploadProgress}%</Text>\n </Flex>\n <Box\n style={{\n height: '4px',\n backgroundColor: '#e5e5e5',\n borderRadius: '2px',\n overflow: 'hidden',\n }}\n >\n <Box\n style={{\n height: '100%',\n width: `${uploadProgress}%`,\n backgroundColor: '#2563eb',\n transition: 'width 0.3s ease',\n }}\n />\n </Box>\n </Stack>\n </Card>\n )\n }\n\n // Render error state\n if (error || value?.status === 'error') {\n return (\n <Stack space={3}>\n <Card padding={3} radius={2} tone=\"critical\">\n <Text size={1}>Error: {error || value?.errorMessage}</Text>\n </Card>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"video/*\"\n onChange={handleInputChange}\n />\n </Stack>\n )\n }\n\n // Render file input + browse option\n return (\n <>\n <div style={{padding: 1}}>\n <Card tone=\"inherit\" border paddingX={3} paddingY={2} radius={2}>\n <Flex align=\"center\" gap={4} justify=\"space-between\">\n <Flex flex={1} align=\"center\" gap={3}>\n <Text size={1} muted><VideoIcon /></Text>\n <Text size={1} muted>Drag or paste video here</Text>\n </Flex>\n <Flex align=\"center\" gap={1}>\n <label style={{display: 'contents'}}>\n <Button\n as=\"span\"\n icon={UploadIcon}\n text=\"Upload\"\n mode=\"bleed\"\n padding={2}\n style={{cursor: 'pointer'}}\n />\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"video/*\"\n onChange={handleInputChange}\n style={{\n position: 'absolute',\n width: '1px',\n height: '1px',\n overflow: 'hidden',\n opacity: 0,\n }}\n />\n </label>\n <Button\n icon={SearchIcon}\n text=\"Select\"\n mode=\"bleed\"\n padding={2}\n onClick={() => setIsBrowserOpen(true)}\n />\n </Flex>\n </Flex>\n </Card>\n </div>\n {isBrowserOpen && (\n <BunnyBrowserModal\n config={config}\n collectionId={config.collectionId}\n onSelect={handleBrowseSelect}\n onClose={() => setIsBrowserOpen(false)}\n />\n )}\n </>\n )\n}\n","import {Box, Flex, Text} from '@sanity/ui'\n\nexport function BunnyPreview(props) {\n const {title, status, thumbnailUrl} = props\n\n const statusColors = {\n uploading: '#f59e0b',\n processing: '#3b82f6',\n ready: '#22c55e',\n error: '#ef4444',\n }\n\n const statusLabels = {\n uploading: 'Uploading',\n processing: 'Processing',\n ready: 'Ready',\n error: 'Error',\n }\n\n return (\n <Flex align=\"center\" gap={3} padding={2}>\n {thumbnailUrl ? (\n <Box\n style={{\n width: 80,\n height: 45,\n borderRadius: 4,\n overflow: 'hidden',\n backgroundColor: '#000',\n flexShrink: 0,\n }}\n >\n <img\n src={thumbnailUrl}\n alt={title || 'Video thumbnail'}\n style={{\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n }}\n />\n </Box>\n ) : (\n <Box\n style={{\n width: 80,\n height: 45,\n borderRadius: 4,\n backgroundColor: '#e5e5e5',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n }}\n >\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{opacity: 0.4}}\n >\n <polygon points=\"5 3 19 12 5 21 5 3\" />\n </svg>\n </Box>\n )}\n <Flex direction=\"column\" gap={1} style={{minWidth: 0}}>\n <Text\n size={1}\n weight=\"semibold\"\n style={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {title || 'Untitled video'}\n </Text>\n {status && (\n <Flex align=\"center\" gap={2}>\n <Box\n style={{\n width: 8,\n height: 8,\n borderRadius: '50%',\n backgroundColor: statusColors[status] || '#9ca3af',\n }}\n />\n <Text size={0} muted>\n {statusLabels[status] || status}\n </Text>\n </Flex>\n )}\n </Flex>\n </Flex>\n )\n}\n","import { defineType, defineField } from 'sanity'\nimport { BunnyInput } from './components/BunnyInput'\nimport { BunnyPreview } from './components/BunnyPreview'\n\nexport const createBunnyVideoSchema = (config) => {\n\treturn defineType({\n\t\ttype: 'object',\n\t\ttitle: 'Bunny Video',\n\t\tname: 'bunnyVideo',\n\t\tfields: [\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Video ID',\n\t\t\t\tname: 'videoId',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Library ID',\n\t\t\t\tname: 'libraryId',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Collection ID',\n\t\t\t\tname: 'collectionId',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Collection name',\n\t\t\t\tname: 'collectionName',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Title',\n\t\t\t\tname: 'title',\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Status',\n\t\t\t\tname: 'status',\n\t\t\t\toptions: {\n\t\t\t\t\tlist: [\n\t\t\t\t\t\t{title: 'Uploading', value: 'uploading'},\n\t\t\t\t\t\t{title: 'Processing', value: 'processing'},\n\t\t\t\t\t\t{title: 'Ready', value: 'ready'},\n\t\t\t\t\t\t{title: 'Error', value: 'error'},\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'number',\n\t\t\t\ttitle: 'Video width',\n\t\t\t\tname: 'width',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'number',\n\t\t\t\ttitle: 'Video height',\n\t\t\t\tname: 'height',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'number',\n\t\t\t\ttitle: 'Video ratio',\n\t\t\t\tname: 'ratio',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Orientation',\n\t\t\t\tname: 'orientation',\n\t\t\t\toptions: {\n\t\t\t\t\tlist: [\n\t\t\t\t\t\t{title: 'Landscape', value: 'landscape'},\n\t\t\t\t\t\t{title: 'Portrait', value: 'portrait'},\n\t\t\t\t\t\t{title: 'Square', value: 'square'},\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Thumbnail URL',\n\t\t\t\tname: 'thumbnailUrl',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Playback URL',\n\t\t\t\tname: 'playbackUrl',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'MP4 URL',\n\t\t\t\tname: 'mp4Url',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'number',\n\t\t\t\ttitle: 'Duration (seconds)',\n\t\t\t\tname: 'duration',\n\t\t\t\treadOnly: true,\n\t\t\t}),\n\t\t\tdefineField({\n\t\t\t\ttype: 'string',\n\t\t\t\ttitle: 'Error Message',\n\t\t\t\tname: 'errorMessage',\n\t\t\t\treadOnly: true,\n\t\t\t\thidden: true,\n\t\t\t}),\n\t\t],\n\t\tcomponents: {\n\t\t\tinput: (props) => BunnyInput({...props, config}),\n\t\t\tpreview: BunnyPreview,\n\t\t},\n\t\tpreview: {\n\t\t\tselect: {\n\t\t\t\ttitle: 'title',\n\t\t\t\tstatus: 'status',\n\t\t\t\tthumbnailUrl: 'thumbnailUrl',\n\t\t\t},\n\t\t},\n\t})\n}","import {definePlugin} from 'sanity'\nimport {createBunnyVideoSchema} from './schema'\n\nexport {getThumbnailUrl, getPlaybackUrl, getMp4Url} from './api'\n\n/**\n * Sanity plugin for Bunny Stream video uploads\n *\n * @example\n * ```js\n * // sanity.config.js\n * import { bunnyInput } from 'sanity-plugin-bunny-input'\n *\n * export default defineConfig({\n * plugins: [\n * bunnyInput({\n * libraryId: 'your-library-id',\n * cdnHostname: 'vz-abc123-xyz.b-cdn.net', // From Bunny dashboard\n * collectionName: 'my-project-videos', // Optional: auto-create/use collection by name\n * // collectionId: 'existing-collection-guid', // Optional: use existing collection directly\n * apiKey: 'your-api-key', // Or use proxyEndpoint for security\n * })\n * ]\n * })\n * ```\n *\n * Then use in your schemas:\n * ```js\n * defineField({\n * name: 'video',\n * title: 'Video',\n * type: 'bunnyVideo'\n * })\n * ```\n */\nexport const bunnyInput = definePlugin((config) => {\n if (!config.libraryId) {\n throw new Error('sanity-plugin-bunny-input: libraryId is required')\n }\n\n if (!config.cdnHostname) {\n throw new Error('sanity-plugin-bunny-input: cdnHostname is required (e.g., vz-abc123-xyz.b-cdn.net)')\n }\n\n if (!config.apiKey && !config.proxyEndpoint) {\n throw new Error('sanity-plugin-bunny-input: Either apiKey or proxyEndpoint is required')\n }\n\n const normalizedCollectionName =\n typeof config.collectionName === 'string' ? config.collectionName.trim() : undefined\n\n const pluginConfig = {\n ...config,\n ...(normalizedCollectionName ? {collectionName: normalizedCollectionName} : {}),\n }\n\n return {\n name: 'sanity-plugin-bunny-input',\n schema: {\n types: [createBunnyVideoSchema(pluginConfig)],\n },\n }\n})\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
createVideo,
|
|
7
7
|
uploadVideo,
|
|
8
8
|
getVideo,
|
|
9
|
-
deleteVideo,
|
|
10
9
|
getOrCreateCollection,
|
|
11
10
|
mapBunnyStatus,
|
|
12
11
|
getThumbnailUrl,
|
|
@@ -173,17 +172,13 @@ export function BunnyInput(props) {
|
|
|
173
172
|
[onChange]
|
|
174
173
|
)
|
|
175
174
|
|
|
176
|
-
const handleRemove = useCallback(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// Log but don't block removal from Sanity
|
|
182
|
-
console.error('Error deleting from Bunny:', err)
|
|
183
|
-
}
|
|
184
|
-
}
|
|
175
|
+
const handleRemove = useCallback(() => {
|
|
176
|
+
// Clears the field's reference to the video only - it does not delete
|
|
177
|
+
// the video from the Bunny library, matching how the Image field's
|
|
178
|
+
// "Clear" behaves. Delete the video from Bunny itself (e.g. via the
|
|
179
|
+
// Bunny dashboard) if you actually want it gone from the library.
|
|
185
180
|
onChange(unset())
|
|
186
|
-
}, [
|
|
181
|
+
}, [onChange])
|
|
187
182
|
|
|
188
183
|
// Render video preview if we have one
|
|
189
184
|
if (value?.videoId && value?.status === 'ready') {
|
|
@@ -233,7 +228,7 @@ export function BunnyInput(props) {
|
|
|
233
228
|
mode="ghost"
|
|
234
229
|
onClick={() => setIsBrowserOpen(true)}
|
|
235
230
|
/>
|
|
236
|
-
<Button text="
|
|
231
|
+
<Button text="Clear" tone="critical" mode="ghost" onClick={handleRemove} />
|
|
237
232
|
</Flex>
|
|
238
233
|
</Flex>
|
|
239
234
|
</Stack>
|