@banou/media-player 0.6.0 → 0.6.2

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.
Files changed (37) hide show
  1. package/README.md +6 -6
  2. package/package.json +51 -51
  3. package/src/assets/picture-in-picture.svg +5 -5
  4. package/src/components/chrome.tsx +95 -95
  5. package/src/components/control-bar.tsx +333 -333
  6. package/src/components/overlay.tsx +91 -91
  7. package/src/components/playback-slider.tsx +174 -174
  8. package/src/components/progress-bar.tsx +251 -251
  9. package/src/components/settings.tsx +321 -321
  10. package/src/components/sound.tsx +100 -100
  11. package/src/components/tooltip-display.tsx +83 -83
  12. package/src/components/volume-slider.tsx +156 -156
  13. package/src/index.tsx +195 -195
  14. package/src/main.tsx +169 -169
  15. package/src/state-machines/data-source.ts +84 -84
  16. package/src/state-machines/index.ts +5 -5
  17. package/src/state-machines/media-properties.ts +82 -82
  18. package/src/state-machines/media-source.ts +129 -129
  19. package/src/state-machines/media.ts +255 -255
  20. package/src/state-machines/subtitles.ts +285 -285
  21. package/src/state-machines/thumbnails.ts +123 -123
  22. package/src/state-machines/utils.ts +94 -94
  23. package/src/utils/actor-utils.ts +19 -19
  24. package/src/utils/colors.ts +8 -8
  25. package/src/utils/context.ts +23 -23
  26. package/src/utils/fonts.ts +159 -159
  27. package/src/utils/index.ts +229 -229
  28. package/src/utils/languages.ts +262 -262
  29. package/src/utils/mp4box.ts +74 -74
  30. package/src/utils/time.ts +15 -15
  31. package/src/utils/use-local-storage.ts +30 -30
  32. package/src/utils/use-scrub.ts +40 -40
  33. package/src/utils/volume-utils.ts +39 -39
  34. package/src/utils/window-height.ts +19 -19
  35. package/src/vite-env.d.ts +1 -1
  36. package/tsconfig.json +28 -28
  37. package/tsconfig.node.json +9 -9
