@banou/media-player 0.6.2 → 0.8.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.
Files changed (123) hide show
  1. package/README.md +149 -3
  2. package/dist/engine/index.d.ts +10 -0
  3. package/dist/engine/index.js +2 -0
  4. package/dist/engine/picture-in-picture.d.ts +27 -0
  5. package/dist/engine/playback.d.ts +45 -0
  6. package/dist/engine/source-buffer.d.ts +12 -0
  7. package/dist/engine/subtitles.d.ts +36 -0
  8. package/dist/engine/thumbnails.d.ts +22 -0
  9. package/dist/engine-Dmc7JSd9.js +470 -0
  10. package/dist/index.d.ts +14 -0
  11. package/dist/index.js +1610 -0
  12. package/dist/react/components/chrome.d.ts +12 -0
  13. package/dist/react/components/control-bar.d.ts +2 -0
  14. package/dist/react/components/overlay.d.ts +4 -0
  15. package/dist/react/components/playback-slider.d.ts +2 -0
  16. package/dist/react/components/progress-bar.d.ts +2 -0
  17. package/dist/react/components/settings.d.ts +2 -0
  18. package/dist/react/components/sound.d.ts +7 -0
  19. package/{build → dist/react}/components/tooltip-display.d.ts +8 -8
  20. package/dist/react/components/volume-slider.d.ts +6 -0
  21. package/dist/react/hooks/use-drag-value.d.ts +23 -0
  22. package/dist/react/hooks/use-picture-in-picture.d.ts +2 -0
  23. package/dist/react/hooks/use-playback.d.ts +8 -0
  24. package/dist/react/hooks/use-thumbnails.d.ts +12 -0
  25. package/dist/react/media.d.ts +98 -0
  26. package/dist/react/player.d.ts +26 -0
  27. package/dist/react/source-feature.d.ts +106 -0
  28. package/dist/react/video-player.d.ts +83 -0
  29. package/dist/utils/source.d.ts +30 -0
  30. package/dist/utils/track-label.d.ts +31 -0
  31. package/dist/utils/volume-utils.d.ts +6 -0
  32. package/package.json +58 -37
  33. package/src/lib/engine/index.ts +14 -0
  34. package/src/lib/engine/picture-in-picture.ts +250 -0
  35. package/src/lib/engine/playback.ts +352 -0
  36. package/src/lib/engine/source-buffer.ts +61 -0
  37. package/src/lib/engine/subtitles.ts +210 -0
  38. package/src/lib/engine/thumbnails.ts +137 -0
  39. package/src/lib/globals.d.ts +8 -0
  40. package/src/lib/index.tsx +51 -0
  41. package/src/lib/react/components/chrome.tsx +130 -0
  42. package/src/{components → lib/react/components}/control-bar.tsx +74 -96
  43. package/src/lib/react/components/overlay.tsx +125 -0
  44. package/src/{components → lib/react/components}/playback-slider.tsx +30 -52
  45. package/src/lib/react/components/progress-bar.tsx +341 -0
  46. package/src/lib/react/components/settings.tsx +384 -0
  47. package/src/lib/react/components/sound.tsx +133 -0
  48. package/src/{components → lib/react/components}/tooltip-display.tsx +15 -12
  49. package/src/{components → lib/react/components}/volume-slider.tsx +32 -46
  50. package/src/lib/react/hooks/use-drag-value.ts +73 -0
  51. package/src/lib/react/hooks/use-picture-in-picture.ts +36 -0
  52. package/src/lib/react/hooks/use-playback.ts +146 -0
  53. package/src/lib/react/hooks/use-thumbnails.ts +78 -0
  54. package/src/lib/react/media.ts +124 -0
  55. package/src/lib/react/player.ts +20 -0
  56. package/src/lib/react/source-feature.ts +108 -0
  57. package/src/lib/react/video-player.tsx +223 -0
  58. package/src/lib/utils/fonts.ts +160 -0
  59. package/src/lib/utils/source.ts +60 -0
  60. package/src/lib/utils/track-label.ts +51 -0
  61. package/src/lib/utils/volume-utils.ts +13 -0
  62. package/build/components/chrome.d.ts +0 -7
  63. package/build/components/control-bar.d.ts +0 -6
  64. package/build/components/overlay.d.ts +0 -4
  65. package/build/components/playback-slider.d.ts +0 -2
  66. package/build/components/progress-bar.d.ts +0 -1
  67. package/build/components/settings.d.ts +0 -2
  68. package/build/components/sound.d.ts +0 -4
  69. package/build/components/volume-slider.d.ts +0 -6
  70. package/build/index.d.ts +0 -26
  71. package/build/index.js +0 -3416
  72. package/build/main.d.ts +0 -1
  73. package/build/state-machines/data-source.d.ts +0 -27
  74. package/build/state-machines/index.d.ts +0 -34038
  75. package/build/state-machines/media-properties.d.ts +0 -45
  76. package/build/state-machines/media-source.d.ts +0 -34
  77. package/build/state-machines/media.d.ts +0 -8558
  78. package/build/state-machines/subtitles.d.ts +0 -56
  79. package/build/state-machines/thumbnails.d.ts +0 -24
  80. package/build/state-machines/utils.d.ts +0 -26
  81. package/build/utils/actor-utils.d.ts +0 -2
  82. package/build/utils/context.d.ts +0 -14
  83. package/build/utils/index.d.ts +0 -4
  84. package/build/utils/languages.d.ts +0 -260
  85. package/build/utils/mp4box.d.ts +0 -61
  86. package/build/utils/use-local-storage.d.ts +0 -3
  87. package/build/utils/use-scrub.d.ts +0 -11
  88. package/build/utils/volume-utils.d.ts +0 -17
  89. package/build/utils/window-height.d.ts +0 -2
  90. package/src/components/chrome.tsx +0 -95
  91. package/src/components/overlay.tsx +0 -91
  92. package/src/components/progress-bar.tsx +0 -251
  93. package/src/components/settings.tsx +0 -322
  94. package/src/components/sound.tsx +0 -101
  95. package/src/index.tsx +0 -195
  96. package/src/main.tsx +0 -169
  97. package/src/state-machines/data-source.ts +0 -84
  98. package/src/state-machines/index.ts +0 -5
  99. package/src/state-machines/media-properties.ts +0 -82
  100. package/src/state-machines/media-source.ts +0 -129
  101. package/src/state-machines/media.ts +0 -255
  102. package/src/state-machines/subtitles.ts +0 -285
  103. package/src/state-machines/thumbnails.ts +0 -123
  104. package/src/state-machines/utils.ts +0 -94
  105. package/src/utils/actor-utils.ts +0 -19
  106. package/src/utils/context.ts +0 -23
  107. package/src/utils/fonts.ts +0 -160
  108. package/src/utils/index.ts +0 -229
  109. package/src/utils/languages.ts +0 -262
  110. package/src/utils/mp4box.ts +0 -74
  111. package/src/utils/use-local-storage.ts +0 -30
  112. package/src/utils/use-scrub.ts +0 -40
  113. package/src/utils/volume-utils.ts +0 -39
  114. package/src/utils/window-height.ts +0 -19
  115. package/src/vite-env.d.ts +0 -1
  116. package/tsconfig.json +0 -28
  117. package/tsconfig.node.json +0 -9
  118. /package/{build → dist}/utils/colors.d.ts +0 -0
  119. /package/{build → dist}/utils/fonts.d.ts +0 -0
  120. /package/{build → dist}/utils/time.d.ts +0 -0
  121. /package/src/{assets → lib/assets}/picture-in-picture.svg +0 -0
  122. /package/src/{utils → lib/utils}/colors.ts +0 -0
  123. /package/src/{utils → lib/utils}/time.ts +0 -0
