@exode-team/react-recorder 1.1.14 → 1.1.15
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/dist/components/AudioRecorder.js +50 -0
- package/dist/components/AudioRecorder.js.map +1 -0
- package/dist/components/AudioRecorder.messages.js +47 -0
- package/dist/components/AudioRecorder.messages.js.map +1 -0
- package/dist/components/AudioRecorder.styled.js +16 -0
- package/dist/components/AudioRecorder.styled.js.map +1 -0
- package/dist/components/IdleControl/IdleControl.js +12 -0
- package/dist/components/IdleControl/IdleControl.js.map +1 -0
- package/dist/components/IdleControl/IdleControl.styled.js +68 -0
- package/dist/components/IdleControl/IdleControl.styled.js.map +1 -0
- package/dist/components/RecordingShell/RecordingShell.js +80 -0
- package/dist/components/RecordingShell/RecordingShell.js.map +1 -0
- package/dist/components/RecordingShell/RecordingShell.styled.js +159 -0
- package/dist/components/RecordingShell/RecordingShell.styled.js.map +1 -0
- package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.js +151 -0
- package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.js.map +1 -0
- package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.styled.js +92 -0
- package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.styled.js.map +1 -0
- package/dist/components/VoiceRecorderInput/PreviewBars.js +52 -0
- package/dist/components/VoiceRecorderInput/PreviewBars.js.map +1 -0
- package/dist/components/VoiceRecorderInput/TimelineBars.js +31 -0
- package/dist/components/VoiceRecorderInput/TimelineBars.js.map +1 -0
- package/dist/components/VoiceRecorderInput/VoiceRecorderInput.js +51 -0
- package/dist/components/VoiceRecorderInput/VoiceRecorderInput.js.map +1 -0
- package/dist/components/VoiceRecorderInput/VoiceRecorderInput.styled.js +175 -0
- package/dist/components/VoiceRecorderInput/VoiceRecorderInput.styled.js.map +1 -0
- package/dist/components/WaveformBars/WaveformBars.js +29 -0
- package/dist/components/WaveformBars/WaveformBars.js.map +1 -0
- package/dist/components/WaveformBars/WaveformBars.styled.js +20 -0
- package/dist/components/WaveformBars/WaveformBars.styled.js.map +1 -0
- package/dist/constants.js +17 -0
- package/dist/constants.js.map +1 -0
- package/dist/hooks/useAudioBlobLoader.js +175 -0
- package/dist/hooks/useAudioBlobLoader.js.map +1 -0
- package/dist/hooks/useAudioPlayback.js +119 -0
- package/dist/hooks/useAudioPlayback.js.map +1 -0
- package/dist/hooks/useAudioRecorderHandlers.js +137 -0
- package/dist/hooks/useAudioRecorderHandlers.js.map +1 -0
- package/dist/hooks/useMediaRecorder.js +127 -0
- package/dist/hooks/useMediaRecorder.js.map +1 -0
- package/dist/hooks/useRecorderVisualization.js +94 -0
- package/dist/hooks/useRecorderVisualization.js.map +1 -0
- package/dist/hooks/useRecordingSender.js +53 -0
- package/dist/hooks/useRecordingSender.js.map +1 -0
- package/dist/hooks/useRecordingTiming.js +93 -0
- package/dist/hooks/useRecordingTiming.js.map +1 -0
- package/dist/hooks/useVoiceMessagePlayer.js +177 -0
- package/dist/hooks/useVoiceMessagePlayer.js.map +1 -0
- package/dist/hooks/useVoiceRecorder.js +153 -0
- package/dist/hooks/useVoiceRecorder.js.map +1 -0
- package/dist/hooks/useVoiceRecorderInput.js +163 -0
- package/dist/hooks/useVoiceRecorderInput.js.map +1 -0
- package/dist/index.js +13 -2659
- package/dist/index.js.map +1 -1
- package/dist/platform/animation-frame-adapter.js +14 -0
- package/dist/platform/animation-frame-adapter.js.map +1 -0
- package/dist/platform/audio-analyzer-adapter.js +74 -0
- package/dist/platform/audio-analyzer-adapter.js.map +1 -0
- package/dist/platform/audio-blob-fetcher.js +19 -0
- package/dist/platform/audio-blob-fetcher.js.map +1 -0
- package/dist/platform/audio-playback-adapter.js +114 -0
- package/dist/platform/audio-playback-adapter.js.map +1 -0
- package/dist/platform/audio-playback-manager.js +26 -0
- package/dist/platform/audio-playback-manager.js.map +1 -0
- package/dist/platform/media-recorder-adapter.js +23 -0
- package/dist/platform/media-recorder-adapter.js.map +1 -0
- package/dist/platform/microphone.js +24 -0
- package/dist/platform/microphone.js.map +1 -0
- package/dist/platform/mpeg-frame.js +60 -0
- package/dist/platform/mpeg-frame.js.map +1 -0
- package/dist/utils/format.js +13 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/math.js +16 -0
- package/dist/utils/math.js.map +1 -0
- package/dist/utils/pseudo-waveform.js +40 -0
- package/dist/utils/pseudo-waveform.js.map +1 -0
- package/dist/utils/voice-blob-cache.js +35 -0
- package/dist/utils/voice-blob-cache.js.map +1 -0
- package/dist/utils/voice-playback-chain.js +42 -0
- package/dist/utils/voice-playback-chain.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio playback platform adapter
|
|
3
|
+
*
|
|
4
|
+
* @author: exode <hello@exode.ru>
|
|
5
|
+
*/
|
|
6
|
+
const SEEK_OFFSET = 5;
|
|
7
|
+
const setupMediaSession = (audio, callbacks) => {
|
|
8
|
+
if (!('mediaSession' in navigator)) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
navigator.mediaSession.setActionHandler('play', () => {
|
|
12
|
+
audio.play().catch(() => { });
|
|
13
|
+
});
|
|
14
|
+
navigator.mediaSession.setActionHandler('pause', () => {
|
|
15
|
+
audio.pause();
|
|
16
|
+
});
|
|
17
|
+
navigator.mediaSession.setActionHandler('stop', () => {
|
|
18
|
+
var _a;
|
|
19
|
+
audio.pause();
|
|
20
|
+
audio.currentTime = 0;
|
|
21
|
+
(_a = callbacks.onStop) === null || _a === void 0 ? void 0 : _a.call(callbacks);
|
|
22
|
+
});
|
|
23
|
+
navigator.mediaSession.setActionHandler('seekbackward', () => {
|
|
24
|
+
var _a;
|
|
25
|
+
audio.currentTime = Math.max(0, audio.currentTime - SEEK_OFFSET);
|
|
26
|
+
(_a = callbacks.onSeekBackward) === null || _a === void 0 ? void 0 : _a.call(callbacks);
|
|
27
|
+
});
|
|
28
|
+
navigator.mediaSession.setActionHandler('seekforward', () => {
|
|
29
|
+
var _a;
|
|
30
|
+
audio.currentTime = Math.min(audio.duration, audio.currentTime + SEEK_OFFSET);
|
|
31
|
+
(_a = callbacks.onSeekForward) === null || _a === void 0 ? void 0 : _a.call(callbacks);
|
|
32
|
+
});
|
|
33
|
+
navigator.mediaSession.setActionHandler('seekto', (details) => {
|
|
34
|
+
if (details.seekTime != null && isFinite(audio.duration)) {
|
|
35
|
+
audio.currentTime = details.seekTime;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
navigator.mediaSession.setActionHandler('previoustrack', () => {
|
|
39
|
+
var _a;
|
|
40
|
+
(_a = callbacks.onPreviousTrack) === null || _a === void 0 ? void 0 : _a.call(callbacks);
|
|
41
|
+
});
|
|
42
|
+
navigator.mediaSession.setActionHandler('nexttrack', () => {
|
|
43
|
+
var _a;
|
|
44
|
+
(_a = callbacks.onNextTrack) === null || _a === void 0 ? void 0 : _a.call(callbacks);
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
const clearMediaSession = () => {
|
|
48
|
+
if (!('mediaSession' in navigator)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const actions = [
|
|
52
|
+
'play', 'pause', 'stop',
|
|
53
|
+
'seekbackward', 'seekforward', 'seekto',
|
|
54
|
+
'previoustrack', 'nexttrack',
|
|
55
|
+
];
|
|
56
|
+
actions.forEach((action) => {
|
|
57
|
+
navigator.mediaSession.setActionHandler(action, null);
|
|
58
|
+
});
|
|
59
|
+
navigator.mediaSession.playbackState = 'none';
|
|
60
|
+
};
|
|
61
|
+
const createAudioPlayback = (url, callbacks) => {
|
|
62
|
+
const audio = new Audio(url);
|
|
63
|
+
/**
|
|
64
|
+
* A detached element is not reliably rendered by every WebKit runtime
|
|
65
|
+
* (notably iPad apps running on macOS), so keep it in the document.
|
|
66
|
+
*/
|
|
67
|
+
audio.style.display = 'none';
|
|
68
|
+
document.body.appendChild(audio);
|
|
69
|
+
audio.onloadedmetadata = () => {
|
|
70
|
+
callbacks.onLoadedMetadata(audio.duration);
|
|
71
|
+
};
|
|
72
|
+
audio.ontimeupdate = () => {
|
|
73
|
+
callbacks.onTimeUpdate(audio.currentTime);
|
|
74
|
+
};
|
|
75
|
+
audio.onended = () => {
|
|
76
|
+
callbacks.onEnded();
|
|
77
|
+
};
|
|
78
|
+
audio.onplay = () => {
|
|
79
|
+
var _a;
|
|
80
|
+
setupMediaSession(audio, callbacks);
|
|
81
|
+
if ('mediaSession' in navigator) {
|
|
82
|
+
navigator.mediaSession.playbackState = 'playing';
|
|
83
|
+
}
|
|
84
|
+
(_a = callbacks.onPlay) === null || _a === void 0 ? void 0 : _a.call(callbacks);
|
|
85
|
+
};
|
|
86
|
+
audio.onpause = () => {
|
|
87
|
+
var _a;
|
|
88
|
+
if ('mediaSession' in navigator) {
|
|
89
|
+
navigator.mediaSession.playbackState = 'paused';
|
|
90
|
+
}
|
|
91
|
+
(_a = callbacks.onPause) === null || _a === void 0 ? void 0 : _a.call(callbacks);
|
|
92
|
+
};
|
|
93
|
+
return { audio, url };
|
|
94
|
+
};
|
|
95
|
+
const cleanupAudio = (audio) => {
|
|
96
|
+
audio.pause();
|
|
97
|
+
audio.remove();
|
|
98
|
+
audio.src = '';
|
|
99
|
+
audio.onended = null;
|
|
100
|
+
audio.ontimeupdate = null;
|
|
101
|
+
audio.onloadedmetadata = null;
|
|
102
|
+
audio.onplay = null;
|
|
103
|
+
audio.onpause = null;
|
|
104
|
+
clearMediaSession();
|
|
105
|
+
};
|
|
106
|
+
const createObjectURL = (blob) => {
|
|
107
|
+
return URL.createObjectURL(blob);
|
|
108
|
+
};
|
|
109
|
+
const revokeObjectURL = (url) => {
|
|
110
|
+
URL.revokeObjectURL(url);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export { cleanupAudio, createAudioPlayback, createObjectURL, revokeObjectURL };
|
|
114
|
+
//# sourceMappingURL=audio-playback-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-playback-adapter.js","sources":["../../src/platform/audio-playback-adapter.ts"],"sourcesContent":["/**\n * Audio playback platform adapter\n *\n * @author: exode <hello@exode.ru>\n */\n\n\nexport interface AudioPlayback {\n audio: HTMLAudioElement;\n url: string;\n}\n\nexport interface PlaybackCallbacks {\n onLoadedMetadata: (duration: number) => void;\n onTimeUpdate: (currentTime: number) => void;\n onEnded: () => void;\n onPlay?: () => void;\n onPause?: () => void;\n onStop?: () => void;\n onSeekBackward?: () => void;\n onSeekForward?: () => void;\n onPreviousTrack?: () => void;\n onNextTrack?: () => void;\n}\n\nconst SEEK_OFFSET = 5;\n\nconst setupMediaSession = (audio: HTMLAudioElement, callbacks: PlaybackCallbacks): void => {\n if (!('mediaSession' in navigator)) {\n return;\n }\n\n navigator.mediaSession.setActionHandler('play', () => {\n audio.play().catch(() => {});\n });\n\n navigator.mediaSession.setActionHandler('pause', () => {\n audio.pause();\n });\n\n navigator.mediaSession.setActionHandler('stop', () => {\n audio.pause();\n audio.currentTime = 0;\n callbacks.onStop?.();\n });\n\n navigator.mediaSession.setActionHandler('seekbackward', () => {\n audio.currentTime = Math.max(0, audio.currentTime - SEEK_OFFSET);\n callbacks.onSeekBackward?.();\n });\n\n navigator.mediaSession.setActionHandler('seekforward', () => {\n audio.currentTime = Math.min(audio.duration, audio.currentTime + SEEK_OFFSET);\n callbacks.onSeekForward?.();\n });\n\n navigator.mediaSession.setActionHandler('seekto', (details) => {\n if (details.seekTime != null && isFinite(audio.duration)) {\n audio.currentTime = details.seekTime;\n }\n });\n\n navigator.mediaSession.setActionHandler('previoustrack', () => {\n callbacks.onPreviousTrack?.();\n });\n\n navigator.mediaSession.setActionHandler('nexttrack', () => {\n callbacks.onNextTrack?.();\n });\n};\n\nconst clearMediaSession = (): void => {\n if (!('mediaSession' in navigator)) {\n return;\n }\n\n const actions: MediaSessionAction[] = [\n 'play', 'pause', 'stop',\n 'seekbackward', 'seekforward', 'seekto',\n 'previoustrack', 'nexttrack',\n ];\n\n actions.forEach((action) => {\n navigator.mediaSession.setActionHandler(action, null);\n });\n\n navigator.mediaSession.playbackState = 'none';\n};\n\nexport const createAudioPlayback = (url: string, callbacks: PlaybackCallbacks): AudioPlayback => {\n const audio = new Audio(url);\n\n /**\n * A detached element is not reliably rendered by every WebKit runtime\n * (notably iPad apps running on macOS), so keep it in the document.\n */\n audio.style.display = 'none';\n document.body.appendChild(audio);\n\n audio.onloadedmetadata = () => {\n callbacks.onLoadedMetadata(audio.duration);\n };\n\n audio.ontimeupdate = () => {\n callbacks.onTimeUpdate(audio.currentTime);\n };\n\n audio.onended = () => {\n callbacks.onEnded();\n };\n\n audio.onplay = () => {\n setupMediaSession(audio, callbacks);\n\n if ('mediaSession' in navigator) {\n navigator.mediaSession.playbackState = 'playing';\n }\n\n callbacks.onPlay?.();\n };\n\n audio.onpause = () => {\n if ('mediaSession' in navigator) {\n navigator.mediaSession.playbackState = 'paused';\n }\n\n callbacks.onPause?.();\n };\n\n return { audio, url };\n};\n\nexport const cleanupAudio = (audio: HTMLAudioElement): void => {\n audio.pause();\n audio.remove();\n audio.src = '';\n audio.onended = null;\n audio.ontimeupdate = null;\n audio.onloadedmetadata = null;\n audio.onplay = null;\n audio.onpause = null;\n clearMediaSession();\n};\n\nexport const createObjectURL = (blob: Blob): string => {\n return URL.createObjectURL(blob);\n};\n\nexport const revokeObjectURL = (url: string): void => {\n URL.revokeObjectURL(url);\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global audio playback manager — ensures only one audio plays at a time
|
|
3
|
+
*
|
|
4
|
+
* @author: exode <hello@exode.ru>
|
|
5
|
+
*/
|
|
6
|
+
const listeners = new Set();
|
|
7
|
+
const audioPlaybackManager = {
|
|
8
|
+
/** Register a stop callback for a player instance */
|
|
9
|
+
subscribe: (callback) => {
|
|
10
|
+
listeners.add(callback);
|
|
11
|
+
return () => {
|
|
12
|
+
listeners.delete(callback);
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
/** Stop all other players except the caller */
|
|
16
|
+
stopOthers: (except) => {
|
|
17
|
+
listeners.forEach((cb) => {
|
|
18
|
+
if (cb !== except) {
|
|
19
|
+
cb();
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export { audioPlaybackManager };
|
|
26
|
+
//# sourceMappingURL=audio-playback-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-playback-manager.js","sources":["../../src/platform/audio-playback-manager.ts"],"sourcesContent":["/**\n * Global audio playback manager — ensures only one audio plays at a time\n *\n * @author: exode <hello@exode.ru>\n */\n\n\ntype StopCallback = () => void;\n\nconst listeners = new Set<StopCallback>();\n\nexport const audioPlaybackManager = {\n /** Register a stop callback for a player instance */\n subscribe: (callback: StopCallback) => {\n listeners.add(callback);\n\n return () => {\n listeners.delete(callback);\n };\n },\n\n /** Stop all other players except the caller */\n stopOthers: (except: StopCallback) => {\n listeners.forEach((cb) => {\n if (cb !== except) {\n cb();\n }\n });\n },\n};\n"],"names":[],"mappings":"AAAA;;;;AAIG;AAKH,MAAM,SAAS,GAAG,IAAI,GAAG,EAAgB;AAElC,MAAM,oBAAoB,GAAG;;AAEhC,IAAA,SAAS,EAAE,CAAC,QAAsB,KAAI;AAClC,QAAA,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC;AAEvB,QAAA,OAAO,MAAK;AACR,YAAA,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC;AAC9B,QAAA,CAAC;IACL,CAAC;;AAGD,IAAA,UAAU,EAAE,CAAC,MAAoB,KAAI;AACjC,QAAA,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,KAAI;AACrB,YAAA,IAAI,EAAE,KAAK,MAAM,EAAE;AACf,gBAAA,EAAE,EAAE;YACR;AACJ,QAAA,CAAC,CAAC;IACN,CAAC;;;;;"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MediaRecorder platform adapter
|
|
3
|
+
*
|
|
4
|
+
* @author: exode <hello@exode.ru>
|
|
5
|
+
*/
|
|
6
|
+
const createRecorderSession = (stream, callbacks, options) => {
|
|
7
|
+
const recorderOptions = {};
|
|
8
|
+
if ((options === null || options === void 0 ? void 0 : options.mimeType) && MediaRecorder.isTypeSupported(options.mimeType)) {
|
|
9
|
+
recorderOptions.mimeType = options.mimeType;
|
|
10
|
+
}
|
|
11
|
+
const recorder = new MediaRecorder(stream, recorderOptions);
|
|
12
|
+
recorder.ondataavailable = callbacks.onDataAvailable;
|
|
13
|
+
recorder.onstop = callbacks.onStop;
|
|
14
|
+
recorder.onpause = callbacks.onPause;
|
|
15
|
+
recorder.onresume = callbacks.onResume;
|
|
16
|
+
return { recorder, stream };
|
|
17
|
+
};
|
|
18
|
+
const stopStream = (stream) => {
|
|
19
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { createRecorderSession, stopStream };
|
|
23
|
+
//# sourceMappingURL=media-recorder-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-recorder-adapter.js","sources":["../../src/platform/media-recorder-adapter.ts"],"sourcesContent":["/**\n * MediaRecorder platform adapter\n *\n * @author: exode <hello@exode.ru>\n */\n\n\nexport interface RecorderSession {\n recorder: MediaRecorder;\n stream: MediaStream;\n}\n\nexport interface RecorderSessionCallbacks {\n onDataAvailable: (event: BlobEvent) => void;\n onStop: () => void;\n onPause: () => void;\n onResume: () => void;\n}\n\nexport const createRecorderSession = (\n stream: MediaStream,\n callbacks: RecorderSessionCallbacks,\n options?: { mimeType?: string },\n): RecorderSession => {\n const recorderOptions: MediaRecorderOptions = {};\n\n if (options?.mimeType && MediaRecorder.isTypeSupported(options.mimeType)) {\n recorderOptions.mimeType = options.mimeType;\n }\n\n const recorder = new MediaRecorder(stream, recorderOptions);\n\n recorder.ondataavailable = callbacks.onDataAvailable;\n recorder.onstop = callbacks.onStop;\n recorder.onpause = callbacks.onPause;\n recorder.onresume = callbacks.onResume;\n\n return { recorder, stream };\n};\n\nexport const stopStream = (stream: MediaStream): void => {\n stream.getTracks().forEach((track) => track.stop());\n};"],"names":[],"mappings":"AAAA;;;;AAIG;AAeI,MAAM,qBAAqB,GAAG,CACjC,MAAmB,EACnB,SAAmC,EACnC,OAA+B,KACd;IACjB,MAAM,eAAe,GAAyB,EAAE;AAEhD,IAAA,IAAI,CAAA,OAAO,KAAA,IAAA,IAAP,OAAO,KAAA,MAAA,GAAA,MAAA,GAAP,OAAO,CAAE,QAAQ,KAAI,aAAa,CAAC,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;AACtE,QAAA,eAAe,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ;IAC/C;IAEA,MAAM,QAAQ,GAAG,IAAI,aAAa,CAAC,MAAM,EAAE,eAAe,CAAC;AAE3D,IAAA,QAAQ,CAAC,eAAe,GAAG,SAAS,CAAC,eAAe;AACpD,IAAA,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM;AAClC,IAAA,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,OAAO;AACpC,IAAA,QAAQ,CAAC,QAAQ,GAAG,SAAS,CAAC,QAAQ;AAEtC,IAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE;AAC/B;AAEO,MAAM,UAAU,GAAG,CAAC,MAAmB,KAAU;AACpD,IAAA,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;AACvD;;;;"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microphone platform adapter
|
|
3
|
+
*
|
|
4
|
+
* @author: exode <hello@exode.ru>
|
|
5
|
+
*/
|
|
6
|
+
const classifyMicError = (error) => {
|
|
7
|
+
const domError = error instanceof DOMException ? error : null;
|
|
8
|
+
if ((domError === null || domError === void 0 ? void 0 : domError.name) === 'NotAllowedError'
|
|
9
|
+
|| (domError === null || domError === void 0 ? void 0 : domError.name) === 'NotFoundError'
|
|
10
|
+
|| (domError === null || domError === void 0 ? void 0 : domError.name) === 'NotReadableError') {
|
|
11
|
+
return 'permission';
|
|
12
|
+
}
|
|
13
|
+
return 'unknown';
|
|
14
|
+
};
|
|
15
|
+
const requestMicrophoneAccess = async () => {
|
|
16
|
+
var _a;
|
|
17
|
+
if (typeof navigator === 'undefined' || !((_a = navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia)) {
|
|
18
|
+
throw new Error('MediaRecorder is not supported in this browser');
|
|
19
|
+
}
|
|
20
|
+
return navigator.mediaDevices.getUserMedia({ audio: true });
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export { classifyMicError, requestMicrophoneAccess };
|
|
24
|
+
//# sourceMappingURL=microphone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"microphone.js","sources":["../../src/platform/microphone.ts"],"sourcesContent":["/**\n * Microphone platform adapter\n *\n * @author: exode <hello@exode.ru>\n */\n\n\nexport type MicErrorKind = 'permission' | 'not-supported' | 'unknown';\n\nexport const classifyMicError = (error: unknown): MicErrorKind => {\n const domError = error instanceof DOMException ? error : null;\n\n if (\n domError?.name === 'NotAllowedError'\n || domError?.name === 'NotFoundError'\n || domError?.name === 'NotReadableError'\n ) {\n return 'permission';\n }\n\n return 'unknown';\n};\n\nexport const requestMicrophoneAccess = async (): Promise<MediaStream> => {\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) {\n throw new Error('MediaRecorder is not supported in this browser');\n }\n\n return navigator.mediaDevices.getUserMedia({ audio: true });\n};"],"names":[],"mappings":"AAAA;;;;AAIG;AAKI,MAAM,gBAAgB,GAAG,CAAC,KAAc,KAAkB;AAC7D,IAAA,MAAM,QAAQ,GAAG,KAAK,YAAY,YAAY,GAAG,KAAK,GAAG,IAAI;IAE7D,IACI,CAAA,QAAQ,KAAA,IAAA,IAAR,QAAQ,uBAAR,QAAQ,CAAE,IAAI,MAAK;WAChB,CAAA,QAAQ,aAAR,QAAQ,KAAA,MAAA,GAAA,MAAA,GAAR,QAAQ,CAAE,IAAI,MAAK;WACnB,CAAA,QAAQ,KAAA,IAAA,IAAR,QAAQ,KAAA,MAAA,GAAA,MAAA,GAAR,QAAQ,CAAE,IAAI,MAAK,kBAAkB,EAC1C;AACE,QAAA,OAAO,YAAY;IACvB;AAEA,IAAA,OAAO,SAAS;AACpB;AAEO,MAAM,uBAAuB,GAAG,YAAiC;;AACpE,IAAA,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,EAAC,CAAA,EAAA,GAAA,SAAS,CAAC,YAAY,MAAA,IAAA,IAAA,EAAA,KAAA,MAAA,GAAA,MAAA,GAAA,EAAA,CAAE,YAAY,CAAA,EAAE;AAC3E,QAAA,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC;IACrE;AAEA,IAAA,OAAO,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC/D;;;;"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPEG frame scanner
|
|
3
|
+
*
|
|
4
|
+
* @author: exode <hello@exode.ru>
|
|
5
|
+
*/
|
|
6
|
+
const MPEG_LAYER_III = 1;
|
|
7
|
+
const MPEG_SAMPLE_RATES = [44100, 48000, 32000];
|
|
8
|
+
const MPEG_BITRATES_V1 = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0];
|
|
9
|
+
const MPEG_BITRATES_V2 = [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0];
|
|
10
|
+
/** Length of the Layer III frame at the given offset, or 0 when it is not one */
|
|
11
|
+
const readFrameLength = (bytes, offset) => {
|
|
12
|
+
if (offset + 3 >= bytes.length || bytes[offset] !== 0xff || (bytes[offset + 1] & 0xe0) !== 0xe0) {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
/** Version ids: 0 = MPEG 2.5, 1 = reserved, 2 = MPEG 2, 3 = MPEG 1 */
|
|
16
|
+
const version = (bytes[offset + 1] >> 3) & 0x03;
|
|
17
|
+
const layer = (bytes[offset + 1] >> 1) & 0x03;
|
|
18
|
+
const bitrateIndex = (bytes[offset + 2] >> 4) & 0x0f;
|
|
19
|
+
const sampleRateIndex = (bytes[offset + 2] >> 2) & 0x03;
|
|
20
|
+
const isReserved = version === 1
|
|
21
|
+
|| layer !== MPEG_LAYER_III
|
|
22
|
+
|| sampleRateIndex === 3
|
|
23
|
+
|| bitrateIndex === 0
|
|
24
|
+
|| bitrateIndex === 0x0f;
|
|
25
|
+
if (isReserved) {
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
const isVersion1 = version === 3;
|
|
29
|
+
const padding = (bytes[offset + 2] >> 1) & 0x01;
|
|
30
|
+
const bitrate = (isVersion1 ? MPEG_BITRATES_V1 : MPEG_BITRATES_V2)[bitrateIndex] * 1000;
|
|
31
|
+
const sampleRate = MPEG_SAMPLE_RATES[sampleRateIndex] / (isVersion1 ? 1 : version === 2 ? 2 : 4);
|
|
32
|
+
return Math.floor((isVersion1 ? 144 : 72) * bitrate / sampleRate) + padding;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Some mp3 (e.g. converted from wma) keep junk between the ID3 tag and the
|
|
36
|
+
* first MPEG frame: media elements skip it, the WebAudio decoder does not.
|
|
37
|
+
* Returns the offset of the first real frame, or 0 when there is none.
|
|
38
|
+
*/
|
|
39
|
+
const findFirstFrameOffset = (bytes) => {
|
|
40
|
+
let start = 0;
|
|
41
|
+
/** ID3v2 header: "ID3" + version + flags + 4 syncsafe size bytes */
|
|
42
|
+
if (bytes.length >= 10 && bytes[0] === 0x49 && bytes[1] === 0x44 && bytes[2] === 0x33) {
|
|
43
|
+
start = 10 + ((bytes[6] << 21) | (bytes[7] << 14) | (bytes[8] << 7) | bytes[9]);
|
|
44
|
+
/** ID3v2.4 may append a 10 byte footer copy of the header */
|
|
45
|
+
if (bytes[5] & 0x10) {
|
|
46
|
+
start += 10;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (let i = start; i < bytes.length - 3; i += 1) {
|
|
50
|
+
const length = readFrameLength(bytes, i);
|
|
51
|
+
/** Junk can open with a valid looking header, so require the next frame to follow */
|
|
52
|
+
if (length && readFrameLength(bytes, i + length)) {
|
|
53
|
+
return i;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return 0;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export { findFirstFrameOffset };
|
|
60
|
+
//# sourceMappingURL=mpeg-frame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mpeg-frame.js","sources":["../../src/platform/mpeg-frame.ts"],"sourcesContent":["/**\n * MPEG frame scanner\n *\n * @author: exode <hello@exode.ru>\n */\n\n\nconst MPEG_LAYER_III = 1;\nconst MPEG_SAMPLE_RATES = [ 44100, 48000, 32000 ];\nconst MPEG_BITRATES_V1 = [ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 ];\nconst MPEG_BITRATES_V2 = [ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 ];\n\n/** Length of the Layer III frame at the given offset, or 0 when it is not one */\nconst readFrameLength = (bytes: Uint8Array, offset: number): number => {\n if (offset + 3 >= bytes.length || bytes[offset] !== 0xff || (bytes[offset + 1] & 0xe0) !== 0xe0) {\n return 0;\n }\n\n /** Version ids: 0 = MPEG 2.5, 1 = reserved, 2 = MPEG 2, 3 = MPEG 1 */\n const version = (bytes[offset + 1] >> 3) & 0x03;\n const layer = (bytes[offset + 1] >> 1) & 0x03;\n const bitrateIndex = (bytes[offset + 2] >> 4) & 0x0f;\n const sampleRateIndex = (bytes[offset + 2] >> 2) & 0x03;\n\n const isReserved = version === 1\n || layer !== MPEG_LAYER_III\n || sampleRateIndex === 3\n || bitrateIndex === 0\n || bitrateIndex === 0x0f;\n\n if (isReserved) {\n return 0;\n }\n\n const isVersion1 = version === 3;\n const padding = (bytes[offset + 2] >> 1) & 0x01;\n const bitrate = (isVersion1 ? MPEG_BITRATES_V1 : MPEG_BITRATES_V2)[bitrateIndex] * 1000;\n const sampleRate = MPEG_SAMPLE_RATES[sampleRateIndex] / (isVersion1 ? 1 : version === 2 ? 2 : 4);\n\n return Math.floor((isVersion1 ? 144 : 72) * bitrate / sampleRate) + padding;\n};\n\n/**\n * Some mp3 (e.g. converted from wma) keep junk between the ID3 tag and the\n * first MPEG frame: media elements skip it, the WebAudio decoder does not.\n * Returns the offset of the first real frame, or 0 when there is none.\n */\nexport const findFirstFrameOffset = (bytes: Uint8Array): number => {\n let start = 0;\n\n /** ID3v2 header: \"ID3\" + version + flags + 4 syncsafe size bytes */\n if (bytes.length >= 10 && bytes[0] === 0x49 && bytes[1] === 0x44 && bytes[2] === 0x33) {\n start = 10 + ((bytes[6] << 21) | (bytes[7] << 14) | (bytes[8] << 7) | bytes[9]);\n\n /** ID3v2.4 may append a 10 byte footer copy of the header */\n if (bytes[5] & 0x10) {\n start += 10;\n }\n }\n\n for (let i = start; i < bytes.length - 3; i += 1) {\n const length = readFrameLength(bytes, i);\n\n /** Junk can open with a valid looking header, so require the next frame to follow */\n if (length && readFrameLength(bytes, i + length)) {\n return i;\n }\n }\n\n return 0;\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format utilities
|
|
3
|
+
*
|
|
4
|
+
* @author: exode <hello@exode.ru>
|
|
5
|
+
*/
|
|
6
|
+
const formatTime = (seconds) => {
|
|
7
|
+
const mins = Math.floor(seconds / 60);
|
|
8
|
+
const secs = Math.floor(seconds % 60);
|
|
9
|
+
return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { formatTime };
|
|
13
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sources":["../../src/utils/format.ts"],"sourcesContent":["/**\n * Format utilities\n *\n * @author: exode <hello@exode.ru>\n */\n\n\nexport const formatTime = (seconds: number): string => {\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n\n return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;\n};"],"names":[],"mappings":"AAAA;;;;AAIG;AAGI,MAAM,UAAU,GAAG,CAAC,OAAe,KAAY;IAClD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;IAErC,OAAO,CAAA,EAAG,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA,CAAE;AAC9E;;;;"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Math utilities
|
|
3
|
+
*
|
|
4
|
+
* @author: exode <hello@exode.ru>
|
|
5
|
+
*/
|
|
6
|
+
const clampValue = (value, lowerBound = 0, upperBound = 100) => {
|
|
7
|
+
const safe = Number.isFinite(value) ? value : 0;
|
|
8
|
+
return Math.min(Math.max(safe, lowerBound), upperBound);
|
|
9
|
+
};
|
|
10
|
+
const roundTo = (value, precision = 2) => {
|
|
11
|
+
const factor = Math.pow(10, precision);
|
|
12
|
+
return Math.round(value * factor) / factor;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export { clampValue, roundTo };
|
|
16
|
+
//# sourceMappingURL=math.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.js","sources":["../../src/utils/math.ts"],"sourcesContent":["/**\n * Math utilities\n *\n * @author: exode <hello@exode.ru>\n */\n\n\nexport const clampValue = (value: number, lowerBound = 0, upperBound = 100): number => {\n const safe = Number.isFinite(value) ? value : 0;\n\n return Math.min(Math.max(safe, lowerBound), upperBound);\n};\n\nexport const roundTo = (value: number, precision = 2): number => {\n const factor = Math.pow(10, precision);\n\n return Math.round(value * factor) / factor;\n};"],"names":[],"mappings":"AAAA;;;;AAIG;AAGI,MAAM,UAAU,GAAG,CAAC,KAAa,EAAE,UAAU,GAAG,CAAC,EAAE,UAAU,GAAG,GAAG,KAAY;AAClF,IAAA,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC;AAE/C,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,UAAU,CAAC;AAC3D;AAEO,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,SAAS,GAAG,CAAC,KAAY;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC;IAEtC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,GAAG,MAAM;AAC9C;;;;"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AUDIO_LEVEL_BARS } from '../constants.js';
|
|
2
|
+
import { clampValue } from './math.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Pseudo waveform utilities
|
|
6
|
+
*
|
|
7
|
+
* @author: exode <hello@exode.ru>
|
|
8
|
+
*/
|
|
9
|
+
/** Deterministic generator: the same source always draws the same waveform */
|
|
10
|
+
const seededRandom = (seed) => {
|
|
11
|
+
let state = Math.abs(Math.floor(seed)) % 2147483647 || 1;
|
|
12
|
+
return () => (state = state * 16807 % 2147483647) / 2147483647;
|
|
13
|
+
};
|
|
14
|
+
const hashString = (value) => {
|
|
15
|
+
let hash = 0;
|
|
16
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
17
|
+
hash = (hash * 31 + value.charCodeAt(i)) | 0;
|
|
18
|
+
}
|
|
19
|
+
return hash;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Decorative levels for runtimes that cannot decode audio at all — WebAudio
|
|
23
|
+
* rejects every mp3 inside iPad apps running on macOS. Purely cosmetic: it
|
|
24
|
+
* stands in for a flat line and says nothing about the recording itself.
|
|
25
|
+
*/
|
|
26
|
+
const createPseudoLevels = (seed, barCount = AUDIO_LEVEL_BARS) => {
|
|
27
|
+
const random = seededRandom(hashString(seed));
|
|
28
|
+
const levels = [];
|
|
29
|
+
let level = 45 + random() * 25;
|
|
30
|
+
for (let i = 0; i < barCount; i += 1) {
|
|
31
|
+
/** Smooth drift instead of pure noise, so it reads as speech rather than static */
|
|
32
|
+
level = clampValue(level + (random() - 0.5) * 42, 14);
|
|
33
|
+
/** Slight fade at both ends — that is how a real recording usually looks */
|
|
34
|
+
levels.push(level * (0.72 + 0.28 * Math.sin((i / (barCount - 1)) * Math.PI)));
|
|
35
|
+
}
|
|
36
|
+
return levels;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export { createPseudoLevels };
|
|
40
|
+
//# sourceMappingURL=pseudo-waveform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pseudo-waveform.js","sources":["../../src/utils/pseudo-waveform.ts"],"sourcesContent":["/**\n * Pseudo waveform utilities\n *\n * @author: exode <hello@exode.ru>\n */\n\nimport { AUDIO_LEVEL_BARS } from '../constants';\n\nimport { clampValue } from './math';\n\n\n/** Deterministic generator: the same source always draws the same waveform */\nconst seededRandom = (seed: number) => {\n let state = Math.abs(Math.floor(seed)) % 2147483647 || 1;\n\n return () => (state = state * 16807 % 2147483647) / 2147483647;\n};\n\nconst hashString = (value: string): number => {\n let hash = 0;\n\n for (let i = 0; i < value.length; i += 1) {\n hash = (hash * 31 + value.charCodeAt(i)) | 0;\n }\n\n return hash;\n};\n\n/**\n * Decorative levels for runtimes that cannot decode audio at all — WebAudio\n * rejects every mp3 inside iPad apps running on macOS. Purely cosmetic: it\n * stands in for a flat line and says nothing about the recording itself.\n */\nexport const createPseudoLevels = (seed: string, barCount: number = AUDIO_LEVEL_BARS): number[] => {\n const random = seededRandom(hashString(seed));\n const levels: number[] = [];\n\n let level = 45 + random() * 25;\n\n for (let i = 0; i < barCount; i += 1) {\n /** Smooth drift instead of pure noise, so it reads as speech rather than static */\n level = clampValue(level + (random() - 0.5) * 42, 14);\n\n /** Slight fade at both ends — that is how a real recording usually looks */\n levels.push(level * (0.72 + 0.28 * Math.sin((i / (barCount - 1)) * Math.PI)));\n }\n\n return levels;\n};\n"],"names":[],"mappings":";;;AAAA;;;;AAIG;AAOH;AACA,MAAM,YAAY,GAAG,CAAC,IAAY,KAAI;AAClC,IAAA,IAAI,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,UAAU,IAAI,CAAC;AAExD,IAAA,OAAO,MAAM,CAAC,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,UAAU,IAAI,UAAU;AAClE,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,KAAa,KAAY;IACzC,IAAI,IAAI,GAAG,CAAC;AAEZ,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE;AACtC,QAAA,IAAI,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IAChD;AAEA,IAAA,OAAO,IAAI;AACf,CAAC;AAED;;;;AAIG;AACI,MAAM,kBAAkB,GAAG,CAAC,IAAY,EAAE,QAAA,GAAmB,gBAAgB,KAAc;IAC9F,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAa,EAAE;IAE3B,IAAI,KAAK,GAAG,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;AAE9B,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE;;AAElC,QAAA,KAAK,GAAG,UAAU,CAAC,KAAK,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC;;AAGrD,QAAA,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACjF;AAEA,IAAA,OAAO,MAAM;AACjB;;;;"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VoiceBlobCache
|
|
3
|
+
*
|
|
4
|
+
* @description In-memory cache for voice message blobs.
|
|
5
|
+
* Allows instant playback of just-sent voice messages
|
|
6
|
+
* before the server URL becomes available.
|
|
7
|
+
*
|
|
8
|
+
* @author: exode <hello@exode.ru>
|
|
9
|
+
*/
|
|
10
|
+
class VoiceBlobCacheClass {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.cache = new Map();
|
|
13
|
+
this.set = (messageId, blob) => {
|
|
14
|
+
this.cache.set(messageId, blob);
|
|
15
|
+
};
|
|
16
|
+
this.get = (messageId) => {
|
|
17
|
+
return this.cache.get(messageId);
|
|
18
|
+
};
|
|
19
|
+
this.delete = (messageId) => {
|
|
20
|
+
this.cache.delete(messageId);
|
|
21
|
+
};
|
|
22
|
+
/** Transfer blob from optimistic ID to real server ID */
|
|
23
|
+
this.transfer = (fromId, toId) => {
|
|
24
|
+
const blob = this.cache.get(fromId);
|
|
25
|
+
if (blob) {
|
|
26
|
+
this.cache.delete(fromId);
|
|
27
|
+
this.cache.set(toId, blob);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const VoiceBlobCache = new VoiceBlobCacheClass();
|
|
33
|
+
|
|
34
|
+
export { VoiceBlobCache };
|
|
35
|
+
//# sourceMappingURL=voice-blob-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice-blob-cache.js","sources":["../../src/utils/voice-blob-cache.ts"],"sourcesContent":["/**\n * VoiceBlobCache\n *\n * @description In-memory cache for voice message blobs.\n * Allows instant playback of just-sent voice messages\n * before the server URL becomes available.\n *\n * @author: exode <hello@exode.ru>\n */\n\n\nclass VoiceBlobCacheClass {\n\n private cache = new Map<number, Blob>();\n\n set = (messageId: number, blob: Blob) => {\n this.cache.set(messageId, blob);\n };\n\n get = (messageId: number): Blob | undefined => {\n return this.cache.get(messageId);\n };\n\n delete = (messageId: number) => {\n this.cache.delete(messageId);\n };\n\n /** Transfer blob from optimistic ID to real server ID */\n transfer = (fromId: number, toId: number) => {\n const blob = this.cache.get(fromId);\n\n if (blob) {\n this.cache.delete(fromId);\n this.cache.set(toId, blob);\n }\n };\n\n}\n\n\nexport const VoiceBlobCache = new VoiceBlobCacheClass();\n"],"names":[],"mappings":"AAAA;;;;;;;;AAQG;AAGH,MAAM,mBAAmB,CAAA;AAAzB,IAAA,WAAA,GAAA;AAEY,QAAA,IAAA,CAAA,KAAK,GAAG,IAAI,GAAG,EAAgB;AAEvC,QAAA,IAAA,CAAA,GAAG,GAAG,CAAC,SAAiB,EAAE,IAAU,KAAI;YACpC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC;AACnC,QAAA,CAAC;AAED,QAAA,IAAA,CAAA,GAAG,GAAG,CAAC,SAAiB,KAAsB;YAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC;AACpC,QAAA,CAAC;AAED,QAAA,IAAA,CAAA,MAAM,GAAG,CAAC,SAAiB,KAAI;AAC3B,YAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC;AAChC,QAAA,CAAC;;AAGD,QAAA,IAAA,CAAA,QAAQ,GAAG,CAAC,MAAc,EAAE,IAAY,KAAI;YACxC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;YAEnC,IAAI,IAAI,EAAE;AACN,gBAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;gBACzB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC;YAC9B;AACJ,QAAA,CAAC;IAEL;AAAC;AAGM,MAAM,cAAc,GAAG,IAAI,mBAAmB;;;;"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VoicePlaybackChain
|
|
3
|
+
*
|
|
4
|
+
* @description Sequential voice message playback chain (like Telegram).
|
|
5
|
+
* Registers voice players by media ID and plays the next one when current ends.
|
|
6
|
+
*
|
|
7
|
+
* @author: exode <hello@exode.ru>
|
|
8
|
+
*/
|
|
9
|
+
class VoicePlaybackChainClass {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.entries = [];
|
|
12
|
+
/** Register a voice player. Returns cleanup function for useEffect. */
|
|
13
|
+
this.register = (id, play) => {
|
|
14
|
+
this.entries = this.entries.filter((e) => e.id !== id);
|
|
15
|
+
this.entries.push({ id, play });
|
|
16
|
+
this.entries.sort((a, b) => a.id - b.id);
|
|
17
|
+
return () => this.unregister(id);
|
|
18
|
+
};
|
|
19
|
+
/** Unregister a voice player by media ID */
|
|
20
|
+
this.unregister = (id) => {
|
|
21
|
+
this.entries = this.entries.filter((e) => e.id !== id);
|
|
22
|
+
};
|
|
23
|
+
/** Play the next voice message after the given media ID */
|
|
24
|
+
this.playNext = (currentId) => {
|
|
25
|
+
const idx = this.entries.findIndex((e) => e.id === currentId);
|
|
26
|
+
if (idx >= 0 && idx < this.entries.length - 1) {
|
|
27
|
+
this.entries[idx + 1].play();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/** Play the previous voice message before the given media ID */
|
|
31
|
+
this.playPrevious = (currentId) => {
|
|
32
|
+
const idx = this.entries.findIndex((e) => e.id === currentId);
|
|
33
|
+
if (idx > 0) {
|
|
34
|
+
this.entries[idx - 1].play();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const VoicePlaybackChain = new VoicePlaybackChainClass();
|
|
40
|
+
|
|
41
|
+
export { VoicePlaybackChain };
|
|
42
|
+
//# sourceMappingURL=voice-playback-chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice-playback-chain.js","sources":["../../src/utils/voice-playback-chain.ts"],"sourcesContent":["/**\n * VoicePlaybackChain\n *\n * @description Sequential voice message playback chain (like Telegram).\n * Registers voice players by media ID and plays the next one when current ends.\n *\n * @author: exode <hello@exode.ru>\n */\n\n\ninterface ChainEntry {\n id: number;\n play: () => void;\n}\n\n\nclass VoicePlaybackChainClass {\n\n private entries: ChainEntry[] = [];\n\n /** Register a voice player. Returns cleanup function for useEffect. */\n register = (id: number, play: () => void) => {\n this.entries = this.entries.filter((e) => e.id !== id);\n this.entries.push({ id, play });\n this.entries.sort((a, b) => a.id - b.id);\n\n return () => this.unregister(id);\n };\n\n /** Unregister a voice player by media ID */\n unregister = (id: number) => {\n this.entries = this.entries.filter((e) => e.id !== id);\n };\n\n /** Play the next voice message after the given media ID */\n playNext = (currentId: number) => {\n const idx = this.entries.findIndex((e) => e.id === currentId);\n\n if (idx >= 0 && idx < this.entries.length - 1) {\n this.entries[idx + 1].play();\n }\n };\n\n /** Play the previous voice message before the given media ID */\n playPrevious = (currentId: number) => {\n const idx = this.entries.findIndex((e) => e.id === currentId);\n\n if (idx > 0) {\n this.entries[idx - 1].play();\n }\n };\n\n}\n\n\nexport const VoicePlaybackChain = new VoicePlaybackChainClass();\n"],"names":[],"mappings":"AAAA;;;;;;;AAOG;AASH,MAAM,uBAAuB,CAAA;AAA7B,IAAA,WAAA,GAAA;QAEY,IAAA,CAAA,OAAO,GAAiB,EAAE;;AAGlC,QAAA,IAAA,CAAA,QAAQ,GAAG,CAAC,EAAU,EAAE,IAAgB,KAAI;YACxC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YACtD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;YAExC,OAAO,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;AACpC,QAAA,CAAC;;AAGD,QAAA,IAAA,CAAA,UAAU,GAAG,CAAC,EAAU,KAAI;YACxB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;AAC1D,QAAA,CAAC;;AAGD,QAAA,IAAA,CAAA,QAAQ,GAAG,CAAC,SAAiB,KAAI;AAC7B,YAAA,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC;AAE7D,YAAA,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE;YAChC;AACJ,QAAA,CAAC;;AAGD,QAAA,IAAA,CAAA,YAAY,GAAG,CAAC,SAAiB,KAAI;AACjC,YAAA,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC;AAE7D,YAAA,IAAI,GAAG,GAAG,CAAC,EAAE;gBACT,IAAI,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE;YAChC;AACJ,QAAA,CAAC;IAEL;AAAC;AAGM,MAAM,kBAAkB,GAAG,IAAI,uBAAuB;;;;"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exode-team/react-recorder",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.15",
|
|
4
4
|
"description": "Accessible audio recorder UI kit for React applications",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
8
9
|
"scripts": {
|
|
9
10
|
"clean": "rimraf dist",
|
|
10
11
|
"build": "rollup -c",
|