@banou/media-player 0.2.6 → 0.4.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 +0 -10
- package/build/components/chrome.d.ts +4 -0
- package/build/components/control-bar.d.ts +5 -0
- package/build/components/overlay.d.ts +1 -0
- package/build/components/progress-bar.d.ts +1 -0
- package/build/components/settings.d.ts +2 -0
- package/build/components/sound.d.ts +4 -0
- package/build/components/tooltip-display.d.ts +20 -0
- package/build/components/volume-slider.d.ts +6 -0
- package/build/index.d.ts +23 -48
- package/build/index.js +2593 -2230
- package/build/main.d.ts +1 -1
- package/build/state-machines/data-source.d.ts +27 -0
- package/build/state-machines/index.d.ts +33185 -0
- package/build/state-machines/media-properties.d.ts +45 -0
- package/build/state-machines/media-source.d.ts +34 -0
- package/build/state-machines/media.d.ts +8344 -0
- package/build/state-machines/subtitles.d.ts +53 -0
- package/build/state-machines/thumbnails.d.ts +24 -0
- package/build/state-machines/utils.d.ts +26 -0
- package/build/utils/actor-utils.d.ts +2 -0
- package/build/utils/colors.d.ts +8 -0
- package/build/utils/context.d.ts +14 -0
- package/build/utils/fonts.d.ts +28 -0
- package/build/{utils.d.ts → utils/index.d.ts} +4 -12
- package/build/utils/languages.d.ts +260 -0
- package/build/{mp4box.d.ts → utils/mp4box.d.ts} +61 -61
- package/build/utils/time.d.ts +2 -0
- package/build/utils/use-local-storage.d.ts +3 -0
- package/build/{use-scrub.d.ts → utils/use-scrub.d.ts} +11 -10
- package/build/utils/volume-utils.d.ts +17 -0
- package/build/utils/window-height.d.ts +2 -0
- package/package.json +15 -12
- package/src/assets/picture-in-picture.svg +5 -0
- package/src/components/chrome.tsx +89 -0
- package/src/components/control-bar.tsx +330 -0
- package/src/components/overlay.tsx +28 -0
- package/src/components/progress-bar.tsx +252 -0
- package/src/components/settings.tsx +269 -0
- package/src/components/sound.tsx +101 -0
- package/src/components/tooltip-display.tsx +83 -0
- package/src/components/volume-slider.tsx +156 -0
- package/src/index.tsx +145 -434
- package/src/main.tsx +66 -39
- package/src/state-machines/data-source.ts +83 -0
- package/src/state-machines/index.ts +5 -0
- package/src/state-machines/media-properties.ts +78 -0
- package/src/state-machines/media-source.ts +129 -0
- package/src/state-machines/media.ts +245 -0
- package/src/state-machines/subtitles.ts +247 -0
- package/src/state-machines/thumbnails.ts +123 -0
- package/src/state-machines/utils.ts +94 -0
- package/src/utils/actor-utils.ts +19 -0
- package/src/utils/colors.ts +9 -0
- package/src/utils/context.ts +23 -0
- package/src/utils/fonts.ts +156 -0
- package/src/{utils.ts → utils/index.ts} +2 -26
- package/src/utils/time.ts +16 -0
- package/src/utils/use-local-storage.ts +30 -0
- package/src/{use-scrub.ts → utils/use-scrub.ts} +2 -1
- package/src/utils/volume-utils.ts +39 -0
- package/src/utils/window-height.ts +19 -0
- package/src/vite-env.d.ts +1 -1
- package/tsconfig.json +4 -2
- package/tsconfig.node.json +9 -9
- package/build/chrome/bottom.d.ts +0 -20
- package/build/chrome/index.d.ts +0 -27
- package/build/chrome/overlay.d.ts +0 -9
- package/build/languages.d.ts +0 -260
- package/build/use-local-storage.d.ts +0 -6
- package/src/chrome/bottom.tsx +0 -651
- package/src/chrome/index.tsx +0 -199
- package/src/chrome/overlay.tsx +0 -104
- package/src/use-local-storage.ts +0 -32
- /package/src/{languages.ts → utils/languages.ts} +0 -0
- /package/src/{mp4box.ts → utils/mp4box.ts} +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { AnyActorSystem } from 'xstate/dist/declarations/src/system'
|
|
2
|
+
import type { AnyEventObject, CallbackActorLogic, CallbackActorRef, EventObject, NonReducibleUnknown } from 'xstate'
|
|
3
|
+
|
|
4
|
+
import PQueue from 'p-queue'
|
|
5
|
+
import { fromCallback } from 'xstate'
|
|
6
|
+
|
|
7
|
+
type Receiver<TEvent extends EventObject> = (listener: {
|
|
8
|
+
bivarianceHack(event: TEvent): void
|
|
9
|
+
}['bivarianceHack']) => void
|
|
10
|
+
|
|
11
|
+
export type CallbackLogicFunction<TEvent extends EventObject = AnyEventObject, TSentEvent extends EventObject = AnyEventObject, TInput = NonReducibleUnknown, TEmitted extends EventObject = EventObject> = ({ input, system, self, sendBack, receive, emit }: {
|
|
12
|
+
input: TInput
|
|
13
|
+
system: AnyActorSystem
|
|
14
|
+
self: CallbackActorRef<TEvent>
|
|
15
|
+
sendBack: (event: TSentEvent) => void
|
|
16
|
+
receive: Receiver<TEvent>
|
|
17
|
+
emit: (emitted: TEmitted) => void
|
|
18
|
+
}) => Promise<(() => void) | void>
|
|
19
|
+
|
|
20
|
+
type FromAsyncCallback = <TEvent extends EventObject, TInput = NonReducibleUnknown, TEmitted extends EventObject = EventObject>(callback: CallbackLogicFunction<TEvent, AnyEventObject, TInput, TEmitted>) => CallbackActorLogic<TEvent, TInput, TEmitted>
|
|
21
|
+
|
|
22
|
+
export const fromAsyncCallback =
|
|
23
|
+
((callback: (...args: any[]) => Promise<() => any>) =>
|
|
24
|
+
fromCallback((...args: any[]) => {
|
|
25
|
+
const callbackPromise = callback(...args)
|
|
26
|
+
return () => {
|
|
27
|
+
callbackPromise
|
|
28
|
+
.then(callbackResult => callbackResult?.())
|
|
29
|
+
}
|
|
30
|
+
})) as FromAsyncCallback
|
|
31
|
+
|
|
32
|
+
export const getTimeRanges = (sourceBuffer: SourceBuffer) =>
|
|
33
|
+
Array(sourceBuffer.buffered.length)
|
|
34
|
+
.fill(undefined)
|
|
35
|
+
.map((_, index) => ({
|
|
36
|
+
index,
|
|
37
|
+
start: sourceBuffer.buffered.start(index),
|
|
38
|
+
end: sourceBuffer.buffered.end(index)
|
|
39
|
+
}))
|
|
40
|
+
|
|
41
|
+
const setupListeners = (sourceBuffer: SourceBuffer, resolve: (value: Event) => void, reject: (reason: Event) => void) => {
|
|
42
|
+
const updateEndListener = (ev: Event) => {
|
|
43
|
+
resolve(ev)
|
|
44
|
+
unregisterListeners()
|
|
45
|
+
}
|
|
46
|
+
const abortListener = (ev: Event) => {
|
|
47
|
+
resolve(ev)
|
|
48
|
+
unregisterListeners()
|
|
49
|
+
}
|
|
50
|
+
const errorListener = (ev: Event) => {
|
|
51
|
+
console.error(ev)
|
|
52
|
+
reject(ev)
|
|
53
|
+
unregisterListeners()
|
|
54
|
+
}
|
|
55
|
+
const unregisterListeners = () => {
|
|
56
|
+
sourceBuffer.removeEventListener('updateend', updateEndListener)
|
|
57
|
+
sourceBuffer.removeEventListener('abort', abortListener)
|
|
58
|
+
sourceBuffer.removeEventListener('error', errorListener)
|
|
59
|
+
}
|
|
60
|
+
sourceBuffer.addEventListener('updateend', updateEndListener, { once: true })
|
|
61
|
+
sourceBuffer.addEventListener('abort', abortListener, { once: true })
|
|
62
|
+
sourceBuffer.addEventListener('error', errorListener, { once: true })
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const updateSourceBuffer = (sourceBuffer: SourceBuffer) => {
|
|
66
|
+
const queue = new PQueue({ concurrency: 1 })
|
|
67
|
+
|
|
68
|
+
const appendBuffer = (buffer: ArrayBuffer) =>
|
|
69
|
+
queue.add(() =>
|
|
70
|
+
new Promise<Event>((resolve, reject) => {
|
|
71
|
+
setupListeners(sourceBuffer, resolve, reject)
|
|
72
|
+
sourceBuffer.appendBuffer(buffer)
|
|
73
|
+
})
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const unbufferRange = async (start: number, end: number) =>
|
|
77
|
+
queue.add(() =>
|
|
78
|
+
new Promise((resolve, reject) => {
|
|
79
|
+
setupListeners(sourceBuffer, resolve, reject)
|
|
80
|
+
sourceBuffer.remove(start, end)
|
|
81
|
+
})
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
const updateTimestampOffset = (timestampOffset: number) =>
|
|
85
|
+
queue.add(() => {
|
|
86
|
+
sourceBuffer.timestampOffset = timestampOffset
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
appendBuffer,
|
|
91
|
+
unbufferRange,
|
|
92
|
+
updateTimestampOffset
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Actor, AnyActorLogic } from "xstate"
|
|
2
|
+
|
|
3
|
+
export const togglePlay = (
|
|
4
|
+
mediaActor: Actor<AnyActorLogic>,
|
|
5
|
+
isPaused: boolean,
|
|
6
|
+
duration: number | undefined,
|
|
7
|
+
currentTime: number
|
|
8
|
+
) => {
|
|
9
|
+
if (!mediaActor) throw new Error('Media actor not found')
|
|
10
|
+
if (!duration) throw new Error('Duration not found')
|
|
11
|
+
if (duration === currentTime) {
|
|
12
|
+
mediaActor.send({ type: 'SET_TIME', value: 0 })
|
|
13
|
+
mediaActor.send({ type: 'PLAY' })
|
|
14
|
+
} else if (isPaused) {
|
|
15
|
+
mediaActor.send({ type: 'PLAY' })
|
|
16
|
+
} else {
|
|
17
|
+
mediaActor.send({ type: 'PAUSE' })
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createContext } from 'react'
|
|
2
|
+
|
|
3
|
+
export type DownloadedRange =
|
|
4
|
+
// | {
|
|
5
|
+
// startTimestamp: number
|
|
6
|
+
// endTimestamp: number
|
|
7
|
+
// }
|
|
8
|
+
| {
|
|
9
|
+
startByteOffset: number
|
|
10
|
+
endByteOffset: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type MediaPlayerContextType = {
|
|
14
|
+
videoElement?: HTMLVideoElement
|
|
15
|
+
title?: string
|
|
16
|
+
subtitle?: string
|
|
17
|
+
size?: number
|
|
18
|
+
downloadedRanges?: DownloadedRange[]
|
|
19
|
+
hideUI: boolean
|
|
20
|
+
update: (context: Omit<MediaPlayerContextType, 'update'>) => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const MediaPlayerContext = createContext<MediaPlayerContextType>({} as MediaPlayerContextType)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
export const fonts = {
|
|
2
|
+
headings: {
|
|
3
|
+
large: `
|
|
4
|
+
font-weight: 600;
|
|
5
|
+
font-size: 2.8rem;
|
|
6
|
+
line-height: 3.4rem;
|
|
7
|
+
@media (min-width: 960px) {
|
|
8
|
+
font-size: 3.4rem;
|
|
9
|
+
line-height: 4.1rem;
|
|
10
|
+
}
|
|
11
|
+
`,
|
|
12
|
+
medium: `
|
|
13
|
+
font-weight: 600;
|
|
14
|
+
font-size: 2.4rem;
|
|
15
|
+
line-height: 2.9rem;
|
|
16
|
+
@media (min-width: 960px) {
|
|
17
|
+
font-size: 2.8rem;
|
|
18
|
+
line-height: 3.4rem;
|
|
19
|
+
}
|
|
20
|
+
`,
|
|
21
|
+
small: `
|
|
22
|
+
font-weight: 500;
|
|
23
|
+
font-size: 1.8rem;
|
|
24
|
+
line-height: 1.9rem;
|
|
25
|
+
@media (min-width: 960px) {
|
|
26
|
+
font-size: 1.8rem;
|
|
27
|
+
line-height: 2.2rem;
|
|
28
|
+
}
|
|
29
|
+
`,
|
|
30
|
+
extraSmall: `
|
|
31
|
+
font-weight: 500;
|
|
32
|
+
font-size: 1.4rem;
|
|
33
|
+
line-height: 1.7rem;
|
|
34
|
+
@media (min-width: 960px) {
|
|
35
|
+
font-size: 1.6rem;
|
|
36
|
+
line-height: 1.9rem;
|
|
37
|
+
}
|
|
38
|
+
`
|
|
39
|
+
},
|
|
40
|
+
bLarge: {
|
|
41
|
+
bold: `
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
font-size: 1.4rem;
|
|
44
|
+
line-height: 2rem;
|
|
45
|
+
@media (min-width: 960px) {
|
|
46
|
+
font-size: 1.6rem;
|
|
47
|
+
line-height: 2.2rem;
|
|
48
|
+
}
|
|
49
|
+
`,
|
|
50
|
+
medium: `
|
|
51
|
+
font-weight: 500;
|
|
52
|
+
font-size: 1.4rem;
|
|
53
|
+
line-height: 2rem;
|
|
54
|
+
@media (min-width: 960px) {
|
|
55
|
+
font-size: 1.6rem;
|
|
56
|
+
line-height: 2.2rem;
|
|
57
|
+
}
|
|
58
|
+
`,
|
|
59
|
+
regular: `
|
|
60
|
+
font-weight: 400;
|
|
61
|
+
font-size: 1.4rem;
|
|
62
|
+
line-height: 2rem;
|
|
63
|
+
@media (min-width: 960px) {
|
|
64
|
+
font-size: 1.6rem;
|
|
65
|
+
line-height: 2.2rem;
|
|
66
|
+
}
|
|
67
|
+
`,
|
|
68
|
+
},
|
|
69
|
+
bMedium: {
|
|
70
|
+
bold: `
|
|
71
|
+
font-weight: 600;
|
|
72
|
+
font-size: 1.2rem;
|
|
73
|
+
line-height: 1.7rem;
|
|
74
|
+
@media (min-width: 960px) {
|
|
75
|
+
font-size: 1.4rem;
|
|
76
|
+
line-height: 2rem;
|
|
77
|
+
}
|
|
78
|
+
`,
|
|
79
|
+
medium: `
|
|
80
|
+
font-weight: 500;
|
|
81
|
+
font-size: 1.2rem;
|
|
82
|
+
line-height: 1.7rem;
|
|
83
|
+
@media (min-width: 960px) {
|
|
84
|
+
font-size: 1.4rem;
|
|
85
|
+
line-height: 2rem;
|
|
86
|
+
}
|
|
87
|
+
`,
|
|
88
|
+
regular: `
|
|
89
|
+
font-weight: 400;
|
|
90
|
+
font-size: 1.2rem;
|
|
91
|
+
line-height: 1.7rem;
|
|
92
|
+
@media (min-width: 960px) {
|
|
93
|
+
font-size: 1.4rem;
|
|
94
|
+
line-height: 2rem;
|
|
95
|
+
}
|
|
96
|
+
`,
|
|
97
|
+
},
|
|
98
|
+
bSmall: {
|
|
99
|
+
bold: `
|
|
100
|
+
font-weight: 600;
|
|
101
|
+
font-size: 1rem;
|
|
102
|
+
line-height: 1.4rem;
|
|
103
|
+
@media (min-width: 960px) {
|
|
104
|
+
font-size: 1.2rem;
|
|
105
|
+
line-height: 1.7rem;
|
|
106
|
+
}
|
|
107
|
+
`,
|
|
108
|
+
medium: `
|
|
109
|
+
font-weight: 500;
|
|
110
|
+
font-size: 1rem;
|
|
111
|
+
line-height: 1.4rem;
|
|
112
|
+
@media (min-width: 960px) {
|
|
113
|
+
font-size: 1.2rem;
|
|
114
|
+
line-height: 1.7rem;
|
|
115
|
+
}
|
|
116
|
+
`,
|
|
117
|
+
regular: `
|
|
118
|
+
font-weight: 400;
|
|
119
|
+
font-size: 1rem;
|
|
120
|
+
line-height: 1.4rem;
|
|
121
|
+
@media (min-width: 960px) {
|
|
122
|
+
font-size: 1.2rem;
|
|
123
|
+
line-height: 1.7rem;
|
|
124
|
+
}
|
|
125
|
+
`,
|
|
126
|
+
},
|
|
127
|
+
bExtraSmall: {
|
|
128
|
+
bold: `
|
|
129
|
+
font-weight: 600;
|
|
130
|
+
font-size: 0.8rem;
|
|
131
|
+
line-height: 1.1rem;
|
|
132
|
+
@media (min-width: 960px) {
|
|
133
|
+
font-size: 1rem;
|
|
134
|
+
line-height: 1.4rem;
|
|
135
|
+
}
|
|
136
|
+
`,
|
|
137
|
+
medium: `
|
|
138
|
+
font-weight: 500;
|
|
139
|
+
font-size: 0.8rem;
|
|
140
|
+
line-height: 1.1rem;
|
|
141
|
+
@media (min-width: 960px) {
|
|
142
|
+
font-size: 1rem;
|
|
143
|
+
line-height: 1.4rem;
|
|
144
|
+
}
|
|
145
|
+
`,
|
|
146
|
+
regular: `
|
|
147
|
+
font-weight: 400;
|
|
148
|
+
font-size: 0.8rem;
|
|
149
|
+
line-height: 1.1rem;
|
|
150
|
+
@media (min-width: 960px) {
|
|
151
|
+
font-size: 1rem;
|
|
152
|
+
line-height: 1.4rem;
|
|
153
|
+
}
|
|
154
|
+
`,
|
|
155
|
+
},
|
|
156
|
+
}
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
export type Chunk = {
|
|
2
|
-
offset: number
|
|
3
|
-
buffer: Uint8Array
|
|
4
|
-
pts: number
|
|
5
|
-
duration: number
|
|
6
|
-
pos: number
|
|
7
|
-
buffered: boolean
|
|
8
|
-
}
|
|
9
1
|
|
|
10
2
|
export function debounceImmediateAndLatest<T extends (...args: any[]) => any>(
|
|
11
3
|
wait: number,
|
|
@@ -40,7 +32,7 @@ export function debounceImmediateAndLatest<T extends (...args: any[]) => any>(
|
|
|
40
32
|
return debouncedFunction as T;
|
|
41
33
|
}
|
|
42
34
|
|
|
43
|
-
export const
|
|
35
|
+
export const queuedThrottleWithLastCall = <T2 extends any[], T extends (...args: T2) => any>(time: number, func: T) => {
|
|
44
36
|
let runningFunction: Promise<ReturnType<T>> | undefined
|
|
45
37
|
let lastCall: Promise<ReturnType<T>> | undefined
|
|
46
38
|
let lastCallArguments: T2 | undefined
|
|
@@ -117,22 +109,6 @@ export const queuedDebounceWithLastCall = <T2 extends any[], T extends (...args:
|
|
|
117
109
|
}
|
|
118
110
|
}
|
|
119
111
|
|
|
120
|
-
const getTimeRanges = (sourceBuffer: SourceBuffer) =>
|
|
121
|
-
Array(sourceBuffer.buffered.length)
|
|
122
|
-
.fill(undefined)
|
|
123
|
-
.map((_, index) => ({
|
|
124
|
-
index,
|
|
125
|
-
start: sourceBuffer.buffered.start(index),
|
|
126
|
-
end: sourceBuffer.buffered.end(index)
|
|
127
|
-
}))
|
|
128
|
-
|
|
129
|
-
const getTimeRange =
|
|
130
|
-
(sourceBuffer: SourceBuffer) =>
|
|
131
|
-
(time: number) =>
|
|
132
|
-
getTimeRanges(sourceBuffer)
|
|
133
|
-
.find(({ start, end }) => time >= start && time <= end)
|
|
134
|
-
|
|
135
|
-
|
|
136
112
|
// todo: reimplement this into a ReadableByteStream https://web.dev/streams/ once Safari gets support
|
|
137
113
|
export const toStreamChunkSize = (SIZE: number) => (stream: ReadableStream) =>
|
|
138
114
|
new ReadableStream<Uint8Array>({
|
|
@@ -176,7 +152,7 @@ export const toStreamChunkSize = (SIZE: number) => (stream: ReadableStream) =>
|
|
|
176
152
|
if (done) controller.close()
|
|
177
153
|
},
|
|
178
154
|
cancel() {
|
|
179
|
-
this.reader
|
|
155
|
+
this.reader?.cancel()
|
|
180
156
|
this.leftOverData = undefined
|
|
181
157
|
}
|
|
182
158
|
} as UnderlyingDefaultSource<Uint8Array> & {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const formatTime = (time: number | undefined): string => {
|
|
2
|
+
if (time === undefined) return '0:00'
|
|
3
|
+
|
|
4
|
+
const hours = Math.floor(time / 3600)
|
|
5
|
+
const minutes = Math.floor((time % 3600) / 60)
|
|
6
|
+
const seconds = Math.floor(time % 60)
|
|
7
|
+
// If there are hours, include them in the format
|
|
8
|
+
if (hours > 0) {
|
|
9
|
+
return `${hours}:${minutes < 10 ? '0' : ''}${minutes}:${seconds < 10 ? '0' : ''}${seconds}`
|
|
10
|
+
}
|
|
11
|
+
return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const formatMediaTime = (currentTime: number, duration: number | undefined): string => {
|
|
15
|
+
return `${formatTime(currentTime)} / ${formatTime(duration)}`
|
|
16
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export const useLocalStorage = <T,>(name: string, defaultValue?: T) => {
|
|
4
|
+
const [storageValue, setStorageValue] = useState(() => localStorage.getItem(name) ?? defaultValue)
|
|
5
|
+
|
|
6
|
+
const updateStorage = (newValue: string) => {
|
|
7
|
+
localStorage.setItem(name, newValue)
|
|
8
|
+
setStorageValue(newValue)
|
|
9
|
+
window.dispatchEvent(new Event('storage'))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const syncLocalStorage = () => {
|
|
14
|
+
const item = localStorage.getItem(name)
|
|
15
|
+
setStorageValue(item ?? defaultValue)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
window.addEventListener('storage', syncLocalStorage)
|
|
19
|
+
return () => {
|
|
20
|
+
window.removeEventListener('storage', syncLocalStorage)
|
|
21
|
+
}
|
|
22
|
+
}, [name])
|
|
23
|
+
|
|
24
|
+
return [storageValue, updateStorage] as const
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default useLocalStorage
|
|
28
|
+
|
|
29
|
+
export type mediaMutedType = 'true' | 'false' | undefined
|
|
30
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Constants for logarithmic scaling
|
|
2
|
+
const MIN_VOLUME = 0.0001 // Avoid zero which would give -Infinity when taking log
|
|
3
|
+
const MAX_VOLUME = 1.0
|
|
4
|
+
|
|
5
|
+
// Defines how "curved" the logarithmic response is
|
|
6
|
+
// Higher values = more dramatic volume change at the low end
|
|
7
|
+
export const VOLUME_EXPONENT = 2.0
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Converts a linear slider value (0-1) to logarithmic volume scale
|
|
11
|
+
* Uses formula: volume = minVolume * (maxVolume/minVolume)^position
|
|
12
|
+
*
|
|
13
|
+
* @param linearValue Linear slider position (0-1)
|
|
14
|
+
* @returns Logarithmic volume value (0-1)
|
|
15
|
+
*/
|
|
16
|
+
export const linearToLogVolume = (linearValue: number): number => {
|
|
17
|
+
// Ensure value is within range
|
|
18
|
+
const normalizedLinear = Math.max(MIN_VOLUME, Math.min(MAX_VOLUME, linearValue))
|
|
19
|
+
|
|
20
|
+
// Calculate using the formula: volume = minVolume * (maxVolume/minVolume)^position
|
|
21
|
+
// We can also use a simpler formula: Math.pow(normalizedLinear, VOLUME_EXPONENT)
|
|
22
|
+
// Which gives a good perceptual curve with proper scaling
|
|
23
|
+
return Math.pow(normalizedLinear, VOLUME_EXPONENT)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Converts a logarithmic volume (0-1) back to linear slider position (0-1)
|
|
28
|
+
* Inverse of the logarithmic formula
|
|
29
|
+
*
|
|
30
|
+
* @param logVolume Logarithmic volume (0-1)
|
|
31
|
+
* @returns Linear slider position (0-1)
|
|
32
|
+
*/
|
|
33
|
+
export const logToLinearVolume = (logVolume: number): number => {
|
|
34
|
+
// Ensure value is within range
|
|
35
|
+
const normalizedLog = Math.max(MIN_VOLUME, Math.min(MAX_VOLUME, logVolume))
|
|
36
|
+
|
|
37
|
+
// Inverse of our simplified formula
|
|
38
|
+
return Math.pow(normalizedLog, MAX_VOLUME/VOLUME_EXPONENT)
|
|
39
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
const useWindowSize = () => {
|
|
4
|
+
const [windowHeight, setWindowHeight] = useState(window.innerHeight)
|
|
5
|
+
const [windowWidth, setWindowWidth] = useState(window.innerWidth)
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const handleResize = () => {
|
|
8
|
+
setWindowHeight(window.innerHeight)
|
|
9
|
+
setWindowWidth(window.innerWidth)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
window.addEventListener('resize', handleResize)
|
|
13
|
+
|
|
14
|
+
return () => { window.removeEventListener('resize', handleResize) }
|
|
15
|
+
}, [])
|
|
16
|
+
return [windowHeight, windowWidth]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default useWindowSize
|
package/src/vite-env.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
|
1
|
+
/// <reference types="vite/client" />
|
package/tsconfig.json
CHANGED
|
@@ -18,9 +18,11 @@
|
|
|
18
18
|
"emitDeclarationOnly": true,
|
|
19
19
|
"jsx": "react-jsx",
|
|
20
20
|
"paths": {
|
|
21
|
-
"mp4box": ["./src/mp4box.ts"]
|
|
22
|
-
}
|
|
21
|
+
"mp4box": ["./src/utils/mp4box.ts"]
|
|
22
|
+
},
|
|
23
|
+
"types": []
|
|
23
24
|
},
|
|
24
25
|
"include": ["src"],
|
|
26
|
+
"exclude": ["node_modules"],
|
|
25
27
|
"references": [{ "path": "./tsconfig.node.json" }]
|
|
26
28
|
}
|
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
|
+
}
|
package/build/chrome/bottom.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { Dispatch, HTMLAttributes, SetStateAction } from 'react';
|
|
2
|
-
import type { ChromeOptions } from '.';
|
|
3
|
-
import type { FKNVideoControl, Subtitle, TransmuxError } from '..';
|
|
4
|
-
export type BottomOptions = {
|
|
5
|
-
duration?: number;
|
|
6
|
-
currentTime?: number;
|
|
7
|
-
togglePlay: () => void;
|
|
8
|
-
isSubtitleMenuHidden: boolean;
|
|
9
|
-
setIsSubtitleMenuHidden: Dispatch<SetStateAction<boolean>>;
|
|
10
|
-
isErrorMenuHidden: boolean;
|
|
11
|
-
setIsErrorMenuHidden: Dispatch<SetStateAction<boolean>>;
|
|
12
|
-
setCurrentSubtitleTrack: Dispatch<SetStateAction<number | undefined>>;
|
|
13
|
-
isFullscreen: boolean;
|
|
14
|
-
toggleFullscreen: () => void;
|
|
15
|
-
subtitleTrack: Subtitle | undefined;
|
|
16
|
-
errors: TransmuxError[];
|
|
17
|
-
customControls?: FKNVideoControl[];
|
|
18
|
-
} & Pick<ChromeOptions, 'seek' | 'setVolume' | 'loadedTime' | 'isPlaying' | 'tracks' | 'pictureInPicture'> & HTMLAttributes<HTMLDivElement>;
|
|
19
|
-
declare const _default: ({ duration, currentTime, togglePlay, seek, setVolume, isSubtitleMenuHidden, setIsSubtitleMenuHidden, isErrorMenuHidden, setIsErrorMenuHidden, setCurrentSubtitleTrack, loadedTime, isPlaying, tracks, isFullscreen, toggleFullscreen, subtitleTrack, pictureInPicture, errors, customControls, ...rest }: BottomOptions) => import("react/jsx-runtime").JSX.Element;
|
|
20
|
-
export default _default;
|
package/build/chrome/index.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { HTMLAttributes, MutableRefObject, ReactNode } from 'react';
|
|
2
|
-
import type { Attachment, FKNVideoControl, Subtitle, TransmuxError } from '..';
|
|
3
|
-
export type ChromeOptions = {
|
|
4
|
-
publicPath: string;
|
|
5
|
-
customOverlay?: ReactNode;
|
|
6
|
-
isPlaying?: boolean;
|
|
7
|
-
loading?: boolean;
|
|
8
|
-
duration?: number;
|
|
9
|
-
loadedTime?: [number, number];
|
|
10
|
-
currentTime?: number;
|
|
11
|
-
pictureInPicture: () => void;
|
|
12
|
-
fullscreen: () => void;
|
|
13
|
-
play: () => void;
|
|
14
|
-
seek: (time: number) => void;
|
|
15
|
-
getVolume: () => number | undefined;
|
|
16
|
-
setVolume: (volume: number) => void;
|
|
17
|
-
attachments: Attachment[] | undefined;
|
|
18
|
-
tracks: Subtitle[];
|
|
19
|
-
video: MutableRefObject<HTMLVideoElement | undefined>;
|
|
20
|
-
errors: TransmuxError[];
|
|
21
|
-
customControls?: FKNVideoControl[];
|
|
22
|
-
libassWorkerUrl: string;
|
|
23
|
-
wasmUrl: string;
|
|
24
|
-
needsInitialInteraction?: boolean;
|
|
25
|
-
} & HTMLAttributes<HTMLDivElement>;
|
|
26
|
-
declare const _default: ({ customOverlay, publicPath, isPlaying, loading, duration, loadedTime, currentTime, pictureInPicture, fullscreen, play, seek, getVolume, setVolume, attachments, tracks, video, errors, customControls, libassWorkerUrl, wasmUrl, needsInitialInteraction, ...rest }: ChromeOptions) => import("react/jsx-runtime").JSX.Element;
|
|
27
|
-
export default _default;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ClassAttributes } from 'react';
|
|
2
|
-
export type OverlayOptions = {
|
|
3
|
-
loading?: boolean;
|
|
4
|
-
togglePlay: (ev: any) => void;
|
|
5
|
-
setCanvasRef: ClassAttributes<HTMLCanvasElement>['ref'];
|
|
6
|
-
needsInitialInteraction?: boolean;
|
|
7
|
-
};
|
|
8
|
-
declare const _default: ({ loading, needsInitialInteraction, togglePlay, setCanvasRef }: OverlayOptions) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
-
export default _default;
|