Joshua Lochner commited on
Commit
ebe3b4a
·
1 Parent(s): 5ac137c

Run prettier

Browse files
src/components/AudioManager.tsx CHANGED
@@ -132,7 +132,13 @@ export enum AudioSource {
132
  export function AudioManager(props: { transcriber: Transcriber }) {
133
  const [progress, setProgress] = useState<number | undefined>(undefined);
134
  const [audioData, setAudioData] = useState<
135
- { buffer: AudioBuffer; url: string; source: AudioSource; mimeType: string } | undefined
 
 
 
 
 
 
136
  >(undefined);
137
  const [audioDownloadUrl, setAudioDownloadUrl] = useState<
138
  string | undefined
@@ -145,7 +151,10 @@ export function AudioManager(props: { transcriber: Transcriber }) {
145
  setAudioDownloadUrl(undefined);
146
  };
147
 
148
- const setAudioFromDownload = async (data: ArrayBuffer, mimeType: string) => {
 
 
 
149
  const audioCTX = new AudioContext({
150
  sampleRate: Constants.SAMPLING_RATE,
151
  });
@@ -199,7 +208,10 @@ export function AudioManager(props: { transcriber: Transcriber }) {
199
  onDownloadProgress(progressEvent) {
200
  setProgress(progressEvent.progress || 0);
201
  },
202
- })) as { data: ArrayBuffer, headers: { "content-type": string } };
 
 
 
203
 
204
  let mimeType = headers["content-type"];
205
  if (!mimeType || mimeType === "audio/wave") {
@@ -247,22 +259,23 @@ export function AudioManager(props: { transcriber: Transcriber }) {
247
  buffer: decoded,
248
  url: blobUrl,
249
  source: AudioSource.FILE,
250
- mimeType: mimeType
251
  });
252
  }}
253
  />
254
- {navigator.mediaDevices && (<>
255
- <VerticalBar />
256
- <RecordTile
257
- icon={<MicrophoneIcon />}
258
- text={"Record"}
259
- setAudioData={(e) => {
260
- props.transcriber.onInputChange();
261
- setAudioFromRecording(e);
262
- }}
263
- />
264
- </>)}
265
-
 
266
  </div>
267
  {
268
  <AudioDataBar
@@ -272,7 +285,10 @@ export function AudioManager(props: { transcriber: Transcriber }) {
272
  </div>
273
  {audioData && (
274
  <>
275
- <AudioPlayer audioUrl={audioData.url} mimeType={audioData.mimeType} />
 
 
 
276
 
277
  <div className='relative w-full flex justify-center items-center'>
278
  <TranscribeButton
@@ -379,13 +395,14 @@ function SettingsModal(props: {
379
  models[key].length == 2,
380
  )
381
  .map((key) => (
382
- <option key={key} value={key}>{`whisper-${key}${props.transcriber.multilingual ? "" : ".en"
383
- } (${
 
384
  // @ts-ignore
385
  models[key][
386
- props.transcriber.quantized ? 0 : 1
387
  ]
388
- }MB)`}</option>
389
  ))}
390
  </select>
391
  <div className='flex justify-between items-center mb-3 px-1'>
@@ -458,7 +475,7 @@ function SettingsModal(props: {
458
  </>
459
  }
460
  onClose={props.onClose}
461
- onSubmit={() => { }}
462
  />
463
  );
464
  }
@@ -545,7 +562,11 @@ function UrlModal(props: {
545
  function FileTile(props: {
546
  icon: JSX.Element;
547
  text: string;
548
- onFileUpdate: (decoded: AudioBuffer, blobUrl: string, mimeType: string) => void;
 
 
 
 
549
  }) {
550
  // const audioPlayer = useRef<HTMLAudioElement>(null);
551
 
 
132
  export function AudioManager(props: { transcriber: Transcriber }) {
133
  const [progress, setProgress] = useState<number | undefined>(undefined);
134
  const [audioData, setAudioData] = useState<
135
+ | {
136
+ buffer: AudioBuffer;
137
+ url: string;
138
+ source: AudioSource;
139
+ mimeType: string;
140
+ }
141
+ | undefined
142
  >(undefined);
143
  const [audioDownloadUrl, setAudioDownloadUrl] = useState<
144
  string | undefined
 
151
  setAudioDownloadUrl(undefined);
152
  };
153
 
154
+ const setAudioFromDownload = async (
155
+ data: ArrayBuffer,
156
+ mimeType: string,
157
+ ) => {
158
  const audioCTX = new AudioContext({
159
  sampleRate: Constants.SAMPLING_RATE,
160
  });
 
208
  onDownloadProgress(progressEvent) {
209
  setProgress(progressEvent.progress || 0);
210
  },
211
+ })) as {
212
+ data: ArrayBuffer;
213
+ headers: { "content-type": string };
214
+ };
215
 
216
  let mimeType = headers["content-type"];
217
  if (!mimeType || mimeType === "audio/wave") {
 
259
  buffer: decoded,
260
  url: blobUrl,
261
  source: AudioSource.FILE,
262
+ mimeType: mimeType,
263
  });
264
  }}
265
  />
266
+ {navigator.mediaDevices && (
267
+ <>
268
+ <VerticalBar />
269
+ <RecordTile
270
+ icon={<MicrophoneIcon />}
271
+ text={"Record"}
272
+ setAudioData={(e) => {
273
+ props.transcriber.onInputChange();
274
+ setAudioFromRecording(e);
275
+ }}
276
+ />
277
+ </>
278
+ )}
279
  </div>
280
  {
281
  <AudioDataBar
 
285
  </div>
286
  {audioData && (
287
  <>
288
+ <AudioPlayer
289
+ audioUrl={audioData.url}
290
+ mimeType={audioData.mimeType}
291
+ />
292
 
293
  <div className='relative w-full flex justify-center items-center'>
294
  <TranscribeButton
 
395
  models[key].length == 2,
396
  )
397
  .map((key) => (
398
+ <option key={key} value={key}>{`whisper-${key}${
399
+ props.transcriber.multilingual ? "" : ".en"
400
+ } (${
401
  // @ts-ignore
402
  models[key][
403
+ props.transcriber.quantized ? 0 : 1
404
  ]
405
+ }MB)`}</option>
406
  ))}
407
  </select>
408
  <div className='flex justify-between items-center mb-3 px-1'>
 
475
  </>
476
  }
477
  onClose={props.onClose}
478
+ onSubmit={() => {}}
479
  />
480
  );
481
  }
 