@@ -1,101 +1,101 @@
1
- import { Volume1, Volume2, VolumeX } from "react-feather"
2
- import { css } from "@emotion/react"
3
-
4
- import { MediaMachineContext } from "../state-machines"
5
- import { TooltipDisplay } from "./tooltip-display"
6
- import { fonts } from "../utils/fonts"
7
- import VolumeSlider from "./volume-slider"
8
- import { MutableRefObject, useMemo } from 'react'
9
- import { linearToLogVolume, logToLinearVolume } from "../utils/volume-utils"
10
-
11
- const style = css`
12
- position: relative;
13
- display: flex;
14
- align-items: center;
15
-
16
- .volume-slider-container {
17
- display: flex;
18
- align-items: center;
19
-
20
- width: 0;
21
- margin-right: 0;
22
- overflow: hidden;
23
- pointer-events: none;
24
-
25
- transition: margin .2s cubic-bezier(0.4,0,1,1), width .2s cubic-bezier(0.4,0,1,1);
26
- -webkit-transition: margin .2s cubic-bezier(0.4,0,1,1), width .2s cubic-bezier(0.4,0,1,1);
27
-
28
- .volume-slider-background {
29
- display: flex;
30
- flex-direction: row;
31
- align-items: center;
32
-
33
- border-radius: 4px;
34
-
35
- .volume-value {
36
- ${fonts.bMedium.regular};
37
- color: #ffffff;
38
- }
39
- }
40
- }
41
-
42
- &:hover .volume-slider-container {
43
- width: 9rem;
44
-
45
- transition: margin .2s cubic-bezier(0,0,0.2,1), width .2s cubic-bezier(0,0,0.2,1);
46
- -webkit-transition: margin .2s cubic-bezier(0,0,0.2,1), width .2s cubic-bezier(0,0,0.2,1);
47
-
48
- pointer-events: auto;
49
- }
50
- `
51
-
52
- // todo: try to find a better type for `ref`
53
- const Sound = ({ ref }: { ref: any }) => {
54
- const mediaActor = MediaMachineContext.useActorRef()
55
- const volume = MediaMachineContext.useSelector((state) => state.context.media.volume)
56
- const muted = MediaMachineContext.useSelector((state) => state.context.media.muted)
57
-
58
- const linearVolume = useMemo(() => logToLinearVolume(volume), [volume])
59
- const setVolume = (newLinearVolume: number, muted: boolean) =>
60
- mediaActor.send({
61
- type: 'SET_VOLUME',
62
- muted,
63
- volume: linearToLogVolume(newLinearVolume)
64
- })
65
-
66
- return (
67
- <div css={style}>
68
- <TooltipDisplay
69
- id='sound'
70
- text={
71
- <button
72
- className='sound'
73
- type='button'
74
- onClick={() => setVolume(linearVolume, !muted)}
75
- ref={ref}
76
- >
77
- {muted || volume === 0
78
- ? <VolumeX size={18} color='#fff' />
79
- : volume <= 0.5
80
- ? <Volume1 size={18} color='#fff' />
81
- : <Volume2 size={18} color='#fff' />
82
- }
83
- </button>
84
- }
85
- toolTipText={
86
- <span>{muted ? 'Unmute (m)' : 'Mute (m)'}</span>
87
- }
88
- />
89
- <div className='volume-slider-container'>
90
- <div className='volume-slider-background'>
91
- <VolumeSlider
92
- value={muted ? 0 : linearVolume}
93
- onChange={linearValue => setVolume(linearValue, false)}
94
- />
95
- </div>
96
- </div>
97
- </div>
98
- )
99
- }
100
-
1
+ import { Volume1, Volume2, VolumeX } from "react-feather"
2
+ import { css } from "@emotion/react"
3
+
4
+ import { MediaMachineContext } from "../state-machines"
5
+ import { TooltipDisplay } from "./tooltip-display"
6
+ import { fonts } from "../utils/fonts"
7
+ import VolumeSlider from "./volume-slider"
8
+ import { MutableRefObject, useMemo } from 'react'
9
+ import { linearToLogVolume, logToLinearVolume } from "../utils/volume-utils"
10
+
11
+ const style = css`
12
+ position: relative;
13
+ display: flex;
14
+ align-items: center;
15
+
16
+ .volume-slider-container {
17
+ display: flex;
18
+ align-items: center;
19
+
20
+ width: 0;
21
+ margin-right: 0;
22
+ overflow: hidden;
23
+ pointer-events: none;
24
+
25
+ transition: margin .2s cubic-bezier(0.4,0,1,1), width .2s cubic-bezier(0.4,0,1,1);
26
+ -webkit-transition: margin .2s cubic-bezier(0.4,0,1,1), width .2s cubic-bezier(0.4,0,1,1);
27
+
28
+ .volume-slider-background {
29
+ display: flex;
30
+ flex-direction: row;
31
+ align-items: center;
32
+
33
+ border-radius: 4px;
34
+
35
+ .volume-value {
36
+ ${fonts.bMedium.regular};
37
+ color: #ffffff;
38
+ }
39
+ }
40
+ }
41
+
42
+ &:hover .volume-slider-container {
43
+ width: 9rem;
44
+
45
+ transition: margin .2s cubic-bezier(0,0,0.2,1), width .2s cubic-bezier(0,0,0.2,1);
46
+ -webkit-transition: margin .2s cubic-bezier(0,0,0.2,1), width .2s cubic-bezier(0,0,0.2,1);
47
+
48
+ pointer-events: auto;
49
+ }
50
+ `
51
+
52
+ // todo: try to find a better type for `ref`
53
+ const Sound = ({ ref }: { ref: any }) => {
54
+ const mediaActor = MediaMachineContext.useActorRef()
55
+ const volume = MediaMachineContext.useSelector((state) => state.context.media.volume)
56
+ const muted = MediaMachineContext.useSelector((state) => state.context.media.muted)
57
+
58
+ const linearVolume = useMemo(() => logToLinearVolume(volume), [volume])
59
+ const setVolume = (newLinearVolume: number, muted: boolean) =>
60
+ mediaActor.send({
61
+ type: 'SET_VOLUME',
62
+ muted,
63
+ volume: linearToLogVolume(newLinearVolume)
64
+ })
65
+
66
+ return (
67
+ <div css={style}>
68
+ <TooltipDisplay
69
+ id='sound'
70
+ text={
71
+ <button
72
+ className='sound'
73
+ type='button'
74
+ onClick={() => setVolume(linearVolume, !muted)}
75
+ ref={ref}
76
+ >
77
+ {muted || volume === 0
78
+ ? <VolumeX size={18} color='#fff' />
79
+ : volume <= 0.5
80
+ ? <Volume1 size={18} color='#fff' />
81
+ : <Volume2 size={18} color='#fff' />
82
+ }
83
+ </button>
84
+ }
85
+ toolTipText={
86
+ <span>{muted ? 'Unmute (m)' : 'Mute (m)'}</span>
87
+ }
88
+ />
89
+ <div className='volume-slider-container'>
90
+ <div className='volume-slider-background'>
91
+ <VolumeSlider
92
+ value={muted ? 0 : linearVolume}
93
+ onChange={linearValue => setVolume(linearValue, false)}
94
+ />
95
+ </div>
96
+ </div>
97
+ </div>
98
+ )
99
+ }
100
+
101
101
  export default Sound
