@banou/media-player 0.2.6 → 0.3.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/build/chrome/bottom.d.ts +4 -2
- package/build/chrome/index.d.ts +2 -1
- package/build/index.js +1095 -1058
- package/build/use-scrub.d.ts +1 -0
- package/package.json +2 -2
- package/src/chrome/bottom.tsx +71 -25
- package/src/chrome/index.tsx +4 -0
- package/src/index.tsx +1 -0
- package/src/main.tsx +1 -1
- package/src/use-scrub.ts +2 -1
- package/src/vite-env.d.ts +1 -1
- package/tsconfig.node.json +9 -9
package/build/use-scrub.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@banou/media-player",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|
|
@@ -43,6 +43,6 @@
|
|
|
43
43
|
"p-queue": "^7.3.4",
|
|
44
44
|
"react": "^18.2.0",
|
|
45
45
|
"react-feather": "^2.0.10",
|
|
46
|
-
"react-tooltip": "^
|
|
46
|
+
"react-tooltip": "^5.26.3"
|
|
47
47
|
}
|
|
48
48
|
}
|
package/src/chrome/bottom.tsx
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/// <reference types="@emotion/react/types/css-prop" />
|
|
2
|
-
import type { Dispatch, DOMAttributes, HTMLAttributes, MouseEvent, MouseEventHandler, SetStateAction } from 'react'
|
|
2
|
+
import type { Dispatch, DOMAttributes, HTMLAttributes, MouseEvent, MouseEventHandler, MutableRefObject, SetStateAction } from 'react'
|
|
3
3
|
|
|
4
4
|
import type { ChromeOptions } from '.'
|
|
5
5
|
import type { FKNVideoControl, Subtitle, TransmuxError } from '..'
|
|
6
6
|
|
|
7
|
-
import ReactTooltip from 'react-tooltip'
|
|
8
7
|
import { css } from '@emotion/react'
|
|
9
8
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
10
9
|
import { Volume, Volume1, Volume2, VolumeX, AlertTriangle } from 'react-feather'
|
|
@@ -12,6 +11,7 @@ import { Volume, Volume1, Volume2, VolumeX, AlertTriangle } from 'react-feather'
|
|
|
12
11
|
import useScrub from '../use-scrub'
|
|
13
12
|
import { tagToLanguage } from '../languages'
|
|
14
13
|
import useNamespacedLocalStorage from '../use-local-storage'
|
|
14
|
+
import { Tooltip } from 'react-tooltip'
|
|
15
15
|
|
|
16
16
|
const style = css`
|
|
17
17
|
position: relative;
|
|
@@ -275,6 +275,8 @@ const style = css`
|
|
|
275
275
|
`
|
|
276
276
|
|
|
277
277
|
export type BottomOptions = {
|
|
278
|
+
video: MutableRefObject<HTMLVideoElement | undefined>
|
|
279
|
+
videoReactive: HTMLVideoElement | undefined,
|
|
278
280
|
duration?: number
|
|
279
281
|
currentTime?: number
|
|
280
282
|
togglePlay: () => void
|
|
@@ -293,6 +295,8 @@ Pick<ChromeOptions, 'seek' | 'setVolume' | 'loadedTime' | 'isPlaying' | 'tracks'
|
|
|
293
295
|
HTMLAttributes<HTMLDivElement>
|
|
294
296
|
|
|
295
297
|
export default ({
|
|
298
|
+
video,
|
|
299
|
+
videoReactive,
|
|
296
300
|
duration,
|
|
297
301
|
currentTime,
|
|
298
302
|
togglePlay,
|
|
@@ -317,14 +321,14 @@ export default ({
|
|
|
317
321
|
const progressBarRef = useRef<HTMLDivElement>(null)
|
|
318
322
|
const volumeBarRef = useRef<HTMLDivElement>(null)
|
|
319
323
|
const [hiddenVolumeArea, setHiddenVolumeArea] = useState(false)
|
|
320
|
-
const { scrub: seekScrub, value: seekScrubValue } = useScrub({ ref: progressBarRef })
|
|
324
|
+
const { scrub: seekScrub, value: seekScrubValue, setValue: setSeekValue } = useScrub({ ref: progressBarRef })
|
|
321
325
|
const isPictureInPictureEnabled = useMemo(() => document.pictureInPictureEnabled, [])
|
|
322
326
|
const [hasShownErrorPopup, setHasShownErrorPopup] = useState(false)
|
|
323
327
|
|
|
324
328
|
const useStoredValue = useNamespacedLocalStorage<{ volume: number, muted: boolean }>('fkn-media-player')
|
|
325
329
|
const [volume, setStoredVolume] = useStoredValue('volume', 1)
|
|
326
330
|
const [isMuted, setStoredMuted] = useStoredValue('muted', false)
|
|
327
|
-
const { scrub: volumeScrub, value: volumeScrubValue } = useScrub({ ref: volumeBarRef, defaultValue: volume })
|
|
331
|
+
const { scrub: volumeScrub, value: volumeScrubValue, setValue: updateVolume } = useScrub({ ref: volumeBarRef, defaultValue: volume })
|
|
328
332
|
|
|
329
333
|
useEffect(() => {
|
|
330
334
|
if (hasShownErrorPopup) return
|
|
@@ -442,19 +446,46 @@ export default ({
|
|
|
442
446
|
.map(str => <div key={str} className="error-menu-message">{str}</div>)
|
|
443
447
|
, [errors])
|
|
444
448
|
|
|
445
|
-
useEffect(() => {
|
|
446
|
-
ReactTooltip.rebuild()
|
|
447
|
-
}, [tracks.length])
|
|
448
|
-
|
|
449
449
|
useEffect(() => {
|
|
450
450
|
const eventListener = (ev: KeyboardEvent) => {
|
|
451
451
|
if (ev.key === 'f') toggleFullscreen()
|
|
452
452
|
if (ev.key === 'k') togglePlay()
|
|
453
|
+
if (ev.key === ' ') togglePlay()
|
|
453
454
|
if (ev.key === 'm') toggleMuteButton()
|
|
455
|
+
if (ev.key === 'ArrowUp') {
|
|
456
|
+
if (volume <= 0.95) {
|
|
457
|
+
updateVolume(volume + 0.05)
|
|
458
|
+
} else {
|
|
459
|
+
updateVolume(1)
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (ev.key === 'ArrowDown') {
|
|
463
|
+
if (volume >= 0.05) {
|
|
464
|
+
updateVolume(volume - 0.05)
|
|
465
|
+
} else {
|
|
466
|
+
updateVolume(0)
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (ev.key === 'ArrowLeft') {
|
|
470
|
+
if (!videoReactive) return
|
|
471
|
+
videoReactive.currentTime -= (
|
|
472
|
+
ev.shiftKey ? 20
|
|
473
|
+
: ev.ctrlKey ? 2
|
|
474
|
+
: 5
|
|
475
|
+
)
|
|
476
|
+
}
|
|
477
|
+
if (ev.key === 'ArrowRight') {
|
|
478
|
+
if (!videoReactive) return
|
|
479
|
+
videoReactive.currentTime += (
|
|
480
|
+
ev.shiftKey ? 20
|
|
481
|
+
: ev.ctrlKey ? 2
|
|
482
|
+
: 5
|
|
483
|
+
)
|
|
484
|
+
}
|
|
454
485
|
}
|
|
455
486
|
window.addEventListener('keydown', eventListener)
|
|
456
487
|
return () => window.removeEventListener('keydown', eventListener)
|
|
457
|
-
}, [toggleFullscreen, togglePlay, toggleMuteButton])
|
|
488
|
+
}, [videoReactive, toggleFullscreen, togglePlay, toggleMuteButton])
|
|
458
489
|
|
|
459
490
|
const [progressBarOverTime, setProgressBarOverTime] = useState<number | undefined>(undefined)
|
|
460
491
|
|
|
@@ -519,14 +550,14 @@ export default ({
|
|
|
519
550
|
<div className="padding" onMouseDown={seekScrub}></div>
|
|
520
551
|
</div>
|
|
521
552
|
<div className="controls">
|
|
522
|
-
<
|
|
553
|
+
<Tooltip id="play-button-tooltip" place="top-start" >
|
|
523
554
|
{
|
|
524
555
|
isPlaying
|
|
525
556
|
? 'Pause (k)'
|
|
526
557
|
: 'Play (k)'
|
|
527
558
|
}
|
|
528
|
-
</
|
|
529
|
-
<button className="play-button" type="button" data-tip data-
|
|
559
|
+
</Tooltip>
|
|
560
|
+
<button className="play-button" type="button" data-tip data-tooltip-id="play-button-tooltip" onClick={togglePlay}>
|
|
530
561
|
{
|
|
531
562
|
isPlaying
|
|
532
563
|
? <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-pause"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg>
|
|
@@ -534,14 +565,17 @@ export default ({
|
|
|
534
565
|
}
|
|
535
566
|
</button>
|
|
536
567
|
<div className="volume-area" onMouseOver={hoverVolumeArea}>
|
|
537
|
-
<
|
|
568
|
+
<Tooltip
|
|
569
|
+
id="mute-button-tooltip"
|
|
570
|
+
place="top"
|
|
571
|
+
>
|
|
538
572
|
{
|
|
539
573
|
isMuted
|
|
540
574
|
? 'Unmute (m)'
|
|
541
575
|
: 'Mute (m)'
|
|
542
576
|
}
|
|
543
|
-
</
|
|
544
|
-
<button className="mute-button" data-tip data-
|
|
577
|
+
</Tooltip>
|
|
578
|
+
<button className="mute-button" data-tip data-tooltip-id="mute-button-tooltip" onClick={toggleMuteButton}>
|
|
545
579
|
{
|
|
546
580
|
isMuted ? <VolumeX/>
|
|
547
581
|
: (volume ?? 0) > 0.66 ? <Volume2/>
|
|
@@ -589,9 +623,17 @@ export default ({
|
|
|
589
623
|
: null
|
|
590
624
|
}
|
|
591
625
|
</div>
|
|
592
|
-
<
|
|
593
|
-
|
|
594
|
-
|
|
626
|
+
<Tooltip
|
|
627
|
+
id="subtitle-button-tooltip"
|
|
628
|
+
globalEventOff="click"
|
|
629
|
+
|
|
630
|
+
disable={!isSubtitleMenuHidden}
|
|
631
|
+
class="tooltip"
|
|
632
|
+
>
|
|
633
|
+
<span>
|
|
634
|
+
Subtitles
|
|
635
|
+
</span>
|
|
636
|
+
</Tooltip>
|
|
595
637
|
<div className="subtitle-area">
|
|
596
638
|
{
|
|
597
639
|
tracks.length
|
|
@@ -610,7 +652,7 @@ export default ({
|
|
|
610
652
|
)
|
|
611
653
|
}
|
|
612
654
|
</div>
|
|
613
|
-
<button className="subtitle-menu-button" data-tip data-
|
|
655
|
+
<button className="subtitle-menu-button" data-tip data-tooltip-id="subtitle-button-tooltip" onClick={subtitleMenuButtonClick}>
|
|
614
656
|
<SubtitleTrackToName subtitleTrack={subtitleTrack}/>
|
|
615
657
|
</button>
|
|
616
658
|
</>
|
|
@@ -618,26 +660,30 @@ export default ({
|
|
|
618
660
|
: null
|
|
619
661
|
}
|
|
620
662
|
</div>
|
|
621
|
-
<
|
|
663
|
+
<Tooltip
|
|
664
|
+
id="pip-button-tooltip"
|
|
665
|
+
|
|
666
|
+
place="top"
|
|
667
|
+
>
|
|
622
668
|
Picture in Picture
|
|
623
|
-
</
|
|
669
|
+
</Tooltip>
|
|
624
670
|
{
|
|
625
671
|
isPictureInPictureEnabled
|
|
626
672
|
? (
|
|
627
|
-
<button className="picture-in-picture" data-tip data-
|
|
673
|
+
<button className="picture-in-picture" data-tip data-tooltip-id="pip-button-tooltip" onClick={pictureInPicture}>
|
|
628
674
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="3" width="21" height="14" rx="2" ry="2"></rect><rect x="12.5" y="8.5" width="8" height="6" rx="2" ry="2"></rect></svg>
|
|
629
675
|
</button>
|
|
630
676
|
)
|
|
631
677
|
: null
|
|
632
678
|
}
|
|
633
|
-
<
|
|
679
|
+
<Tooltip id="fullscreen-button-tooltip" place="top-end">
|
|
634
680
|
{
|
|
635
681
|
isFullscreen
|
|
636
682
|
? 'Exit full screen (f)'
|
|
637
683
|
: 'Full screen (f)'
|
|
638
684
|
}
|
|
639
|
-
</
|
|
640
|
-
<button className="fullscreen" data-tip data-
|
|
685
|
+
</Tooltip>
|
|
686
|
+
<button className="fullscreen" data-tip data-tooltip-id="fullscreen-button-tooltip" onClick={toggleFullscreen}>
|
|
641
687
|
{
|
|
642
688
|
isFullscreen
|
|
643
689
|
? <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-minimize"><path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"></path></svg>
|
package/src/chrome/index.tsx
CHANGED
|
@@ -46,6 +46,7 @@ export type ChromeOptions = {
|
|
|
46
46
|
attachments: Attachment[] | undefined
|
|
47
47
|
tracks: Subtitle[]
|
|
48
48
|
video: MutableRefObject<HTMLVideoElement | undefined>
|
|
49
|
+
videoReactive: HTMLVideoElement | undefined,
|
|
49
50
|
errors: TransmuxError[]
|
|
50
51
|
customControls?: FKNVideoControl[]
|
|
51
52
|
libassWorkerUrl: string
|
|
@@ -70,6 +71,7 @@ export default ({
|
|
|
70
71
|
attachments,
|
|
71
72
|
tracks,
|
|
72
73
|
video,
|
|
74
|
+
videoReactive,
|
|
73
75
|
errors,
|
|
74
76
|
customControls,
|
|
75
77
|
libassWorkerUrl,
|
|
@@ -178,6 +180,8 @@ export default ({
|
|
|
178
180
|
togglePlay={togglePlay}
|
|
179
181
|
isFullscreen={isFullscreen}
|
|
180
182
|
pictureInPicture={pictureInPicture}
|
|
183
|
+
video={video}
|
|
184
|
+
videoReactive={videoReactive}
|
|
181
185
|
seek={seek}
|
|
182
186
|
setCurrentSubtitleTrack={setCurrentSubtitleTrack}
|
|
183
187
|
isSubtitleMenuHidden={isSubtitleMenuHidden}
|
package/src/index.tsx
CHANGED
|
@@ -450,6 +450,7 @@ const FKNVideo = forwardRef<HTMLVideoElement, VideoHTMLAttributes<HTMLInputEleme
|
|
|
450
450
|
publicPath={publicPath}
|
|
451
451
|
isPlaying={isPlaying}
|
|
452
452
|
video={videoRef}
|
|
453
|
+
videoReactive={videoElement}
|
|
453
454
|
needsInitialInteraction={needsInitialInteraction}
|
|
454
455
|
loading={loading}
|
|
455
456
|
duration={duration}
|
package/src/main.tsx
CHANGED
|
@@ -25,7 +25,7 @@ const Mount = () => {
|
|
|
25
25
|
|
|
26
26
|
const onFetch = async (offset: number, end?: number) =>
|
|
27
27
|
fetch(
|
|
28
|
-
'/
|
|
28
|
+
'/video.mkv',
|
|
29
29
|
{
|
|
30
30
|
headers: {
|
|
31
31
|
Range: `bytes=${offset}-${end ?? (!BACKPRESSURE_STREAM_ENABLED ? Math.min(offset + BASE_BUFFER_SIZE, size!) : '')}`
|
package/src/use-scrub.ts
CHANGED
package/src/vite-env.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
|
1
|
+
/// <reference types="vite/client" />
|
package/tsconfig.node.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"composite": true,
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "Node",
|
|
6
|
-
"allowSyntheticDefaultImports": true
|
|
7
|
-
},
|
|
8
|
-
"include": ["vite.config.ts"]
|
|
9
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"composite": true,
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Node",
|
|
6
|
+
"allowSyntheticDefaultImports": true
|
|
7
|
+
},
|
|
8
|
+
"include": ["vite.config.ts"]
|
|
9
|
+
}
|