562
  function FileTile(props: {
563
  icon: JSX.Element;
564
  text: string;
565
+ onFileUpdate: (
566
+ decoded: AudioBuffer,
567
+ blobUrl: string,
568
+ mimeType: string,
569
+ ) => void;
570
  }) {
571
  // const audioPlayer = useRef<HTMLAudioElement>(null);
572
 
src/components/AudioPlayer.tsx CHANGED
@@ -1,6 +1,9 @@
1
  import { useEffect, useRef } from "react";
2
 
3
- export default function AudioPlayer(props: { audioUrl: string, mimeType: string }) {
 
 
 
4
  const audioPlayer = useRef<HTMLAudioElement>(null);
5
  const audioSource = useRef<HTMLSourceElement>(null);
6
 
 
1
  import { useEffect, useRef } from "react";
2
 
3
+ export default function AudioPlayer(props: {
4
+ audioUrl: string;
5
+ mimeType: string;
6
+ }) {
7
  const audioPlayer = useRef<HTMLAudioElement>(null);
8
  const audioSource = useRef<HTMLSourceElement>(null);
9
 
src/components/AudioRecorder.tsx CHANGED
@@ -1,15 +1,15 @@
1
- import { useState, useEffect, useRef } from 'react';
2
 
3
- import { formatAudioTimestamp } from '../utils/AudioUtils';
4
- import { webmFixDuration } from '../utils/BlobFix';
5
 
6
  function getMimeType() {
7
  const types = [
8
- 'audio/webm',
9
- 'audio/mp4',
10
- 'audio/ogg',
11
- 'audio/wav',
12
- 'audio/aac'
13
  ];
14
  for (let i = 0; i < types.length; i++) {
15
  if (MediaRecorder.isTypeSupported(types[i])) {
@@ -40,15 +40,19 @@ export default function AudioRecorder(props: {
40
 
41
  try {
42
  if (!streamRef.current) {
43
- streamRef.current = await navigator.mediaDevices.getUserMedia({ audio: true });
 
 
44
  }
45
 
46
  const mimeType = getMimeType();
47
- const mediaRecorder = new MediaRecorder(streamRef.current, { mimeType });
 
 
48
 
49
  mediaRecorderRef.current = mediaRecorder;
50
 
51
- mediaRecorder.addEventListener('dataavailable', async (event) => {
52
  if (event.data.size > 0) {
53
  chunksRef.current.push(event.data);
54
  }
@@ -58,8 +62,8 @@ export default function AudioRecorder(props: {
58
  // Received a stop event
59
  let blob = new Blob(chunksRef.current, { type: mimeType });
60
 
61
- if (mimeType === 'audio/webm') {
62
- blob = await webmFixDuration(blob, duration, blob.type)
63
  }
64
 
65
  setRecordedBlob(blob);
@@ -70,14 +74,16 @@ export default function AudioRecorder(props: {
70
  });
71
  mediaRecorder.start();
72
  setRecording(true);
73
-
74
  } catch (error) {
75
- console.error('Error accessing microphone:', error);
76
  }
77
  };
78
 
79
  const stopRecording = () => {
80
- if (mediaRecorderRef.current && mediaRecorderRef.current.state === 'recording') {
 
 
 
81
  mediaRecorderRef.current.stop(); // set state to inactive
82
  setDuration(0);
83
  setRecording(false);
@@ -116,18 +122,26 @@ export default function AudioRecorder(props: {
116
  <div className='flex flex-col justify-center items-center'>
117
  <button
118
  type='button'
119
- className={`m-2 inline-flex justify-center rounded-md border border-transparent px-4 py-2 text-sm font-medium text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 transition-all duration-200 ${recording ? 'bg-red-500 hover:bg-red-600' : 'bg-green-500 hover:bg-green-600'
120
- }`}
 
 
 
121
  onClick={handleToggleRecording}
122
  >
123
- {recording ? `Stop Recording (${formatAudioTimestamp(duration)})` : 'Start Recording'}
 
 
124
  </button>
125
 
126
  {recordedBlob && (
127
  <audio className='w-full' ref={audioRef} controls>
128
- <source src={URL.createObjectURL(recordedBlob)} type={recordedBlob.type} />
 
 
 
129
  </audio>
130
  )}
131
  </div>
132
  );
133
- };
 
1
+ import { useState, useEffect, useRef } from "react";
2
 
3
+ import { formatAudioTimestamp } from "../utils/AudioUtils";
4
+ import { webmFixDuration } from "../utils/BlobFix";
5
 
6
  function getMimeType() {
7
  const types = [
8
+ "audio/webm",
9
+ "audio/mp4",
10
+ "audio/ogg",
11
+ "audio/wav",
12
+ "audio/aac",
13
  ];
14
  for (let i = 0; i < types.length; i++) {
15
  if (MediaRecorder.isTypeSupported(types[i])) {
 
40
 
41
  try {
42
  if (!streamRef.current) {
43
+ streamRef.current = await navigator.mediaDevices.getUserMedia({
44
+ audio: true,
45
+ });
46
  }
47
 
48
  const mimeType = getMimeType();
49
+ const mediaRecorder = new MediaRecorder(streamRef.current, {
50
+ mimeType,
51
+ });
52
 
53
  mediaRecorderRef.current = mediaRecorder;
54
 
55
+ mediaRecorder.addEventListener("dataavailable", async (event) => {
56
  if (event.data.size > 0) {
57
  chunksRef.current.push(event.data);
58
  }
 
62
  // Received a stop event
63
  let blob = new Blob(chunksRef.current, { type: mimeType });
64
 
65
+ if (mimeType === "audio/webm") {
66
+ blob = await webmFixDuration(blob, duration, blob.type);
67
  }
68
 
69
  setRecordedBlob(blob);
 
74
  });
75
  mediaRecorder.start();
76
  setRecording(true);
 
77
  } catch (error) {
78
+ console.error("Error accessing microphone:", error);
79
  }
80
  };
81
 
82
  const stopRecording = () => {
83
+ if (
84
+ mediaRecorderRef.current &&
85
+ mediaRecorderRef.current.state === "recording"
86
+ ) {
87
  mediaRecorderRef.current.stop(); // set state to inactive
88
  setDuration(0);
89
  setRecording(false);
 
122
  <div className='flex flex-col justify-center items-center'>
123
  <button
124
  type='button'
125
+ className={`m-2 inline-flex justify-center rounded-md border border-transparent px-4 py-2 text-sm font-medium text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 transition-all duration-200 ${
126
+ recording
127
+ ? "bg-red-500 hover:bg-red-600"
128
+ : "bg-green-500 hover:bg-green-600"
129
+ }`}
130
  onClick={handleToggleRecording}
131
  >
132
+ {recording
133
+ ? `Stop Recording (${formatAudioTimestamp(duration)})`
134
+ : "Start Recording"}
135
  </button>
136
 
137
  {recordedBlob && (
138
  <audio className='w-full' ref={audioRef} controls>
139
+ <source
140
+ src={URL.createObjectURL(recordedBlob)}
141
+ type={recordedBlob.type}
142
+ />
143
  </audio>
144
  )}
145
  </div>
146
  );
147
+ }
src/components/Transcript.tsx CHANGED
@@ -20,11 +20,14 @@ export default function Transcript({ transcribedData }: Props) {
20
  };
21
  const exportTXT = () => {
22
  let chunks = transcribedData?.chunks ?? [];
23
- let text = chunks.map((chunk) => chunk.text).join('').trim();
 
 
 
24
 
25
  const blob = new Blob([text], { type: "text/plain" });
26
  saveBlob(blob, "transcript.txt");
27
- }
28
  const exportJSON = () => {
29
  let jsonData = JSON.stringify(transcribedData?.chunks ?? [], null, 2);
30
 
@@ -41,8 +44,8 @@ export default function Transcript({ transcribedData }: Props) {
41
  if (divRef.current) {
42
  const diff = Math.abs(
43
  divRef.current.offsetHeight +
44
- divRef.current.scrollTop -
45
- divRef.current.scrollHeight,
46
  );
47
 
48
  if (diff <= 64) {
 
20
  };
21
  const exportTXT = () => {
22
  let chunks = transcribedData?.chunks ?? [];
23
+ let text = chunks
24
+ .map((chunk) => chunk.text)
25
+ .join("")
26
+ .trim();
27
 
28
  const blob = new Blob([text], { type: "text/plain" });
29
  saveBlob(blob, "transcript.txt");
30
+ };
31
  const exportJSON = () => {
32
  let jsonData = JSON.stringify(transcribedData?.chunks ?? [], null, 2);
33
 
 
44
  if (divRef.current) {
45
  const diff = Math.abs(
46
  divRef.current.offsetHeight +
47
+ divRef.current.scrollTop -
48
+ divRef.current.scrollHeight,
49
  );
50
 
51
  if (diff <= 64) {
src/utils/BlobFix.ts CHANGED
@@ -10,7 +10,6 @@
10
  * (forked from https://github.com/yusitnikov/fix-webm-duration)
11
  */
12
 
13
-
14
  /*
15
  * This is the list of possible WEBM file sections by their IDs.
16
  * Possible types: Container, Binary, Uint, Int, String, Float, Date
@@ -21,246 +20,246 @@ interface Section {
21
  }
22
 
23
  const sections: Record<number, Section> = {
24
- 0xa45dfa3: { name: 'EBML', type: 'Container' },
25
- 0x286: { name: 'EBMLVersion', type: 'Uint' },
26
- 0x2f7: { name: 'EBMLReadVersion', type: 'Uint' },
27
- 0x2f2: { name: 'EBMLMaxIDLength', type: 'Uint' },
28
- 0x2f3: { name: 'EBMLMaxSizeLength', type: 'Uint' },
29
- 0x282: { name: 'DocType', type: 'String' },
30
- 0x287: { name: 'DocTypeVersion', type: 'Uint' },
31
- 0x285: { name: 'DocTypeReadVersion', type: 'Uint' },
32
- 0x6c: { name: 'Void', type: 'Binary' },
33
- 0x3f: { name: 'CRC-32', type: 'Binary' },
34
- 0xb538667: { name: 'SignatureSlot', type: 'Container' },
35
- 0x3e8a: { name: 'SignatureAlgo', type: 'Uint' },
36
- 0x3e9a: { name: 'SignatureHash', type: 'Uint' },
37
- 0x3ea5: { name: 'SignaturePublicKey', type: 'Binary' },
38
- 0x3eb5: { name: 'Signature', type: 'Binary' },
39
- 0x3e5b: { name: 'SignatureElements', type: 'Container' },
40
- 0x3e7b: { name: 'SignatureElementList', type: 'Container' },
41
- 0x2532: { name: 'SignedElement', type: 'Binary' },
42
- 0x8538067: { name: 'Segment', type: 'Container' },
43
- 0x14d9b74: { name: 'SeekHead', type: 'Container' },
44
- 0xdbb: { name: 'Seek', type: 'Container' },
45
- 0x13ab: { name: 'SeekID', type: 'Binary' },
46
- 0x13ac: { name: 'SeekPosition', type: 'Uint' },
47
- 0x549a966: { name: 'Info', type: 'Container' },
48
- 0x33a4: { name: 'SegmentUID', type: 'Binary' },
49
- 0x3384: { name: 'SegmentFilename', type: 'String' },
50
- 0x1cb923: { name: 'PrevUID', type: 'Binary' },
51
- 0x1c83ab: { name: 'PrevFilename', type: 'String' },
52
- 0x1eb923: { name: 'NextUID', type: 'Binary' },
53
- 0x1e83bb: { name: 'NextFilename', type: 'String' },
54
- 0x444: { name: 'SegmentFamily', type: 'Binary' },
55
- 0x2924: { name: 'ChapterTranslate', type: 'Container' },
56
- 0x29fc: { name: 'ChapterTranslateEditionUID', type: 'Uint' },
57
- 0x29bf: { name: 'ChapterTranslateCodec', type: 'Uint' },
58
- 0x29a5: { name: 'ChapterTranslateID', type: 'Binary' },
59
- 0xad7b1: { name: 'TimecodeScale', type: 'Uint' },
60
- 0x489: { name: 'Duration', type: 'Float' },
61
- 0x461: { name: 'DateUTC', type: 'Date' },
62
- 0x3ba9: { name: 'Title', type: 'String' },
63
- 0xd80: { name: 'MuxingApp', type: 'String' },
64
- 0x1741: { name: 'WritingApp', type: 'String' },
65
  // 0xf43b675: { name: 'Cluster', type: 'Container' },
66
- 0x67: { name: 'Timecode', type: 'Uint' },
67
- 0x1854: { name: 'SilentTracks', type: 'Container' },
68
- 0x18d7: { name: 'SilentTrackNumber', type: 'Uint' },
69
- 0x27: { name: 'Position', type: 'Uint' },
70
- 0x2b: { name: 'PrevSize', type: 'Uint' },
71
- 0x23: { name: 'SimpleBlock', type: 'Binary' },
72
- 0x20: { name: 'BlockGroup', type: 'Container' },
73
- 0x21: { name: 'Block', type: 'Binary' },
74
- 0x22: { name: 'BlockVirtual', type: 'Binary' },
75
- 0x35a1: { name: 'BlockAdditions', type: 'Container' },
76
- 0x26: { name: 'BlockMore', type: 'Container' },
77
- 0x6e: { name: 'BlockAddID', type: 'Uint' },
78
- 0x25: { name: 'BlockAdditional', type: 'Binary' },
79
- 0x1b: { name: 'BlockDuration', type: 'Uint' },
80
- 0x7a: { name: 'ReferencePriority', type: 'Uint' },
81
- 0x7b: { name: 'ReferenceBlock', type: 'Int' },
82
- 0x7d: { name: 'ReferenceVirtual', type: 'Int' },
83
- 0x24: { name: 'CodecState', type: 'Binary' },
84
- 0x35a2: { name: 'DiscardPadding', type: 'Int' },
85
- 0xe: { name: 'Slices', type: 'Container' },
86
- 0x68: { name: 'TimeSlice', type: 'Container' },
87
- 0x4c: { name: 'LaceNumber', type: 'Uint' },
88
- 0x4d: { name: 'FrameNumber', type: 'Uint' },
89
- 0x4b: { name: 'BlockAdditionID', type: 'Uint' },
90
- 0x4e: { name: 'Delay', type: 'Uint' },
91
- 0x4f: { name: 'SliceDuration', type: 'Uint' },
92
- 0x48: { name: 'ReferenceFrame', type: 'Container' },
93
- 0x49: { name: 'ReferenceOffset', type: 'Uint' },
94
- 0x4a: { name: 'ReferenceTimeCode', type: 'Uint' },
95
- 0x2f: { name: 'EncryptedBlock', type: 'Binary' },
96
- 0x654ae6b: { name: 'Tracks', type: 'Container' },
97
- 0x2e: { name: 'TrackEntry', type: 'Container' },
98
- 0x57: { name: 'TrackNumber', type: 'Uint' },
99
- 0x33c5: { name: 'TrackUID', type: 'Uint' },
100
- 0x3: { name: 'TrackType', type: 'Uint' },
101
- 0x39: { name: 'FlagEnabled', type: 'Uint' },
102
- 0x8: { name: 'FlagDefault', type: 'Uint' },
103
- 0x15aa: { name: 'FlagForced', type: 'Uint' },
104
- 0x1c: { name: 'FlagLacing', type: 'Uint' },
105
- 0x2de7: { name: 'MinCache', type: 'Uint' },
106
- 0x2df8: { name: 'MaxCache', type: 'Uint' },
107
- 0x3e383: { name: 'DefaultDuration', type: 'Uint' },
108
- 0x34e7a: { name: 'DefaultDecodedFieldDuration', type: 'Uint' },
109
- 0x3314f: { name: 'TrackTimecodeScale', type: 'Float' },
110
- 0x137f: { name: 'TrackOffset', type: 'Int' },
111
- 0x15ee: { name: 'MaxBlockAdditionID', type: 'Uint' },
112
- 0x136e: { name: 'Name', type: 'String' },
113
- 0x2b59c: { name: 'Language', type: 'String' },
114
- 0x6: { name: 'CodecID', type: 'String' },
115
- 0x23a2: { name: 'CodecPrivate', type: 'Binary' },
116
- 0x58688: { name: 'CodecName', type: 'String' },
117
- 0x3446: { name: 'AttachmentLink', type: 'Uint' },
118
- 0x1a9697: { name: 'CodecSettings', type: 'String' },
119
- 0x1b4040: { name: 'CodecInfoURL', type: 'String' },
120
- 0x6b240: { name: 'CodecDownloadURL', type: 'String' },
121
- 0x2a: { name: 'CodecDecodeAll', type: 'Uint' },
122
- 0x2fab: { name: 'TrackOverlay', type: 'Uint' },
123
- 0x16aa: { name: 'CodecDelay', type: 'Uint' },
124
- 0x16bb: { name: 'SeekPreRoll', type: 'Uint' },
125
- 0x2624: { name: 'TrackTranslate', type: 'Container' },
126
- 0x26fc: { name: 'TrackTranslateEditionUID', type: 'Uint' },
127
- 0x26bf: { name: 'TrackTranslateCodec', type: 'Uint' },
128
- 0x26a5: { name: 'TrackTranslateTrackID', type: 'Binary' },
129
- 0x60: { name: 'Video', type: 'Container' },
130
- 0x1a: { name: 'FlagInterlaced', type: 'Uint' },
131
- 0x13b8: { name: 'StereoMode', type: 'Uint' },
132
- 0x13c0: { name: 'AlphaMode', type: 'Uint' },
133
- 0x13b9: { name: 'OldStereoMode', type: 'Uint' },
134
- 0x30: { name: 'PixelWidth', type: 'Uint' },
135
- 0x3a: { name: 'PixelHeight', type: 'Uint' },
136
- 0x14aa: { name: 'PixelCropBottom', type: 'Uint' },
137
- 0x14bb: { name: 'PixelCropTop', type: 'Uint' },
138
- 0x14cc: { name: 'PixelCropLeft', type: 'Uint' },
139
- 0x14dd: { name: 'PixelCropRight', type: 'Uint' },
140
- 0x14b0: { name: 'DisplayWidth', type: 'Uint' },
141
- 0x14ba: { name: 'DisplayHeight', type: 'Uint' },
142
- 0x14b2: { name: 'DisplayUnit', type: 'Uint' },
143
- 0x14b3: { name: 'AspectRatioType', type: 'Uint' },
144
- 0xeb524: { name: 'ColourSpace', type: 'Binary' },
145
- 0xfb523: { name: 'GammaValue', type: 'Float' },
146
- 0x383e3: { name: 'FrameRate', type: 'Float' },
147
- 0x61: { name: 'Audio', type: 'Container' },
148
- 0x35: { name: 'SamplingFrequency', type: 'Float' },
149
- 0x38b5: { name: 'OutputSamplingFrequency', type: 'Float' },
150
- 0x1f: { name: 'Channels', type: 'Uint' },
151
- 0x3d7b: { name: 'ChannelPositions', type: 'Binary' },
152
- 0x2264: { name: 'BitDepth', type: 'Uint' },
153
- 0x62: { name: 'TrackOperation', type: 'Container' },
154
- 0x63: { name: 'TrackCombinePlanes', type: 'Container' },
155
- 0x64: { name: 'TrackPlane', type: 'Container' },
156
- 0x65: { name: 'TrackPlaneUID', type: 'Uint' },
157
- 0x66: { name: 'TrackPlaneType', type: 'Uint' },
158
- 0x69: { name: 'TrackJoinBlocks', type: 'Container' },
159
- 0x6d: { name: 'TrackJoinUID', type: 'Uint' },
160
- 0x40: { name: 'TrickTrackUID', type: 'Uint' },
161
- 0x41: { name: 'TrickTrackSegmentUID', type: 'Binary' },
162
- 0x46: { name: 'TrickTrackFlag', type: 'Uint' },
163
- 0x47: { name: 'TrickMasterTrackUID', type: 'Uint' },
164
- 0x44: { name: 'TrickMasterTrackSegmentUID', type: 'Binary' },
165
- 0x2d80: { name: 'ContentEncodings', type: 'Container' },
166
- 0x2240: { name: 'ContentEncoding', type: 'Container' },
167
- 0x1031: { name: 'ContentEncodingOrder', type: 'Uint' },
168
- 0x1032: { name: 'ContentEncodingScope', type: 'Uint' },
169
- 0x1033: { name: 'ContentEncodingType', type: 'Uint' },
170
- 0x1034: { name: 'ContentCompression', type: 'Container' },
171
- 0x254: { name: 'ContentCompAlgo', type: 'Uint' },
172
- 0x255: { name: 'ContentCompSettings', type: 'Binary' },
173
- 0x1035: { name: 'ContentEncryption', type: 'Container' },
174
- 0x7e1: { name: 'ContentEncAlgo', type: 'Uint' },
175
- 0x7e2: { name: 'ContentEncKeyID', type: 'Binary' },
176
- 0x7e3: { name: 'ContentSignature', type: 'Binary' },
177
- 0x7e4: { name: 'ContentSigKeyID', type: 'Binary' },
178
- 0x7e5: { name: 'ContentSigAlgo', type: 'Uint' },
179
- 0x7e6: { name: 'ContentSigHashAlgo', type: 'Uint' },
180
- 0xc53bb6b: { name: 'Cues', type: 'Container' },
181
- 0x3b: { name: 'CuePoint', type: 'Container' },
182
- 0x33: { name: 'CueTime', type: 'Uint' },
183
- 0x37: { name: 'CueTrackPositions', type: 'Container' },
184
- 0x77: { name: 'CueTrack', type: 'Uint' },
185
- 0x71: { name: 'CueClusterPosition', type: 'Uint' },
186
- 0x70: { name: 'CueRelativePosition', type: 'Uint' },
187
- 0x32: { name: 'CueDuration', type: 'Uint' },
188
- 0x1378: { name: 'CueBlockNumber', type: 'Uint' },
189
- 0x6a: { name: 'CueCodecState', type: 'Uint' },
190
- 0x5b: { name: 'CueReference', type: 'Container' },
191
- 0x16: { name: 'CueRefTime', type: 'Uint' },
192
- 0x17: { name: 'CueRefCluster', type: 'Uint' },
193
- 0x135f: { name: 'CueRefNumber', type: 'Uint' },
194
- 0x6b: { name: 'CueRefCodecState', type: 'Uint' },
195
- 0x941a469: { name: 'Attachments', type: 'Container' },
196
- 0x21a7: { name: 'AttachedFile', type: 'Container' },
197
- 0x67e: { name: 'FileDescription', type: 'String' },
198
- 0x66e: { name: 'FileName', type: 'String' },
199
- 0x660: { name: 'FileMimeType', type: 'String' },
200
- 0x65c: { name: 'FileData', type: 'Binary' },
201
- 0x6ae: { name: 'FileUID', type: 'Uint' },
202
- 0x675: { name: 'FileReferral', type: 'Binary' },
203
- 0x661: { name: 'FileUsedStartTime', type: 'Uint' },
204
- 0x662: { name: 'FileUsedEndTime', type: 'Uint' },
205
- 0x43a770: { name: 'Chapters', type: 'Container' },
206
- 0x5b9: { name: 'EditionEntry', type: 'Container' },
207
- 0x5bc: { name: 'EditionUID', type: 'Uint' },
208
- 0x5bd: { name: 'EditionFlagHidden', type: 'Uint' },
209
- 0x5db: { name: 'EditionFlagDefault', type: 'Uint' },
210
- 0x5dd: { name: 'EditionFlagOrdered', type: 'Uint' },
211
- 0x36: { name: 'ChapterAtom', type: 'Container' },
212
- 0x33c4: { name: 'ChapterUID', type: 'Uint' },
213
- 0x1654: { name: 'ChapterStringUID', type: 'String' },
214
- 0x11: { name: 'ChapterTimeStart', type: 'Uint' },
215
- 0x12: { name: 'ChapterTimeEnd', type: 'Uint' },
216
- 0x18: { name: 'ChapterFlagHidden', type: 'Uint' },
217
- 0x598: { name: 'ChapterFlagEnabled', type: 'Uint' },
218
- 0x2e67: { name: 'ChapterSegmentUID', type: 'Binary' },
219
- 0x2ebc: { name: 'ChapterSegmentEditionUID', type: 'Uint' },
220
- 0x23c3: { name: 'ChapterPhysicalEquiv', type: 'Uint' },
221
- 0xf: { name: 'ChapterTrack', type: 'Container' },
222
- 0x9: { name: 'ChapterTrackNumber', type: 'Uint' },
223
- 0x0: { name: 'ChapterDisplay', type: 'Container' },
224
- 0x5: { name: 'ChapString', type: 'String' },
225
- 0x37c: { name: 'ChapLanguage', type: 'String' },
226
- 0x37e: { name: 'ChapCountry', type: 'String' },
227
- 0x2944: { name: 'ChapProcess', type: 'Container' },
228
- 0x2955: { name: 'ChapProcessCodecID', type: 'Uint' },
229
- 0x50d: { name: 'ChapProcessPrivate', type: 'Binary' },
230
- 0x2911: { name: 'ChapProcessCommand', type: 'Container' },
231
- 0x2922: { name: 'ChapProcessTime', type: 'Uint' },
232
- 0x2933: { name: 'ChapProcessData', type: 'Binary' },
233
- 0x254c367: { name: 'Tags', type: 'Container' },
234
- 0x3373: { name: 'Tag', type: 'Container' },
235
- 0x23c0: { name: 'Targets', type: 'Container' },
236
- 0x28ca: { name: 'TargetTypeValue', type: 'Uint' },
237
- 0x23ca: { name: 'TargetType', type: 'String' },
238
- 0x23c5: { name: 'TagTrackUID', type: 'Uint' },
239
- 0x23c9: { name: 'TagEditionUID', type: 'Uint' },
240
- 0x23c4: { name: 'TagChapterUID', type: 'Uint' },
241
- 0x23c6: { name: 'TagAttachmentUID', type: 'Uint' },
242
- 0x27c8: { name: 'SimpleTag', type: 'Container' },
243
- 0x5a3: { name: 'TagName', type: 'String' },
244
- 0x47a: { name: 'TagLanguage', type: 'String' },
245
- 0x484: { name: 'TagDefault', type: 'Uint' },
246
- 0x487: { name: 'TagString', type: 'String' },
247
- 0x485: { name: 'TagBinary', type: 'Binary' },
248
  };
249
 
250
  class WebmBase<T> {
251
  source?: Uint8Array;
252
  data?: T;
253
 
254
- constructor(private name = 'Unknown', private type = 'Unknown') { }
255
 
256
- updateBySource() { }
257
 
258
  setSource(source: Uint8Array) {
259
  this.source = source;
260
  this.updateBySource();
261
  }
262
 
263
- updateByData() { }
264
 
265
  setData(data: T) {
266
  this.data = data;
@@ -270,12 +269,12 @@ class WebmBase<T> {
270
 
271
  class WebmUint extends WebmBase<string> {
272
  constructor(name: string, type: string) {
273
- super(name, type || 'Uint');
274
  }
275
 
276
  updateBySource() {
277
  // use hex representation of a number instead of number value
278
- this.data = '';
279
  for (let i = 0; i < this.source!.length; i++) {
280
  const hex = this.source![i].toString(16);
281
  this.data += padHex(hex);
@@ -301,12 +300,12 @@ class WebmUint extends WebmBase<string> {
301
  }
302
 
303
  function padHex(hex: string) {
304
- return hex.length % 2 === 1 ? '0' + hex : hex;
305
  }
306
 
307
  class WebmFloat extends WebmBase<number> {
308
  constructor(name: string, type: string) {
309
- super(name, type || 'Float');
310
  }
311
 
312
  getFloatArrayType() {
@@ -345,7 +344,7 @@ class WebmContainer extends WebmBase<ContainerData[]> {
345
  data: ContainerData[] = [];
346
 
347
  constructor(name: string, type: string) {
348
- super(name, type || 'Container');
349
  }
350
 
351
  readByte() {
@@ -375,16 +374,16 @@ class WebmContainer extends WebmBase<ContainerData[]> {
375
  end = Math.min(this.offset + len, this.source!.length);
376
  const data = this.source!.slice(this.offset, end);
377
 
378
- const info = sections[id] || { name: 'Unknown', type: 'Unknown' };
379
  let ctr: any = WebmBase;
380
  switch (info.type) {
381
- case 'Container':
382
  ctr = WebmContainer;
383
  break;
384
- case 'Uint':
385
  ctr = WebmUint;
386
  break;
387
- case 'Float':
388
  ctr = WebmFloat;
389
  break;
390
  }
@@ -402,7 +401,7 @@ class WebmContainer extends WebmBase<ContainerData[]> {
402
  var bytes = 1, flag = 0x80;
403
  x >= flag && bytes < 8;
404
  bytes++, flag *= 0x80
405
- ) { }
406
 
407
  if (!draft) {
408
  let value = flag + x;
@@ -455,7 +454,7 @@ class WebmContainer extends WebmBase<ContainerData[]> {
455
 
456
  class WebmFile extends WebmContainer {
457
  constructor(source: Uint8Array) {
458
- super('File', 'File');
459
  this.setSource(source);
460
  }
461
 
@@ -466,13 +465,15 @@ class WebmFile extends WebmContainer {
466
  }
467
 
468
  const infoSection = segmentSection.getSectionById(
469
- 0x549a966
470
  ) as WebmContainer;
471
  if (!infoSection) {
472
  return false;
473
  }
474
 
475
- const timeScaleSection = infoSection.getSectionById(0xad7b1) as WebmFloat;
 
 
476
  if (!timeScaleSection) {
477
  return false;
478
  }
@@ -486,7 +487,7 @@ class WebmFile extends WebmContainer {
486
  }
487
  } else {
488
  // append Duration section
489
- durationSection = new WebmFloat('Duration', 'Float');
490
  durationSection.setValue(duration);
491
  infoSection.data.push({
492
  id: 0x489,
@@ -503,7 +504,7 @@ class WebmFile extends WebmContainer {
503
  return true;
504
  }
505
 
506
- toBlob(type = 'video/webm') {
507
  return new Blob([this.source!.buffer], { type });
508
  }
509
  }
@@ -518,13 +519,13 @@ class WebmFile extends WebmContainer {
518
  export const webmFixDuration = (
519
  blob: Blob,
520
  duration: number,
521
- type = 'video/webm'
522
  ): Promise<Blob> => {
523
  return new Promise((resolve, reject) => {
524
  try {
525
  const reader = new FileReader();
526
 
527
- reader.addEventListener('loadend', () => {
528
  try {
529
  const result = reader.result as ArrayBuffer;
530
  const file = new WebmFile(new Uint8Array(result));
@@ -538,7 +539,7 @@ export const webmFixDuration = (
538
  }
539
  });
540
 
541
- reader.addEventListener('error', () => reject());
542
 
543
  reader.readAsArrayBuffer(blob);
544
  } catch (ex) {
 
10
  * (forked from https://github.com/yusitnikov/fix-webm-duration)
11
  */
12
 
 
13
  /*
14
  * This is the list of possible WEBM file sections by their IDs.
15
  * Possible types: Container, Binary, Uint, Int, String, Float, Date
 
20
  }
21
 
22
  const sections: Record<number, Section> = {
23
+ 0xa45dfa3: { name: "EBML", type: "Container" },
24
+ 0x286: { name: "EBMLVersion", type: "Uint" },
25
+ 0x2f7: { name: "EBMLReadVersion", type: "Uint" },
26
+ 0x2f2: { name: "EBMLMaxIDLength", type: "Uint" },
27
+ 0x2f3: { name: "EBMLMaxSizeLength", type: "Uint" },
28
+ 0x282: { name: "DocType", type: "String" },
29
+ 0x287: { name: "DocTypeVersion", type: "Uint" },
30
+ 0x285: { name: "DocTypeReadVersion", type: "Uint" },
31
+ 0x6c: { name: "Void", type: "Binary" },
32
+ 0x3f: { name: "CRC-32", type: "Binary" },
33
+ 0xb538667: { name: "SignatureSlot", type: "Container" },
34
+ 0x3e8a: { name: "SignatureAlgo", type: "Uint" },
35
+ 0x3e9a: { name: "SignatureHash", type: "Uint" },
36
+ 0x3ea5: { name: "SignaturePublicKey", type: "Binary" },
37
+ 0x3eb5: { name: "Signature", type: "Binary" },
38
+ 0x3e5b: { name: "SignatureElements", type: "Container" },
39
+ 0x3e7b: { name: "SignatureElementList", type: "Container" },
40
+ 0x2532: { name: "SignedElement", type: "Binary" },
41
+ 0x8538067: { name: "Segment", type: "Container" },
42
+ 0x14d9b74: { name: "SeekHead", type: "Container" },
43
+ 0xdbb: { name: "Seek", type: "Container" },
44
+ 0x13ab: { name: "SeekID", type: "Binary" },
45
+ 0x13ac: { name: "SeekPosition", type: "Uint" },
46
+ 0x549a966: { name: "Info", type: "Container" },
47
+ 0x33a4: { name: "SegmentUID", type: "Binary" },
48
+ 0x3384: { name: "SegmentFilename", type: "String" },
49
+ 0x1cb923: { name: "PrevUID", type: "Binary" },
50
+ 0x1c83ab: { name: "PrevFilename", type: "String" },
51
+ 0x1eb923: { name: "NextUID", type: "Binary" },
52
+ 0x1e83bb: { name: "NextFilename", type: "String" },
53
+ 0x444: { name: "SegmentFamily", type: "Binary" },
54
+ 0x2924: { name: "ChapterTranslate", type: "Container" },
55
+ 0x29fc: { name: "ChapterTranslateEditionUID", type: "Uint" },
56
+ 0x29bf: { name: "ChapterTranslateCodec", type: "Uint" },
57
+ 0x29a5: { name: "ChapterTranslateID", type: "Binary" },
58
+ 0xad7b1: { name: "TimecodeScale", type: "Uint" },
59
+ 0x489: { name: "Duration", type: "Float" },
60
+ 0x461: { name: "DateUTC", type: "Date" },
61
+ 0x3ba9: { name: "Title", type: "String" },
62
+ 0xd80: { name: "MuxingApp", type: "String" },
63
+ 0x1741: { name: "WritingApp", type: "String" },
64
  // 0xf43b675: { name: 'Cluster', type: 'Container' },
65
+ 0x67: { name: "Timecode", type: "Uint" },
66
+ 0x1854: { name: "SilentTracks", type: "Container" },
67
+ 0x18d7: { name: "SilentTrackNumber", type: "Uint" },
68
+ 0x27: { name: "Position", type: "Uint" },
69
+ 0x2b: { name: "PrevSize", type: "Uint" },
70
+ 0x23: { name: "SimpleBlock", type: "Binary" },
71
+ 0x20: { name: "BlockGroup", type: "Container" },
72
+ 0x21: { name: "Block", type: "Binary" },
73
+ 0x22: { name: "BlockVirtual", type: "Binary" },
74
+ 0x35a1: { name: "BlockAdditions", type: "Container" },
75
+ 0x26: { name: "BlockMore", type: "Container" },
76
+ 0x6e: { name: "BlockAddID", type: "Uint" },
77
+ 0x25: { name: "BlockAdditional", type: "Binary" },
78
+ 0x1b: { name: "BlockDuration", type: "Uint" },
79
+ 0x7a: { name: "ReferencePriority", type: "Uint" },
80
+ 0x7b: { name: "ReferenceBlock", type: "Int" },
81
+ 0x7d: { name: "ReferenceVirtual", type: "Int" },
82
+ 0x24: { name: "CodecState", type: "Binary" },
83
+ 0x35a2: { name: "DiscardPadding", type: "Int" },
84
+ 0xe: { name: "Slices", type: "Container" },
85
+ 0x68: { name: "TimeSlice", type: "Container" },
86
+ 0x4c: { name: "LaceNumber", type: "Uint" },
87
+ 0x4d: { name: "FrameNumber", type: "Uint" },
88
+ 0x4b: { name: "BlockAdditionID", type: "Uint" },
89
+ 0x4e: { name: "Delay", type: "Uint" },
90
+ 0x4f: { name: "SliceDuration", type: "Uint" },
91
+ 0x48: { name: "ReferenceFrame", type: "Container" },
92
+ 0x49: { name: "ReferenceOffset", type: "Uint" },
93
+ 0x4a: { name: "ReferenceTimeCode", type: "Uint" },
94
+ 0x2f: { name: "EncryptedBlock", type: "Binary" },
95
+ 0x654ae6b: { name: "Tracks", type: "Container" },
96
+ 0x2e: { name: "TrackEntry", type: "Container" },
97
+ 0x57: { name: "TrackNumber", type: "Uint" },
98
+ 0x33c5: { name: "TrackUID", type: "Uint" },
99
+ 0x3: { name: "TrackType", type: "Uint" },
100
+ 0x39: { name: "FlagEnabled", type: "Uint" },
101
+ 0x8: { name: "FlagDefault", type: "Uint" },
102
+ 0x15aa: { name: "FlagForced", type: "Uint" },
103
+ 0x1c: { name: "FlagLacing", type: "Uint" },
104
+ 0x2de7: { name: "MinCache", type: "Uint" },
105
+ 0x2df8: { name: "MaxCache", type: "Uint" },
106
+ 0x3e383: { name: "DefaultDuration", type: "Uint" },
107
+ 0x34e7a: { name: "DefaultDecodedFieldDuration", type: "Uint" },
108
+ 0x3314f: { name: "TrackTimecodeScale", type: "Float" },
109
+ 0x137f: { name: "TrackOffset", type: "Int" },
110
+ 0x15ee: { name: "MaxBlockAdditionID", type: "Uint" },
111
+ 0x136e: { name: "Name", type: "String" },
112
+ 0x2b59c: { name: "Language", type: "String" },
113
+ 0x6: { name: "CodecID", type: "String" },
114
+ 0x23a2: { name: "CodecPrivate", type: "Binary" },
115
+ 0x58688: { name: "CodecName", type: "String" },
116
+ 0x3446: { name: "AttachmentLink", type: "Uint" },
117
+ 0x1a9697: { name: "CodecSettings", type: "String" },
118
+ 0x1b4040: { name: "CodecInfoURL", type: "String" },
119
+ 0x6b240: { name: "CodecDownloadURL", type: "String" },
120
+ 0x2a: { name: "CodecDecodeAll", type: "Uint" },
121
+ 0x2fab: { name: "TrackOverlay", type: "Uint" },
122
+ 0x16aa: { name: "CodecDelay", type: "Uint" },
123
+ 0x16bb: { name: "SeekPreRoll", type: "Uint" },
124
+ 0x2624: { name: "TrackTranslate", type: "Container" },
125
+ 0x26fc: { name: "TrackTranslateEditionUID", type: "Uint" },
126
+ 0x26bf: { name: "TrackTranslateCodec", type: "Uint" },
127
+ 0x26a5: { name: "TrackTranslateTrackID", type: "Binary" },
128
+ 0x60: { name: "Video", type: "Container" },
129
+ 0x1a: { name: "FlagInterlaced", type: "Uint" },
130
+ 0x13b8: { name: "StereoMode", type: "Uint" },
131
+ 0x13c0: { name: "AlphaMode", type: "Uint" },
132
+ 0x13b9: { name: "OldStereoMode", type: "Uint" },
133
+ 0x30: { name: "PixelWidth", type: "Uint" },
134
+ 0x3a: { name: "PixelHeight", type: "Uint" },
135
+ 0x14aa: { name: "PixelCropBottom", type: "Uint" },
136
+ 0x14bb: { name: "PixelCropTop", type: "Uint" },
137
+ 0x14cc: { name: "PixelCropLeft", type: "Uint" },
138
+ 0x14dd: { name: "PixelCropRight", type: "Uint" },
139
+ 0x14b0: { name: "DisplayWidth", type: "Uint" },
140
+ 0x14ba: { name: "DisplayHeight", type: "Uint" },
141
+ 0x14b2: { name: "DisplayUnit", type: "Uint" },
142
+ 0x14b3: { name: "AspectRatioType", type: "Uint" },
143
+ 0xeb524: { name: "ColourSpace", type: "Binary" },
144
+ 0xfb523: { name: "GammaValue", type: "Float" },
145
+ 0x383e3: { name: "FrameRate", type: "Float" },
146
+ 0x61: { name: "Audio", type: "Container" },
147
+ 0x35: { name: "SamplingFrequency", type: "Float" },
148
+ 0x38b5: { name: "OutputSamplingFrequency", type: "Float" },
149
+ 0x1f: { name: "Channels", type: "Uint" },
150
+ 0x3d7b: { name: "ChannelPositions", type: "Binary" },
151
+ 0x2264: { name: "BitDepth", type: "Uint" },
152
+ 0x62: { name: "TrackOperation", type: "Container" },
153
+ 0x63: { name: "TrackCombinePlanes", type: "Container" },
154
+ 0x64: { name: "TrackPlane", type: "Container" },
155
+ 0x65: { name: "TrackPlaneUID", type: "Uint" },
156
+ 0x66: { name: "TrackPlaneType", type: "Uint" },
157
+ 0x69: { name: "TrackJoinBlocks", type: "Container" },
158
+ 0x6d: { name: "TrackJoinUID", type: "Uint" },
159
+ 0x40: { name: "TrickTrackUID", type: "Uint" },
160
+ 0x41: { name: "TrickTrackSegmentUID", type: "Binary" },
161
+ 0x46: { name: "TrickTrackFlag", type: "Uint" },
162
+ 0x47: { name: "TrickMasterTrackUID", type: "Uint" },
163
+ 0x44: { name: "TrickMasterTrackSegmentUID", type: "Binary" },
164
+ 0x2d80: { name: "ContentEncodings", type: "Container" },
165
+ 0x2240: { name: "ContentEncoding", type: "Container" },
166
+ 0x1031: { name: "ContentEncodingOrder", type: "Uint" },
167
+ 0x1032: { name: "ContentEncodingScope", type: "Uint" },
168
+ 0x1033: { name: "ContentEncodingType", type: "Uint" },
169
+ 0x1034: { name: "ContentCompression", type: "Container" },
170
+ 0x254: { name: "ContentCompAlgo", type: "Uint" },
171
+ 0x255: { name: "ContentCompSettings", type: "Binary" },
172
+ 0x1035: { name: "ContentEncryption", type: "Container" },
173
+ 0x7e1: { name: "ContentEncAlgo", type: "Uint" },
174
+ 0x7e2: { name: "ContentEncKeyID", type: "Binary" },
175
+ 0x7e3: { name: "ContentSignature", type: "Binary" },
176
+ 0x7e4: { name: "ContentSigKeyID", type: "Binary" },
177
+ 0x7e5: { name: "ContentSigAlgo", type: "Uint" },
178
+ 0x7e6: { name: "ContentSigHashAlgo", type: "Uint" },
179
+ 0xc53bb6b: { name: "Cues", type: "Container" },
180
+ 0x3b: { name: "CuePoint", type: "Container" },
181
+ 0x33: { name: "CueTime", type: "Uint" },
182
+ 0x37: { name: "CueTrackPositions", type: "Container" },
183
+ 0x77: { name: "CueTrack", type: "Uint" },
184
+ 0x71: { name: "CueClusterPosition", type: "Uint" },
185
+ 0x70: { name: "CueRelativePosition", type: "Uint" },
186
+ 0x32: { name: "CueDuration", type: "Uint" },
187
+ 0x1378: { name: "CueBlockNumber", type: "Uint" },
188
+ 0x6a: { name: "CueCodecState", type: "Uint" },
189
+ 0x5b: { name: "CueReference", type: "Container" },
190
+ 0x16: { name: "CueRefTime", type: "Uint" },
191
+ 0x17: { name: "CueRefCluster", type: "Uint" },
192
+ 0x135f: { name: "CueRefNumber", type: "Uint" },
193
+ 0x6b: { name: "CueRefCodecState", type: "Uint" },
194
+ 0x941a469: { name: "Attachments", type: "Container" },
195
+ 0x21a7: { name: "AttachedFile", type: "Container" },
196
+ 0x67e: { name: "FileDescription", type: "String" },
197
+ 0x66e: { name: "FileName", type: "String" },
198
+ 0x660: { name: "FileMimeType", type: "String" },
199
+ 0x65c: { name: "FileData", type: "Binary" },
200
+ 0x6ae: { name: "FileUID", type: "Uint" },
201
+ 0x675: { name: "FileReferral", type: "Binary" },
202
+ 0x661: { name: "FileUsedStartTime", type: "Uint" },
203
+ 0x662: { name: "FileUsedEndTime", type: "Uint" },
204
+ 0x43a770: { name: "Chapters", type: "Container" },
205
+ 0x5b9: { name: "EditionEntry", type: "Container" },
206
+ 0x5bc: { name: "EditionUID", type: "Uint" },
207
+ 0x5bd: { name: "EditionFlagHidden", type: "Uint" },
208
+ 0x5db: { name: "EditionFlagDefault", type: "Uint" },
209
+ 0x5dd: { name: "EditionFlagOrdered", type: "Uint" },
210
+ 0x36: { name: "ChapterAtom", type: "Container" },
211
+ 0x33c4: { name: "ChapterUID", type: "Uint" },
212
+ 0x1654: { name: "ChapterStringUID", type: "String" },
213
+ 0x11: { name: "ChapterTimeStart", type: "Uint" },
214
+ 0x12: { name: "ChapterTimeEnd", type: "Uint" },
215
+ 0x18: { name: "ChapterFlagHidden", type: "Uint" },
216
+ 0x598: { name: "ChapterFlagEnabled", type: "Uint" },
217
+ 0x2e67: { name: "ChapterSegmentUID", type: "Binary" },
218
+ 0x2ebc: { name: "ChapterSegmentEditionUID", type: "Uint" },
219
+ 0x23c3: { name: "ChapterPhysicalEquiv", type: "Uint" },
220
+ 0xf: { name: "ChapterTrack", type: "Container" },
221
+ 0x9: { name: "ChapterTrackNumber", type: "Uint" },
222
+ 0x0: { name: "ChapterDisplay", type: "Container" },
223
+ 0x5: { name: "ChapString", type: "String" },
224
+ 0x37c: { name: "ChapLanguage", type: "String" },
225
+ 0x37e: { name: "ChapCountry", type: "String" },
226
+ 0x2944: { name: "ChapProcess", type: "Container" },
227
+ 0x2955: { name: "ChapProcessCodecID", type: "Uint" },
228
+ 0x50d: { name: "ChapProcessPrivate", type: "Binary" },
229
+ 0x2911: { name: "ChapProcessCommand", type: "Container" },
230
+ 0x2922: { name: "ChapProcessTime", type: "Uint" },
231
+ 0x2933: { name: "ChapProcessData", type: "Binary" },
232
+ 0x254c367: { name: "Tags", type: "Container" },
233
+ 0x3373: { name: "Tag", type: "Container" },
234
+ 0x23c0: { name: "Targets", type: "Container" },
235
+ 0x28ca: { name: "TargetTypeValue", type: "Uint" },
236
+ 0x23ca: { name: "TargetType", type: "String" },
237
+ 0x23c5: { name: "TagTrackUID", type: "Uint" },
238
+ 0x23c9: { name: "TagEditionUID", type: "Uint" },
239
+ 0x23c4: { name: "TagChapterUID", type: "Uint" },
240
+ 0x23c6: { name: "TagAttachmentUID", type: "Uint" },
241
+ 0x27c8: { name: "SimpleTag", type: "Container" },
242
+ 0x5a3: { name: "TagName", type: "String" },
243
+ 0x47a: { name: "TagLanguage", type: "String" },
244
+ 0x484: { name: "TagDefault", type: "Uint" },
245
+ 0x487: { name: "TagString", type: "String" },
246
+ 0x485: { name: "TagBinary", type: "Binary" },
247
  };
248
 
249
  class WebmBase<T> {
250
  source?: Uint8Array;
251
  data?: T;
252
 
253
+ constructor(private name = "Unknown", private type = "Unknown") {}
254
 
255
+ updateBySource() {}
256
 
257
  setSource(source: Uint8Array) {
258
  this.source = source;
259
  this.updateBySource();
260
  }
261
 
262
+ updateByData() {}
263
 
264
  setData(data: T) {
265
  this.data = data;
 
269
 
270
  class WebmUint extends WebmBase<string> {
271
  constructor(name: string, type: string) {
272
+ super(name, type || "Uint");
273
  }
274
 
275
  updateBySource() {
276
  // use hex representation of a number instead of number value
277
+ this.data = "";
278
  for (let i = 0; i < this.source!.length; i++) {
279
  const hex = this.source![i].toString(16);
280
  this.data += padHex(hex);
 
300
  }
301
 
302
  function padHex(hex: string) {
303
+ return hex.length % 2 === 1 ? "0" + hex : hex;
304
  }
305
 
306
  class WebmFloat extends WebmBase<number> {
307
  constructor(name: string, type: string) {
308
+ super(name, type || "Float");
309
  }
310
 
311
  getFloatArrayType() {
 
344
  data: ContainerData[] = [];
345
 
346
  constructor(name: string, type: string) {
347
+ super(name, type || "Container");
348
  }
349
 
350
  readByte() {
 
374
  end = Math.min(this.offset + len, this.source!.length);
375
  const data = this.source!.slice(this.offset, end);
376
 
377
+ const info = sections[id] || { name: "Unknown", type: "Unknown" };
378
  let ctr: any = WebmBase;
379
  switch (info.type) {
380
+ case "Container":
381
  ctr = WebmContainer;
382
  break;
383
+ case "Uint":
384
  ctr = WebmUint;
385
  break;
386
+ case "Float":
387
  ctr = WebmFloat;
388
  break;
389
  }
 
401
  var bytes = 1, flag = 0x80;
402
  x >= flag && bytes < 8;
403
  bytes++, flag *= 0x80
404
+ ) {}
405
 
406
  if (!draft) {
407
  let value = flag + x;
 
454
 
455
  class WebmFile extends WebmContainer {
456
  constructor(source: Uint8Array) {
457
+ super("File", "File");
458
  this.setSource(source);
459
  }
460
 
 
465
  }
466
 
467
  const infoSection = segmentSection.getSectionById(
468
+ 0x549a966,
469
  ) as WebmContainer;
470
  if (!infoSection) {
471
  return false;
472
  }
473
 
474
+ const timeScaleSection = infoSection.getSectionById(
475
+ 0xad7b1,
476
+ ) as WebmFloat;
477
  if (!timeScaleSection) {
478
  return false;
479
  }
 
487
  }
488
  } else {
489
  // append Duration section
490
+ durationSection = new WebmFloat("Duration", "Float");
491
  durationSection.setValue(duration);
492
  infoSection.data.push({
493
  id: 0x489,
 
504
  return true;
505
  }
506
 
507
+ toBlob(type = "video/webm") {
508
  return new Blob([this.source!.buffer], { type });
509
  }
510
  }
 
519
  export const webmFixDuration = (
520
  blob: Blob,
521
  duration: number,
522
+ type = "video/webm",
523
  ): Promise<Blob> => {
524
  return new Promise((resolve, reject) => {
525
  try {
526
  const reader = new FileReader();
527
 
528
+ reader.addEventListener("loadend", () => {
529
  try {
530
  const result = reader.result as ArrayBuffer;
531
  const file = new WebmFile(new Uint8Array(result));
 
539
  }
540
  });
541
 
542
+ reader.addEventListener("error", () => reject());
543
 
544
  reader.readAsArrayBuffer(blob);
545
  } catch (ex) {