|
1 | 1 | import { languages } from '@nmit-coursition/utils' |
| 2 | +import { transformToHighlightedVTT } from '@nmit-coursition/utils' |
2 | 3 | import { MediaPlayer, type MediaPlayerInstance, MediaProvider, type MediaSrc, Track } from '@vidstack/react' |
3 | | -import { DefaultVideoLayout, defaultLayoutIcons } from '@vidstack/react/player/layouts/default' |
4 | | -import { useEffect, useRef } from 'react' |
| 4 | +import { |
| 5 | + DefaultMenuCheckbox, |
| 6 | + DefaultMenuItem, |
| 7 | + DefaultVideoLayout, |
| 8 | + defaultLayoutIcons, |
| 9 | +} from '@vidstack/react/player/layouts/default' |
| 10 | +import { useEffect, useRef, useState } from 'react' |
5 | 11 |
|
6 | 12 | import '@vidstack/react/player/styles/default/theme.css' |
7 | 13 | import '@vidstack/react/player/styles/default/layouts/video.css' |
| 14 | +import './video-player.css' |
8 | 15 |
|
9 | 16 | export type { MediaSrc } from '@vidstack/react' |
10 | 17 |
|
11 | 18 | interface VideoPlayerProps { |
12 | 19 | source: MediaSrc |
13 | 20 | subtitles?: string |
14 | 21 | subtitlesLang?: keyof typeof languages |
| 22 | + aspectRatio?: string |
15 | 23 | } |
16 | 24 |
|
17 | | -export function VideoPlayer({ source, subtitles, subtitlesLang = 'en-gb' }: VideoPlayerProps) { |
| 25 | +function HighlightSettings({ |
| 26 | + highlightEnabled, |
| 27 | + handleHighlightToggle, |
| 28 | +}: { highlightEnabled: boolean; handleHighlightToggle: (checked: boolean) => void }) { |
| 29 | + return ( |
| 30 | + <DefaultMenuItem label='Short popup style'> |
| 31 | + <DefaultMenuCheckbox |
| 32 | + label='Short popup style' |
| 33 | + checked={highlightEnabled} |
| 34 | + onChange={(checked, trigger) => trigger?.isTrusted && handleHighlightToggle(checked)} |
| 35 | + /> |
| 36 | + </DefaultMenuItem> |
| 37 | + ) |
| 38 | +} |
| 39 | + |
| 40 | +export function VideoPlayer({ source, subtitles, subtitlesLang = 'en-gb', aspectRatio }: VideoPlayerProps) { |
18 | 41 | const player = useRef<MediaPlayerInstance>(null) |
| 42 | + const [highlightingEnabled, setHighlightingEnabled] = useState(false) |
| 43 | + const [highlightedSubtitles, setHighlightedSubtitles] = useState<string>() |
| 44 | + |
| 45 | + useEffect(() => { |
| 46 | + if (!subtitles) return |
| 47 | + |
| 48 | + fetch(subtitles) |
| 49 | + .then((response) => response.text()) |
| 50 | + .then((content) => { |
| 51 | + const highlighted = transformToHighlightedVTT(content, 'vtt') |
| 52 | + const blob = new Blob([highlighted], { type: 'text/vtt' }) |
| 53 | + const url = URL.createObjectURL(blob) |
| 54 | + setHighlightedSubtitles(url) |
| 55 | + return () => URL.revokeObjectURL(url) |
| 56 | + }) |
| 57 | + .catch((error) => { |
| 58 | + console.error('Failed to transform subtitles:', error) |
| 59 | + }) |
| 60 | + }, [subtitles]) |
19 | 61 |
|
20 | 62 | useEffect(() => { |
21 | 63 | if (!player.current) return |
22 | | - player.current.startLoading() |
23 | | - }, [source]) |
| 64 | + const textTracks = player.current.textTracks |
| 65 | + if (textTracks[0]) { |
| 66 | + textTracks[0].mode = 'showing' |
| 67 | + } |
| 68 | + }, [highlightingEnabled]) |
24 | 69 |
|
25 | 70 | return ( |
26 | | - <MediaPlayer ref={player} src={source} className='w-full aspect-video' load='visible'> |
| 71 | + <MediaPlayer ref={player} src={source} load='visible' className='h-full w-full' aspectRatio={aspectRatio}> |
27 | 72 | <MediaProvider> |
28 | | - {subtitles && ( |
29 | | - <Track src={subtitles} kind='subtitles' label={languages[subtitlesLang]} lang={subtitlesLang} default /> |
30 | | - )} |
| 73 | + <Track |
| 74 | + src={highlightingEnabled ? highlightedSubtitles : subtitles} |
| 75 | + kind='subtitles' |
| 76 | + label={languages[subtitlesLang]} |
| 77 | + lang={subtitlesLang} |
| 78 | + default |
| 79 | + /> |
31 | 80 | </MediaProvider> |
32 | | - <DefaultVideoLayout icons={defaultLayoutIcons} thumbnails='' /> |
| 81 | + <DefaultVideoLayout |
| 82 | + icons={defaultLayoutIcons} |
| 83 | + menuGroup='bottom' |
| 84 | + slots={{ |
| 85 | + captionsMenuItemsStart: ( |
| 86 | + <HighlightSettings highlightEnabled={highlightingEnabled} handleHighlightToggle={setHighlightingEnabled} /> |
| 87 | + ), |
| 88 | + }} |
| 89 | + ></DefaultVideoLayout> |
33 | 90 | </MediaPlayer> |
34 | 91 | ) |
35 | 92 | } |
0 commit comments