@openrails/contentkit-ui 0.0.0-stage → 0.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +425 -2
- package/dist/appearance.d.ts +38 -0
- package/dist/appearance.js +15 -0
- package/dist/appearance.js.map +1 -0
- package/dist/assets/hash.worker-BYfo-7MQ.js.map +1 -0
- package/dist/client/aspect.d.ts +6 -0
- package/dist/client/aspect.js +17 -0
- package/dist/client/aspect.js.map +1 -0
- package/dist/client/client.d.ts +51 -0
- package/dist/client/client.js +35 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/codes.d.ts +12 -0
- package/dist/client/codes.js +19 -0
- package/dist/client/codes.js.map +1 -0
- package/dist/client/content/bans.d.ts +15 -0
- package/dist/client/content/bans.js +40 -0
- package/dist/client/content/bans.js.map +1 -0
- package/dist/client/content/comments.d.ts +36 -0
- package/dist/client/content/comments.js +93 -0
- package/dist/client/content/comments.js.map +1 -0
- package/dist/client/content/favorites.d.ts +14 -0
- package/dist/client/content/favorites.js +42 -0
- package/dist/client/content/favorites.js.map +1 -0
- package/dist/client/content/moderation.d.ts +18 -0
- package/dist/client/content/moderation.js +37 -0
- package/dist/client/content/moderation.js.map +1 -0
- package/dist/client/content/polls.d.ts +47 -0
- package/dist/client/content/polls.js +148 -0
- package/dist/client/content/polls.js.map +1 -0
- package/dist/client/content/posts.d.ts +44 -0
- package/dist/client/content/posts.js +91 -0
- package/dist/client/content/posts.js.map +1 -0
- package/dist/client/content/reactions.d.ts +12 -0
- package/dist/client/content/reactions.js +36 -0
- package/dist/client/content/reactions.js.map +1 -0
- package/dist/client/content/types.d.ts +91 -0
- package/dist/client/content/types.js +8 -0
- package/dist/client/content/types.js.map +1 -0
- package/dist/client/crop.d.ts +32 -0
- package/dist/client/crop.js +103 -0
- package/dist/client/crop.js.map +1 -0
- package/dist/client/errors.d.ts +54 -0
- package/dist/client/errors.js +80 -0
- package/dist/client/errors.js.map +1 -0
- package/dist/client/gallery.d.ts +43 -0
- package/dist/client/gallery.js +86 -0
- package/dist/client/gallery.js.map +1 -0
- package/dist/client/generated/error-codes.d.ts +5 -0
- package/dist/client/generated/error-codes.js +131 -0
- package/dist/client/generated/error-codes.js.map +1 -0
- package/dist/client/generated/routes.d.ts +1086 -0
- package/dist/client/generated/routes.js +1400 -0
- package/dist/client/generated/routes.js.map +1 -0
- package/dist/client/generated/wire.d.ts +630 -0
- package/dist/client/generated/wire.js +6 -0
- package/dist/client/generated/wire.js.map +1 -0
- package/dist/client/http.d.ts +79 -0
- package/dist/client/http.js +74 -0
- package/dist/client/http.js.map +1 -0
- package/dist/client/image.d.ts +24 -0
- package/dist/client/image.js +65 -0
- package/dist/client/image.js.map +1 -0
- package/dist/client/index.d.ts +33 -0
- package/dist/client/media/api.d.ts +36 -0
- package/dist/client/media/api.js +61 -0
- package/dist/client/media/api.js.map +1 -0
- package/dist/client/media/client.d.ts +247 -0
- package/dist/client/media/client.js +407 -0
- package/dist/client/media/client.js.map +1 -0
- package/dist/client/media/encode.d.ts +3 -0
- package/dist/client/media/encode.js +8 -0
- package/dist/client/media/encode.js.map +1 -0
- package/dist/client/media/hash-stream.d.ts +6 -0
- package/dist/client/media/hash-stream.js +16 -0
- package/dist/client/media/hash-stream.js.map +1 -0
- package/dist/client/media/hash.d.ts +12 -0
- package/dist/client/media/hash.js +48 -0
- package/dist/client/media/hash.js.map +1 -0
- package/dist/client/media/hash.worker-inline.js +18 -0
- package/dist/client/media/hash.worker-inline.js.map +1 -0
- package/dist/client/media/pacer.d.ts +21 -0
- package/dist/client/media/pacer.js +25 -0
- package/dist/client/media/pacer.js.map +1 -0
- package/dist/client/media/queue.d.ts +112 -0
- package/dist/client/media/queue.js +274 -0
- package/dist/client/media/queue.js.map +1 -0
- package/dist/client/media/ref.d.ts +6 -0
- package/dist/client/media/ref.js +10 -0
- package/dist/client/media/ref.js.map +1 -0
- package/dist/client/media/rules.d.ts +52 -0
- package/dist/client/media/rules.js +139 -0
- package/dist/client/media/rules.js.map +1 -0
- package/dist/client/media/transport.d.ts +13 -0
- package/dist/client/media/transport.js +33 -0
- package/dist/client/media/transport.js.map +1 -0
- package/dist/client/media/windows.d.ts +15 -0
- package/dist/client/media/windows.js +39 -0
- package/dist/client/media/windows.js.map +1 -0
- package/dist/client/playback.d.ts +200 -0
- package/dist/client/playback.js +142 -0
- package/dist/client/playback.js.map +1 -0
- package/dist/client/player.d.ts +102 -0
- package/dist/client/player.js +155 -0
- package/dist/client/player.js.map +1 -0
- package/dist/client/public.d.ts +25 -0
- package/dist/client/public.js +18 -0
- package/dist/client/public.js.map +1 -0
- package/dist/client/rendition.d.ts +19 -0
- package/dist/client/rendition.js +16 -0
- package/dist/client/rendition.js.map +1 -0
- package/dist/client/route.d.ts +39 -0
- package/dist/client/route.js +31 -0
- package/dist/client/route.js.map +1 -0
- package/dist/client/taxonomy.d.ts +30 -0
- package/dist/client/taxonomy.js +77 -0
- package/dist/client/taxonomy.js.map +1 -0
- package/dist/client.js +33 -0
- package/dist/components/encode-progress.d.ts +15 -0
- package/dist/components/encode-progress.js +49 -0
- package/dist/components/encode-progress.js.map +1 -0
- package/dist/components/hover-preview.d.ts +29 -0
- package/dist/components/hover-preview.js +49 -0
- package/dist/components/hover-preview.js.map +1 -0
- package/dist/components/image-crop-dialog.d.ts +48 -0
- package/dist/components/image-crop-dialog.js +359 -0
- package/dist/components/image-crop-dialog.js.map +1 -0
- package/dist/components/lazy-mount.d.ts +10 -0
- package/dist/components/lazy-mount.js +22 -0
- package/dist/components/lazy-mount.js.map +1 -0
- package/dist/components/media-folder-editor.d.ts +45 -0
- package/dist/components/media-folder-editor.js +564 -0
- package/dist/components/media-folder-editor.js.map +1 -0
- package/dist/components/media-gallery.d.ts +54 -0
- package/dist/components/media-gallery.js +597 -0
- package/dist/components/media-gallery.js.map +1 -0
- package/dist/components/media-readiness-notice.d.ts +24 -0
- package/dist/components/media-readiness-notice.js +61 -0
- package/dist/components/media-readiness-notice.js.map +1 -0
- package/dist/components/player-controls.d.ts +71 -0
- package/dist/components/player-controls.js +264 -0
- package/dist/components/player-controls.js.map +1 -0
- package/dist/components/progress.d.ts +5 -0
- package/dist/components/progress.js +14 -0
- package/dist/components/progress.js.map +1 -0
- package/dist/components/rendition-img.d.ts +34 -0
- package/dist/components/rendition-img.js +78 -0
- package/dist/components/rendition-img.js.map +1 -0
- package/dist/components/slot-editor.d.ts +95 -0
- package/dist/components/slot-editor.js +182 -0
- package/dist/components/slot-editor.js.map +1 -0
- package/dist/components/slot-image.d.ts +23 -0
- package/dist/components/slot-image.js +48 -0
- package/dist/components/slot-image.js.map +1 -0
- package/dist/components/slot-upload.d.ts +41 -0
- package/dist/components/slot-upload.js +213 -0
- package/dist/components/slot-upload.js.map +1 -0
- package/dist/components/social/bans.d.ts +42 -0
- package/dist/components/social/bans.js +300 -0
- package/dist/components/social/bans.js.map +1 -0
- package/dist/components/social/comments.d.ts +42 -0
- package/dist/components/social/comments.js +517 -0
- package/dist/components/social/comments.js.map +1 -0
- package/dist/components/social/engagement.d.ts +45 -0
- package/dist/components/social/engagement.js +129 -0
- package/dist/components/social/engagement.js.map +1 -0
- package/dist/components/social/index.d.ts +6 -0
- package/dist/components/social/moderation.d.ts +32 -0
- package/dist/components/social/moderation.js +383 -0
- package/dist/components/social/moderation.js.map +1 -0
- package/dist/components/social/parts.d.ts +50 -0
- package/dist/components/social/parts.js +106 -0
- package/dist/components/social/parts.js.map +1 -0
- package/dist/components/social/poll-editor.d.ts +34 -0
- package/dist/components/social/poll-editor.js +567 -0
- package/dist/components/social/poll-editor.js.map +1 -0
- package/dist/components/social/poll.d.ts +30 -0
- package/dist/components/social/poll.js +252 -0
- package/dist/components/social/poll.js.map +1 -0
- package/dist/components/sortable-list.d.ts +29 -0
- package/dist/components/sortable-list.js +121 -0
- package/dist/components/sortable-list.js.map +1 -0
- package/dist/components/video-mini-player.d.ts +35 -0
- package/dist/components/video-mini-player.js +236 -0
- package/dist/components/video-mini-player.js.map +1 -0
- package/dist/components/video-player.d.ts +146 -0
- package/dist/components/video-player.js +630 -0
- package/dist/components/video-player.js.map +1 -0
- package/dist/components/video-poster-picker.d.ts +59 -0
- package/dist/components/video-poster-picker.js +335 -0
- package/dist/components/video-poster-picker.js.map +1 -0
- package/dist/components/video-poster.d.ts +24 -0
- package/dist/components/video-poster.js +46 -0
- package/dist/components/video-poster.js.map +1 -0
- package/dist/i18n/context.d.ts +4 -0
- package/dist/i18n/context.js +11 -0
- package/dist/i18n/context.js.map +1 -0
- package/dist/i18n/messages.d.ts +40 -0
- package/dist/i18n/messages.js +94 -0
- package/dist/i18n/messages.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +41 -0
- package/dist/locales/de.d.ts +2 -0
- package/dist/locales/de.js +271 -0
- package/dist/locales/de.js.map +1 -0
- package/dist/locales/en.d.ts +457 -0
- package/dist/locales/en.js +271 -0
- package/dist/locales/en.js.map +1 -0
- package/dist/locales/es.d.ts +2 -0
- package/dist/locales/es.js +271 -0
- package/dist/locales/es.js.map +1 -0
- package/dist/locales/ja.d.ts +2 -0
- package/dist/locales/ja.js +271 -0
- package/dist/locales/ja.js.map +1 -0
- package/dist/locales/ko.d.ts +2 -0
- package/dist/locales/ko.js +271 -0
- package/dist/locales/ko.js.map +1 -0
- package/dist/locales/social/de.d.ts +2 -0
- package/dist/locales/social/de.js +198 -0
- package/dist/locales/social/de.js.map +1 -0
- package/dist/locales/social/en.d.ts +195 -0
- package/dist/locales/social/en.js +198 -0
- package/dist/locales/social/en.js.map +1 -0
- package/dist/locales/social/es.d.ts +2 -0
- package/dist/locales/social/es.js +198 -0
- package/dist/locales/social/es.js.map +1 -0
- package/dist/locales/social/ja.d.ts +2 -0
- package/dist/locales/social/ja.js +198 -0
- package/dist/locales/social/ja.js.map +1 -0
- package/dist/locales/social/ko.d.ts +2 -0
- package/dist/locales/social/ko.js +198 -0
- package/dist/locales/social/ko.js.map +1 -0
- package/dist/locales/social/zh.d.ts +2 -0
- package/dist/locales/social/zh.js +198 -0
- package/dist/locales/social/zh.js.map +1 -0
- package/dist/locales/zh.d.ts +2 -0
- package/dist/locales/zh.js +271 -0
- package/dist/locales/zh.js.map +1 -0
- package/dist/provider.d.ts +22 -0
- package/dist/provider.js +59 -0
- package/dist/provider.js.map +1 -0
- package/dist/react/canonical.d.ts +33 -0
- package/dist/react/canonical.js +88 -0
- package/dist/react/canonical.js.map +1 -0
- package/dist/react/comments.d.ts +82 -0
- package/dist/react/comments.js +147 -0
- package/dist/react/comments.js.map +1 -0
- package/dist/react/config.d.ts +13 -0
- package/dist/react/config.js +14 -0
- package/dist/react/config.js.map +1 -0
- package/dist/react/context.d.ts +48 -0
- package/dist/react/context.js +37 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/crop.d.ts +49 -0
- package/dist/react/crop.js +107 -0
- package/dist/react/crop.js.map +1 -0
- package/dist/react/engagement.d.ts +38 -0
- package/dist/react/engagement.js +88 -0
- package/dist/react/engagement.js.map +1 -0
- package/dist/react/folder.d.ts +83 -0
- package/dist/react/folder.js +183 -0
- package/dist/react/folder.js.map +1 -0
- package/dist/react/gallery.d.ts +140 -0
- package/dist/react/gallery.js +477 -0
- package/dist/react/gallery.js.map +1 -0
- package/dist/react/index.d.ts +21 -0
- package/dist/react/inline-preview.d.ts +24 -0
- package/dist/react/inline-preview.js +76 -0
- package/dist/react/inline-preview.js.map +1 -0
- package/dist/react/player.d.ts +70 -0
- package/dist/react/player.js +214 -0
- package/dist/react/player.js.map +1 -0
- package/dist/react/polls.d.ts +74 -0
- package/dist/react/polls.js +176 -0
- package/dist/react/polls.js.map +1 -0
- package/dist/react/posts.d.ts +59 -0
- package/dist/react/posts.js +149 -0
- package/dist/react/posts.js.map +1 -0
- package/dist/react/provider.d.ts +23 -0
- package/dist/react/provider.js +35 -0
- package/dist/react/provider.js.map +1 -0
- package/dist/react/public.d.ts +39 -0
- package/dist/react/public.js +70 -0
- package/dist/react/public.js.map +1 -0
- package/dist/react/read.d.ts +52 -0
- package/dist/react/read.js +143 -0
- package/dist/react/read.js.map +1 -0
- package/dist/react/resources.d.ts +70 -0
- package/dist/react/resources.js +247 -0
- package/dist/react/resources.js.map +1 -0
- package/dist/react/slot.d.ts +102 -0
- package/dist/react/slot.js +181 -0
- package/dist/react/slot.js.map +1 -0
- package/dist/react/store.d.ts +51 -0
- package/dist/react/store.js +164 -0
- package/dist/react/store.js.map +1 -0
- package/dist/react/upload.d.ts +85 -0
- package/dist/react/upload.js +150 -0
- package/dist/react/upload.js.map +1 -0
- package/dist/react/use-resource.d.ts +43 -0
- package/dist/react/use-resource.js +66 -0
- package/dist/react/use-resource.js.map +1 -0
- package/dist/react/video.d.ts +118 -0
- package/dist/react/video.js +184 -0
- package/dist/react/video.js.map +1 -0
- package/dist/react/viewport.d.ts +19 -0
- package/dist/react/viewport.js +30 -0
- package/dist/react/viewport.js.map +1 -0
- package/dist/react.js +21 -0
- package/dist/scope.d.ts +14 -0
- package/dist/scope.js +30 -0
- package/dist/scope.js.map +1 -0
- package/dist/styles.css +3 -0
- package/dist/ui/alert-dialog.d.ts +18 -0
- package/dist/ui/alert-dialog.js +86 -0
- package/dist/ui/alert-dialog.js.map +1 -0
- package/dist/ui/alert.d.ts +10 -0
- package/dist/ui/alert.js +38 -0
- package/dist/ui/alert.js.map +1 -0
- package/dist/ui/avatar.d.ts +11 -0
- package/dist/ui/avatar.js +31 -0
- package/dist/ui/avatar.js.map +1 -0
- package/dist/ui/badge.d.ts +7 -0
- package/dist/ui/badge.js +31 -0
- package/dist/ui/badge.js.map +1 -0
- package/dist/ui/button.d.ts +8 -0
- package/dist/ui/button.js +46 -0
- package/dist/ui/button.js.map +1 -0
- package/dist/ui/dialog.d.ts +17 -0
- package/dist/ui/dialog.js +91 -0
- package/dist/ui/dialog.js.map +1 -0
- package/dist/ui/dropdown-menu.d.ts +15 -0
- package/dist/ui/dropdown-menu.js +105 -0
- package/dist/ui/dropdown-menu.js.map +1 -0
- package/dist/ui/input.d.ts +3 -0
- package/dist/ui/input.js +17 -0
- package/dist/ui/input.js.map +1 -0
- package/dist/ui/label.d.ts +3 -0
- package/dist/ui/label.js +15 -0
- package/dist/ui/label.js.map +1 -0
- package/dist/ui/progress.d.ts +7 -0
- package/dist/ui/progress.js +31 -0
- package/dist/ui/progress.js.map +1 -0
- package/dist/ui/select.d.ts +15 -0
- package/dist/ui/select.js +98 -0
- package/dist/ui/select.js.map +1 -0
- package/dist/ui/skeleton.d.ts +2 -0
- package/dist/ui/skeleton.js +14 -0
- package/dist/ui/skeleton.js.map +1 -0
- package/dist/ui/slider.d.ts +3 -0
- package/dist/ui/slider.js +35 -0
- package/dist/ui/slider.js.map +1 -0
- package/dist/ui/switch.d.ts +5 -0
- package/dist/ui/switch.js +20 -0
- package/dist/ui/switch.js.map +1 -0
- package/dist/ui/tabs.d.ts +10 -0
- package/dist/ui/tabs.js +47 -0
- package/dist/ui/tabs.js.map +1 -0
- package/dist/ui/textarea.d.ts +3 -0
- package/dist/ui/textarea.js +15 -0
- package/dist/ui/textarea.js.map +1 -0
- package/dist/ui/toggle-group.d.ts +5 -0
- package/dist/ui/toggle-group.js +23 -0
- package/dist/ui/toggle-group.js.map +1 -0
- package/dist/urls/index.d.ts +100 -0
- package/dist/urls.js +118 -0
- package/dist/urls.js.map +1 -0
- package/package.json +118 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gallery.js","names":[],"sources":["../../src/react/gallery.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState, type KeyboardEvent, type MouseEvent, type PointerEvent } from \"react\";\nimport type { GalleryView } from \"../client/gallery.js\";\nimport type { CapLevelController, Level } from \"hls.js\";\nimport {\n NETWORK_FAILURES_BEFORE_ERROR,\n abrHlsConfig,\n capRung,\n classifyHlsError,\n classifyMediaError,\n hlsConfig,\n initialEstimate,\n otherCodecLevels,\n expiryDelay,\n refreshable,\n startRung,\n statusKind,\n type AbrPolicy,\n type ConnectionHint,\n type PlaybackError,\n} from \"../client/playback.js\";\nimport { PLAYER_TRACKS_KEY, PLAYER_VOLUME_KEY, pickTrack, readTrackPrefs, readVolume, writeTrackPrefs, type PlayerTrack } from \"../client/player.js\";\n\nexport const GALLERY_VIEW_KEY = \"ckui.media-gallery.view\";\n\nexport interface GalleryViewOptions {\n /** Controlled view. */\n view?: GalleryView;\n defaultView?: GalleryView;\n onViewChange?: (view: GalleryView) => void;\n /** localStorage key remembering the viewer's choice; null disables. */\n storageKey?: string | null;\n}\n\nfunction stored(key: string | null): GalleryView | undefined {\n if (!key) return undefined;\n try {\n const v = globalThis.localStorage?.getItem(key);\n return v === \"grid\" || v === \"carousel\" ? v : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Carousel or grid, controlled or not; the viewer's last choice is remembered. */\nexport function useGalleryView({ view, defaultView = \"carousel\", onViewChange, storageKey = GALLERY_VIEW_KEY }: GalleryViewOptions = {}) {\n const [own, setOwn] = useState<GalleryView>(() => stored(storageKey) ?? defaultView);\n const set = useCallback(\n (v: GalleryView) => {\n setOwn(v);\n if (storageKey)\n try {\n globalThis.localStorage?.setItem(storageKey, v);\n } catch {\n // private mode or blocked storage: the choice lasts this page\n }\n onViewChange?.(v);\n },\n [storageKey, onViewChange],\n );\n return [view ?? own, set] as const;\n}\n\nexport interface CarouselOptions {\n count: number;\n index?: number;\n defaultIndex?: number;\n onIndexChange?: (index: number) => void;\n}\n\nexport interface UseCarousel {\n index: number;\n go: (index: number) => void;\n next: () => void;\n prev: () => void;\n /** Pixels the track follows the finger by during a swipe. */\n offset: number;\n dragging: boolean;\n /** Spread on the swipe surface (touch-action: pan-y keeps vertical scrolling). */\n swipe: {\n onPointerDown: (e: PointerEvent<HTMLElement>) => void;\n onPointerMove: (e: PointerEvent<HTMLElement>) => void;\n onPointerUp: (e: PointerEvent<HTMLElement>) => void;\n onPointerCancel: (e: PointerEvent<HTMLElement>) => void;\n onClickCapture: (e: MouseEvent<HTMLElement>) => void;\n };\n /** ← → Home End. */\n onKeyDown: (e: KeyboardEvent<HTMLElement>) => void;\n}\n\n// A drag starting on a playing video's control bar scrubs instead of swiping.\nfunction onControls(e: PointerEvent<HTMLElement>) {\n const t = e.target as HTMLElement;\n if (t.closest?.(\"[data-ckui-noswipe]\")) return true;\n if (!(t instanceof HTMLVideoElement) || !t.controls) return false;\n return e.pointerType === \"mouse\" || e.clientY > t.getBoundingClientRect().bottom - 56;\n}\n\n/** Index, keyboard and swipe state for any carousel UI. */\nexport function useCarousel({ count, index: given, defaultIndex = 0, onIndexChange }: CarouselOptions): UseCarousel {\n const [own, setOwn] = useState(defaultIndex);\n const index = Math.min(Math.max(0, given ?? own), Math.max(0, count - 1));\n const go = useCallback(\n (i: number) => {\n const n = Math.min(Math.max(0, i), Math.max(0, count - 1));\n setOwn(n);\n onIndexChange?.(n);\n },\n [count, onIndexChange],\n );\n const [offset, setOffset] = useState(0);\n const [dragging, setDragging] = useState(false);\n const drag = useRef<{ id: number; x: number; y: number; t: number; width: number; axis?: \"x\" | \"y\" } | null>(null);\n const swallowClick = useRef(false);\n\n const end = (e: PointerEvent<HTMLElement>, cancel: boolean) => {\n const d = drag.current;\n if (!d || d.id !== e.pointerId) return;\n drag.current = null;\n if (d.axis !== \"x\") return;\n const dx = e.clientX - d.x;\n const fast = Math.abs(dx) / Math.max(1, e.timeStamp - d.t) > 0.5;\n setDragging(false);\n setOffset(0);\n swallowClick.current = true;\n if (!cancel && (Math.abs(dx) > d.width * 0.2 || (fast && Math.abs(dx) > 30))) go(index + (dx < 0 ? 1 : -1));\n };\n\n return {\n index,\n go,\n next: () => go(index + 1),\n prev: () => go(index - 1),\n offset,\n dragging,\n swipe: {\n onPointerDown: (e) => {\n // A touch swipe ends without a click, so the flag must not outlive the gesture: it would eat the next tap (a nav button's).\n swallowClick.current = false;\n if (count < 2 || (e.pointerType === \"mouse\" && e.button !== 0) || onControls(e)) return;\n drag.current = { id: e.pointerId, x: e.clientX, y: e.clientY, t: e.timeStamp, width: e.currentTarget.clientWidth || 1 };\n },\n onPointerMove: (e) => {\n const d = drag.current;\n if (!d || d.id !== e.pointerId) return;\n const dx = e.clientX - d.x;\n const dy = e.clientY - d.y;\n if (!d.axis) {\n if (Math.abs(dx) < 8 && Math.abs(dy) < 8) return;\n d.axis = Math.abs(dx) > Math.abs(dy) ? \"x\" : \"y\";\n if (d.axis === \"y\") {\n drag.current = null;\n return;\n }\n e.currentTarget.setPointerCapture?.(e.pointerId);\n setDragging(true);\n }\n const edge = (index === 0 && dx > 0) || (index === count - 1 && dx < 0);\n setOffset(edge ? dx / 3 : dx);\n },\n onPointerUp: (e) => end(e, false),\n onPointerCancel: (e) => end(e, true),\n onClickCapture: (e) => {\n if (!swallowClick.current) return;\n swallowClick.current = false;\n e.preventDefault();\n e.stopPropagation();\n },\n },\n onKeyDown: (e) => {\n const to = { ArrowLeft: index - 1, ArrowRight: index + 1, Home: 0, End: count - 1 }[e.key];\n // A key the player took (a seek) is not a slide change.\n if (to === undefined || e.defaultPrevented || (e.target as HTMLElement).closest?.(\"input, textarea, video, [contenteditable], [role=menu]\")) return;\n e.preventDefault();\n go(to);\n },\n };\n}\n\nexport type PlayerStatus = \"idle\" | \"loading\" | \"playing\" | \"paused\" | \"buffering\" | \"ended\" | \"error\";\n\nexport interface HlsPlayerOptions {\n /** The master playlist; nothing loads while absent. */\n src?: string | null;\n /** Adds auth to playlist and segment requests (e.g. a bearer header for the app's read API). */\n xhrSetup?: (xhr: XMLHttpRequest, url: string) => void | Promise<void>;\n /**\n * Re-grants access (refetch the read API). Called shortly before `expires`,\n * and once after a 401, 403 or 404 — media-gateway answers an expired token\n * with 404 — after which the player retries, resuming where it was.\n */\n refresh?: () => unknown;\n /**\n * The read's `expires` (unix seconds). When a later one arrives the player\n * loads its playlists again at the playhead if their URLs carry the token.\n */\n expires?: number;\n /** Inactive pauses (a swiped-away slide). Default true. */\n active?: boolean;\n /** No playback progress and no bytes for this long is an error. Default 10 s. */\n stallTimeout?: number;\n /** Adaptive-bitrate policy; defaults start at 1080p when the connection allows. */\n abr?: AbrPolicy;\n /** localStorage key remembering the viewer's manual quality; null disables. */\n qualityKey?: string | null;\n /** Where playback starts, seconds (a resume point); a committed preview starts here too. */\n startAt?: number;\n /** Loads and plays at once. A browser that blocks it leaves the play button: never a muted start. */\n autoPlay?: boolean;\n /** The audio language (BCP 47) until the viewer chooses one. */\n audioLanguage?: string;\n /** The subtitle language until the viewer chooses; absent or null: off. */\n subtitleLanguage?: string | null;\n /** localStorage key remembering the viewer's volume and mute; null disables. */\n volumeKey?: string | null;\n /** localStorage key remembering the viewer's audio and subtitle languages; null disables. */\n trackKey?: string | null;\n}\n\nexport const PLAYER_QUALITY_KEY = \"ckui.player.quality\";\n\n// One timer per refresh callback: a gallery and its players share a read.\nconst refreshTimers = new Map<() => unknown, { expires: number; subs: number; timer: ReturnType<typeof setTimeout> }>();\n\n/**\n * Calls refresh shortly before expires (a read's, unix seconds), once however\n * many components ask with the same callback. The next read's later expiry\n * arms it again; a refresh that fails or changes nothing does not repeat.\n */\nexport function useRefreshBeforeExpiry(expires: number | undefined, refresh: (() => unknown) | undefined) {\n useEffect(() => {\n const delay = expiryDelay(expires);\n if (delay === null || !expires || !refresh) return;\n let entry = refreshTimers.get(refresh);\n if (entry?.expires !== expires) {\n if (entry) clearTimeout(entry.timer);\n const made = {\n expires,\n subs: 0,\n timer: setTimeout(() => {\n if (refreshTimers.get(refresh) === made) refreshTimers.delete(refresh);\n Promise.resolve().then(refresh).catch(() => {});\n }, delay),\n };\n refreshTimers.set(refresh, (entry = made));\n }\n const mine = entry;\n mine.subs++;\n return () => {\n if (--mine.subs > 0 || refreshTimers.get(refresh) !== mine) return;\n clearTimeout(mine.timer);\n refreshTimers.delete(refresh);\n };\n }, [expires, refresh]);\n}\n\n/** A selectable rendition; `height` is its short side (portrait 1080×1920 is 1080). */\nexport interface QualityLevel {\n index: number;\n height: number;\n width: number;\n bitrate: number;\n}\n\nexport interface PlayerQuality {\n /** Ascending; empty until the manifest loads, and always under native HLS (Safari), which picks for itself. */\n levels: QualityLevel[];\n /** The locked level index, or -1 for Auto. */\n selected: number;\n /** The level playing now, or -1 before the first switch. */\n current: number;\n /** Locks a level (-1: back to Auto) and remembers the choice. */\n select: (index: number) => void;\n}\n\nexport interface PlayerTracks {\n /** Empty until the manifest lists them. */\n list: PlayerTrack[];\n /** The playing track's index; -1 for none (subtitles off). */\n selected: number;\n /** Switches track (-1: subtitles off) and remembers its language. */\n select: (index: number) => void;\n}\n\nexport interface UseHlsPlayer {\n /** Attach to the `<video>`. */\n ref: (el: HTMLVideoElement | null) => void;\n status: PlayerStatus;\n error?: PlaybackError;\n /** Playback began at least once (show native controls). */\n started: boolean;\n /** Plays unmuted with controls; during a preview, restarts it from 0. */\n play: () => void;\n /** Loads and plays muted from `at` seconds at the lowest rendition (ABR then takes over); not after play. */\n preview: (at: number) => void;\n /** Stops a preview and unloads the player; failures during one are silent. */\n unload: () => void;\n /** A preview is loaded or playing. */\n previewing: boolean;\n /** Reloads from scratch and plays. */\n retry: () => void;\n quality: PlayerQuality;\n audio: PlayerTracks;\n /** The selected subtitle track's TextTrack is kept \"hidden\": its cues are the caller's to draw. */\n subtitles: PlayerTracks;\n}\n\nconst HLS_TYPE = \"application/vnd.apple.mpegurl\";\nlet hinted = false;\n\nfunction corsHint() {\n if (hinted || typeof console === \"undefined\") return;\n hinted = true;\n console.warn(\n `contentkit: video requests failed with no response (status 0). If media is served from another origin, ` +\n `media-gateway must allow this page: set MEDIA_GATEWAY_CORS_ORIGINS to include ${globalThis.location?.origin ?? \"the app origin\"}.`,\n );\n}\n\n// Safari plays HLS natively and hides statuses; one playlist request tells access from absence.\nfunction probe(url: string, setup?: HlsPlayerOptions[\"xhrSetup\"]): Promise<number> {\n return new Promise((resolve) => {\n const xhr = new XMLHttpRequest();\n const send = () => {\n if (xhr.readyState === 0) xhr.open(\"GET\", url, true);\n xhr.onload = () => resolve(xhr.status);\n xhr.onerror = xhr.ontimeout = () => resolve(0);\n xhr.timeout = 8000;\n xhr.send();\n };\n Promise.resolve(setup?.(xhr, url)).then(send, () => resolve(0));\n });\n}\n\nfunction storedQuality(key: string | null): number | \"auto\" | null {\n if (!key) return null;\n try {\n const v = globalThis.localStorage?.getItem(key);\n return v === \"auto\" ? v : v && Number(v) > 0 ? Number(v) : null;\n } catch {\n return null;\n }\n}\n\nfunction storeQuality(key: string | null, v: number | \"auto\") {\n if (!key) return;\n try {\n globalThis.localStorage?.setItem(key, String(v));\n } catch {\n // private mode or blocked storage\n }\n}\n\ninterface NativeAudioTrack {\n label: string;\n language: string;\n enabled: boolean;\n}\ntype NativeAudioTracks = ArrayLike<NativeAudioTrack> & EventTarget;\n\nconst subtitleTracks = (el: HTMLVideoElement) => Array.from(el.textTracks).filter((t) => t.kind === \"subtitles\" || t.kind === \"captions\");\n\nconst asTracks = (ts: readonly { name?: string; label?: string; lang?: string; language?: string; forced?: boolean }[]): PlayerTrack[] =>\n ts.map((t, index) => {\n const lang = t.lang || t.language || undefined;\n return { index, label: t.name || t.label || lang || \"\", lang, ...(t.forced ? { forced: true } : {}) };\n });\n\n// The last throughput measured on this page seeds the next player.\nlet measured = 0;\n\nfunction connection(): ConnectionHint | null {\n const c = (globalThis.navigator as { connection?: ConnectionHint } | undefined)?.connection;\n return c ? { downlink: c.downlink, saveData: c.saveData } : null;\n}\n\n// hls.js's cap to the player's device-pixel size, with the policy's floor and ceiling.\nfunction capController(Base: typeof CapLevelController | undefined, policy: AbrPolicy | undefined): typeof CapLevelController | undefined {\n if (!Base) return undefined;\n return class extends Base {\n override getMaxLevel(capLevelIndex: number): number {\n const levels: Level[] = (this as unknown as { hls: { levels: Level[] } | null }).hls?.levels ?? [];\n return capRung(levels.slice(0, capLevelIndex + 1), this.mediaWidth, this.mediaHeight, policy);\n }\n };\n}\n\n/**\n * HLS playback that fails fast with a reason: hls.js (lazy-loaded) or native\n * HLS, tuned retries, a grant refresh before expiry and one on 401/403/404,\n * and a stall watchdog.\n */\nexport function useHlsPlayer({\n src,\n xhrSetup,\n refresh,\n expires,\n active = true,\n stallTimeout = 10_000,\n abr,\n qualityKey = PLAYER_QUALITY_KEY,\n startAt,\n autoPlay,\n audioLanguage,\n subtitleLanguage,\n volumeKey = PLAYER_VOLUME_KEY,\n trackKey = PLAYER_TRACKS_KEY,\n}: HlsPlayerOptions): UseHlsPlayer {\n const [el, setEl] = useState<HTMLVideoElement | null>(null);\n const [status, setStatus] = useState<PlayerStatus>(\"idle\");\n const [error, setError] = useState<PlaybackError>();\n const [started, setStarted] = useState(false);\n // Nothing loads until the first play.\n const [armed, setArmed] = useState(false);\n const [session, setSession] = useState(0);\n const [previewing, setPreviewing] = useState(false);\n // The preview's start while one is loaded; null otherwise.\n const previewAt = useRef<number | null>(null);\n const stopPreview = useRef<() => void>(() => {});\n const want = useRef(false);\n const start = useRef<(() => void) | null>(null);\n const refreshed = useRef(false);\n const resumeAt = useRef(0);\n const lastProgress = useRef(0);\n const [levels, setLevels] = useState<QualityLevel[]>([]);\n const [selected, setSelected] = useState(-1);\n const [current, setCurrent] = useState(-1);\n const choose = useRef<(index: number) => void>(() => {});\n const [audioList, setAudioList] = useState<PlayerTrack[]>([]);\n const [audioSel, setAudioSel] = useState(-1);\n const [subList, setSubList] = useState<PlayerTrack[]>([]);\n const [subSel, setSubSel] = useState(-1);\n const chooseAudio = useRef<(index: number) => void>(() => {});\n const chooseSubs = useRef<(index: number) => void>(() => {});\n const startedRef = useRef(started);\n startedRef.current = started;\n // A preview becoming real playback: drop its buffer so the chosen level or ABR takes over.\n const commit = useRef<() => void>(() => {});\n const opts = useRef({ xhrSetup, refresh, abr, qualityKey, startAt, audioLanguage, subtitleLanguage, volumeKey, trackKey });\n opts.current = { xhrSetup, refresh, abr, qualityKey, startAt, audioLanguage, subtitleLanguage, volumeKey, trackKey };\n // Loads the playlists again at the playhead when they hold the old token.\n const renew = useRef<() => void>(() => {});\n useRefreshBeforeExpiry(expires, refresh);\n const granted = useRef(expires);\n useEffect(() => {\n const before = granted.current;\n granted.current = expires;\n if (expires && before && expires > before) renew.current();\n }, [expires]);\n\n // A new source starts at its resume point.\n useEffect(() => {\n resumeAt.current = opts.current.startAt ?? 0;\n }, [src]);\n\n useEffect(() => {\n if (!el || !src || !armed) return;\n let dead = false;\n let destroy = () => {};\n let failures = 0;\n let native = false;\n setError(undefined);\n setLevels([]);\n setSelected(-1);\n setCurrent(-1);\n setAudioList([]);\n setAudioSel(-1);\n setSubList([]);\n setSubSel(-1);\n choose.current = () => {};\n chooseAudio.current = () => {};\n chooseSubs.current = () => {};\n commit.current = () => {};\n renew.current = () => {};\n const reload = () => {\n if (dead) return;\n if (previewAt.current !== null) return stopPreview.current();\n resumeAt.current = el.currentTime || 0;\n dead = true;\n destroy();\n setSession((s) => s + 1);\n };\n setStatus(want.current ? \"loading\" : \"idle\");\n const fail = (e: PlaybackError) => {\n if (dead) return;\n const again = opts.current.refresh;\n if (refreshable(e) && again && !refreshed.current) {\n refreshed.current = true;\n resumeAt.current = el.currentTime || 0;\n if (previewAt.current !== null) previewAt.current = resumeAt.current || previewAt.current;\n dead = true;\n destroy();\n setStatus(\"loading\");\n Promise.resolve()\n .then(again)\n .catch(() => {})\n .finally(() => setSession((s) => s + 1));\n return;\n }\n if (previewAt.current !== null) return stopPreview.current();\n if (e.kind === \"network\") corsHint();\n dead = true;\n destroy();\n want.current = false;\n setError(e);\n setStatus(\"error\");\n };\n const onMediaError = () => native && fail(classifyMediaError(el.error));\n // A blocked autoplay leaves the play button; it never retries muted.\n const play = () =>\n el.play().catch((e: unknown) => {\n if (dead || (e as { name?: string } | null)?.name !== \"NotAllowedError\") return;\n want.current = false;\n setStatus((s) => (s === \"error\" ? s : startedRef.current ? \"paused\" : \"idle\"));\n });\n const prefs = () => readTrackPrefs(opts.current.trackKey);\n const wantedSubs = (tracks: readonly { lang?: string; language?: string }[]) => {\n const p = prefs().subtitles;\n return pickTrack(\n tracks.map((t) => ({ lang: t.lang || t.language })),\n p === \"off\" ? null : (p ?? opts.current.subtitleLanguage),\n );\n };\n el.addEventListener(\"error\", onMediaError);\n import(\"hls.js\").then(\n ({ default: Hls }) => {\n if (dead) return;\n if (Hls.isSupported()) {\n let loading = false;\n const conn = connection();\n const policy = conn?.saveData ? { ...opts.current.abr, preferHighRes: false } : opts.current.abr;\n const estimate = initialEstimate(policy, conn, measured);\n const hls = new Hls({\n ...hlsConfig,\n ...abrHlsConfig(policy, estimate),\n capLevelController: capController(Hls.DefaultConfig.capLevelController, policy),\n autoStartLoad: false,\n xhrSetup: (xhr, url) => opts.current.xhrSetup?.(xhr, url),\n });\n destroy = () => hls.destroy();\n // The player draws the cues of the chosen track (\"hidden\"), above its controls.\n hls.subtitleDisplay = false;\n const syncAudio = () => {\n setAudioList(asTracks(hls.audioTracks));\n setAudioSel(hls.audioTrack);\n };\n const syncSubs = () => {\n setSubList(asTracks(hls.subtitleTracks));\n setSubSel(hls.subtitleTrack);\n };\n hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, () => {\n const i = pickTrack(hls.audioTracks, prefs().audio ?? opts.current.audioLanguage);\n if (i >= 0 && i !== hls.audioTrack) hls.audioTrack = i;\n syncAudio();\n });\n hls.on(Hls.Events.AUDIO_TRACK_SWITCHED, syncAudio);\n // A preview loads no subtitles; the commit applies the choice.\n const applySubs = () => {\n if (previewAt.current === null && hls.subtitleTracks.length) hls.subtitleTrack = wantedSubs(hls.subtitleTracks);\n };\n // hls.js selects its own default right after this event: choose after it.\n hls.on(Hls.Events.SUBTITLE_TRACKS_UPDATED, () => {\n syncSubs();\n queueMicrotask(() => {\n if (dead) return;\n applySubs();\n syncSubs();\n });\n });\n hls.on(Hls.Events.SUBTITLE_TRACK_SWITCH, syncSubs);\n chooseAudio.current = (i) => {\n hls.audioTrack = i;\n };\n chooseSubs.current = (i) => {\n hls.subtitleTrack = i;\n setSubSel(i);\n };\n hls.on(Hls.Events.ERROR, (_, d) => {\n const e = classifyHlsError(d);\n if (d.fatal || e.kind === \"access\" || e.kind === \"not_found\" || e.kind === \"rate_limited\") fail(e);\n else if (e.kind === \"network\" && ++failures >= NETWORK_FAILURES_BEFORE_ERROR) fail(e);\n });\n // A manual choice before loading applies at start.\n let locked = -1;\n const autoStart = () => {\n const box = el.getBoundingClientRect();\n const dpr = globalThis.devicePixelRatio || 1;\n const cap = capRung(hls.levels, box.width * dpr, box.height * dpr, policy);\n return startRung(hls.levels, hls.bandwidthEstimate || estimate, cap, conn);\n };\n // Flush everything buffered and reload at the playhead (-1: ABR's pick).\n let reseek = false;\n const flush = (level: number) => {\n reseek = true;\n hls.currentLevel = level;\n };\n // Browsers keep playing the GOP already decoding after a flush; seeking in place drops it.\n hls.on(Hls.Events.BUFFER_FLUSHED, () => {\n if (!reseek) return;\n reseek = false;\n // eslint-disable-next-line no-self-assign -- a seek in place is the point\n el.currentTime = el.currentTime;\n });\n choose.current = (i) => {\n locked = i;\n setSelected(i);\n if (!loading) return;\n // Auto hands back to ABR without a flush.\n if (i >= 0) flush(i);\n else hls.nextLevel = -1;\n };\n commit.current = () => {\n applySubs();\n if (!loading) return;\n flush(locked);\n if (locked < 0) hls.nextAutoLevel = autoStart();\n };\n hls.on(Hls.Events.LEVEL_SWITCHED, (_, d) => setCurrent(d.level));\n hls.on(Hls.Events.FRAG_LOADED, () => {\n failures = 0;\n lastProgress.current = Date.now();\n measured = hls.bandwidthEstimate;\n });\n // Media playlists are loaded once: their segment URLs keep the token they were signed with.\n const tokenInUrl = () => hls.levels.some((l) => /[?&]t=/.test(l.details?.fragments[0]?.url ?? \"\"));\n renew.current = () => {\n if (loading && tokenInUrl()) reload();\n };\n let parsed = false;\n start.current = () => {\n if (!parsed) return;\n if (!loading) {\n loading = true;\n const at = previewAt.current;\n if (at !== null) {\n hls.startLevel = 0;\n hls.startLoad(at);\n } else {\n hls.startLevel = locked >= 0 ? locked : autoStart();\n if (locked >= 0) hls.loadLevel = locked;\n hls.startLoad(resumeAt.current || -1);\n }\n resumeAt.current = 0;\n }\n void play();\n };\n hls.on(Hls.Events.MANIFEST_PARSED, () => {\n parsed = true;\n for (const i of otherCodecLevels(hls.levels, hls.firstLevel)) hls.removeLevel(i);\n const max = policy?.maxHeight ?? Infinity;\n const list = hls.levels\n .map((l, index) => ({ index, height: Math.min(l.width, l.height) || l.height, width: l.width, bitrate: l.bitrate }))\n .filter((l) => l.height > 0 && l.height <= max);\n setLevels(list);\n // A remembered rung this video lacks (e.g. 2160p not yet encoded) plays Auto.\n const saved = storedQuality(opts.current.qualityKey);\n const match = typeof saved === \"number\" ? list.find((l) => l.height === saved) : undefined;\n if (match) choose.current(match.index);\n if (want.current) start.current?.();\n });\n hls.loadSource(src);\n hls.attachMedia(el);\n return;\n }\n if (!el.canPlayType(HLS_TYPE)) return fail({ kind: \"unsupported\", code: \"no-hls\" });\n void probe(src, opts.current.xhrSetup).then((code) => {\n if (dead) return;\n if (code < 200 || code >= 400) return fail({ kind: statusKind(code), code: `probe/${code}`, status: code });\n native = true;\n renew.current = reload; // its playlists cannot be inspected\n const audioTracks = (el as { audioTracks?: NativeAudioTracks }).audioTracks;\n const syncTracks = () => {\n const a = audioTracks ? Array.from(audioTracks) : [];\n setAudioList(asTracks(a));\n setAudioSel(a.findIndex((t) => t.enabled));\n const t = subtitleTracks(el);\n setSubList(asTracks(t));\n setSubSel(t.findIndex((x) => x.mode !== \"disabled\"));\n };\n chooseAudio.current = (i) => {\n const t = audioTracks?.[i];\n if (t) t.enabled = true;\n syncTracks();\n };\n // Safari lists text tracks as it finds them: the wanted language applies until the viewer chooses.\n let chosen = false;\n const showSubs = (i: number) => {\n subtitleTracks(el).forEach((t, j) => (t.mode = j === i ? \"hidden\" : \"disabled\"));\n syncTracks();\n };\n chooseSubs.current = (i) => {\n chosen = true;\n showSubs(i);\n };\n const wantedNative = () => {\n if (chosen || previewAt.current !== null) return syncTracks();\n showSubs(wantedSubs(asTracks(subtitleTracks(el))));\n };\n const firstTracks = () => {\n const a = audioTracks ? Array.from(audioTracks) : [];\n const i = pickTrack(asTracks(a), prefs().audio ?? opts.current.audioLanguage);\n if (i >= 0 && !a[i]!.enabled) a[i]!.enabled = true;\n wantedNative();\n };\n el.addEventListener(\"loadedmetadata\", firstTracks, { once: true });\n el.textTracks.addEventListener?.(\"addtrack\", wantedNative);\n audioTracks?.addEventListener(\"change\", syncTracks);\n el.src = src;\n if (previewAt.current !== null) el.currentTime = previewAt.current;\n else if (resumeAt.current) el.currentTime = resumeAt.current;\n resumeAt.current = 0;\n destroy = () => {\n el.removeEventListener(\"loadedmetadata\", firstTracks);\n el.textTracks.removeEventListener?.(\"addtrack\", wantedNative);\n audioTracks?.removeEventListener(\"change\", syncTracks);\n el.removeAttribute(\"src\");\n el.load();\n };\n commit.current = wantedNative;\n start.current = () => void play();\n if (want.current) start.current();\n });\n },\n () => fail({ kind: \"unsupported\", code: \"hls.js\" }),\n );\n return () => {\n dead = true;\n renew.current = () => {};\n start.current = null;\n el.removeEventListener(\"error\", onMediaError);\n destroy();\n };\n }, [el, src, armed, session]);\n\n // Media element state.\n useEffect(() => {\n if (!el) return;\n const set = (s: PlayerStatus) => () => setStatus((cur) => (cur === \"error\" ? cur : s));\n const handlers: Record<string, () => void> = {\n playing: () => {\n if (previewAt.current === null) setStarted(true);\n refreshed.current = false;\n lastProgress.current = Date.now();\n set(\"playing\")();\n },\n pause: () => {\n if (!el.ended) set(\"paused\")();\n },\n waiting: () => want.current && set(\"buffering\")(),\n ended: () => (previewAt.current !== null ? stopPreview.current() : set(\"ended\")()),\n progress: () => (lastProgress.current = Date.now()),\n play: () => {\n want.current = true;\n lastProgress.current = Date.now();\n },\n };\n for (const [k, h] of Object.entries(handlers)) el.addEventListener(k, h);\n return () => {\n for (const [k, h] of Object.entries(handlers)) el.removeEventListener(k, h);\n };\n }, [el]);\n\n // Only the visible player plays.\n useEffect(() => {\n if (!active && el && !el.paused) el.pause();\n if (!active) want.current = false;\n }, [active, el]);\n\n // Stall watchdog: wanting playback, yet neither time nor bytes move.\n const watching = status === \"loading\" || status === \"buffering\" || status === \"playing\";\n useEffect(() => {\n if (!watching || !el) return;\n let time = el.currentTime;\n lastProgress.current = Math.max(lastProgress.current, Date.now());\n const id = setInterval(() => {\n if (!want.current || el.paused) return;\n if (el.currentTime !== time) {\n time = el.currentTime;\n lastProgress.current = Date.now();\n } else if (Date.now() - lastProgress.current > stallTimeout) {\n if (previewAt.current !== null) return stopPreview.current();\n el.pause();\n want.current = false;\n setError({ kind: \"stalled\", code: \"stall\" });\n setStatus(\"error\");\n }\n }, 1000);\n return () => clearInterval(id);\n }, [watching, el, stallTimeout]);\n\n const retry = useCallback(() => {\n want.current = true;\n refreshed.current = false;\n lastProgress.current = Date.now();\n setError(undefined);\n setStatus(\"loading\");\n setArmed(true);\n setSession((s) => s + 1);\n }, []);\n\n // The viewer's volume and mute; a preview's mute is the player's, never stored.\n const restoreVolume = useCallback(() => {\n if (!el) return;\n const v = readVolume(opts.current.volumeKey);\n el.volume = v.volume;\n el.muted = v.muted;\n }, [el]);\n\n const unload = useCallback(() => {\n if (previewAt.current === null) return;\n previewAt.current = null;\n want.current = false;\n setPreviewing(false);\n setArmed(false); // the load effect's cleanup destroys hls and detaches the media\n setStatus(\"idle\");\n if (el) {\n el.pause();\n restoreVolume();\n }\n }, [el, restoreVolume]);\n stopPreview.current = unload;\n\n const preview = useCallback(\n (at: number) => {\n // Not once playback was asked for: the preview would swallow its failures.\n if (!el || started || error || want.current || previewAt.current !== null) return;\n previewAt.current = Math.max(0, at);\n el.muted = true;\n want.current = true;\n lastProgress.current = Date.now();\n setPreviewing(true);\n setArmed(true);\n start.current?.();\n },\n [el, started, error],\n );\n\n const play = useCallback(() => {\n if (error) return retry();\n if (previewAt.current !== null) {\n // Committed: the preview's player becomes the real one, from the start (or the resume point).\n previewAt.current = null;\n setPreviewing(false);\n if (el) {\n restoreVolume();\n el.currentTime = opts.current.startAt ?? 0;\n }\n commit.current();\n setStarted(true);\n } else if (!startedRef.current) restoreVolume();\n want.current = true;\n lastProgress.current = Date.now();\n setStatus(\"loading\");\n setArmed(true);\n start.current?.();\n }, [error, retry, el, restoreVolume]);\n\n const playRef = useRef(play);\n playRef.current = play;\n const auto = useRef(autoPlay);\n auto.current = autoPlay;\n useEffect(() => {\n if (el && src && auto.current) playRef.current();\n }, [el, src]);\n\n const levelsRef = useRef(levels);\n levelsRef.current = levels;\n const select = useCallback((index: number) => {\n choose.current(index);\n const h = levelsRef.current.find((l) => l.index === index)?.height;\n storeQuality(opts.current.qualityKey, index === -1 || !h ? \"auto\" : h);\n }, []);\n\n const audioRef = useRef(audioList);\n audioRef.current = audioList;\n const subsRef = useRef(subList);\n subsRef.current = subList;\n const selectAudio = useCallback((index: number) => {\n chooseAudio.current(index);\n const lang = audioRef.current[index]?.lang;\n if (lang) writeTrackPrefs(opts.current.trackKey, { audio: lang });\n }, []);\n const selectSubs = useCallback((index: number) => {\n chooseSubs.current(index);\n const lang = index < 0 ? \"off\" : subsRef.current[index]?.lang;\n if (lang) writeTrackPrefs(opts.current.trackKey, { subtitles: lang });\n }, []);\n\n return {\n ref: setEl,\n status,\n error,\n started,\n play,\n preview,\n unload,\n previewing,\n retry,\n quality: { levels, selected, current, select },\n audio: { list: audioList, selected: audioSel, select: selectAudio },\n subtitles: { list: subList, selected: subSel, select: selectSubs },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { ContentKitProvider, type ContentKitProviderProps } from './provider.js';
|
|
2
|
+
export { useContentKit, useContentKitClient, useContentURLs, useErrorReporter, useOptionalContentKitClient, type ContentKitContextValue, type ContentKitErrorHandler, type ContentKitErrorInfo, type ContentKitOperation, type Navigate, type SocialOperation, } from './context.js';
|
|
3
|
+
export { useMediaRead, type MediaReadOptions, type ReadWindow, type UseMediaRead } from './read.js';
|
|
4
|
+
export { nameIn, thumbnailOf, useMediaFolder, type FolderGroup, type MediaFolderOptions, type UseMediaFolder } from './folder.js';
|
|
5
|
+
export { useEditorCrop, type EditorCropOptions, type EditorCropState, type UseEditorCrop } from './crop.js';
|
|
6
|
+
export { usePresets, usePublicImage, type PublicImageOptions, type UsePresets, type UsePublicImage } from './public.js';
|
|
7
|
+
export { useCanonicalContent, type CanonicalContent, type CanonicalContentOptions } from './canonical.js';
|
|
8
|
+
export { useNearViewport, type NearViewportOptions } from './viewport.js';
|
|
9
|
+
export { useAdminComments, useCanComment, useCommentBans, useCommentReplies, useComments, useLatestComments, useModerationQueue, withReaction, type UseAdminComments, type UseCommentBans, type UseComments, type UseCommentsOptions, type UseModerationQueue, } from './comments.js';
|
|
10
|
+
export { useContentConfig, type UseContentConfig } from './config.js';
|
|
11
|
+
export { useFavorite, useReaction, type UseFavorite, type UseReaction } from './engagement.js';
|
|
12
|
+
export { usePoll, usePollEditor, usePolls, withVote, type PollFilter, type PollImages, type UsePoll, type UsePollEditor } from './polls.js';
|
|
13
|
+
export { isPublished, usePost, usePosts, type PostFilter, type UsePost } from './posts.js';
|
|
14
|
+
export { useContentScope, type UseList } from './use-resource.js';
|
|
15
|
+
export { ResourceStore, resourcesFor, type Page, type Resource } from './resources.js';
|
|
16
|
+
export { useCrop, useEncodeProgress, useUpload, useUploadQueue, type UploadQueueOptions, type UploadStatus, type UseCrop, type UseCropOptions, type UseEncodeProgress, type UseUpload, type UseUploadQueue, } from './upload.js';
|
|
17
|
+
export { editOutput, useSlotCrop, useSlotImage, type SlotCropMode, type SlotCropOptions, type SlotCropState, type SlotImageOptions, type UseSlotCrop, type UseSlotImage, } from './slot.js';
|
|
18
|
+
export { round3, useFrameStrip, useVideoFrame, useVideoImages, useVideoPoster, type FrameStripOptions, type StripFrame, type UseVideoFrame, type UseVideoImages, type UseVideoPoster, type VideoFrameOptions, type VideoImagesOptions, type VideoPosterOptions, type VideoSaveState, } from './video.js';
|
|
19
|
+
export { GALLERY_VIEW_KEY, PLAYER_QUALITY_KEY, useCarousel, useGalleryView, useHlsPlayer, useRefreshBeforeExpiry, type CarouselOptions, type GalleryViewOptions, type HlsPlayerOptions, type PlayerQuality, type PlayerStatus, type PlayerTracks, type QualityLevel, type UseCarousel, type UseHlsPlayer, } from './gallery.js';
|
|
20
|
+
export type { PlaybackProgress, ProgressReason } from './player.js';
|
|
21
|
+
export { HOVER_DELAY, InlinePreviewContext, useInlinePreview, useReducedMotion, type InlinePreviewOptions } from './inline-preview.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** The host's default for inline previews (ContentKitUiProvider `inlinePreview`). */
|
|
2
|
+
export declare const InlinePreviewContext: import('react').Context<boolean>;
|
|
3
|
+
/** Hover this long (ms) before a preview starts. */
|
|
4
|
+
export declare const HOVER_DELAY = 500;
|
|
5
|
+
/** The user's reduced-motion preference, live. */
|
|
6
|
+
export declare function useReducedMotion(): boolean;
|
|
7
|
+
export interface InlinePreviewOptions {
|
|
8
|
+
/** The component's own setting; default the provider's. */
|
|
9
|
+
setting?: boolean;
|
|
10
|
+
/** Whether this element can preview now (a playable video, not yet played). */
|
|
11
|
+
available: boolean;
|
|
12
|
+
/** Hovered (fine pointers) or watched for visibility (touch). */
|
|
13
|
+
target: HTMLElement | null;
|
|
14
|
+
/** Plays for real: stops any preview and holds new ones off. */
|
|
15
|
+
playing?: boolean;
|
|
16
|
+
start: () => void;
|
|
17
|
+
stop: () => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Drives one element's inline preview: on hover after HOVER_DELAY with a fine
|
|
21
|
+
* pointer, else while it is the most visible candidate. Off with reduced
|
|
22
|
+
* motion, Save-Data or the host setting.
|
|
23
|
+
*/
|
|
24
|
+
export declare function useInlinePreview(o: InlinePreviewOptions): void;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createContext as e, useContext as t, useEffect as n, useRef as r, useState as i } from "react";
|
|
2
|
+
//#region src/react/inline-preview.ts
|
|
3
|
+
var a = e(!0), o = 500, s = .5, c = null, l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Set(), d;
|
|
4
|
+
function f(e) {
|
|
5
|
+
c === e || u.size || (c?.stop(), c = e, e.start());
|
|
6
|
+
}
|
|
7
|
+
function p(e) {
|
|
8
|
+
c === e && (c = null, e.stop());
|
|
9
|
+
}
|
|
10
|
+
function m() {
|
|
11
|
+
clearTimeout(d), d = setTimeout(() => {
|
|
12
|
+
let e = null, t = s;
|
|
13
|
+
for (let [n, r] of l) r >= t && ([e, t] = [n, r]);
|
|
14
|
+
e ? f(e) : c && p(c);
|
|
15
|
+
}, 150);
|
|
16
|
+
}
|
|
17
|
+
function h(e, t) {
|
|
18
|
+
t ? (u.add(e), c && c !== e && p(c), c === e && (c = null)) : u.delete(e) && l.size && m();
|
|
19
|
+
}
|
|
20
|
+
var g = (e) => typeof matchMedia == "function" && matchMedia(e).matches;
|
|
21
|
+
function _() {
|
|
22
|
+
let e = "(prefers-reduced-motion: reduce)", [t, r] = i(() => g(e));
|
|
23
|
+
return n(() => {
|
|
24
|
+
if (typeof matchMedia != "function") return;
|
|
25
|
+
let t = matchMedia(e), n = () => r(t.matches);
|
|
26
|
+
return t.addEventListener("change", n), () => t.removeEventListener("change", n);
|
|
27
|
+
}, []), t;
|
|
28
|
+
}
|
|
29
|
+
function v(e) {
|
|
30
|
+
let i = t(a), o = _(), s = r(e);
|
|
31
|
+
s.current = e;
|
|
32
|
+
let c = r(null);
|
|
33
|
+
c.current ??= {
|
|
34
|
+
start: () => s.current.start(),
|
|
35
|
+
stop: () => s.current.stop()
|
|
36
|
+
};
|
|
37
|
+
let u = !!globalThis.navigator?.connection?.saveData, d = (e.setting ?? i) && e.available && !o && !u, { target: v } = e;
|
|
38
|
+
n(() => {
|
|
39
|
+
let e = c.current;
|
|
40
|
+
if (!d || !v) return;
|
|
41
|
+
if (g("(hover: hover) and (pointer: fine)")) {
|
|
42
|
+
let t, n = (n) => {
|
|
43
|
+
n.pointerType && n.pointerType !== "mouse" || (clearTimeout(t), t = setTimeout(() => f(e), 500));
|
|
44
|
+
}, r = () => {
|
|
45
|
+
clearTimeout(t), p(e);
|
|
46
|
+
};
|
|
47
|
+
return v.addEventListener("pointerenter", n), v.addEventListener("pointerleave", r), () => {
|
|
48
|
+
clearTimeout(t), v.removeEventListener("pointerenter", n), v.removeEventListener("pointerleave", r), p(e);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (typeof IntersectionObserver != "function") return;
|
|
52
|
+
let t = new IntersectionObserver(([t]) => {
|
|
53
|
+
if (!t) return;
|
|
54
|
+
let n = t.rootBounds?.height || globalThis.innerHeight || 1, r = t.isIntersecting ? Math.max(t.intersectionRatio, t.intersectionRect.height / n) : 0;
|
|
55
|
+
r > 0 ? l.set(e, r) : l.delete(e), m();
|
|
56
|
+
}, { threshold: [
|
|
57
|
+
0,
|
|
58
|
+
.25,
|
|
59
|
+
.5,
|
|
60
|
+
.75,
|
|
61
|
+
1
|
|
62
|
+
] });
|
|
63
|
+
return t.observe(v), () => {
|
|
64
|
+
t.disconnect(), l.delete(e), p(e), m();
|
|
65
|
+
};
|
|
66
|
+
}, [d, v]);
|
|
67
|
+
let y = !!e.playing;
|
|
68
|
+
n(() => {
|
|
69
|
+
let e = c.current;
|
|
70
|
+
return h(e, y), () => h(e, !1);
|
|
71
|
+
}, [y]);
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { o as HOVER_DELAY, a as InlinePreviewContext, v as useInlinePreview, _ as useReducedMotion };
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=inline-preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-preview.js","names":[],"sources":["../../src/react/inline-preview.ts"],"sourcesContent":["import { createContext, useContext, useEffect, useRef, useState } from \"react\";\n\n/** The host's default for inline previews (ContentKitUiProvider `inlinePreview`). */\nexport const InlinePreviewContext = createContext(true);\n\n/** Hover this long (ms) before a preview starts. */\nexport const HOVER_DELAY = 500;\n/** A touch-device preview needs this share of the element (or of the viewport) in view. */\nconst MIN_VISIBLE = 0.5;\n\ninterface Entry {\n start: () => void;\n stop: () => void;\n}\n\n// One preview at a time, page-wide; none while a video plays for real.\nlet current: Entry | null = null;\nconst visible = new Map<Entry, number>();\nconst playing = new Set<Entry>();\nlet pickTimer: ReturnType<typeof setTimeout> | undefined;\n\nfunction activate(e: Entry) {\n if (current === e || playing.size) return;\n current?.stop();\n current = e;\n e.start();\n}\n\nfunction deactivate(e: Entry) {\n if (current !== e) return;\n current = null;\n e.stop();\n}\n\n// Touch devices: the most visible candidate plays.\nfunction schedulePick() {\n clearTimeout(pickTimer);\n pickTimer = setTimeout(() => {\n let best: Entry | null = null;\n let score = MIN_VISIBLE;\n for (const [e, s] of visible) if (s >= score) [best, score] = [e, s];\n if (best) activate(best);\n else if (current) deactivate(current);\n }, 150);\n}\n\nfunction setPlaying(e: Entry, on: boolean) {\n if (on) {\n playing.add(e);\n if (current && current !== e) deactivate(current);\n if (current === e) current = null; // committed: no longer a preview\n } else if (playing.delete(e) && visible.size) schedulePick();\n}\n\nconst query = (q: string) => typeof matchMedia === \"function\" && matchMedia(q).matches;\n\n/** The user's reduced-motion preference, live. */\nexport function useReducedMotion(): boolean {\n const q = \"(prefers-reduced-motion: reduce)\";\n const [reduced, setReduced] = useState(() => query(q));\n useEffect(() => {\n if (typeof matchMedia !== \"function\") return;\n const m = matchMedia(q);\n const on = () => setReduced(m.matches);\n m.addEventListener(\"change\", on);\n return () => m.removeEventListener(\"change\", on);\n }, []);\n return reduced;\n}\n\nexport interface InlinePreviewOptions {\n /** The component's own setting; default the provider's. */\n setting?: boolean;\n /** Whether this element can preview now (a playable video, not yet played). */\n available: boolean;\n /** Hovered (fine pointers) or watched for visibility (touch). */\n target: HTMLElement | null;\n /** Plays for real: stops any preview and holds new ones off. */\n playing?: boolean;\n start: () => void;\n stop: () => void;\n}\n\n/**\n * Drives one element's inline preview: on hover after HOVER_DELAY with a fine\n * pointer, else while it is the most visible candidate. Off with reduced\n * motion, Save-Data or the host setting.\n */\nexport function useInlinePreview(o: InlinePreviewOptions): void {\n const host = useContext(InlinePreviewContext);\n const reduced = useReducedMotion();\n const fns = useRef(o);\n fns.current = o;\n const entry = useRef<Entry>(null!);\n entry.current ??= { start: () => fns.current.start(), stop: () => fns.current.stop() };\n const saveData = !!(globalThis.navigator as { connection?: { saveData?: boolean } } | undefined)?.connection?.saveData;\n const enabled = (o.setting ?? host) && o.available && !reduced && !saveData;\n const { target } = o;\n\n useEffect(() => {\n const e = entry.current;\n if (!enabled || !target) return;\n if (query(\"(hover: hover) and (pointer: fine)\")) {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const enter = (ev: PointerEvent) => {\n if (ev.pointerType && ev.pointerType !== \"mouse\") return;\n clearTimeout(timer);\n timer = setTimeout(() => activate(e), HOVER_DELAY);\n };\n const leave = () => {\n clearTimeout(timer);\n deactivate(e);\n };\n target.addEventListener(\"pointerenter\", enter);\n target.addEventListener(\"pointerleave\", leave);\n return () => {\n clearTimeout(timer);\n target.removeEventListener(\"pointerenter\", enter);\n target.removeEventListener(\"pointerleave\", leave);\n deactivate(e);\n };\n }\n if (typeof IntersectionObserver !== \"function\") return;\n const io = new IntersectionObserver(\n ([x]) => {\n if (!x) return;\n const view = x.rootBounds?.height || globalThis.innerHeight || 1;\n const score = x.isIntersecting ? Math.max(x.intersectionRatio, x.intersectionRect.height / view) : 0;\n if (score > 0) visible.set(e, score);\n else visible.delete(e);\n schedulePick();\n },\n { threshold: [0, 0.25, 0.5, 0.75, 1] },\n );\n io.observe(target);\n return () => {\n io.disconnect();\n visible.delete(e);\n deactivate(e);\n schedulePick();\n };\n }, [enabled, target]);\n\n const on = !!o.playing;\n useEffect(() => {\n const e = entry.current;\n setPlaying(e, on);\n return () => setPlaying(e, false);\n }, [on]);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { SpriteCue } from '../client/player.js';
|
|
2
|
+
import { HlsPlayerOptions } from './gallery.js';
|
|
3
|
+
/** What the controls show about a `<video>`, kept current from its events. */
|
|
4
|
+
export interface MediaState {
|
|
5
|
+
time: number;
|
|
6
|
+
duration: number;
|
|
7
|
+
/** The end of the buffered range holding the playhead. */
|
|
8
|
+
buffered: number;
|
|
9
|
+
paused: boolean;
|
|
10
|
+
ended: boolean;
|
|
11
|
+
volume: number;
|
|
12
|
+
muted: boolean;
|
|
13
|
+
rate: number;
|
|
14
|
+
/** The decoded frame's width / height, once known. */
|
|
15
|
+
aspect?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function useMediaState(el: HTMLVideoElement | null): MediaState;
|
|
18
|
+
/**
|
|
19
|
+
* Fullscreen on the player's root, so the controls and menus come along;
|
|
20
|
+
* iPhone Safari, which only fullscreens a video, gets its native player.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useFullscreen(root: HTMLElement | null, el: HTMLVideoElement | null): {
|
|
23
|
+
active: boolean;
|
|
24
|
+
supported: boolean;
|
|
25
|
+
toggle: () => void;
|
|
26
|
+
};
|
|
27
|
+
export declare function usePictureInPicture(el: HTMLVideoElement | null): {
|
|
28
|
+
active: boolean;
|
|
29
|
+
supported: boolean;
|
|
30
|
+
toggle: () => void;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The cues showing now on the video's chosen subtitle track. The track is
|
|
34
|
+
* kept "hidden" so the browser draws nothing and the player places the
|
|
35
|
+
* cues itself, clear of its controls.
|
|
36
|
+
*/
|
|
37
|
+
export declare function useActiveCues(el: HTMLVideoElement | null): VTTCue[];
|
|
38
|
+
/** A seek sprite: its cues and the size of the sheet they cut. */
|
|
39
|
+
export interface Sprite {
|
|
40
|
+
cues: SpriteCue[];
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
}
|
|
44
|
+
type XhrSetup = HlsPlayerOptions["xhrSetup"];
|
|
45
|
+
/** A ladder's sprite.vtt and its sheet, fetched once per URL (through xhrSetup, like the playlists). */
|
|
46
|
+
export declare function loadSprite(vtt: string, setup?: XhrSetup): Promise<Sprite | null>;
|
|
47
|
+
export declare function useSprite(vtt: string | null | undefined, setup?: XhrSetup): Sprite | null;
|
|
48
|
+
/** Why a progress report was sent. */
|
|
49
|
+
export type ProgressReason = "interval" | "pause" | "seek" | "ended" | "hidden" | "unload";
|
|
50
|
+
/** The playhead and the time actually watched, for resume points and watch history. */
|
|
51
|
+
export interface PlaybackProgress {
|
|
52
|
+
/** Seconds. */
|
|
53
|
+
time: number;
|
|
54
|
+
duration: number;
|
|
55
|
+
/** Seconds actually played since this source loaded; seeking adds nothing. */
|
|
56
|
+
played: number;
|
|
57
|
+
ended: boolean;
|
|
58
|
+
reason: ProgressReason;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Reports progress while a committed playback runs: every `interval` ms of
|
|
62
|
+
* playback, and on pause, seek, end, page hide and unload.
|
|
63
|
+
*/
|
|
64
|
+
export declare function useProgressReports(el: HTMLVideoElement | null, o: {
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
source?: string | null;
|
|
67
|
+
interval: number;
|
|
68
|
+
onProgress?: (p: PlaybackProgress) => void;
|
|
69
|
+
}): void;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { parseSpriteVtt as e, playedBetween as t } from "../client/player.js";
|
|
2
|
+
import { useCallback as n, useEffect as r, useRef as i, useState as a } from "react";
|
|
3
|
+
//#region src/react/player.ts
|
|
4
|
+
var o = {
|
|
5
|
+
time: 0,
|
|
6
|
+
duration: 0,
|
|
7
|
+
buffered: 0,
|
|
8
|
+
paused: !0,
|
|
9
|
+
ended: !1,
|
|
10
|
+
volume: 1,
|
|
11
|
+
muted: !1,
|
|
12
|
+
rate: 1
|
|
13
|
+
}, s = [
|
|
14
|
+
"timeupdate",
|
|
15
|
+
"durationchange",
|
|
16
|
+
"loadedmetadata",
|
|
17
|
+
"progress",
|
|
18
|
+
"play",
|
|
19
|
+
"pause",
|
|
20
|
+
"ended",
|
|
21
|
+
"volumechange",
|
|
22
|
+
"ratechange",
|
|
23
|
+
"seeking",
|
|
24
|
+
"seeked",
|
|
25
|
+
"resize",
|
|
26
|
+
"emptied"
|
|
27
|
+
];
|
|
28
|
+
function c(e) {
|
|
29
|
+
let t = e.currentTime || 0, n = 0;
|
|
30
|
+
for (let r = 0; r < e.buffered.length; r++) e.buffered.start(r) <= t + .5 && t <= e.buffered.end(r) && (n = e.buffered.end(r));
|
|
31
|
+
return {
|
|
32
|
+
time: t,
|
|
33
|
+
duration: Number.isFinite(e.duration) ? e.duration : 0,
|
|
34
|
+
buffered: n,
|
|
35
|
+
paused: e.paused,
|
|
36
|
+
ended: e.ended,
|
|
37
|
+
volume: e.volume,
|
|
38
|
+
muted: e.muted,
|
|
39
|
+
rate: e.playbackRate,
|
|
40
|
+
aspect: e.videoWidth > 0 && e.videoHeight > 0 ? e.videoWidth / e.videoHeight : void 0
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function l(e) {
|
|
44
|
+
let [t, n] = a(o);
|
|
45
|
+
return r(() => {
|
|
46
|
+
if (!e) return;
|
|
47
|
+
let t = () => n(c(e));
|
|
48
|
+
t();
|
|
49
|
+
for (let n of s) e.addEventListener(n, t);
|
|
50
|
+
return () => {
|
|
51
|
+
for (let n of s) e.removeEventListener(n, t);
|
|
52
|
+
};
|
|
53
|
+
}, [e]), t;
|
|
54
|
+
}
|
|
55
|
+
var u = () => {
|
|
56
|
+
if (typeof document > "u") return null;
|
|
57
|
+
let e = document;
|
|
58
|
+
return e.fullscreenElement ?? e.webkitFullscreenElement ?? null;
|
|
59
|
+
};
|
|
60
|
+
function d(e, t) {
|
|
61
|
+
let [i, o] = a(!1);
|
|
62
|
+
r(() => {
|
|
63
|
+
if (!e) return;
|
|
64
|
+
let n = t, r = () => o(u() === e || !!n?.webkitDisplayingFullscreen);
|
|
65
|
+
return document.addEventListener("fullscreenchange", r), document.addEventListener("webkitfullscreenchange", r), n?.addEventListener("webkitbeginfullscreen", r), n?.addEventListener("webkitendfullscreen", r), () => {
|
|
66
|
+
document.removeEventListener("fullscreenchange", r), document.removeEventListener("webkitfullscreenchange", r), n?.removeEventListener("webkitbeginfullscreen", r), n?.removeEventListener("webkitendfullscreen", r);
|
|
67
|
+
};
|
|
68
|
+
}, [e, t]);
|
|
69
|
+
let s = e, c = t;
|
|
70
|
+
return {
|
|
71
|
+
active: i,
|
|
72
|
+
supported: !!(s?.requestFullscreen || s?.webkitRequestFullscreen || c?.webkitEnterFullscreen),
|
|
73
|
+
toggle: n(() => {
|
|
74
|
+
if (!s) return;
|
|
75
|
+
let e = document;
|
|
76
|
+
if (u() === s) return void (e.exitFullscreen ? e.exitFullscreen().catch(() => {}) : e.webkitExitFullscreen?.());
|
|
77
|
+
if (c?.webkitDisplayingFullscreen) return c.webkitExitFullscreen?.();
|
|
78
|
+
if (s.requestFullscreen) return void s.requestFullscreen().catch(() => {});
|
|
79
|
+
if (s.webkitRequestFullscreen) return s.webkitRequestFullscreen();
|
|
80
|
+
c?.webkitEnterFullscreen?.();
|
|
81
|
+
}, [s, c])
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function f(e) {
|
|
85
|
+
let [t, i] = a(!1);
|
|
86
|
+
return r(() => {
|
|
87
|
+
if (!e) return;
|
|
88
|
+
let t = () => i(!0), n = () => i(!1);
|
|
89
|
+
return e.addEventListener("enterpictureinpicture", t), e.addEventListener("leavepictureinpicture", n), () => {
|
|
90
|
+
e.removeEventListener("enterpictureinpicture", t), e.removeEventListener("leavepictureinpicture", n);
|
|
91
|
+
};
|
|
92
|
+
}, [e]), {
|
|
93
|
+
active: t,
|
|
94
|
+
supported: typeof document < "u" && !!document.pictureInPictureEnabled && !!e && !e.disablePictureInPicture,
|
|
95
|
+
toggle: n(() => {
|
|
96
|
+
e && (document.pictureInPictureElement === e ? document.exitPictureInPicture().catch(() => {}) : e.requestPictureInPicture?.().catch(() => {}));
|
|
97
|
+
}, [e])
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
var p = (e) => e.kind === "subtitles" || e.kind === "captions";
|
|
101
|
+
function m(e) {
|
|
102
|
+
let [t, n] = a([]);
|
|
103
|
+
return r(() => {
|
|
104
|
+
if (!e) return;
|
|
105
|
+
let t = e.textTracks, r = null, i = () => n(r?.activeCues ? Array.from(r.activeCues) : []), a = () => {
|
|
106
|
+
let e = Array.from(t).find((e) => p(e) && e.mode !== "disabled") ?? null;
|
|
107
|
+
if (e?.mode === "showing" && (e.mode = "hidden"), e === r) return i();
|
|
108
|
+
r?.removeEventListener("cuechange", i), r = e, r?.addEventListener("cuechange", i), i();
|
|
109
|
+
};
|
|
110
|
+
return a(), t.addEventListener?.("change", a), t.addEventListener?.("addtrack", a), t.addEventListener?.("removetrack", a), () => {
|
|
111
|
+
r?.removeEventListener("cuechange", i), t.removeEventListener?.("change", a), t.removeEventListener?.("addtrack", a), t.removeEventListener?.("removetrack", a);
|
|
112
|
+
};
|
|
113
|
+
}, [e]), t;
|
|
114
|
+
}
|
|
115
|
+
var h = /* @__PURE__ */ new Map();
|
|
116
|
+
function g(e, t) {
|
|
117
|
+
return new Promise((n) => {
|
|
118
|
+
let r = new XMLHttpRequest();
|
|
119
|
+
Promise.resolve(t?.(r, e)).then(() => {
|
|
120
|
+
r.readyState === 0 && r.open("GET", e, !0), r.onload = () => n(r.status === 200 ? r.responseText : ""), r.onerror = () => n(""), r.send();
|
|
121
|
+
}, () => n(""));
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function _(t, n) {
|
|
125
|
+
let r = h.get(t);
|
|
126
|
+
return r || (r = g(t, n).then((n) => {
|
|
127
|
+
let r = n ? e(n, new URL(t, globalThis.location?.href).href) : [];
|
|
128
|
+
return r.length ? new Promise((e) => {
|
|
129
|
+
let t = new Image();
|
|
130
|
+
t.onload = () => e({
|
|
131
|
+
cues: r,
|
|
132
|
+
width: t.naturalWidth,
|
|
133
|
+
height: t.naturalHeight
|
|
134
|
+
}), t.onerror = () => e(null), t.src = r[0].url;
|
|
135
|
+
}) : null;
|
|
136
|
+
}), h.set(t, r), r.then((e) => e || h.delete(t))), r;
|
|
137
|
+
}
|
|
138
|
+
function v(e, t) {
|
|
139
|
+
let [n, o] = a(null), s = i(t);
|
|
140
|
+
return s.current = t, r(() => {
|
|
141
|
+
if (!e) return;
|
|
142
|
+
let t = !0;
|
|
143
|
+
return _(e, s.current).then((n) => t && o({
|
|
144
|
+
vtt: e,
|
|
145
|
+
sprite: n
|
|
146
|
+
})), () => {
|
|
147
|
+
t = !1;
|
|
148
|
+
};
|
|
149
|
+
}, [e]), n && n.vtt === e ? n.sprite : null;
|
|
150
|
+
}
|
|
151
|
+
function y(e, n) {
|
|
152
|
+
let a = i(n.onProgress);
|
|
153
|
+
a.current = n.onProgress;
|
|
154
|
+
let o = i(n.enabled);
|
|
155
|
+
o.current = n.enabled;
|
|
156
|
+
let { interval: s, source: c } = n;
|
|
157
|
+
r(() => {
|
|
158
|
+
if (!e || !c) return;
|
|
159
|
+
let n = 0, r = null, i = 0, l = !1, u = 0, d = 0, f = !1, p = () => {
|
|
160
|
+
(e.currentSrc || e.readyState > 0) && (u = e.currentTime || 0, d = Number.isFinite(e.duration) ? e.duration : d, f = e.ended);
|
|
161
|
+
}, m = () => ({
|
|
162
|
+
at: Date.now(),
|
|
163
|
+
pos: e.currentTime || 0
|
|
164
|
+
}), h = () => {
|
|
165
|
+
let i = m();
|
|
166
|
+
r && !e.paused && (n += t(r, i)), r = i;
|
|
167
|
+
}, g = (e) => {
|
|
168
|
+
o.current && a.current && (l = !0, i = Date.now(), a.current({
|
|
169
|
+
time: u,
|
|
170
|
+
duration: d,
|
|
171
|
+
played: n,
|
|
172
|
+
ended: f,
|
|
173
|
+
reason: e
|
|
174
|
+
}));
|
|
175
|
+
}, _ = {
|
|
176
|
+
playing: () => {
|
|
177
|
+
p(), r = m();
|
|
178
|
+
},
|
|
179
|
+
timeupdate: () => {
|
|
180
|
+
e.paused || e.seeking || (h(), p(), Date.now() - i >= s && g("interval"));
|
|
181
|
+
},
|
|
182
|
+
durationchange: p,
|
|
183
|
+
pause: () => {
|
|
184
|
+
h(), p(), e.ended || g("pause");
|
|
185
|
+
},
|
|
186
|
+
seeking: () => {
|
|
187
|
+
r = null;
|
|
188
|
+
},
|
|
189
|
+
seeked: () => {
|
|
190
|
+
p(), r = e.paused ? null : m(), g("seek");
|
|
191
|
+
},
|
|
192
|
+
ended: () => {
|
|
193
|
+
h(), p(), g("ended");
|
|
194
|
+
}
|
|
195
|
+
}, v = !1, y = () => {
|
|
196
|
+
(document.visibilityState === "hidden" || v) && (h(), p(), g("hidden"));
|
|
197
|
+
}, b = () => {
|
|
198
|
+
v = !0, y(), v = !1;
|
|
199
|
+
};
|
|
200
|
+
for (let [t, n] of Object.entries(_)) e.addEventListener(t, n);
|
|
201
|
+
return document.addEventListener("visibilitychange", y), globalThis.addEventListener?.("pagehide", b), () => {
|
|
202
|
+
for (let [t, n] of Object.entries(_)) e.removeEventListener(t, n);
|
|
203
|
+
document.removeEventListener("visibilitychange", y), globalThis.removeEventListener?.("pagehide", b), (l || n > 0) && g("unload");
|
|
204
|
+
};
|
|
205
|
+
}, [
|
|
206
|
+
e,
|
|
207
|
+
c,
|
|
208
|
+
s
|
|
209
|
+
]);
|
|
210
|
+
}
|
|
211
|
+
//#endregion
|
|
212
|
+
export { _ as loadSprite, m as useActiveCues, d as useFullscreen, l as useMediaState, f as usePictureInPicture, y as useProgressReports, v as useSprite };
|
|
213
|
+
|
|
214
|
+
//# sourceMappingURL=player.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"player.js","names":[],"sources":["../../src/react/player.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { parseSpriteVtt, playedBetween, type PlaySample, type SpriteCue } from \"../client/player.js\";\nimport type { HlsPlayerOptions } from \"./gallery.js\";\n\n/** What the controls show about a `<video>`, kept current from its events. */\nexport interface MediaState {\n time: number;\n duration: number;\n /** The end of the buffered range holding the playhead. */\n buffered: number;\n paused: boolean;\n ended: boolean;\n volume: number;\n muted: boolean;\n rate: number;\n /** The decoded frame's width / height, once known. */\n aspect?: number;\n}\n\nconst idle: MediaState = { time: 0, duration: 0, buffered: 0, paused: true, ended: false, volume: 1, muted: false, rate: 1 };\n\nconst MEDIA_EVENTS = [\"timeupdate\", \"durationchange\", \"loadedmetadata\", \"progress\", \"play\", \"pause\", \"ended\", \"volumechange\", \"ratechange\", \"seeking\", \"seeked\", \"resize\", \"emptied\"];\n\nfunction snapshot(el: HTMLVideoElement): MediaState {\n const time = el.currentTime || 0;\n let buffered = 0;\n for (let i = 0; i < el.buffered.length; i++)\n if (el.buffered.start(i) <= time + 0.5 && time <= el.buffered.end(i)) buffered = el.buffered.end(i);\n const duration = Number.isFinite(el.duration) ? el.duration : 0;\n return {\n time,\n duration,\n buffered,\n paused: el.paused,\n ended: el.ended,\n volume: el.volume,\n muted: el.muted,\n rate: el.playbackRate,\n aspect: el.videoWidth > 0 && el.videoHeight > 0 ? el.videoWidth / el.videoHeight : undefined,\n };\n}\n\nexport function useMediaState(el: HTMLVideoElement | null): MediaState {\n const [state, setState] = useState(idle);\n useEffect(() => {\n if (!el) return;\n const sync = () => setState(snapshot(el));\n sync();\n for (const e of MEDIA_EVENTS) el.addEventListener(e, sync);\n return () => {\n for (const e of MEDIA_EVENTS) el.removeEventListener(e, sync);\n };\n }, [el]);\n return state;\n}\n\ntype WebkitDocument = Document & { webkitFullscreenElement?: Element | null; webkitExitFullscreen?: () => void };\ntype WebkitElement = HTMLElement & { webkitRequestFullscreen?: () => void };\ntype WebkitVideo = HTMLVideoElement & { webkitEnterFullscreen?: () => void; webkitExitFullscreen?: () => void; webkitDisplayingFullscreen?: boolean };\n\nconst fullscreenElement = () => {\n if (typeof document === \"undefined\") return null;\n const d = document as WebkitDocument;\n return d.fullscreenElement ?? d.webkitFullscreenElement ?? null;\n};\n\n/**\n * Fullscreen on the player's root, so the controls and menus come along;\n * iPhone Safari, which only fullscreens a video, gets its native player.\n */\nexport function useFullscreen(root: HTMLElement | null, el: HTMLVideoElement | null) {\n const [active, setActive] = useState(false);\n useEffect(() => {\n if (!root) return;\n const v = el as WebkitVideo | null;\n const sync = () => setActive(fullscreenElement() === root || !!v?.webkitDisplayingFullscreen);\n document.addEventListener(\"fullscreenchange\", sync);\n document.addEventListener(\"webkitfullscreenchange\", sync);\n v?.addEventListener(\"webkitbeginfullscreen\", sync);\n v?.addEventListener(\"webkitendfullscreen\", sync);\n return () => {\n document.removeEventListener(\"fullscreenchange\", sync);\n document.removeEventListener(\"webkitfullscreenchange\", sync);\n v?.removeEventListener(\"webkitbeginfullscreen\", sync);\n v?.removeEventListener(\"webkitendfullscreen\", sync);\n };\n }, [root, el]);\n const r = root as WebkitElement | null;\n const v = el as WebkitVideo | null;\n const supported = !!(r?.requestFullscreen || r?.webkitRequestFullscreen || v?.webkitEnterFullscreen);\n const toggle = useCallback(() => {\n if (!r) return;\n const d = document as WebkitDocument;\n if (fullscreenElement() === r) return void (d.exitFullscreen ? d.exitFullscreen().catch(() => {}) : d.webkitExitFullscreen?.());\n if (v?.webkitDisplayingFullscreen) return v.webkitExitFullscreen?.();\n if (r.requestFullscreen) return void r.requestFullscreen().catch(() => {});\n if (r.webkitRequestFullscreen) return r.webkitRequestFullscreen();\n v?.webkitEnterFullscreen?.();\n }, [r, v]);\n return { active, supported, toggle };\n}\n\nexport function usePictureInPicture(el: HTMLVideoElement | null) {\n const [active, setActive] = useState(false);\n useEffect(() => {\n if (!el) return;\n const on = () => setActive(true);\n const off = () => setActive(false);\n el.addEventListener(\"enterpictureinpicture\", on);\n el.addEventListener(\"leavepictureinpicture\", off);\n return () => {\n el.removeEventListener(\"enterpictureinpicture\", on);\n el.removeEventListener(\"leavepictureinpicture\", off);\n };\n }, [el]);\n const supported = typeof document !== \"undefined\" && !!document.pictureInPictureEnabled && !!el && !el.disablePictureInPicture;\n const toggle = useCallback(() => {\n if (!el) return;\n if (document.pictureInPictureElement === el) void document.exitPictureInPicture().catch(() => {});\n else void el.requestPictureInPicture?.().catch(() => {});\n }, [el]);\n return { active, supported, toggle };\n}\n\nconst isSubtitles = (t: TextTrack) => t.kind === \"subtitles\" || t.kind === \"captions\";\n\n/**\n * The cues showing now on the video's chosen subtitle track. The track is\n * kept \"hidden\" so the browser draws nothing and the player places the\n * cues itself, clear of its controls.\n */\nexport function useActiveCues(el: HTMLVideoElement | null): VTTCue[] {\n const [cues, setCues] = useState<VTTCue[]>([]);\n useEffect(() => {\n if (!el) return;\n const tracks = el.textTracks;\n let track: TextTrack | null = null;\n const show = () => setCues(track?.activeCues ? (Array.from(track.activeCues) as VTTCue[]) : []);\n const pick = () => {\n const next = Array.from(tracks).find((t) => isSubtitles(t) && t.mode !== \"disabled\") ?? null;\n if (next?.mode === \"showing\") next.mode = \"hidden\";\n if (next === track) return show();\n track?.removeEventListener(\"cuechange\", show);\n track = next;\n track?.addEventListener(\"cuechange\", show);\n show();\n };\n pick();\n tracks.addEventListener?.(\"change\", pick);\n tracks.addEventListener?.(\"addtrack\", pick);\n tracks.addEventListener?.(\"removetrack\", pick);\n return () => {\n track?.removeEventListener(\"cuechange\", show);\n tracks.removeEventListener?.(\"change\", pick);\n tracks.removeEventListener?.(\"addtrack\", pick);\n tracks.removeEventListener?.(\"removetrack\", pick);\n };\n }, [el]);\n return cues;\n}\n\n/** A seek sprite: its cues and the size of the sheet they cut. */\nexport interface Sprite {\n cues: SpriteCue[];\n width: number;\n height: number;\n}\n\ntype XhrSetup = HlsPlayerOptions[\"xhrSetup\"];\nconst sprites = new Map<string, Promise<Sprite | null>>();\n\nfunction fetchText(url: string, setup: XhrSetup): Promise<string> {\n return new Promise((resolve) => {\n const xhr = new XMLHttpRequest();\n const send = () => {\n if (xhr.readyState === 0) xhr.open(\"GET\", url, true);\n xhr.onload = () => resolve(xhr.status === 200 ? xhr.responseText : \"\");\n xhr.onerror = () => resolve(\"\");\n xhr.send();\n };\n Promise.resolve(setup?.(xhr, url)).then(send, () => resolve(\"\"));\n });\n}\n\n/** A ladder's sprite.vtt and its sheet, fetched once per URL (through xhrSetup, like the playlists). */\nexport function loadSprite(vtt: string, setup?: XhrSetup): Promise<Sprite | null> {\n let p = sprites.get(vtt);\n if (!p) {\n p = fetchText(vtt, setup).then((text) => {\n const cues = text ? parseSpriteVtt(text, new URL(vtt, globalThis.location?.href).href) : [];\n if (!cues.length) return null;\n return new Promise<Sprite | null>((resolve) => {\n const img = new Image();\n img.onload = () => resolve({ cues, width: img.naturalWidth, height: img.naturalHeight });\n img.onerror = () => resolve(null);\n img.src = cues[0]!.url;\n });\n });\n sprites.set(vtt, p);\n void p.then((s) => s || sprites.delete(vtt));\n }\n return p;\n}\n\nexport function useSprite(vtt: string | null | undefined, setup?: XhrSetup): Sprite | null {\n const [sprite, setSprite] = useState<{ vtt: string; sprite: Sprite | null } | null>(null);\n const setupRef = useRef(setup);\n setupRef.current = setup;\n useEffect(() => {\n if (!vtt) return;\n let live = true;\n void loadSprite(vtt, setupRef.current).then((s) => live && setSprite({ vtt, sprite: s }));\n return () => {\n live = false;\n };\n }, [vtt]);\n return sprite && sprite.vtt === vtt ? sprite.sprite : null;\n}\n\n/** Why a progress report was sent. */\nexport type ProgressReason = \"interval\" | \"pause\" | \"seek\" | \"ended\" | \"hidden\" | \"unload\";\n\n/** The playhead and the time actually watched, for resume points and watch history. */\nexport interface PlaybackProgress {\n /** Seconds. */\n time: number;\n duration: number;\n /** Seconds actually played since this source loaded; seeking adds nothing. */\n played: number;\n ended: boolean;\n reason: ProgressReason;\n}\n\n/**\n * Reports progress while a committed playback runs: every `interval` ms of\n * playback, and on pause, seek, end, page hide and unload.\n */\nexport function useProgressReports(\n el: HTMLVideoElement | null,\n o: { enabled: boolean; source?: string | null; interval: number; onProgress?: (p: PlaybackProgress) => void },\n) {\n const cb = useRef(o.onProgress);\n cb.current = o.onProgress;\n const enabled = useRef(o.enabled);\n enabled.current = o.enabled;\n const { interval, source } = o;\n useEffect(() => {\n if (!el || !source) return;\n let played = 0;\n let sample: PlaySample | null = null;\n let last = 0;\n let reported = false;\n // The last known playhead: by unload the player may have detached the media.\n let pos = 0;\n let dur = 0;\n let ended = false;\n const observe = () => {\n if (el.currentSrc || el.readyState > 0) {\n pos = el.currentTime || 0;\n dur = Number.isFinite(el.duration) ? el.duration : dur;\n ended = el.ended;\n }\n };\n const now = (): PlaySample => ({ at: Date.now(), pos: el.currentTime || 0 });\n const tally = () => {\n const s = now();\n if (sample && !el.paused) played += playedBetween(sample, s);\n sample = s;\n };\n const report = (reason: ProgressReason) => {\n if (!enabled.current || !cb.current) return;\n reported = true;\n last = Date.now();\n cb.current({ time: pos, duration: dur, played, ended, reason });\n };\n const handlers: Record<string, () => void> = {\n playing: () => {\n observe();\n sample = now();\n },\n timeupdate: () => {\n if (el.paused || el.seeking) return;\n tally();\n observe();\n if (Date.now() - last >= interval) report(\"interval\");\n },\n durationchange: observe,\n pause: () => {\n tally();\n observe();\n if (!el.ended) report(\"pause\");\n },\n seeking: () => {\n sample = null;\n },\n seeked: () => {\n observe();\n sample = el.paused ? null : now();\n report(\"seek\");\n },\n ended: () => {\n tally();\n observe();\n report(\"ended\");\n },\n };\n let pageHide = false;\n const hidden = () => {\n if (document.visibilityState !== \"hidden\" && !pageHide) return;\n tally();\n observe();\n report(\"hidden\");\n };\n const pagehide = () => {\n pageHide = true;\n hidden();\n pageHide = false;\n };\n for (const [k, h] of Object.entries(handlers)) el.addEventListener(k, h);\n document.addEventListener(\"visibilitychange\", hidden);\n globalThis.addEventListener?.(\"pagehide\", pagehide);\n return () => {\n for (const [k, h] of Object.entries(handlers)) el.removeEventListener(k, h);\n document.removeEventListener(\"visibilitychange\", hidden);\n globalThis.removeEventListener?.(\"pagehide\", pagehide);\n if (reported || played > 0) report(\"unload\");\n };\n }, [el, source, interval]);\n}\n"],"mappings":"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"}
|