@@ -1,83 +1,83 @@
1
- import { ReactNode } from 'react'
2
- import { css } from '@emotion/react'
3
- import { PlacesType, Tooltip } from 'react-tooltip'
4
-
5
- import { fonts } from '../utils/fonts'
6
-
7
- const style = (size: buttonSize) => css`
8
- display: flex;
9
- justify-content: flex-end;
10
-
11
- border-radius: 0.4rem;
12
- user-select: none;
13
-
14
- z-index: 3;
15
-
16
- * {
17
- ${fonts.bMedium.regular}
18
- }
19
-
20
- ${size === buttonSize.sm && css`
21
- padding: 0.4rem!important;
22
- `}
23
- ${size === buttonSize.md && css`
24
- padding: 0.6rem!important;
25
- `}
26
- ${size === buttonSize.lg && css`
27
- padding: 1.2rem!important;
28
- `}
29
- `
30
-
31
- interface TooltipDisplayProps {
32
- id: string
33
- toolTipText: ReactNode
34
- text: ReactNode
35
- delayShow?: number
36
- closeDelay?: number
37
- offset?: number
38
- tooltipPlace?: PlacesType
39
- size?: buttonSize
40
- disabled?: boolean
41
- }
42
-
43
- export enum buttonSize {
44
- sm = 'sm',
45
- md = 'md',
46
- lg = 'lg'
47
- }
48
-
49
- export const TooltipDisplay = ({
50
- id,
51
- toolTipText,
52
- text,
53
- delayShow = 0,
54
- closeDelay = 0,
55
- offset = 20,
56
- tooltipPlace = 'top',
57
- disabled = false,
58
- size = buttonSize.md
59
- }: TooltipDisplayProps) => (
60
- <>
61
- <div
62
- data-tooltip-id={id}
63
- data-open={true}
64
- data-tooltip-offset={offset}
65
- data-tooltip-delay-show={delayShow}
66
- data-tooltip-delay-hide={closeDelay}
67
- data-tooltip-place={tooltipPlace}
68
- >
69
- {text}
70
- </div>
71
- {
72
- !disabled && (
73
- <Tooltip
74
- css={style(size)}
75
- id={id}
76
- noArrow={true}
77
- >
78
- {toolTipText}
79
- </Tooltip>
80
- )
81
- }
82
- </>
83
- )
1
+ import { ReactNode } from 'react'
2
+ import { css } from '@emotion/react'
3
+ import { PlacesType, Tooltip } from 'react-tooltip'
4
+
5
+ import { fonts } from '../utils/fonts'
6
+
7
+ const style = (size: buttonSize) => css`
8
+ display: flex;
9
+ justify-content: flex-end;
10
+
11
+ border-radius: 0.4rem;
12
+ user-select: none;
13
+
14
+ z-index: 3;
15
+
16
+ * {
17
+ ${fonts.bMedium.regular}
18
+ }
19
+
20
+ ${size === buttonSize.sm && css`
21
+ padding: 0.4rem!important;
22
+ `}
23
+ ${size === buttonSize.md && css`
24
+ padding: 0.6rem!important;
25
+ `}
26
+ ${size === buttonSize.lg && css`
27
+ padding: 1.2rem!important;
28
+ `}
29
+ `
30
+
31
+ interface TooltipDisplayProps {
32
+ id: string
33
+ toolTipText: ReactNode
34
+ text: ReactNode
35
+ delayShow?: number
36
+ closeDelay?: number
37
+ offset?: number
38
+ tooltipPlace?: PlacesType
39
+ size?: buttonSize
40
+ disabled?: boolean
41
+ }
42
+
43
+ export enum buttonSize {
44
+ sm = 'sm',
45
+ md = 'md',
46
+ lg = 'lg'
47
+ }
48
+
49
+ export const TooltipDisplay = ({
50
+ id,
51
+ toolTipText,
52
+ text,
53
+ delayShow = 0,
54
+ closeDelay = 0,
55
+ offset = 20,
56
+ tooltipPlace = 'top',
57
+ disabled = false,
58
+ size = buttonSize.md
59
+ }: TooltipDisplayProps) => (
60
+ <>
61
+ <div
62
+ data-tooltip-id={id}
63
+ data-open={true}
64
+ data-tooltip-offset={offset}
65
+ data-tooltip-delay-show={delayShow}
66
+ data-tooltip-delay-hide={closeDelay}
67
+ data-tooltip-place={tooltipPlace}
68
+ >
69
+ {text}
70
+ </div>
71
+ {
72
+ !disabled && (
73
+ <Tooltip
74
+ css={style(size)}
75
+ id={id}
76
+ noArrow={true}
77
+ >
78
+ {toolTipText}
79
+ </Tooltip>
80
+ )
81
+ }
82
+ </>
83
+ )
@@ -1,156 +1,156 @@
1
- import { useRef, useState, useEffect, useMemo } from 'react'
2
- import { css } from '@emotion/react'
3
-
4
- import { TooltipDisplay } from './tooltip-display'
5
- import { MediaMachineContext } from '../state-machines'
6
- import { logToLinearVolume } from '../utils/volume-utils'
7
-
8
- const style = css`
9
- display: flex;
10
- align-items: center;
11
-
12
- padding: 16px 4px;
13
- margin: 0 2px;
14
-
15
- cursor: pointer;
16
-
17
- > div {
18
- position: relative;
19
-
20
- background: #ccc;
21
- border-radius: 4px;
22
-
23
- width: 80px;
24
- height: 3px;
25
- }
26
-
27
- .volume-handle {
28
- position: absolute;
29
- top: -3.5px;
30
-
31
- background: #ffffff;
32
- border-radius: 50%;
33
- transform: translateX(-50%);
34
- pointer-events: none;
35
-
36
- width: 10px;
37
- height: 10px;
38
- }
39
- `
40
-
41
- type VolumeSliderType = {
42
- value: number
43
- onChange: (v: number) => void
44
- }
45
-
46
- const VolumeSlider = ({ value, onChange }: VolumeSliderType) => {
47
- const volume = MediaMachineContext.useSelector((state) => state.context.media.volume)
48
- const muted = MediaMachineContext.useSelector((state) => state.context.media.muted)
49
-
50
- const sliderRef = useRef<HTMLDivElement>(null)
51
- const [isDragging, setIsDragging] = useState(false)
52
-
53
- const getNewVolumeFromEvent = (clientX: number): number => {
54
- if (!sliderRef.current) return value
55
- const rect = sliderRef.current.getBoundingClientRect()
56
- const xPos = clientX - rect.left
57
- const clamped = Math.max(0, Math.min(xPos, rect.width))
58
- return clamped / rect.width
59
- }
60
-
61
- const handlePointerDown = (e: React.MouseEvent<HTMLDivElement>) => {
62
- setIsDragging(true)
63
- const newVolume = getNewVolumeFromEvent(e.clientX)
64
- onChange(newVolume)
65
- }
66
-
67
- const handlePointerMove = (e: MouseEvent) => {
68
- if (!isDragging) return
69
- const newVolume = getNewVolumeFromEvent(e.clientX)
70
- onChange(newVolume)
71
- }
72
-
73
- const handlePointerUp = () => {
74
- setIsDragging(false)
75
- }
76
-
77
- const handleWheel = (e: WheelEvent) => {
78
- e.preventDefault()
79
- const step = 0.05
80
- let newVol = value
81
- if (e.deltaY < 0) {
82
- // scroll up
83
- newVol = Math.min(1, newVol + step)
84
- } else {
85
- // scroll down
86
- newVol = Math.max(0, newVol - step)
87
- }
88
- onChange(newVol)
89
- }
90
-
91
- useEffect(() => {
92
- window.addEventListener('mousemove', handlePointerMove)
93
- window.addEventListener('mouseup', handlePointerUp)
94
- return () => {
95
- window.removeEventListener('mousemove', handlePointerMove)
96
- window.removeEventListener('mouseup', handlePointerUp)
97
- }
98
- }, [isDragging])
99
-
100
- useEffect(() => {
101
- const slider = sliderRef.current
102
- if (slider) {
103
- slider.addEventListener('wheel', handleWheel, { passive: false })
104
- }
105
- return () => {
106
- if (slider) {
107
- slider.removeEventListener('wheel', handleWheel)
108
- }
109
- }
110
- }, [value])
111
-
112
- const fillPercent = value * 100
113
- const fillColor = '#fff'
114
- const emptyColor = '#3A3A3A'
115
-
116
- const displayVolumePercent = useMemo(
117
- () => Math.round(muted ? 0 : logToLinearVolume(volume) * 100),
118
- [volume]
119
- )
120
-
121
- return (
122
- <TooltipDisplay
123
- id='volume-slider'
124
- text={
125
- <div
126
- ref={sliderRef}
127
- css={style}
128
- onMouseDown={handlePointerDown}
129
- >
130
- <div
131
- style={{
132
- background: `linear-gradient(to right,
133
- ${fillColor} 0% ${fillPercent}%,
134
- ${emptyColor} ${fillPercent}% 100%
135
- )`
136
- }}
137
- >
138
- <div
139
- className="volume-handle"
140
- style={{
141
- left: `${value * 100}%`
142
- }}
143
- />
144
- </div>
145
- </div>
146
- }
147
- toolTipText={
148
- <div className='volume-value'>
149
- {displayVolumePercent}%
150
- </div>
151
- }
152
- />
153
- )
154
- }
155
-
156
- export default VolumeSlider
1
+ import { useRef, useState, useEffect, useMemo } from 'react'
2
+ import { css } from '@emotion/react'
3
+
4
+ import { TooltipDisplay } from './tooltip-display'
5
+ import { MediaMachineContext } from '../state-machines'
6
+ import { logToLinearVolume } from '../utils/volume-utils'
7
+
8
+ const style = css`
9
+ display: flex;
10
+ align-items: center;
11
+
12
+ padding: 16px 4px;
13
+ margin: 0 2px;
14
+
15
+ cursor: pointer;
16
+
17
+ > div {
18
+ position: relative;
19
+
20
+ background: #ccc;
21
+ border-radius: 4px;
22
+
23
+ width: 80px;
24
+ height: 3px;
25
+ }
26
+
27
+ .volume-handle {
28
+ position: absolute;
29
+ top: -3.5px;
30
+
31
+ background: #ffffff;
32
+ border-radius: 50%;
33
+ transform: translateX(-50%);
34
+ pointer-events: none;
35
+
36
+ width: 10px;
37
+ height: 10px;
38
+ }
39
+ `
40
+
41
+ type VolumeSliderType = {
42
+ value: number
43
+ onChange: (v: number) => void
44
+ }
45
+
46
+ const VolumeSlider = ({ value, onChange }: VolumeSliderType) => {
47
+ const volume = MediaMachineContext.useSelector((state) => state.context.media.volume)
48
+ const muted = MediaMachineContext.useSelector((state) => state.context.media.muted)
49
+
50
+ const sliderRef = useRef<HTMLDivElement>(null)
51
+ const [isDragging, setIsDragging] = useState(false)
52
+
53
+ const getNewVolumeFromEvent = (clientX: number): number => {
54
+ if (!sliderRef.current) return value
55
+ const rect = sliderRef.current.getBoundingClientRect()
56
+ const xPos = clientX - rect.left
57
+ const clamped = Math.max(0, Math.min(xPos, rect.width))
58
+ return clamped / rect.width
59
+ }
60
+
61
+ const handlePointerDown = (e: React.MouseEvent<HTMLDivElement>) => {
62
+ setIsDragging(true)
63
+ const newVolume = getNewVolumeFromEvent(e.clientX)
64
+ onChange(newVolume)
65
+ }
66
+
67
+ const handlePointerMove = (e: MouseEvent) => {
68
+ if (!isDragging) return
69
+ const newVolume = getNewVolumeFromEvent(e.clientX)
70
+ onChange(newVolume)
71
+ }
72
+
73
+ const handlePointerUp = () => {
74
+ setIsDragging(false)
75
+ }
76
+
77
+ const handleWheel = (e: WheelEvent) => {
78
+ e.preventDefault()
79
+ const step = 0.05
80
+ let newVol = value
81
+ if (e.deltaY < 0) {
82
+ // scroll up
83
+ newVol = Math.min(1, newVol + step)
84
+ } else {
85
+ // scroll down
86
+ newVol = Math.max(0, newVol - step)
87
+ }
88
+ onChange(newVol)
89
+ }
90
+
91
+ useEffect(() => {
92
+ window.addEventListener('mousemove', handlePointerMove)
93
+ window.addEventListener('mouseup', handlePointerUp)
94
+ return () => {
95
+ window.removeEventListener('mousemove', handlePointerMove)
96
+ window.removeEventListener('mouseup', handlePointerUp)
97
+ }
98
+ }, [isDragging])
99
+
100
+ useEffect(() => {
101
+ const slider = sliderRef.current
102
+ if (slider) {
103
+ slider.addEventListener('wheel', handleWheel, { passive: false })
104
+ }
105
+ return () => {
106
+ if (slider) {
107
+ slider.removeEventListener('wheel', handleWheel)
108
+ }
109
+ }
110
+ }, [value])
111
+
112
+ const fillPercent = value * 100
113
+ const fillColor = '#fff'
114
+ const emptyColor = '#3A3A3A'
115
+
116
+ const displayVolumePercent = useMemo(
117
+ () => Math.round(muted ? 0 : logToLinearVolume(volume) * 100),
118
+ [volume]
119
+ )
120
+
121
+ return (
122
+ <TooltipDisplay
123
+ id='volume-slider'
124
+ text={
125
+ <div
126
+ ref={sliderRef}
127
+ css={style}
128
+ onMouseDown={handlePointerDown}
129
+ >
130
+ <div
131
+ style={{
132
+ background: `linear-gradient(to right,
133
+ ${fillColor} 0% ${fillPercent}%,
134
+ ${emptyColor} ${fillPercent}% 100%
135
+ )`
136
+ }}
137
+ >
138
+ <div
139
+ className="volume-handle"
140
+ style={{
141
+ left: `${value * 100}%`
142
+ }}
143
+ />
144
+ </div>
145
+ </div>
146
+ }
147
+ toolTipText={
148
+ <div className='volume-value'>
149
+ {displayVolumePercent}%
150
+ </div>
151
+ }
152
+ />
153
+ )
154
+ }
155
+
156
+ export default VolumeSlider