@banou/media-player 0.0.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.
@@ -0,0 +1,262 @@
1
+ type Language = {
2
+ tag: string
3
+ name: string
4
+ originalName: string
5
+ locale: string
6
+ }
7
+
8
+ export const languages: Language[] = JSON.parse(`[{"tag":"zu","name":"isiZulu","originalName":"isiZulu","locale":"zu-ZA"},{"tag":"zh","name":"Chinese","originalName":"中文","locale":"zh-CHS"},{"tag":"yo","name":"Yoruba","originalName":"Yoruba","locale":"yo-NG"},{"tag":"xh","name":"isiXhosa","originalName":"isiXhosa","locale":"xh-ZA"},{"tag":"wo","name":"Wolof","originalName":"Wolof","locale":"wo-SN"},{"tag":"vi","name":"Vietnamese","originalName":"Tiếng Việt","locale":"vi"},{"tag":"uz","name":"Uzbek","originalName":"U'zbek","locale":"uz"},{"tag":"ur","name":"Urdu","originalName":"اُردو","locale":"ur"},{"tag":"uk","name":"Ukrainian","originalName":"україньска","locale":"uk"},{"tag":"ug","name":"Uyghur","originalName":"ئۇيغۇرچە","locale":"ug-CN"},{"tag":"tzm","name":"Tamazight","originalName":"Tamazight","locale":"tzm-Latn-DZ"},{"tag":"tt","name":"Tatar","originalName":"Татар","locale":"tt"},{"tag":"tr","name":"Turkish","originalName":"Türkçe","locale":"tr"},{"tag":"tn","name":"Setswana","originalName":"Setswana","locale":"tn-ZA"},{"tag":"tk","name":"Turkmen","originalName":"türkmençe","locale":"tk-TM"},{"tag":"th","name":"Thai","originalName":"ไทย","locale":"th"},{"tag":"tg","name":"Tajik","originalName":"Тоҷикӣ","locale":"tg-Cyrl-TJ"},{"tag":"te","name":"Telugu","originalName":"తెలుగు","locale":"te"},{"tag":"ta","name":"Tamil","originalName":"தமிழ்","locale":"ta"},{"tag":"syr","name":"Syriac","originalName":"ܣܘܪܝܝܐ","locale":"syr"},{"tag":"sw","name":"Kiswahili","originalName":"Kiswahili","locale":"sw"},{"tag":"sv","name":"Swedish","originalName":"svenska","locale":"sv"},{"tag":"sr","name":"Serbian","originalName":"srpski","locale":"sr"},{"tag":"sq","name":"Albanian","originalName":"shqipe","locale":"sq"},{"tag":"sms","name":"Sami","originalName":"sääm´ǩiõll","locale":"sms-FI"},{"tag":"smn","name":"Sami","originalName":"sämikielâ","locale":"smn-FI"},{"tag":"smj","name":"Sami","originalName":"julevusámegiella","locale":"smj-NO"},{"tag":"sma","name":"Sami","originalName":"åarjelsaemiengiele","locale":"sma-NO"},{"tag":"sl","name":"Slovenian","originalName":"slovenski","locale":"sl"},{"tag":"sk","name":"Slovak","originalName":"slovenčina","locale":"sk"},{"tag":"si","name":"Sinhala","originalName":"සිංහ","locale":"si-LK"},{"tag":"se","name":"Sami","originalName":"davvisámegiella","locale":"se-FI"},{"tag":"sah","name":"Yakut","originalName":"саха","locale":"sah-RU"},{"tag":"sa","name":"Sanskrit","originalName":"संस्कृत","locale":"sa"},{"tag":"rw","name":"Kinyarwanda","originalName":"Kinyarwanda","locale":"rw-RW"},{"tag":"ru","name":"Russian","originalName":"русский","locale":"ru"},{"tag":"ro","name":"Romanian","originalName":"română","locale":"ro"},{"tag":"rm","name":"Romansh","originalName":"Rumantsch","locale":"rm-CH"},{"tag":"quz","name":"Quechua","originalName":"runasimi","locale":"quz-BO"},{"tag":"qut","name":"K'iche","originalName":"K'iche","locale":"qut-GT"},{"tag":"pt","name":"Portuguese","originalName":"Português","locale":"pt"},{"tag":"ps","name":"Pashto","originalName":"پښتو","locale":"ps-AF"},{"tag":"prs","name":"Dari","originalName":"درى","locale":"prs-AF"},{"tag":"pl","name":"Polish","originalName":"polski","locale":"pl"},{"tag":"pa","name":"Punjabi","originalName":"ਪੰਜਾਬੀ","locale":"pa"},{"tag":"or","name":"Oriya","originalName":"ଓଡ଼ିଆ","locale":"or-IN"},{"tag":"oc","name":"Occitan","originalName":"Occitan","locale":"oc-FR"},{"tag":"nso","name":"Sesotho sa Leboa","originalName":"Sesotho sa Leboa","locale":"nso-ZA"},{"tag":"no","name":"Norwegian","originalName":"norsk","locale":"no"},{"tag":"nn","name":"Norwegian, Nynorsk","originalName":"norsk, nynorsk","locale":"nn-NO"},{"tag":"nl","name":"Dutch","originalName":"Nederlands","locale":"nl"},{"tag":"ne","name":"Nepali","originalName":"नेपाली","locale":"ne-NP"},{"tag":"nb","name":"Norwegian, Bokmål","originalName":"norsk, bokmål","locale":"nb-NO"},{"tag":"mt","name":"Maltese","originalName":"Malti","locale":"mt-MT"},{"tag":"ms","name":"Malay","originalName":"Bahasa Malaysia","locale":"ms"},{"tag":"mr","name":"Marathi","originalName":"मराठी","locale":"mr"},{"tag":"moh","name":"Mohawk","originalName":"Kanien'kéha","locale":"moh-CA"},{"tag":"mn","name":"Mongolian","originalName":"Монгол хэл","locale":"mn"},{"tag":"ml","name":"Malayalam","originalName":"മലയാളം","locale":"ml-IN"},{"tag":"mk","name":"Macedonian","originalName":"македонски јазик","locale":"mk"},{"tag":"mi","name":"Maori","originalName":"Reo Māori","locale":"mi-NZ"},{"tag":"lv","name":"Latvian","originalName":"latviešu","locale":"lv"},{"tag":"lt","name":"Lithuanian","originalName":"lietuvių","locale":"lt"},{"tag":"lo","name":"Lao","originalName":"ລາວ","locale":"lo-LA"},{"tag":"lb","name":"Luxembourgish","originalName":"Lëtzebuergesch","locale":"lb-LU"},{"tag":"ky","name":"Kyrgyz","originalName":"Кыргыз","locale":"ky"},{"tag":"kok","name":"Konkani","originalName":"कोंकणी","locale":"kok"},{"tag":"ko","name":"Korean","originalName":"한국어","locale":"ko"},{"tag":"kn","name":"Kannada","originalName":"ಕನ್ನಡ","locale":"kn"},{"tag":"km","name":"Khmer","originalName":"ខ្មែរ","locale":"km-KH"},{"tag":"kl","name":"Greenlandic","originalName":"kalaallisut","locale":"kl-GL"},{"tag":"kk","name":"Kazakh","originalName":"Қазащb","locale":"kk"},{"tag":"ka","name":"Georgian","originalName":"ქართული","locale":"ka"},{"tag":"ja","name":"Japanese","originalName":"日本語","locale":"ja"},{"tag":"iu","name":"Inuktitut","originalName":"ᐃᓄᒃᑎᑐᑦ","locale":"iu-Cans-CA"},{"tag":"it","name":"Italian","originalName":"italiano","locale":"it"},{"tag":"is","name":"Icelandic","originalName":"íslenska","locale":"is"},{"tag":"ii","name":"Yi","originalName":"ꆈꌠꁱꂷ","locale":"ii-CN"},{"tag":"ig","name":"Igbo","originalName":"Igbo","locale":"ig-NG"},{"tag":"id","name":"Indonesian","originalName":"Bahasa Indonesia","locale":"id"},{"tag":"hy","name":"Armenian","originalName":"Հայերեն","locale":"hy"},{"tag":"hu","name":"Hungarian","originalName":"magyar","locale":"hu"},{"tag":"hsb","name":"Upper Sorbian","originalName":"hornjoserbšćina","locale":"hsb-DE"},{"tag":"hr","name":"Croatian","originalName":"hrvatski","locale":"hr"},{"tag":"hi","name":"Hindi","originalName":"हिंदी","locale":"hi"},{"tag":"he","name":"Hebrew","originalName":"עברית","locale":"he"},{"tag":"ha","name":"Hausa","originalName":"Hausa","locale":"ha-Latn-NG"},{"tag":"gu","name":"Gujarati","originalName":"ગુજરાતી","locale":"gu"},{"tag":"gsw","name":"Alsatian","originalName":"Elsässisch","locale":"gsw-FR"},{"tag":"gl","name":"Galician","originalName":"galego","locale":"gl"},{"tag":"gd","name":"Scottish Gaelic","originalName":"Gàidhlig","locale":"gd-GB"},{"tag":"ga","name":"Irish","originalName":"Gaeilge","locale":"ga-IE"},{"tag":"fy","name":"Frisian","originalName":"Frysk","locale":"fy-NL"},{"tag":"fr","name":"French","originalName":"français","locale":"fr"},{"tag":"fo","name":"Faroese","originalName":"føroyskt","locale":"fo"},{"tag":"fil","name":"Filipino","originalName":"Filipino","locale":"fil-PH"},{"tag":"fi","name":"Finnish","originalName":"suomi","locale":"fi"},{"tag":"fa","name":"Persian","originalName":"فارسى","locale":"fa"},{"tag":"eu","name":"Basque","originalName":"euskara","locale":"eu"},{"tag":"et","name":"Estonian","originalName":"eesti","locale":"et"},{"tag":"es","name":"Spanish","originalName":"español","locale":"es"},{"tag":"en","name":"English","originalName":"English","locale":"en"},{"tag":"el","name":"Greek","originalName":"ελληνικά","locale":"el"},{"tag":"dv","name":"Divehi","originalName":"ދިވެހިބަސް","locale":"dv"},{"tag":"dsb","name":"Lower Sorbian","originalName":"dolnoserbšćina","locale":"dsb-DE"},{"tag":"de","name":"German","originalName":"Deutsch","locale":"de"},{"tag":"da","name":"Danish","originalName":"dansk","locale":"da"},{"tag":"cy","name":"Welsh","originalName":"Cymraeg","locale":"cy-GB"},{"tag":"cs","name":"Czech","originalName":"čeština","locale":"cs"},{"tag":"co","name":"Corsican","originalName":"Corsu","locale":"co-FR"},{"tag":"ca","name":"Catalan","originalName":"català","locale":"ca"},{"tag":"bs","name":"Bosnian","originalName":"босански","locale":"bs-Cyrl-BA"},{"tag":"br","name":"Breton","originalName":"brezhoneg","locale":"br-FR"},{"tag":"bo","name":"Tibetan","originalName":"བོད་ཡིག","locale":"bo-CN"},{"tag":"bn","name":"Bengali","originalName":"বাংলা","locale":"bn-BD"},{"tag":"bg","name":"Bulgarian","originalName":"български","locale":"bg"},{"tag":"be","name":"Belarusian","originalName":"Беларускі","locale":"be"},{"tag":"ba","name":"Bashkir","originalName":"Башҡорт","locale":"ba-RU"},{"tag":"az","name":"Azeri","originalName":"Azərbaycan­ılı","locale":"az"},{"tag":"as","name":"Assamese","originalName":"অসমীয়া","locale":"as-IN"},{"tag":"arn","name":"Mapudungun","originalName":"Mapudungun","locale":"arn-CL"},{"tag":"ar","name":"Arabic","originalName":"العربية","locale":"ar"},{"tag":"am","name":"Amharic","originalName":"አማርኛ","locale":"am-ET"},{"tag":"af","name":"Afrikaans","originalName":"Afrikaans","locale":"af"},{"tag":"iv","name":"Invariant Language","originalName":"Invariant Language","locale":""},{"tag":"Two letter","name":"English","originalName":"Native","locale":"Culture .NET"}]`)
9
+
10
+ export const languageToTag = (lang: string) => languages.find(language => language.name === lang)?.tag
11
+ export const tagToLanguage = (langTag: string) => languages.find(language => language.tag === langTag)?.name
12
+
13
+ export enum LanguageTag {
14
+ /** isiZulu */
15
+ ZU = 'zu',// "isiZulu",
16
+ /** Chinese */
17
+ ZH = 'zh',// "Chinese",
18
+ /** Yoruba */
19
+ YO = 'yo',// "Yoruba",
20
+ /** isiXhosa */
21
+ XH = 'xh',// "isiXhosa",
22
+ /** Wolof */
23
+ WO = 'wo',// "Wolof",
24
+ /** Vietnamese */
25
+ VI = 'vi',// "Vietnamese",
26
+ /** Uzbek */
27
+ UZ = 'uz',// "Uzbek",
28
+ /** Urdu */
29
+ UR = 'ur',// "Urdu",
30
+ /** Ukrainian */
31
+ UK = 'uk',// "Ukrainian",
32
+ /** Uyghur */
33
+ UG = 'ug',// "Uyghur",
34
+ /** Tamazight */
35
+ TZM = 'tzm',// "Tamazight",
36
+ /** Tatar */
37
+ TT = 'tt',// "Tatar",
38
+ /** Turkish */
39
+ TR = 'tr',// "Turkish",
40
+ /** Setswana */
41
+ TN = 'tn',// "Setswana",
42
+ /** Turkmen */
43
+ TK = 'tk',// "Turkmen",
44
+ /** Thai */
45
+ TH = 'th',// "Thai",
46
+ /** Tajik */
47
+ TG = 'tg',// "Tajik",
48
+ /** Telugu */
49
+ TE = 'te',// "Telugu",
50
+ /** Tamil */
51
+ TA = 'ta',// "Tamil",
52
+ /** Syriac */
53
+ SYR = 'syr',// "Syriac",
54
+ /** Kiswahili */
55
+ SW = 'sw',// "Kiswahili",
56
+ /** Swedish */
57
+ SV = 'sv',// "Swedish",
58
+ /** Serbian */
59
+ SR = 'sr',// "Serbian",
60
+ /** Albanian */
61
+ SQ = 'sq',// "Albanian",
62
+ /** Sami */
63
+ SMS = 'sms',// "Sami",
64
+ /** Sami */
65
+ SMN = 'smn',// "Sami",
66
+ /** Sami */
67
+ SMJ = 'smj',// "Sami",
68
+ /** Sami */
69
+ SMA = 'sma',// "Sami",
70
+ /** Slovenian */
71
+ SL = 'sl',// "Slovenian",
72
+ /** Slovak */
73
+ SK = 'sk',// "Slovak",
74
+ /** Sinhala */
75
+ SI = 'si',// "Sinhala",
76
+ /** Sami */
77
+ SE = 'se',// "Sami",
78
+ /** Yakut */
79
+ SAH = 'sah',// "Yakut",
80
+ /** Sanskrit */
81
+ SA = 'sa',// "Sanskrit",
82
+ /** Kinyarwanda */
83
+ RW = 'rw',// "Kinyarwanda",
84
+ /** Russian */
85
+ RU = 'ru',// "Russian",
86
+ /** Romanian */
87
+ RO = 'ro',// "Romanian",
88
+ /** Romansh */
89
+ RM = 'rm',// "Romansh",
90
+ /** Quechua */
91
+ QUZ = 'quz',// "Quechua",
92
+ /** K */
93
+ QUT = 'qut',// "K'iche",
94
+ /** Portuguese */
95
+ PT = 'pt',// "Portuguese",
96
+ /** Pashto */
97
+ PS = 'ps',// "Pashto",
98
+ /** Dari */
99
+ PRS = 'prs',// "Dari",
100
+ /** Polish */
101
+ PL = 'pl',// "Polish",
102
+ /** Punjabi */
103
+ PA = 'pa',// "Punjabi",
104
+ /** Oriya */
105
+ OR = 'or',// "Oriya",
106
+ /** Occitan */
107
+ OC = 'oc',// "Occitan",
108
+ /** Sesotho sa Leboa */
109
+ NSO = 'nso',// "Sesotho sa Leboa",
110
+ /** Norwegian */
111
+ NO = 'no',// "Norwegian",
112
+ /** Norwegian, Nynorsk */
113
+ NN = 'nn',// "Norwegian, Nynorsk",
114
+ /** Dutch */
115
+ NL = 'nl',// "Dutch",
116
+ /** Nepali */
117
+ NE = 'ne',// "Nepali",
118
+ /** Norwegian, Bokmål */
119
+ NB = 'nb',// "Norwegian, Bokmål",
120
+ /** Maltese */
121
+ MT = 'mt',// "Maltese",
122
+ /** Malay */
123
+ MS = 'ms',// "Malay",
124
+ /** Marathi */
125
+ MR = 'mr',// "Marathi",
126
+ /** Mohawk */
127
+ MOH = 'moh',// "Mohawk",
128
+ /** Mongolian */
129
+ MN = 'mn',// "Mongolian",
130
+ /** Malayalam */
131
+ ML = 'ml',// "Malayalam",
132
+ /** Macedonian */
133
+ MK = 'mk',// "Macedonian",
134
+ /** Maori */
135
+ MI = 'mi',// "Maori",
136
+ /** Latvian */
137
+ LV = 'lv',// "Latvian",
138
+ /** Lithuanian */
139
+ LT = 'lt',// "Lithuanian",
140
+ /** Lao */
141
+ LO = 'lo',// "Lao",
142
+ /** Luxembourgish */
143
+ LB = 'lb',// "Luxembourgish",
144
+ /** Kyrgyz */
145
+ KY = 'ky',// "Kyrgyz",
146
+ /** Konkani */
147
+ KOK = 'kok',// "Konkani",
148
+ /** Korean */
149
+ KO = 'ko',// "Korean",
150
+ /** Kannada */
151
+ KN = 'kn',// "Kannada",
152
+ /** Khmer */
153
+ KM = 'km',// "Khmer",
154
+ /** Greenlandic */
155
+ KL = 'kl',// "Greenlandic",
156
+ /** Kazakh */
157
+ KK = 'kk',// "Kazakh",
158
+ /** Georgian */
159
+ KA = 'ka',// "Georgian",
160
+ /** Japanese */
161
+ JA = 'ja',// "Japanese",
162
+ /** Inuktitut */
163
+ IU = 'iu',// "Inuktitut",
164
+ /** Italian */
165
+ IT = 'it',// "Italian",
166
+ /** Icelandic */
167
+ IS = 'is',// "Icelandic",
168
+ /** Yi */
169
+ II = 'ii',// "Yi",
170
+ /** Igbo */
171
+ IG = 'ig',// "Igbo",
172
+ /** Indonesian */
173
+ ID = 'id',// "Indonesian",
174
+ /** Armenian */
175
+ HY = 'hy',// "Armenian",
176
+ /** Hungarian */
177
+ HU = 'hu',// "Hungarian",
178
+ /** Upper Sorbian */
179
+ HSB = 'hsb',// "Upper Sorbian",
180
+ /** Croatian */
181
+ HR = 'hr',// "Croatian",
182
+ /** Hindi */
183
+ HI = 'hi',// "Hindi",
184
+ /** Hebrew */
185
+ HE = 'he',// "Hebrew",
186
+ /** Hausa */
187
+ HA = 'ha',// "Hausa",
188
+ /** Gujarati */
189
+ GU = 'gu',// "Gujarati",
190
+ /** Alsatian */
191
+ GSW = 'gsw',// "Alsatian",
192
+ /** Galician */
193
+ GL = 'gl',// "Galician",
194
+ /** Scottish Gaelic */
195
+ GD = 'gd',// "Scottish Gaelic",
196
+ /** Irish */
197
+ GA = 'ga',// "Irish",
198
+ /** Frisian */
199
+ FY = 'fy',// "Frisian",
200
+ /** French */
201
+ FR = 'fr',// "French",
202
+ /** Faroese */
203
+ FO = 'fo',// "Faroese",
204
+ /** Filipino */
205
+ FIL = 'fil',// "Filipino",
206
+ /** Finnish */
207
+ FI = 'fi',// "Finnish",
208
+ /** Persian */
209
+ FA = 'fa',// "Persian",
210
+ /** Basque */
211
+ EU = 'eu',// "Basque",
212
+ /** Estonian */
213
+ ET = 'et',// "Estonian",
214
+ /** Spanish */
215
+ ES = 'es',// "Spanish",
216
+ /** English */
217
+ EN = 'en',// "English",
218
+ /** Greek */
219
+ EL = 'el',// "Greek",
220
+ /** Divehi */
221
+ DV = 'dv',// "Divehi",
222
+ /** Lower Sorbian */
223
+ DSB = 'dsb',// "Lower Sorbian",
224
+ /** German */
225
+ DE = 'de',// "German",
226
+ /** Danish */
227
+ DA = 'da',// "Danish",
228
+ /** Welsh */
229
+ CY = 'cy',// "Welsh",
230
+ /** Czech */
231
+ CS = 'cs',// "Czech",
232
+ /** Corsican */
233
+ CO = 'co',// "Corsican",
234
+ /** Catalan */
235
+ CA = 'ca',// "Catalan",
236
+ /** Bosnian */
237
+ BS = 'bs',// "Bosnian",
238
+ /** Breton */
239
+ BR = 'br',// "Breton",
240
+ /** Tibetan */
241
+ BO = 'bo',// "Tibetan",
242
+ /** Bengali */
243
+ BN = 'bn',// "Bengali",
244
+ /** Bulgarian */
245
+ BG = 'bg',// "Bulgarian",
246
+ /** Belarusian */
247
+ BE = 'be',// "Belarusian",
248
+ /** Bashkir */
249
+ BA = 'ba',// "Bashkir",
250
+ /** Azeri */
251
+ AZ = 'az',// "Azeri",
252
+ /** Assamese */
253
+ AS = 'as',// "Assamese",
254
+ /** Mapudungun */
255
+ ARN = 'arn',// "Mapudungun",
256
+ /** Arabic */
257
+ AR = 'ar',// "Arabic",
258
+ /** Amharic */
259
+ AM = 'am',// "Amharic",
260
+ /** Afrikaans */
261
+ AF = 'af'// "Afrikaans"
262
+ }
package/src/main.tsx ADDED
@@ -0,0 +1,187 @@
1
+ /// <reference types="@emotion/react/types/css-prop" />
2
+ import { useEffect, useMemo, useState } from 'react'
3
+ import { createRoot } from 'react-dom/client'
4
+ import { css, Global } from '@emotion/react'
5
+
6
+ import MediaPlayer from './index'
7
+ import { bufferStream } from './utils'
8
+
9
+ const mountStyle = css`
10
+ display: grid;
11
+ height: 100%;
12
+ width: 100%;
13
+ `
14
+
15
+ const BASE_BUFFER_SIZE = 5_000_000
16
+ const STREAM_RESPONSES = false
17
+
18
+ const Mount = () => {
19
+ const [videoElemRef, setVideoElemRef] = useState<HTMLVideoElement | null>()
20
+ const [size, setSize] = useState<number>()
21
+
22
+ useEffect(() => {
23
+ if (!videoElemRef) return
24
+ videoElemRef.addEventListener('error', err => console.log('err', err))
25
+ }, [videoElemRef])
26
+
27
+ const [currentStreamOffset, setCurrentStreamOffset] = useState<number>(0)
28
+ const [streamReader, setStreamReader] = useState<ReadableStreamDefaultReader<Uint8Array>>()
29
+
30
+ useEffect(() => {
31
+ if (!streamReader) return
32
+ return () => {
33
+ streamReader.cancel()
34
+ }
35
+ }, [streamReader])
36
+
37
+ const setupStream = async (offset: number) => {
38
+ if (streamReader) {
39
+ streamReader.cancel()
40
+ }
41
+ const streamResponse = await onFetch(offset, undefined, true)
42
+ if (!streamResponse.body) throw new Error('no body')
43
+ const stream = bufferStream({ stream: streamResponse.body, size: BASE_BUFFER_SIZE })
44
+ const reader = stream.getReader()
45
+ setStreamReader(reader)
46
+ setCurrentStreamOffset(offset)
47
+ return reader
48
+ }
49
+
50
+ const onFetch = async (offset: number, end?: number, force?: boolean) => {
51
+ if (force || end !== undefined && ((end - offset) + 1) !== BASE_BUFFER_SIZE) {
52
+ return (
53
+ fetch(
54
+ '/video3.mkv',
55
+ {
56
+ headers: {
57
+ Range: `bytes=${offset}-${end ?? ''}`
58
+ }
59
+ }
60
+ )
61
+ )
62
+ }
63
+ const _streamReader =
64
+ currentStreamOffset !== offset
65
+ ? await setupStream(offset)
66
+ : streamReader
67
+
68
+ if (!_streamReader) throw new Error('Stream reader not ready')
69
+ return new Response(
70
+ await _streamReader
71
+ .read()
72
+ .then(({ value }) => {
73
+ if (value) {
74
+ setCurrentStreamOffset(offset => offset + value.byteLength)
75
+ }
76
+ return value
77
+ })
78
+ )
79
+ }
80
+
81
+ useEffect(() => {
82
+ onFetch(0, 1, true).then(async ({ headers, body }) => {
83
+ if (!body) throw new Error('no body')
84
+ const contentRangeContentLength = headers.get('Content-Range')?.split('/').at(1)
85
+ const contentLength =
86
+ contentRangeContentLength
87
+ ? Number(contentRangeContentLength)
88
+ : Number(headers.get('Content-Length'))
89
+ if (STREAM_RESPONSES) await setupStream(0)
90
+ setSize(contentLength)
91
+ })
92
+ }, [])
93
+
94
+ const jassubWorkerUrl = useMemo(() => {
95
+ const workerUrl = new URL('/build/jassub-worker.js', import.meta.url).toString()
96
+ const blob = new Blob([`importScripts(${JSON.stringify(workerUrl)})`], { type: 'application/javascript' })
97
+ return URL.createObjectURL(blob)
98
+ }, [])
99
+
100
+ const libavWorkerUrl = useMemo(() => {
101
+ const workerUrl = new URL('/build/libav.js', new URL(window.location.toString()).origin).toString()
102
+ const blob = new Blob([`importScripts(${JSON.stringify(workerUrl)})`], { type: 'application/javascript' })
103
+ return URL.createObjectURL(blob)
104
+ }, [])
105
+
106
+ return (
107
+ <div css={mountStyle}>
108
+ <MediaPlayer
109
+ baseBufferSize={BASE_BUFFER_SIZE}
110
+ ref={setVideoElemRef}
111
+ size={size}
112
+ fetch={(offset, end) => onFetch(offset, end, STREAM_RESPONSES ? false : true)}
113
+ publicPath={new URL('/build/', new URL(import.meta.url).origin).toString()}
114
+ wasmUrl={new URL('/build/jassub-worker-modern.wasm', new URL(import.meta.url).origin).toString()}
115
+ libavWorkerUrl={libavWorkerUrl}
116
+ libassWorkerUrl={jassubWorkerUrl}
117
+ />
118
+ </div>
119
+ )
120
+ }
121
+
122
+ const globalStyle = css`
123
+ @import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;500;600;700&family=Fira+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,500;1,600;1,700;1,800;1,900&family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');
124
+
125
+ *, *::before, *::after {
126
+ box-sizing: border-box;
127
+ margin: 0;
128
+ padding: 0;
129
+ }
130
+
131
+ html {
132
+ font-size: 62.5%;
133
+ height: 100%;
134
+ width: 100%;
135
+ }
136
+
137
+ body {
138
+ margin: 0;
139
+ height: 100%;
140
+ width: 100%;
141
+ font-size: 1.6rem;
142
+ font-family: Fira Sans;
143
+ color: #fff;
144
+
145
+ font-family: Montserrat;
146
+ // font-family: "Segoe UI", Roboto, "Fira Sans", "Helvetica Neue", Arial, sans-serif;
147
+ }
148
+
149
+ body > div {
150
+ height: 100%;
151
+ width: 100%;
152
+ }
153
+
154
+ a {
155
+ color: #777777;
156
+ text-decoration: none;
157
+ }
158
+
159
+ a:hover {
160
+ color: #fff;
161
+ text-decoration: underline;
162
+ }
163
+
164
+ ul {
165
+ list-style: none;
166
+ }
167
+ `
168
+
169
+ const mountElement = document.createElement('div')
170
+
171
+ const root = createRoot(
172
+ document.body.appendChild(mountElement)
173
+ )
174
+
175
+ root.render(
176
+ <>
177
+ <Global styles={globalStyle}/>
178
+ <Mount/>
179
+ </>
180
+ )
181
+
182
+ if (import.meta.hot) {
183
+ import.meta.hot.dispose((data) => {
184
+ root.unmount()
185
+ mountElement.remove()
186
+ })
187
+ }
package/src/mp4box.ts ADDED
@@ -0,0 +1,74 @@
1
+ declare module "mp4box" {
2
+
3
+ export interface MP4MediaTrack {
4
+ id: number
5
+ created: Date
6
+ modified: Date
7
+ movie_duration: number
8
+ layer: number
9
+ alternate_group: number
10
+ volume: number
11
+ track_width: number
12
+ track_height: number
13
+ timescale: number
14
+ duration: number
15
+ bitrate: number
16
+ codec: string
17
+ language: string
18
+ nb_samples: number
19
+ }
20
+
21
+ export interface MP4VideoData {
22
+ width: number
23
+ height: number
24
+ }
25
+
26
+ export interface MP4VideoTrack extends MP4MediaTrack {
27
+ video: MP4VideoData
28
+ }
29
+
30
+ export interface MP4AudioData {
31
+ sample_rate: number
32
+ channel_count: number
33
+ sample_size: number
34
+ }
35
+
36
+ export interface MP4AudioTrack extends MP4MediaTrack {
37
+ audio: MP4AudioData
38
+ }
39
+
40
+ export type MP4Track = MP4VideoTrack | MP4AudioTrack
41
+
42
+ export interface MP4Info {
43
+ duration: number
44
+ timescale: number
45
+ fragment_duration: number
46
+ isFragmented: boolean
47
+ isProgressive: boolean
48
+ hasIOD: boolean
49
+ brands: string[]
50
+ created: Date
51
+ modified: Date
52
+ tracks: MP4Track[]
53
+ }
54
+
55
+ export type MP4ArrayBuffer = ArrayBuffer & { fileStart: number }
56
+
57
+ export interface MP4File {
58
+
59
+ onMoovStart?: () => void
60
+ onReady?: (info: MP4Info) => void
61
+ onError?: (e: Error) => void
62
+
63
+ appendBuffer(data: MP4ArrayBuffer): number
64
+ start(): void
65
+ stop(): void
66
+ flush(): void
67
+
68
+ }
69
+
70
+ export function createFile(): MP4File
71
+
72
+ }
73
+
74
+ export { }
@@ -0,0 +1,32 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ const useNamespacedLocalStorage = <T extends { [key: PropertyKey]: string | boolean | number | undefined | null | object | any[] }>(namespace: string) => {
4
+ const [state, setState] = useState<T>(() => {
5
+ const storedValue = localStorage.getItem(namespace)
6
+ return (
7
+ storedValue
8
+ ? JSON.parse(storedValue)
9
+ : {}
10
+ )
11
+ })
12
+
13
+ useEffect(() => {
14
+ localStorage.setItem(namespace, JSON.stringify(state));
15
+ }, [namespace, state])
16
+
17
+ return <T2 extends keyof T, T3 extends T[T2]>(key: T2, value: T[T2]) => {
18
+ useEffect(() => {
19
+ setState(state => ({ ...state, [key]: value }))
20
+ }, [])
21
+ return [
22
+ state[key],
23
+ (newValue: T3 | ((oldValue: T3) => T3)) =>
24
+ typeof newValue === 'function'
25
+ // @ts-expect-error
26
+ ? setState(state => ({ ...state, [key]: newValue(state[key]) }))
27
+ : setState(state => ({ ...state, [key]: newValue }))
28
+ ] as const
29
+ }
30
+ }
31
+
32
+ export default useNamespacedLocalStorage
@@ -0,0 +1,39 @@
1
+ import type { MouseEventHandler, RefObject } from 'react'
2
+
3
+ import { useState, useEffect } from 'react'
4
+
5
+ export default ({ ref, defaultValue }: { ref: RefObject<HTMLElement>, defaultValue?: number }) => {
6
+ const [value, setValue] = useState(defaultValue)
7
+ const [scrubbing, setScrubbing] = useState(false)
8
+
9
+ const scrub: MouseEventHandler<HTMLDivElement> = (ev) => {
10
+ setScrubbing(true)
11
+ if (!ref.current) return
12
+ const { clientX: x } = ev
13
+ const { left, right } = ref.current.getBoundingClientRect()
14
+ setValue(Math.min(Math.max(((x - left) / (right - left)), 0), 1))
15
+ }
16
+
17
+ useEffect(() => {
18
+ if (!scrubbing) return
19
+ const mouseUp = () => setScrubbing(false)
20
+ const mouseMove = (ev: globalThis.MouseEvent) => {
21
+ if (!ref.current) return
22
+ const { clientX: x } = ev
23
+ const { left, right } = ref.current.getBoundingClientRect()
24
+ setValue(Math.min(Math.max(((x - left) / (right - left)), 0), 1))
25
+ }
26
+ document.addEventListener('mousemove', mouseMove)
27
+ document.addEventListener('mouseup', mouseUp)
28
+ return () => {
29
+ document.removeEventListener('mousemove', mouseMove)
30
+ document.removeEventListener('mouseup', mouseUp)
31
+ }
32
+ }, [scrubbing])
33
+
34
+ return {
35
+ value,
36
+ scrubbing,
37
+ scrub
38
+ }
39
+ }