@@ -1,251 +0,0 @@
1
- import { DOMAttributes, useContext, useEffect, useMemo, useRef, useState } from 'react'
2
- import { css } from '@emotion/react'
3
-
4
- import { MediaMachineContext } from '../state-machines'
5
- import { MediaPlayerContext } from '../utils/context'
6
- import useScrub from '../utils/use-scrub'
7
- import { fonts } from '../utils/fonts'
8
-
9
- const style = css`
10
- position: relative;
11
- height: .4rem;
12
-
13
- transition: transform .2s ease-in-out;
14
-
15
- margin: 0 12px;
16
- @media (min-width: 768px) {
17
- margin: 0 16px;
18
- }
19
- @media (min-width: 2560px) {
20
- margin: 0 24px;
21
- }
22
-
23
- user-select: none;
24
- cursor: pointer;
25
-
26
- :hover {
27
- .background-bar {
28
- transform: scaleY(1.5);
29
- }
30
- .loaded {
31
- transform: scaleY(1.5);
32
- top: -.1rem;
33
- }
34
- .play-container {
35
- transform: scaleY(1.5);
36
- }
37
- }
38
-
39
- .background-bar {
40
- position: absolute;
41
- inset: 0;
42
- background-color: hsla(0, 100%, 100%, .2);
43
- }
44
-
45
- .cursor-time {
46
- display: flex;
47
- justify-content: center;
48
-
49
- text-shadow: 0 0 4px rgba(0, 0, 0, 1);
50
- ${fonts.bMedium.bold}
51
-
52
- position: absolute;
53
- top: -2.5rem;
54
- width: 5rem;
55
-
56
- margin-left: -2.5rem;
57
- pointer-events: none;
58
- }
59
-
60
- .loaded {
61
- transform-origin: 0 0;
62
- position: absolute;
63
- bottom: 0;
64
- height: .4rem;
65
- width: 100%;
66
- .loaded-part {
67
- transform-origin: 0 0;
68
- bottom: 0;
69
- height: .4rem;
70
- width: 100%;
71
- position: absolute;
72
- background-color: hsla(0, 100%, 100%, .4);
73
- }
74
- }
75
-
76
- .play-container {
77
- position: absolute;
78
- bottom: 0;
79
- width: 100%;
80
- height: .4rem;
81
- }
82
-
83
- .play {
84
- transform-origin: 0 0;
85
- background-color: #f03;
86
- position: absolute;
87
- bottom: 0;
88
- height: .4rem;
89
- width: 100%;
90
- }
91
-
92
- .padding {
93
- position: absolute;
94
- bottom: -7.5px;
95
- height: 2rem;
96
- width: 100%;
97
- }
98
-
99
- .thumbnail {
100
- display: flex;
101
- justify-content: center;
102
-
103
- position: absolute;
104
- top: -17.5rem;
105
- height: calc(25rem * 9/16);
106
- width: 25rem;
107
-
108
- margin-left: -12.5rem;
109
- pointer-events: none;
110
-
111
- img {
112
- border-radius: .4rem;
113
- box-shadow: 0 0 1rem rgba(0, 0, 0, .5);
114
-
115
- width: 100%;
116
- height: 100%;
117
- object-fit: cover;
118
- }
119
- }
120
- `
121
-
122
- export const ProgressBar = () => {
123
- const mediaActor = MediaMachineContext.useActorRef()
124
- const currentTime = MediaMachineContext.useSelector((state) => state.context.media.currentTime)
125
- const duration = MediaMachineContext.useSelector((state) => state.context.media.duration)
126
- const indexes = MediaMachineContext.useSelector((state) => state.context.indexes)
127
- const thumbnails = MediaMachineContext.useSelector((state) => state.context.thumbnails)
128
-
129
- const mediaPlayerContext = useContext(MediaPlayerContext)
130
-
131
- const progressBarRef = useRef<HTMLDivElement>(null)
132
- const { scrub: seekScrub, value: seekScrubValue, setValue: setSeekValue } = useScrub({ ref: progressBarRef })
133
-
134
- const [progressBarHoverTime, setProgressBarOverTime] = useState<number | undefined>(undefined)
135
-
136
- const onProgressBarOver: DOMAttributes<HTMLDivElement>['onMouseMove'] = (ev) => {
137
- const percentage = ev.nativeEvent.offsetX / ev.currentTarget.offsetWidth
138
- const time = percentage * (duration ?? 0)
139
- setProgressBarOverTime(time)
140
- }
141
-
142
- const hideProgressBarTime = () => {
143
- if (!progressBarRef.current) return
144
- setProgressBarOverTime(undefined)
145
- }
146
-
147
- // file download % does not equal video time %, as the video contains sometimes, big headers including fonts, which might be ~50mb
148
- const loadedParts = useMemo(() =>
149
- mediaPlayerContext
150
- .downloadedRanges
151
- ?.map((range, i) => {
152
- if (!mediaPlayerContext.size || !duration) return null
153
- const matchingIndexes = indexes.filter(index => range.startByteOffset <= index.pos && index.pos <= range.endByteOffset)
154
- const firstIndex = matchingIndexes.at(0)
155
- const lastIndex = matchingIndexes.at(-1)
156
- if (!firstIndex || !lastIndex) return null
157
- const start = firstIndex.timestamp / duration
158
- const end = lastIndex.timestamp / duration
159
- const rangeDuration = Number((end - start).toFixed(2)) // to prevent cases like `0.9995140832939585`
160
- const left = start * 100
161
- return (
162
- <div key={i} className="loaded-part" style={{ transform: `scaleX(${rangeDuration})`, marginLeft: `${left}%` }}></div>
163
- )
164
- })
165
- ?? [],
166
- [duration, indexes.length, mediaPlayerContext.downloadedRanges?.map(({ startByteOffset, endByteOffset }) => `${startByteOffset}/${endByteOffset}`).join(',')]
167
- )
168
-
169
- const cusorTimeString = useMemo(() => {
170
- if (!progressBarHoverTime || progressBarHoverTime < 0) return undefined
171
- const hours = Math.floor(progressBarHoverTime! / 3600)
172
- const minutes = Math.floor((progressBarHoverTime! - hours * 3600) / 60)
173
- const seconds = Math.floor(progressBarHoverTime! - hours * 3600 - minutes * 60)
174
- const hoursString =
175
- hours > 0
176
- ? `${hours}:`
177
- : ''
178
- return `${hoursString}${minutes < 10 ? '0' : ''}${minutes}:${seconds < 10 ? '0' : ''}${seconds}`
179
- }, [progressBarHoverTime])
180
-
181
- useEffect(() => {
182
- if (!mediaActor || seekScrubValue === undefined || !duration) return
183
- const timestamp = seekScrubValue * duration
184
- mediaActor.send({ type: 'SET_TIME', value: timestamp })
185
- }, [mediaActor, seekScrubValue, duration])
186
-
187
- const scaleX = useMemo(() => {
188
- return typeof duration !== 'number' || typeof currentTime !== 'number'
189
- ? 0
190
- : 1 / ((duration ?? 0) / (currentTime ?? 0))
191
- }, [duration, currentTime])
192
-
193
- const thumbnail = useMemo(() => {
194
- if (!thumbnails.length || !progressBarHoverTime) return undefined
195
- return (
196
- thumbnails
197
- .find(({ timestamp, duration }) =>
198
- timestamp <= progressBarHoverTime && progressBarHoverTime <= timestamp + duration
199
- )
200
- )
201
- }, [thumbnails.length, progressBarHoverTime])
202
-
203
- return (
204
- <div
205
- css={style}
206
- ref={progressBarRef}
207
- className="progress-bar"
208
- onMouseMove={onProgressBarOver}
209
- onMouseOut={hideProgressBarTime}
210
- >
211
- <div className="background-bar" />
212
- {
213
- progressBarHoverTime
214
- ? (
215
- <div
216
- className="cursor-time"
217
- style={{ left: `clamp(1.8rem, ${1 / ((duration ?? 0) / (progressBarHoverTime ?? 1)) * 100}%, calc(100% - 1.8rem))` }}
218
- >
219
- {cusorTimeString}
220
- </div>
221
- )
222
- : undefined
223
- }
224
- <div className="progress"></div>
225
- {/* bar showing the currently loaded progress */}
226
- <div className="loaded">
227
- {loadedParts}
228
- </div>
229
- {/* <div className="load" style={{ transform: `scaleX(${1 / ((duration ?? 0) / (loadedTime?.[1] ?? 0))})` }}></div> */}
230
- {/* bar to show when hovering to potentially seek */}
231
- <div className="hover"></div>
232
- {/* bar displaying the current playback progress */}
233
- <div className='play-container'>
234
- <div className="play" style={{ transform: `scaleX(${scaleX})` }}></div>
235
- </div>
236
- <div className="chapters"></div>
237
- <div className="scrubber"></div>
238
- <div className="padding" onMouseDown={seekScrub}></div>
239
- <div
240
- className="thumbnail"
241
- style={{ left: `clamp(12.5rem, ${1 / ((duration ?? 0) / (progressBarHoverTime ?? 1)) * 100}%, calc(100% - 12.5rem))` }}
242
- >
243
- {
244
- thumbnail
245
- ? <img src={thumbnail.url}/>
246
- : undefined
247
- }
248
- </div>
249
- </div>
250
- )
251
- }
@@ -1,322 +0,0 @@
1
- import { useEffect, useMemo, useRef, useState } from "react"
2
- import { css } from "@emotion/react"
3
- import { ChevronLeft, ChevronRight, Settings } from "react-feather"
4
-
5
- import { MediaMachineContext } from "../state-machines"
6
- import { TooltipDisplay } from "./tooltip-display"
7
- import { fonts } from "../utils/fonts"
8
- import PlaybackSlider from "./playback-slider"
9
- import useLocalStorage, { booleanType } from "../utils/use-local-storage"
10
-
11
- const style = css`
12
- position: relative;
13
-
14
- .popover {
15
- position: absolute;
16
- right: 50%;
17
- transform: translateX(50%);
18
-
19
- overflow-y: auto;
20
- width: 180px;
21
- height: 160px;
22
- top: -180px;
23
- @media (min-width: 768px) {
24
- width: 250px;
25
- height: 160px;
26
- }
27
-
28
- display: flex;
29
- flex-direction: column;
30
-
31
- border-radius: 8px;
32
- background-color: rgba(28,28,28,0.95);
33
- ${fonts.bMedium.regular}
34
-
35
- z-index: 4;
36
-
37
- > div {
38
- display: flex;
39
- align-items: center;
40
- justify-content: space-between;
41
-
42
- color: #fff;
43
- outline: none;
44
-
45
- padding: 8px 6px 8px 12px;
46
-
47
- width: 100%;
48
-
49
- :first-of-type {
50
- border-radius: 8px 8px 0 0;
51
- }
52
- :last-of-type {
53
- border-radius: 0 0 8px 8px;
54
- }
55
- :not(&.no-hover) {
56
- cursor: pointer;
57
- }
58
- :not(&.no-hover):hover {
59
- background-color: rgba(255,255,255,.1);
60
- }
61
-
62
- > div {
63
- display: flex;
64
- align-items: center;
65
- justify-content: center;
66
-
67
- .secondary {
68
- color: #eee;
69
- ${fonts.bSmall.regular}
70
- }
71
- }
72
- }
73
-
74
- .back {
75
- display: flex;
76
- align-items: center;
77
- justify-content: flex-start;
78
- border-bottom: 1px solid #4D4D4E;
79
-
80
- svg {
81
- transform: translateX(-4px);
82
- transition: transform 0.2s ease-in-out;
83
- }
84
-
85
- :hover {
86
- svg {
87
- transform: translateX(-8px);
88
- }
89
- }
90
- }
91
- }
92
-
93
- .playback-rate {
94
- .slider {
95
- width: 100%;
96
- }
97
- .options {
98
- display: grid;
99
- grid-template-columns: 1fr 1fr 1fr 1fr;
100
- align-items: center;
101
-
102
- padding: 8px 12px;
103
-
104
- > div {
105
- display: flex;
106
- align-items: center;
107
- border-radius: 4px;
108
-
109
- padding: 8px 6px;
110
- height: 100%;
111
-
112
- cursor: pointer;
113
- :hover {
114
- background-color: rgba(255,255,255,.1);
115
- }
116
- }
117
- }
118
- }
119
-
120
- .subtitle {
121
- .description {
122
- word-break: break-word;
123
- }
124
- }
125
- `
126
-
127
- enum PopoverContent {
128
- Default,
129
- PlaybackRate,
130
- Advanced,
131
- SelectNewSources,
132
- Subtitles
133
- }
134
-
135
- const SettingsAction = () => {
136
- const mediaActor = MediaMachineContext.useActorRef()
137
- const playbackRate = MediaMachineContext.useSelector((state) => state.context.media.playbackRate)
138
- const subtitleStreams = MediaMachineContext.useSelector((state) => state.context.subtitleStreams)
139
- const selectedSubtitleStreamIndex = MediaMachineContext.useSelector((state) => state.context.selectedSubtitleStreamIndex)
140
- const [hideMediaStats, setHideMediaStats] = useLocalStorage('hideMediaStats', 'false') as [booleanType, (newValue: booleanType) => void]
141
-
142
- const [isOpenPopover, setIsOpenPopover] = useState(false)
143
- const [popoverContent, setPopoverContent] = useState(PopoverContent.Default)
144
- const settingsContainerRef = useRef<HTMLDivElement>(null)
145
-
146
- const togglePopover = () => {
147
- setIsOpenPopover(!isOpenPopover)
148
- setPopoverContent(PopoverContent.Default)
149
- }
150
-
151
- const setPlaybackRate = (rate: number) => {
152
- mediaActor.send({ type: 'SET_PLAYBACK_RATE', playbackRate: rate })
153
- setPopoverContent(PopoverContent.Default)
154
- }
155
-
156
- useEffect(() => {
157
- const handleClickOutside = (ev: MouseEvent) => {
158
- if (
159
- isOpenPopover &&
160
- settingsContainerRef.current &&
161
- !settingsContainerRef.current.contains(ev.target as Node)
162
- ) {
163
- togglePopover()
164
- }
165
- }
166
- document.addEventListener('mousedown', handleClickOutside)
167
- return () => document.removeEventListener('mousedown', handleClickOutside)
168
- }, [isOpenPopover])
169
-
170
- const languagesWithStreamIndex = useMemo(
171
- () => subtitleStreams.map(({ header }) => ({ streamIndex: header.streamIndex, language: header.parsed.info.Title })),
172
- [subtitleStreams.length]
173
- )
174
-
175
- const setLanguage = (languageWithStreamIndex: { streamIndex: number | undefined, language?: string }) => () => {
176
- mediaActor.send({ type: 'SELECT_SUBTITLE_STREAM', streamIndex: languageWithStreamIndex.streamIndex })
177
- togglePopover()
178
- }
179
-
180
- const changePopoverContent = (newPopoverContent: PopoverContent) => () => {
181
- setPopoverContent(newPopoverContent)
182
- }
183
-
184
- return (
185
- <div css={style} ref={settingsContainerRef}>
186
- <TooltipDisplay
187
- id='settings'
188
- disabled={isOpenPopover}
189
- text={
190
- <button
191
- className='settings'
192
- type='button'
193
- onClick={togglePopover}
194
- >
195
- <Settings />
196
- </button>
197
- }
198
- toolTipText={
199
- <span>
200
- Settings
201
- </span>
202
- }
203
- />
204
- {
205
- isOpenPopover && popoverContent === PopoverContent.Default && (
206
- <div className='popover menu'>
207
- <div onClick={changePopoverContent(PopoverContent.Advanced)}>
208
- <span>Advanced</span>
209
- <div>
210
- <ChevronRight />
211
- </div>
212
- </div>
213
- {/* <div onClick={changePopoverContent(PopoverContent.SelectNewSources)}>
214
- <div>Select new sources</div>
215
- <div>
216
- <ChevronRight />
217
- </div>
218
- </div> */}
219
- <div onClick={changePopoverContent(PopoverContent.Subtitles)}>
220
- <div>Subtitles</div>
221
- <div>
222
- <ChevronRight />
223
- </div>
224
- </div>
225
- <div onClick={changePopoverContent(PopoverContent.PlaybackRate)}>
226
- <div>Playback speed</div>
227
- <div>
228
- <span className='secondary'>
229
- {
230
- playbackRate === 1
231
- ? '(Default)'
232
- : playbackRate.toFixed(2)
233
- }
234
- </span>
235
- <ChevronRight />
236
- </div>
237
- </div>
238
- </div>
239
- )
240
- }
241
- {
242
- isOpenPopover && popoverContent === PopoverContent.PlaybackRate && (
243
- <div className='popover playback-rate'>
244
- <div className="back" onClick={changePopoverContent(PopoverContent.Default)}>
245
- <ChevronLeft />
246
- <span>Plackback speed</span>
247
- </div>
248
- <div className="slider no-hover">
249
- <PlaybackSlider />
250
- </div>
251
- <div className="options no-hover">
252
- <div onClick={() => setPlaybackRate(0.5)}>
253
- 0.5x
254
- </div>
255
- <div onClick={() => setPlaybackRate(1)}>
256
- 1x
257
- </div>
258
- <div onClick={() => setPlaybackRate(1.5)}>
259
- 1.5x
260
- </div>
261
- <div onClick={() => setPlaybackRate(2)}>
262
- 2x
263
- </div>
264
- </div>
265
- </div>
266
- )
267
- }
268
- {
269
- isOpenPopover && popoverContent === PopoverContent.Advanced && (
270
- <div className='popover advanced'>
271
- <div className="back" onClick={changePopoverContent(PopoverContent.Default)}>
272
- <ChevronLeft />
273
- <span>Advanced</span>
274
- </div>
275
- <div onClick={() => setHideMediaStats(hideMediaStats === 'true' ? 'false' : 'true')}>
276
- <span>Hide stats</span>
277
- <span>{hideMediaStats === 'true' ? '✓' : ''}</span>
278
- </div>
279
- </div>
280
- )
281
- }
282
- {
283
- isOpenPopover && popoverContent === PopoverContent.SelectNewSources && (
284
- <div className='popover sources'>
285
- <div className="back" onClick={changePopoverContent(PopoverContent.Default)}>
286
- <ChevronLeft />
287
- <span>Select new sources</span>
288
- </div>
289
- </div>
290
- )
291
- }
292
- {
293
- isOpenPopover && popoverContent === PopoverContent.Subtitles && (
294
- <div className='popover subtitle'>
295
- <div className="back" onClick={changePopoverContent(PopoverContent.Default)}>
296
- <ChevronLeft />
297
- <span>Subtitles</span>
298
- </div>
299
- <div onClick={setLanguage({ streamIndex: undefined })}>
300
- <span>Disable</span>
301
- <span>{selectedSubtitleStreamIndex === undefined ? '✓' : ''}</span>
302
- </div>
303
- {
304
- languagesWithStreamIndex.map((languageWithStreamIndex) => (
305
- <div
306
- key={languageWithStreamIndex.streamIndex}
307
- onClick={setLanguage(languageWithStreamIndex)}
308
- className="description"
309
- >
310
- <span>{languageWithStreamIndex.language}</span>
311
- <span>{selectedSubtitleStreamIndex === languageWithStreamIndex.streamIndex ? '✓' : ''}</span>
312
- </div>
313
- ))
314
- }
315
- </div>
316
- )
317
- }
318
- </div>
319
- )
320
- }
321
-
322
- export default SettingsAction
@@ -1,101 +0,0 @@
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
- export default Sound