@magmamath/students-features 0.9.107-rc.52 → 0.9.107-rc.53
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/commonjs/features/voice/playing/model/VoicePlayer.model.js +60 -128
- package/dist/commonjs/features/voice/playing/model/VoicePlayer.model.js.map +1 -1
- package/dist/module/features/voice/playing/model/VoicePlayer.model.js +61 -129
- package/dist/module/features/voice/playing/model/VoicePlayer.model.js.map +1 -1
- package/dist/typescript/commonjs/features/voice/playing/model/VoicePlayer.model.d.ts +2 -5
- package/dist/typescript/commonjs/features/voice/playing/model/VoicePlayer.model.d.ts.map +1 -1
- package/dist/typescript/module/features/voice/playing/model/VoicePlayer.model.d.ts +2 -5
- package/dist/typescript/module/features/voice/playing/model/VoicePlayer.model.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/features/voice/playing/model/VoicePlayer.model.ts +63 -111
|
@@ -13,43 +13,45 @@ class VoicePlayerModel {
|
|
|
13
13
|
downloader = new _VoiceTranscriptionsDownloaderModel.VoiceTranscriptionsDownloaderModel();
|
|
14
14
|
dropdown = new _VoiceTranscriptionsDropdownModel.VoiceTranscriptionsDropdownModel();
|
|
15
15
|
currentPlayingAttempt = null;
|
|
16
|
-
|
|
17
|
-
// Events
|
|
18
|
-
playAttempt = (0, _effector.createEvent)();
|
|
19
|
-
pauseAudio = (0, _effector.createEvent)();
|
|
20
|
-
stopAudio = (0, _effector.createEvent)();
|
|
21
16
|
setCurrentAttempt = (0, _effector.createEvent)();
|
|
22
17
|
setTranscriptsLoaded = (0, _effector.createEvent)();
|
|
23
18
|
setAudioLoading = (0, _effector.createEvent)();
|
|
24
19
|
setIsPlaying = (0, _effector.createEvent)();
|
|
25
20
|
reset = (0, _effector.createEvent)();
|
|
26
|
-
|
|
27
|
-
// Stores
|
|
28
21
|
$currentAttempt = (0, _effector.restore)(this.setCurrentAttempt, null).reset(this.reset);
|
|
29
22
|
$transcriptsLoaded = (0, _effector.restore)(this.setTranscriptsLoaded, false).reset(this.reset);
|
|
30
23
|
$audioLoading = (0, _effector.restore)(this.setAudioLoading, false).reset(this.reset);
|
|
31
24
|
$isPlaying = (0, _effector.restore)(this.setIsPlaying, false).reset(this.reset);
|
|
25
|
+
pauseAudio = (0, _effector.createEffect)(() => {
|
|
26
|
+
if (this.player) {
|
|
27
|
+
this.player.pause();
|
|
28
|
+
} else {
|
|
29
|
+
console.warn('Cannot pause: audio player not initialized');
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
stopAudio = (0, _effector.createEffect)(() => {
|
|
33
|
+
if (this.player) {
|
|
34
|
+
this.player.remove();
|
|
35
|
+
this.currentPlayingAttempt = null;
|
|
36
|
+
this.setCurrentAttempt(null);
|
|
37
|
+
} else {
|
|
38
|
+
console.warn('Cannot stop: audio player not initialized');
|
|
39
|
+
}
|
|
40
|
+
});
|
|
32
41
|
constructor(params) {
|
|
33
42
|
this.api = params.api;
|
|
34
43
|
this.audioIds = params.audioIds;
|
|
35
44
|
this.downloader.setApi(params.api);
|
|
36
|
-
|
|
37
|
-
// Initialize collection if audioIds are provided
|
|
38
45
|
if (params.audioIds) {
|
|
39
46
|
this.initializeCollection();
|
|
40
47
|
}
|
|
41
|
-
this.setupEffects();
|
|
42
48
|
}
|
|
43
|
-
|
|
44
|
-
// Public method to initialize or update audioIds
|
|
45
49
|
initializeWithAudioIds(audioIds) {
|
|
46
50
|
this.audioIds = audioIds;
|
|
47
|
-
// Clear existing collection before reinitializing
|
|
48
51
|
this.collection.clear();
|
|
49
52
|
this.initializeCollection();
|
|
50
53
|
}
|
|
51
54
|
initializeCollection() {
|
|
52
|
-
// Initialize collection with all attempts from audioIds
|
|
53
55
|
if (!this.audioIds) {
|
|
54
56
|
console.warn('VoicePlayerModel: audioIds is not provided');
|
|
55
57
|
return;
|
|
@@ -61,50 +63,12 @@ class VoicePlayerModel {
|
|
|
61
63
|
});
|
|
62
64
|
});
|
|
63
65
|
}
|
|
64
|
-
setupEffects() {
|
|
65
|
-
// Download and play audio when user selects an attempt
|
|
66
|
-
(0, _effector.sample)({
|
|
67
|
-
clock: this.playAttempt,
|
|
68
|
-
target: this.handlePlayAttempt
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Pause audio playback
|
|
72
|
-
(0, _effector.sample)({
|
|
73
|
-
clock: this.pauseAudio,
|
|
74
|
-
target: (0, _effector.createEffect)(() => {
|
|
75
|
-
if (this.player) {
|
|
76
|
-
this.player.pause();
|
|
77
|
-
} else {
|
|
78
|
-
console.warn('Cannot pause: audio player not initialized');
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// Stop audio playback
|
|
84
|
-
(0, _effector.sample)({
|
|
85
|
-
clock: this.stopAudio,
|
|
86
|
-
target: (0, _effector.createEffect)(() => {
|
|
87
|
-
if (this.player) {
|
|
88
|
-
this.player.remove();
|
|
89
|
-
this.currentPlayingAttempt = null;
|
|
90
|
-
this.setCurrentAttempt(null);
|
|
91
|
-
} else {
|
|
92
|
-
console.warn('Cannot stop: audio player not initialized');
|
|
93
|
-
}
|
|
94
|
-
})
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Load all transcripts for all attempts
|
|
99
66
|
loadAllTranscripts = (0, _effector.createEffect)(async () => {
|
|
100
67
|
const attempts = this.collection.getAll();
|
|
101
68
|
const transcriptPromises = attempts.map(async item => {
|
|
102
|
-
// Skip if transcript already loaded
|
|
103
69
|
if (this.collection.hasTranscript(item.attemptNumber)) {
|
|
104
70
|
return;
|
|
105
71
|
}
|
|
106
|
-
|
|
107
|
-
// Mark as loading
|
|
108
72
|
this.collection.update(item.attemptNumber, {
|
|
109
73
|
transcriptLoading: true
|
|
110
74
|
});
|
|
@@ -125,13 +89,9 @@ class VoicePlayerModel {
|
|
|
125
89
|
await Promise.all(transcriptPromises);
|
|
126
90
|
this.setTranscriptsLoaded(true);
|
|
127
91
|
});
|
|
128
|
-
|
|
129
|
-
// Handle play button press for an attempt
|
|
130
92
|
handlePlayAttempt = (0, _effector.createEffect)(async attemptNumber => {
|
|
131
93
|
const item = this.collection.get(attemptNumber);
|
|
132
94
|
if (!item) return;
|
|
133
|
-
|
|
134
|
-
// Abort any ongoing download from previous attempt
|
|
135
95
|
const currentAttempt = this.$currentAttempt.getState();
|
|
136
96
|
if (currentAttempt !== null && currentAttempt !== attemptNumber) {
|
|
137
97
|
const previousItem = this.collection.get(currentAttempt);
|
|
@@ -144,14 +104,10 @@ class VoicePlayerModel {
|
|
|
144
104
|
});
|
|
145
105
|
}
|
|
146
106
|
}
|
|
147
|
-
|
|
148
|
-
// If audio already downloaded, just play it
|
|
149
107
|
if (this.collection.hasAudio(attemptNumber)) {
|
|
150
108
|
this.playAudio(attemptNumber);
|
|
151
109
|
return;
|
|
152
110
|
}
|
|
153
|
-
|
|
154
|
-
// Download audio file
|
|
155
111
|
try {
|
|
156
112
|
this.setAudioLoading(true);
|
|
157
113
|
const {
|
|
@@ -171,13 +127,10 @@ class VoicePlayerModel {
|
|
|
171
127
|
audioDownloadPromise: undefined
|
|
172
128
|
});
|
|
173
129
|
this.setAudioLoading(false);
|
|
174
|
-
|
|
175
|
-
// Play the audio after successful download
|
|
176
130
|
this.playAudio(attemptNumber);
|
|
177
131
|
} catch (error) {
|
|
178
132
|
console.error('Audio download error:', error);
|
|
179
133
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
180
|
-
// Download was aborted, just clean up
|
|
181
134
|
return;
|
|
182
135
|
}
|
|
183
136
|
this.collection.update(attemptNumber, {
|
|
@@ -189,90 +142,76 @@ class VoicePlayerModel {
|
|
|
189
142
|
this.setAudioLoading(false);
|
|
190
143
|
}
|
|
191
144
|
});
|
|
192
|
-
playAudio(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
// If currently playing a different attempt, stop it first
|
|
206
|
-
if (this.currentPlayingAttempt !== null && this.currentPlayingAttempt !== attemptNumber) {
|
|
207
|
-
this.player.remove();
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// If already playing this attempt, pause it
|
|
211
|
-
if (this.currentPlayingAttempt === attemptNumber && this.$isPlaying.getState()) {
|
|
212
|
-
this.player.pause();
|
|
145
|
+
playAudio = (0, _effector.attach)({
|
|
146
|
+
source: this.$isPlaying,
|
|
147
|
+
mapParams: (attemptNumber, isPlaying) => ({
|
|
148
|
+
attemptNumber,
|
|
149
|
+
isPlaying
|
|
150
|
+
}),
|
|
151
|
+
effect: (0, _effector.createEffect)(async ({
|
|
152
|
+
attemptNumber,
|
|
153
|
+
isPlaying
|
|
154
|
+
}) => {
|
|
155
|
+
const item = this.collection.get(attemptNumber);
|
|
156
|
+
if (!item?.audioUri) {
|
|
157
|
+
console.warn('No audio URI available for attempt:', attemptNumber);
|
|
213
158
|
return;
|
|
214
159
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (this.currentPlayingAttempt === attemptNumber && !this.$isPlaying.getState()) {
|
|
218
|
-
this.player.play();
|
|
160
|
+
if (!this.player) {
|
|
161
|
+
console.warn('Audio player not initialized');
|
|
219
162
|
return;
|
|
220
163
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
164
|
+
try {
|
|
165
|
+
if (this.currentPlayingAttempt !== null && this.currentPlayingAttempt !== attemptNumber) {
|
|
166
|
+
this.player.remove();
|
|
167
|
+
}
|
|
168
|
+
if (this.currentPlayingAttempt === attemptNumber && isPlaying) {
|
|
169
|
+
this.player.pause();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (this.currentPlayingAttempt === attemptNumber && !isPlaying) {
|
|
173
|
+
this.player.play();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.player.replace(item.audioUri);
|
|
177
|
+
this.currentPlayingAttempt = attemptNumber;
|
|
178
|
+
this.setCurrentAttempt(attemptNumber);
|
|
179
|
+
this.player.play();
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error('Error playing audio:', error);
|
|
182
|
+
this.setIsPlaying(false);
|
|
183
|
+
let errorMessage = 'Failed to play audio';
|
|
184
|
+
if (error instanceof Error) {
|
|
185
|
+
errorMessage = error.message;
|
|
186
|
+
}
|
|
187
|
+
this.collection.update(attemptNumber, {
|
|
188
|
+
audioError: errorMessage
|
|
189
|
+
});
|
|
233
190
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Handle playback completion (called from hook)
|
|
191
|
+
})
|
|
192
|
+
});
|
|
241
193
|
handlePlaybackComplete() {
|
|
242
194
|
this.currentPlayingAttempt = null;
|
|
243
195
|
this.setCurrentAttempt(null);
|
|
244
196
|
}
|
|
245
|
-
|
|
246
|
-
// Public method to get transcript for an attempt
|
|
247
197
|
getTranscript(attemptNumber) {
|
|
248
198
|
return this.collection.get(attemptNumber)?.transcript;
|
|
249
199
|
}
|
|
250
|
-
|
|
251
|
-
// Public method to check if transcript is loading
|
|
252
200
|
isTranscriptLoading(attemptNumber) {
|
|
253
201
|
return this.collection.get(attemptNumber)?.transcriptLoading || false;
|
|
254
202
|
}
|
|
255
|
-
|
|
256
|
-
// Public method to check if audio is loading
|
|
257
203
|
isAudioLoading(attemptNumber) {
|
|
258
204
|
return this.collection.get(attemptNumber)?.audioLoading || false;
|
|
259
205
|
}
|
|
260
|
-
|
|
261
|
-
// Public method to get all attempts
|
|
262
206
|
getAllAttempts() {
|
|
263
207
|
return this.collection.getAll();
|
|
264
208
|
}
|
|
265
|
-
|
|
266
|
-
// Cleanup method
|
|
267
209
|
cleanup() {
|
|
268
|
-
// Stop and cleanup audio player
|
|
269
210
|
if (this.player) {
|
|
270
211
|
try {
|
|
271
212
|
this.player.remove();
|
|
272
213
|
} catch (error) {}
|
|
273
214
|
}
|
|
274
|
-
|
|
275
|
-
// Cleanup blob URLs to prevent memory leaks
|
|
276
215
|
this.collection.getAll().forEach(item => {
|
|
277
216
|
if (item.audioUri && item.audioUri.startsWith('blob:')) {
|
|
278
217
|
try {
|
|
@@ -282,8 +221,6 @@ class VoicePlayerModel {
|
|
|
282
221
|
}
|
|
283
222
|
}
|
|
284
223
|
});
|
|
285
|
-
|
|
286
|
-
// Abort any ongoing downloads
|
|
287
224
|
this.collection.getAll().forEach(item => {
|
|
288
225
|
if (item.controller) {
|
|
289
226
|
this.downloader.abortDownload(item.controller);
|
|
@@ -300,14 +237,9 @@ class VoicePlayerModel {
|
|
|
300
237
|
if (this.currentPlayingAttempt === attemptNumber && this.$isPlaying.getState()) {
|
|
301
238
|
this.pauseAudio();
|
|
302
239
|
} else {
|
|
303
|
-
this.
|
|
240
|
+
this.handlePlayAttempt(attemptNumber);
|
|
304
241
|
}
|
|
305
242
|
}
|
|
306
|
-
|
|
307
|
-
// Public method to check if a specific attempt is playing
|
|
308
|
-
isAttemptPlaying(attemptNumber) {
|
|
309
|
-
return this.currentPlayingAttempt === attemptNumber && this.$isPlaying.getState();
|
|
310
|
-
}
|
|
311
243
|
}
|
|
312
244
|
exports.VoicePlayerModel = VoicePlayerModel;
|
|
313
245
|
//# sourceMappingURL=VoicePlayer.model.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_effector","require","_VoiceTranscriptionsCollection","_VoiceTranscriptionsDownloaderModel","_VoiceTranscriptionsDropdownModel","VoicePlayerModel","collection","VoiceTranscriptionsCollection","downloader","VoiceTranscriptionsDownloaderModel","dropdown","VoiceTranscriptionsDropdownModel","currentPlayingAttempt","
|
|
1
|
+
{"version":3,"names":["_effector","require","_VoiceTranscriptionsCollection","_VoiceTranscriptionsDownloaderModel","_VoiceTranscriptionsDropdownModel","VoicePlayerModel","collection","VoiceTranscriptionsCollection","downloader","VoiceTranscriptionsDownloaderModel","dropdown","VoiceTranscriptionsDropdownModel","currentPlayingAttempt","setCurrentAttempt","createEvent","setTranscriptsLoaded","setAudioLoading","setIsPlaying","reset","$currentAttempt","restore","$transcriptsLoaded","$audioLoading","$isPlaying","pauseAudio","createEffect","player","pause","console","warn","stopAudio","remove","constructor","params","api","audioIds","setApi","initializeCollection","initializeWithAudioIds","clear","Object","entries","forEach","attemptNumber","audioFileId","add","Number","loadAllTranscripts","attempts","getAll","transcriptPromises","map","item","hasTranscript","update","transcriptLoading","response","getAudioFileTranscript","transcript","text","transcriptError","undefined","error","Error","message","Promise","all","handlePlayAttempt","get","currentAttempt","getState","previousItem","controller","audioLoading","abortDownload","audioDownloadPromise","hasAudio","playAudio","collectionItem","downloadPromise","download","result","audioUri","uri","audioError","name","attach","source","mapParams","isPlaying","effect","play","replace","errorMessage","handlePlaybackComplete","getTranscript","isTranscriptLoading","isAudioLoading","getAllAttempts","cleanup","startsWith","URL","revokeObjectURL","e","togglePlayPause","exports"],"sourceRoot":"../../../../../../src","sources":["features/voice/playing/model/VoicePlayer.model.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { createEffect, createEvent, restore
|
|
3
|
+
import { attach, createEffect, createEvent, restore } from 'effector';
|
|
4
4
|
import { VoiceTranscriptionsCollection } from "./VoiceTranscriptionsCollection.js";
|
|
5
5
|
import { VoiceTranscriptionsDownloaderModel } from "./VoiceTranscriptionsDownloader.model.js";
|
|
6
6
|
import { VoiceTranscriptionsDropdownModel } from "./VoiceTranscriptionsDropdown.model.js";
|
|
@@ -9,43 +9,45 @@ export class VoicePlayerModel {
|
|
|
9
9
|
downloader = new VoiceTranscriptionsDownloaderModel();
|
|
10
10
|
dropdown = new VoiceTranscriptionsDropdownModel();
|
|
11
11
|
currentPlayingAttempt = null;
|
|
12
|
-
|
|
13
|
-
// Events
|
|
14
|
-
playAttempt = createEvent();
|
|
15
|
-
pauseAudio = createEvent();
|
|
16
|
-
stopAudio = createEvent();
|
|
17
12
|
setCurrentAttempt = createEvent();
|
|
18
13
|
setTranscriptsLoaded = createEvent();
|
|
19
14
|
setAudioLoading = createEvent();
|
|
20
15
|
setIsPlaying = createEvent();
|
|
21
16
|
reset = createEvent();
|
|
22
|
-
|
|
23
|
-
// Stores
|
|
24
17
|
$currentAttempt = restore(this.setCurrentAttempt, null).reset(this.reset);
|
|
25
18
|
$transcriptsLoaded = restore(this.setTranscriptsLoaded, false).reset(this.reset);
|
|
26
19
|
$audioLoading = restore(this.setAudioLoading, false).reset(this.reset);
|
|
27
20
|
$isPlaying = restore(this.setIsPlaying, false).reset(this.reset);
|
|
21
|
+
pauseAudio = createEffect(() => {
|
|
22
|
+
if (this.player) {
|
|
23
|
+
this.player.pause();
|
|
24
|
+
} else {
|
|
25
|
+
console.warn('Cannot pause: audio player not initialized');
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
stopAudio = createEffect(() => {
|
|
29
|
+
if (this.player) {
|
|
30
|
+
this.player.remove();
|
|
31
|
+
this.currentPlayingAttempt = null;
|
|
32
|
+
this.setCurrentAttempt(null);
|
|
33
|
+
} else {
|
|
34
|
+
console.warn('Cannot stop: audio player not initialized');
|
|
35
|
+
}
|
|
36
|
+
});
|
|
28
37
|
constructor(params) {
|
|
29
38
|
this.api = params.api;
|
|
30
39
|
this.audioIds = params.audioIds;
|
|
31
40
|
this.downloader.setApi(params.api);
|
|
32
|
-
|
|
33
|
-
// Initialize collection if audioIds are provided
|
|
34
41
|
if (params.audioIds) {
|
|
35
42
|
this.initializeCollection();
|
|
36
43
|
}
|
|
37
|
-
this.setupEffects();
|
|
38
44
|
}
|
|
39
|
-
|
|
40
|
-
// Public method to initialize or update audioIds
|
|
41
45
|
initializeWithAudioIds(audioIds) {
|
|
42
46
|
this.audioIds = audioIds;
|
|
43
|
-
// Clear existing collection before reinitializing
|
|
44
47
|
this.collection.clear();
|
|
45
48
|
this.initializeCollection();
|
|
46
49
|
}
|
|
47
50
|
initializeCollection() {
|
|
48
|
-
// Initialize collection with all attempts from audioIds
|
|
49
51
|
if (!this.audioIds) {
|
|
50
52
|
console.warn('VoicePlayerModel: audioIds is not provided');
|
|
51
53
|
return;
|
|
@@ -57,50 +59,12 @@ export class VoicePlayerModel {
|
|
|
57
59
|
});
|
|
58
60
|
});
|
|
59
61
|
}
|
|
60
|
-
setupEffects() {
|
|
61
|
-
// Download and play audio when user selects an attempt
|
|
62
|
-
sample({
|
|
63
|
-
clock: this.playAttempt,
|
|
64
|
-
target: this.handlePlayAttempt
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Pause audio playback
|
|
68
|
-
sample({
|
|
69
|
-
clock: this.pauseAudio,
|
|
70
|
-
target: createEffect(() => {
|
|
71
|
-
if (this.player) {
|
|
72
|
-
this.player.pause();
|
|
73
|
-
} else {
|
|
74
|
-
console.warn('Cannot pause: audio player not initialized');
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Stop audio playback
|
|
80
|
-
sample({
|
|
81
|
-
clock: this.stopAudio,
|
|
82
|
-
target: createEffect(() => {
|
|
83
|
-
if (this.player) {
|
|
84
|
-
this.player.remove();
|
|
85
|
-
this.currentPlayingAttempt = null;
|
|
86
|
-
this.setCurrentAttempt(null);
|
|
87
|
-
} else {
|
|
88
|
-
console.warn('Cannot stop: audio player not initialized');
|
|
89
|
-
}
|
|
90
|
-
})
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Load all transcripts for all attempts
|
|
95
62
|
loadAllTranscripts = createEffect(async () => {
|
|
96
63
|
const attempts = this.collection.getAll();
|
|
97
64
|
const transcriptPromises = attempts.map(async item => {
|
|
98
|
-
// Skip if transcript already loaded
|
|
99
65
|
if (this.collection.hasTranscript(item.attemptNumber)) {
|
|
100
66
|
return;
|
|
101
67
|
}
|
|
102
|
-
|
|
103
|
-
// Mark as loading
|
|
104
68
|
this.collection.update(item.attemptNumber, {
|
|
105
69
|
transcriptLoading: true
|
|
106
70
|
});
|
|
@@ -121,13 +85,9 @@ export class VoicePlayerModel {
|
|
|
121
85
|
await Promise.all(transcriptPromises);
|
|
122
86
|
this.setTranscriptsLoaded(true);
|
|
123
87
|
});
|
|
124
|
-
|
|
125
|
-
// Handle play button press for an attempt
|
|
126
88
|
handlePlayAttempt = createEffect(async attemptNumber => {
|
|
127
89
|
const item = this.collection.get(attemptNumber);
|
|
128
90
|
if (!item) return;
|
|
129
|
-
|
|
130
|
-
// Abort any ongoing download from previous attempt
|
|
131
91
|
const currentAttempt = this.$currentAttempt.getState();
|
|
132
92
|
if (currentAttempt !== null && currentAttempt !== attemptNumber) {
|
|
133
93
|
const previousItem = this.collection.get(currentAttempt);
|
|
@@ -140,14 +100,10 @@ export class VoicePlayerModel {
|
|
|
140
100
|
});
|
|
141
101
|
}
|
|
142
102
|
}
|
|
143
|
-
|
|
144
|
-
// If audio already downloaded, just play it
|
|
145
103
|
if (this.collection.hasAudio(attemptNumber)) {
|
|
146
104
|
this.playAudio(attemptNumber);
|
|
147
105
|
return;
|
|
148
106
|
}
|
|
149
|
-
|
|
150
|
-
// Download audio file
|
|
151
107
|
try {
|
|
152
108
|
this.setAudioLoading(true);
|
|
153
109
|
const {
|
|
@@ -167,13 +123,10 @@ export class VoicePlayerModel {
|
|
|
167
123
|
audioDownloadPromise: undefined
|
|
168
124
|
});
|
|
169
125
|
this.setAudioLoading(false);
|
|
170
|
-
|
|
171
|
-
// Play the audio after successful download
|
|
172
126
|
this.playAudio(attemptNumber);
|
|
173
127
|
} catch (error) {
|
|
174
128
|
console.error('Audio download error:', error);
|
|
175
129
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
176
|
-
// Download was aborted, just clean up
|
|
177
130
|
return;
|
|
178
131
|
}
|
|
179
132
|
this.collection.update(attemptNumber, {
|
|
@@ -185,90 +138,76 @@ export class VoicePlayerModel {
|
|
|
185
138
|
this.setAudioLoading(false);
|
|
186
139
|
}
|
|
187
140
|
});
|
|
188
|
-
playAudio(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
// If currently playing a different attempt, stop it first
|
|
202
|
-
if (this.currentPlayingAttempt !== null && this.currentPlayingAttempt !== attemptNumber) {
|
|
203
|
-
this.player.remove();
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// If already playing this attempt, pause it
|
|
207
|
-
if (this.currentPlayingAttempt === attemptNumber && this.$isPlaying.getState()) {
|
|
208
|
-
this.player.pause();
|
|
141
|
+
playAudio = attach({
|
|
142
|
+
source: this.$isPlaying,
|
|
143
|
+
mapParams: (attemptNumber, isPlaying) => ({
|
|
144
|
+
attemptNumber,
|
|
145
|
+
isPlaying
|
|
146
|
+
}),
|
|
147
|
+
effect: createEffect(async ({
|
|
148
|
+
attemptNumber,
|
|
149
|
+
isPlaying
|
|
150
|
+
}) => {
|
|
151
|
+
const item = this.collection.get(attemptNumber);
|
|
152
|
+
if (!item?.audioUri) {
|
|
153
|
+
console.warn('No audio URI available for attempt:', attemptNumber);
|
|
209
154
|
return;
|
|
210
155
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (this.currentPlayingAttempt === attemptNumber && !this.$isPlaying.getState()) {
|
|
214
|
-
this.player.play();
|
|
156
|
+
if (!this.player) {
|
|
157
|
+
console.warn('Audio player not initialized');
|
|
215
158
|
return;
|
|
216
159
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
160
|
+
try {
|
|
161
|
+
if (this.currentPlayingAttempt !== null && this.currentPlayingAttempt !== attemptNumber) {
|
|
162
|
+
this.player.remove();
|
|
163
|
+
}
|
|
164
|
+
if (this.currentPlayingAttempt === attemptNumber && isPlaying) {
|
|
165
|
+
this.player.pause();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (this.currentPlayingAttempt === attemptNumber && !isPlaying) {
|
|
169
|
+
this.player.play();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this.player.replace(item.audioUri);
|
|
173
|
+
this.currentPlayingAttempt = attemptNumber;
|
|
174
|
+
this.setCurrentAttempt(attemptNumber);
|
|
175
|
+
this.player.play();
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error('Error playing audio:', error);
|
|
178
|
+
this.setIsPlaying(false);
|
|
179
|
+
let errorMessage = 'Failed to play audio';
|
|
180
|
+
if (error instanceof Error) {
|
|
181
|
+
errorMessage = error.message;
|
|
182
|
+
}
|
|
183
|
+
this.collection.update(attemptNumber, {
|
|
184
|
+
audioError: errorMessage
|
|
185
|
+
});
|
|
229
186
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Handle playback completion (called from hook)
|
|
187
|
+
})
|
|
188
|
+
});
|
|
237
189
|
handlePlaybackComplete() {
|
|
238
190
|
this.currentPlayingAttempt = null;
|
|
239
191
|
this.setCurrentAttempt(null);
|
|
240
192
|
}
|
|
241
|
-
|
|
242
|
-
// Public method to get transcript for an attempt
|
|
243
193
|
getTranscript(attemptNumber) {
|
|
244
194
|
return this.collection.get(attemptNumber)?.transcript;
|
|
245
195
|
}
|
|
246
|
-
|
|
247
|
-
// Public method to check if transcript is loading
|
|
248
196
|
isTranscriptLoading(attemptNumber) {
|
|
249
197
|
return this.collection.get(attemptNumber)?.transcriptLoading || false;
|
|
250
198
|
}
|
|
251
|
-
|
|
252
|
-
// Public method to check if audio is loading
|
|
253
199
|
isAudioLoading(attemptNumber) {
|
|
254
200
|
return this.collection.get(attemptNumber)?.audioLoading || false;
|
|
255
201
|
}
|
|
256
|
-
|
|
257
|
-
// Public method to get all attempts
|
|
258
202
|
getAllAttempts() {
|
|
259
203
|
return this.collection.getAll();
|
|
260
204
|
}
|
|
261
|
-
|
|
262
|
-
// Cleanup method
|
|
263
205
|
cleanup() {
|
|
264
|
-
// Stop and cleanup audio player
|
|
265
206
|
if (this.player) {
|
|
266
207
|
try {
|
|
267
208
|
this.player.remove();
|
|
268
209
|
} catch (error) {}
|
|
269
210
|
}
|
|
270
|
-
|
|
271
|
-
// Cleanup blob URLs to prevent memory leaks
|
|
272
211
|
this.collection.getAll().forEach(item => {
|
|
273
212
|
if (item.audioUri && item.audioUri.startsWith('blob:')) {
|
|
274
213
|
try {
|
|
@@ -278,8 +217,6 @@ export class VoicePlayerModel {
|
|
|
278
217
|
}
|
|
279
218
|
}
|
|
280
219
|
});
|
|
281
|
-
|
|
282
|
-
// Abort any ongoing downloads
|
|
283
220
|
this.collection.getAll().forEach(item => {
|
|
284
221
|
if (item.controller) {
|
|
285
222
|
this.downloader.abortDownload(item.controller);
|
|
@@ -296,13 +233,8 @@ export class VoicePlayerModel {
|
|
|
296
233
|
if (this.currentPlayingAttempt === attemptNumber && this.$isPlaying.getState()) {
|
|
297
234
|
this.pauseAudio();
|
|
298
235
|
} else {
|
|
299
|
-
this.
|
|
236
|
+
this.handlePlayAttempt(attemptNumber);
|
|
300
237
|
}
|
|
301
238
|
}
|
|
302
|
-
|
|
303
|
-
// Public method to check if a specific attempt is playing
|
|
304
|
-
isAttemptPlaying(attemptNumber) {
|
|
305
|
-
return this.currentPlayingAttempt === attemptNumber && this.$isPlaying.getState();
|
|
306
|
-
}
|
|
307
239
|
}
|
|
308
240
|
//# sourceMappingURL=VoicePlayer.model.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["createEffect","createEvent","restore","
|
|
1
|
+
{"version":3,"names":["attach","createEffect","createEvent","restore","VoiceTranscriptionsCollection","VoiceTranscriptionsDownloaderModel","VoiceTranscriptionsDropdownModel","VoicePlayerModel","collection","downloader","dropdown","currentPlayingAttempt","setCurrentAttempt","setTranscriptsLoaded","setAudioLoading","setIsPlaying","reset","$currentAttempt","$transcriptsLoaded","$audioLoading","$isPlaying","pauseAudio","player","pause","console","warn","stopAudio","remove","constructor","params","api","audioIds","setApi","initializeCollection","initializeWithAudioIds","clear","Object","entries","forEach","attemptNumber","audioFileId","add","Number","loadAllTranscripts","attempts","getAll","transcriptPromises","map","item","hasTranscript","update","transcriptLoading","response","getAudioFileTranscript","transcript","text","transcriptError","undefined","error","Error","message","Promise","all","handlePlayAttempt","get","currentAttempt","getState","previousItem","controller","audioLoading","abortDownload","audioDownloadPromise","hasAudio","playAudio","collectionItem","downloadPromise","download","result","audioUri","uri","audioError","name","source","mapParams","isPlaying","effect","play","replace","errorMessage","handlePlaybackComplete","getTranscript","isTranscriptLoading","isAudioLoading","getAllAttempts","cleanup","startsWith","URL","revokeObjectURL","e","togglePlayPause"],"sourceRoot":"../../../../../../src","sources":["features/voice/playing/model/VoicePlayer.model.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -15,9 +15,6 @@ export declare class VoicePlayerModel {
|
|
|
15
15
|
audioIds?: PlayerAudioIds;
|
|
16
16
|
player: AudioPlayer;
|
|
17
17
|
private currentPlayingAttempt;
|
|
18
|
-
readonly playAttempt: import("effector").EventCallable<number>;
|
|
19
|
-
readonly pauseAudio: import("effector").EventCallable<void>;
|
|
20
|
-
readonly stopAudio: import("effector").EventCallable<void>;
|
|
21
18
|
readonly setCurrentAttempt: import("effector").EventCallable<number | null>;
|
|
22
19
|
readonly setTranscriptsLoaded: import("effector").EventCallable<boolean>;
|
|
23
20
|
readonly setAudioLoading: import("effector").EventCallable<boolean>;
|
|
@@ -27,10 +24,11 @@ export declare class VoicePlayerModel {
|
|
|
27
24
|
readonly $transcriptsLoaded: import("effector").StoreWritable<boolean>;
|
|
28
25
|
readonly $audioLoading: import("effector").StoreWritable<boolean>;
|
|
29
26
|
readonly $isPlaying: import("effector").StoreWritable<boolean>;
|
|
27
|
+
readonly pauseAudio: import("effector").Effect<void, void, Error>;
|
|
28
|
+
readonly stopAudio: import("effector").Effect<void, void, Error>;
|
|
30
29
|
constructor(params: VoiceTranscriptionsPlayerModelParams);
|
|
31
30
|
initializeWithAudioIds(audioIds: PlayerAudioIds): void;
|
|
32
31
|
private initializeCollection;
|
|
33
|
-
private setupEffects;
|
|
34
32
|
readonly loadAllTranscripts: import("effector").Effect<void, void, Error>;
|
|
35
33
|
private readonly handlePlayAttempt;
|
|
36
34
|
private playAudio;
|
|
@@ -41,7 +39,6 @@ export declare class VoicePlayerModel {
|
|
|
41
39
|
getAllAttempts(): VoiceTranscriptionItem[];
|
|
42
40
|
cleanup(): void;
|
|
43
41
|
togglePlayPause(attemptNumber: number): void;
|
|
44
|
-
isAttemptPlaying(attemptNumber: number): boolean;
|
|
45
42
|
}
|
|
46
43
|
export {};
|
|
47
44
|
//# sourceMappingURL=VoicePlayer.model.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoicePlayer.model.d.ts","sourceRoot":"","sources":["../../../../../../../src/features/voice/playing/model/VoicePlayer.model.ts"],"names":[],"mappings":"AACA,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,EACvB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EAAE,kCAAkC,EAAE,MAAM,uCAAuC,CAAA;AAC1F,OAAO,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAA;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5D,KAAK,oCAAoC,GAAG;IAC1C,GAAG,EAAE,cAAc,CAAA;IACnB,QAAQ,CAAC,EAAE,cAAc,CAAA;CAC1B,CAAA;
|
|
1
|
+
{"version":3,"file":"VoicePlayer.model.d.ts","sourceRoot":"","sources":["../../../../../../../src/features/voice/playing/model/VoicePlayer.model.ts"],"names":[],"mappings":"AACA,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,EACvB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EAAE,kCAAkC,EAAE,MAAM,uCAAuC,CAAA;AAC1F,OAAO,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAA;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5D,KAAK,oCAAoC,GAAG;IAC1C,GAAG,EAAE,cAAc,CAAA;IACnB,QAAQ,CAAC,EAAE,cAAc,CAAA;CAC1B,CAAA;AAMD,qBAAa,gBAAgB;IAC3B,SAAgB,UAAU,gCAAsC;IAChE,SAAgB,UAAU,qCAA2C;IACrE,SAAgB,QAAQ,mCAAyC;IACjE,SAAgB,GAAG,EAAE,cAAc,CAAA;IAC5B,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,MAAM,EAAG,WAAW,CAAA;IAC3B,OAAO,CAAC,qBAAqB,CAAsB;IAEnD,SAAgB,iBAAiB,kDAA+B;IAChE,SAAgB,oBAAoB,4CAAyB;IAC7D,SAAgB,eAAe,4CAAyB;IACxD,SAAgB,YAAY,4CAAyB;IACrD,SAAgB,KAAK,yCAAgB;IAErC,SAAgB,eAAe,kDAA0D;IACzF,SAAgB,kBAAkB,4CAA8D;IAChG,SAAgB,aAAa,4CAAyD;IACtF,SAAgB,UAAU,4CAAsD;IAEhF,SAAgB,UAAU,+CAMxB;IACF,SAAgB,SAAS,+CAQvB;gBAEU,MAAM,EAAE,oCAAoC;IAWjD,sBAAsB,CAAC,QAAQ,EAAE,cAAc;IAMtD,OAAO,CAAC,oBAAoB;IAc5B,SAAgB,kBAAkB,+CA4BhC;IAEF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CA2DhC;IAEF,OAAO,CAAC,SAAS,CAgDf;IAEK,sBAAsB;IAKtB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIxD,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO;IAInD,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO;IAI9C,cAAc,IAAI,sBAAsB,EAAE;IAI1C,OAAO;IA8BP,eAAe,CAAC,aAAa,EAAE,MAAM;CAO7C"}
|
|
@@ -15,9 +15,6 @@ export declare class VoicePlayerModel {
|
|
|
15
15
|
audioIds?: PlayerAudioIds;
|
|
16
16
|
player: AudioPlayer;
|
|
17
17
|
private currentPlayingAttempt;
|
|
18
|
-
readonly playAttempt: import("effector").EventCallable<number>;
|
|
19
|
-
readonly pauseAudio: import("effector").EventCallable<void>;
|
|
20
|
-
readonly stopAudio: import("effector").EventCallable<void>;
|
|
21
18
|
readonly setCurrentAttempt: import("effector").EventCallable<number | null>;
|
|
22
19
|
readonly setTranscriptsLoaded: import("effector").EventCallable<boolean>;
|
|
23
20
|
readonly setAudioLoading: import("effector").EventCallable<boolean>;
|
|
@@ -27,10 +24,11 @@ export declare class VoicePlayerModel {
|
|
|
27
24
|
readonly $transcriptsLoaded: import("effector").StoreWritable<boolean>;
|
|
28
25
|
readonly $audioLoading: import("effector").StoreWritable<boolean>;
|
|
29
26
|
readonly $isPlaying: import("effector").StoreWritable<boolean>;
|
|
27
|
+
readonly pauseAudio: import("effector").Effect<void, void, Error>;
|
|
28
|
+
readonly stopAudio: import("effector").Effect<void, void, Error>;
|
|
30
29
|
constructor(params: VoiceTranscriptionsPlayerModelParams);
|
|
31
30
|
initializeWithAudioIds(audioIds: PlayerAudioIds): void;
|
|
32
31
|
private initializeCollection;
|
|
33
|
-
private setupEffects;
|
|
34
32
|
readonly loadAllTranscripts: import("effector").Effect<void, void, Error>;
|
|
35
33
|
private readonly handlePlayAttempt;
|
|
36
34
|
private playAudio;
|
|
@@ -41,7 +39,6 @@ export declare class VoicePlayerModel {
|
|
|
41
39
|
getAllAttempts(): VoiceTranscriptionItem[];
|
|
42
40
|
cleanup(): void;
|
|
43
41
|
togglePlayPause(attemptNumber: number): void;
|
|
44
|
-
isAttemptPlaying(attemptNumber: number): boolean;
|
|
45
42
|
}
|
|
46
43
|
export {};
|
|
47
44
|
//# sourceMappingURL=VoicePlayer.model.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoicePlayer.model.d.ts","sourceRoot":"","sources":["../../../../../../../src/features/voice/playing/model/VoicePlayer.model.ts"],"names":[],"mappings":"AACA,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,EACvB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EAAE,kCAAkC,EAAE,MAAM,uCAAuC,CAAA;AAC1F,OAAO,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAA;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5D,KAAK,oCAAoC,GAAG;IAC1C,GAAG,EAAE,cAAc,CAAA;IACnB,QAAQ,CAAC,EAAE,cAAc,CAAA;CAC1B,CAAA;
|
|
1
|
+
{"version":3,"file":"VoicePlayer.model.d.ts","sourceRoot":"","sources":["../../../../../../../src/features/voice/playing/model/VoicePlayer.model.ts"],"names":[],"mappings":"AACA,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,EACvB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EAAE,kCAAkC,EAAE,MAAM,uCAAuC,CAAA;AAC1F,OAAO,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAA;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE5D,KAAK,oCAAoC,GAAG;IAC1C,GAAG,EAAE,cAAc,CAAA;IACnB,QAAQ,CAAC,EAAE,cAAc,CAAA;CAC1B,CAAA;AAMD,qBAAa,gBAAgB;IAC3B,SAAgB,UAAU,gCAAsC;IAChE,SAAgB,UAAU,qCAA2C;IACrE,SAAgB,QAAQ,mCAAyC;IACjE,SAAgB,GAAG,EAAE,cAAc,CAAA;IAC5B,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,MAAM,EAAG,WAAW,CAAA;IAC3B,OAAO,CAAC,qBAAqB,CAAsB;IAEnD,SAAgB,iBAAiB,kDAA+B;IAChE,SAAgB,oBAAoB,4CAAyB;IAC7D,SAAgB,eAAe,4CAAyB;IACxD,SAAgB,YAAY,4CAAyB;IACrD,SAAgB,KAAK,yCAAgB;IAErC,SAAgB,eAAe,kDAA0D;IACzF,SAAgB,kBAAkB,4CAA8D;IAChG,SAAgB,aAAa,4CAAyD;IACtF,SAAgB,UAAU,4CAAsD;IAEhF,SAAgB,UAAU,+CAMxB;IACF,SAAgB,SAAS,+CAQvB;gBAEU,MAAM,EAAE,oCAAoC;IAWjD,sBAAsB,CAAC,QAAQ,EAAE,cAAc;IAMtD,OAAO,CAAC,oBAAoB;IAc5B,SAAgB,kBAAkB,+CA4BhC;IAEF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CA2DhC;IAEF,OAAO,CAAC,SAAS,CAgDf;IAEK,sBAAsB;IAKtB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIxD,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO;IAInD,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO;IAI9C,cAAc,IAAI,sBAAsB,EAAE;IAI1C,OAAO;IA8BP,eAAe,CAAC,aAAa,EAAE,MAAM;CAO7C"}
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createEffect, createEvent, restore,
|
|
1
|
+
import { attach, createEffect, createEvent, restore, StoreValue } from 'effector'
|
|
2
2
|
import {
|
|
3
3
|
VoiceTranscriptionsCollection,
|
|
4
4
|
VoiceTranscriptionItem,
|
|
@@ -12,6 +12,10 @@ type VoiceTranscriptionsPlayerModelParams = {
|
|
|
12
12
|
api: VoicePlayerApi
|
|
13
13
|
audioIds?: PlayerAudioIds
|
|
14
14
|
}
|
|
15
|
+
type PlayAudioParams = {
|
|
16
|
+
attemptNumber: number
|
|
17
|
+
isPlaying: StoreValue<VoicePlayerModel['$isPlaying']>
|
|
18
|
+
}
|
|
15
19
|
|
|
16
20
|
export class VoicePlayerModel {
|
|
17
21
|
public readonly collection = new VoiceTranscriptionsCollection()
|
|
@@ -22,46 +26,52 @@ export class VoicePlayerModel {
|
|
|
22
26
|
public player!: AudioPlayer
|
|
23
27
|
private currentPlayingAttempt: number | null = null
|
|
24
28
|
|
|
25
|
-
// Events
|
|
26
|
-
public readonly playAttempt = createEvent<number>()
|
|
27
|
-
public readonly pauseAudio = createEvent()
|
|
28
|
-
public readonly stopAudio = createEvent()
|
|
29
29
|
public readonly setCurrentAttempt = createEvent<number | null>()
|
|
30
30
|
public readonly setTranscriptsLoaded = createEvent<boolean>()
|
|
31
31
|
public readonly setAudioLoading = createEvent<boolean>()
|
|
32
32
|
public readonly setIsPlaying = createEvent<boolean>()
|
|
33
33
|
public readonly reset = createEvent()
|
|
34
34
|
|
|
35
|
-
// Stores
|
|
36
35
|
public readonly $currentAttempt = restore(this.setCurrentAttempt, null).reset(this.reset)
|
|
37
36
|
public readonly $transcriptsLoaded = restore(this.setTranscriptsLoaded, false).reset(this.reset)
|
|
38
37
|
public readonly $audioLoading = restore(this.setAudioLoading, false).reset(this.reset)
|
|
39
38
|
public readonly $isPlaying = restore(this.setIsPlaying, false).reset(this.reset)
|
|
40
39
|
|
|
40
|
+
public readonly pauseAudio = createEffect(() => {
|
|
41
|
+
if (this.player) {
|
|
42
|
+
this.player.pause()
|
|
43
|
+
} else {
|
|
44
|
+
console.warn('Cannot pause: audio player not initialized')
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
public readonly stopAudio = createEffect(() => {
|
|
48
|
+
if (this.player) {
|
|
49
|
+
this.player.remove()
|
|
50
|
+
this.currentPlayingAttempt = null
|
|
51
|
+
this.setCurrentAttempt(null)
|
|
52
|
+
} else {
|
|
53
|
+
console.warn('Cannot stop: audio player not initialized')
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
41
57
|
constructor(params: VoiceTranscriptionsPlayerModelParams) {
|
|
42
58
|
this.api = params.api
|
|
43
59
|
this.audioIds = params.audioIds
|
|
44
60
|
|
|
45
61
|
this.downloader.setApi(params.api)
|
|
46
62
|
|
|
47
|
-
// Initialize collection if audioIds are provided
|
|
48
63
|
if (params.audioIds) {
|
|
49
64
|
this.initializeCollection()
|
|
50
65
|
}
|
|
51
|
-
|
|
52
|
-
this.setupEffects()
|
|
53
66
|
}
|
|
54
67
|
|
|
55
|
-
// Public method to initialize or update audioIds
|
|
56
68
|
public initializeWithAudioIds(audioIds: PlayerAudioIds) {
|
|
57
69
|
this.audioIds = audioIds
|
|
58
|
-
// Clear existing collection before reinitializing
|
|
59
70
|
this.collection.clear()
|
|
60
71
|
this.initializeCollection()
|
|
61
72
|
}
|
|
62
73
|
|
|
63
74
|
private initializeCollection() {
|
|
64
|
-
// Initialize collection with all attempts from audioIds
|
|
65
75
|
if (!this.audioIds) {
|
|
66
76
|
console.warn('VoicePlayerModel: audioIds is not provided')
|
|
67
77
|
return
|
|
@@ -75,51 +85,14 @@ export class VoicePlayerModel {
|
|
|
75
85
|
})
|
|
76
86
|
}
|
|
77
87
|
|
|
78
|
-
private setupEffects() {
|
|
79
|
-
// Download and play audio when user selects an attempt
|
|
80
|
-
sample({
|
|
81
|
-
clock: this.playAttempt,
|
|
82
|
-
target: this.handlePlayAttempt,
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
// Pause audio playback
|
|
86
|
-
sample({
|
|
87
|
-
clock: this.pauseAudio,
|
|
88
|
-
target: createEffect(() => {
|
|
89
|
-
if (this.player) {
|
|
90
|
-
this.player.pause()
|
|
91
|
-
} else {
|
|
92
|
-
console.warn('Cannot pause: audio player not initialized')
|
|
93
|
-
}
|
|
94
|
-
}),
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
// Stop audio playback
|
|
98
|
-
sample({
|
|
99
|
-
clock: this.stopAudio,
|
|
100
|
-
target: createEffect(() => {
|
|
101
|
-
if (this.player) {
|
|
102
|
-
this.player.remove()
|
|
103
|
-
this.currentPlayingAttempt = null
|
|
104
|
-
this.setCurrentAttempt(null)
|
|
105
|
-
} else {
|
|
106
|
-
console.warn('Cannot stop: audio player not initialized')
|
|
107
|
-
}
|
|
108
|
-
}),
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Load all transcripts for all attempts
|
|
113
88
|
public readonly loadAllTranscripts = createEffect(async () => {
|
|
114
89
|
const attempts = this.collection.getAll()
|
|
115
90
|
|
|
116
91
|
const transcriptPromises = attempts.map(async (item) => {
|
|
117
|
-
// Skip if transcript already loaded
|
|
118
92
|
if (this.collection.hasTranscript(item.attemptNumber)) {
|
|
119
93
|
return
|
|
120
94
|
}
|
|
121
95
|
|
|
122
|
-
// Mark as loading
|
|
123
96
|
this.collection.update(item.attemptNumber, { transcriptLoading: true })
|
|
124
97
|
|
|
125
98
|
try {
|
|
@@ -142,12 +115,10 @@ export class VoicePlayerModel {
|
|
|
142
115
|
this.setTranscriptsLoaded(true)
|
|
143
116
|
})
|
|
144
117
|
|
|
145
|
-
// Handle play button press for an attempt
|
|
146
118
|
private readonly handlePlayAttempt = createEffect(async (attemptNumber: number) => {
|
|
147
119
|
const item = this.collection.get(attemptNumber)
|
|
148
120
|
if (!item) return
|
|
149
121
|
|
|
150
|
-
// Abort any ongoing download from previous attempt
|
|
151
122
|
const currentAttempt = this.$currentAttempt.getState()
|
|
152
123
|
if (currentAttempt !== null && currentAttempt !== attemptNumber) {
|
|
153
124
|
const previousItem = this.collection.get(currentAttempt)
|
|
@@ -161,13 +132,11 @@ export class VoicePlayerModel {
|
|
|
161
132
|
}
|
|
162
133
|
}
|
|
163
134
|
|
|
164
|
-
// If audio already downloaded, just play it
|
|
165
135
|
if (this.collection.hasAudio(attemptNumber)) {
|
|
166
136
|
this.playAudio(attemptNumber)
|
|
167
137
|
return
|
|
168
138
|
}
|
|
169
139
|
|
|
170
|
-
// Download audio file
|
|
171
140
|
try {
|
|
172
141
|
this.setAudioLoading(true)
|
|
173
142
|
const { collectionItem, downloadPromise } = await this.downloader.download({
|
|
@@ -189,12 +158,10 @@ export class VoicePlayerModel {
|
|
|
189
158
|
|
|
190
159
|
this.setAudioLoading(false)
|
|
191
160
|
|
|
192
|
-
// Play the audio after successful download
|
|
193
161
|
this.playAudio(attemptNumber)
|
|
194
162
|
} catch (error) {
|
|
195
163
|
console.error('Audio download error:', error)
|
|
196
164
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
197
|
-
// Download was aborted, just clean up
|
|
198
165
|
return
|
|
199
166
|
}
|
|
200
167
|
|
|
@@ -209,93 +176,84 @@ export class VoicePlayerModel {
|
|
|
209
176
|
}
|
|
210
177
|
})
|
|
211
178
|
|
|
212
|
-
private playAudio
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (!this.player) {
|
|
221
|
-
console.warn('Audio player not initialized')
|
|
222
|
-
return
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
// If currently playing a different attempt, stop it first
|
|
227
|
-
if (this.currentPlayingAttempt !== null && this.currentPlayingAttempt !== attemptNumber) {
|
|
228
|
-
this.player.remove()
|
|
179
|
+
private playAudio = attach({
|
|
180
|
+
source: this.$isPlaying,
|
|
181
|
+
mapParams: (attemptNumber: number, isPlaying: boolean) => ({ attemptNumber, isPlaying }),
|
|
182
|
+
effect: createEffect(async ({ attemptNumber, isPlaying }: PlayAudioParams) => {
|
|
183
|
+
const item = this.collection.get(attemptNumber)
|
|
184
|
+
if (!item?.audioUri) {
|
|
185
|
+
console.warn('No audio URI available for attempt:', attemptNumber)
|
|
186
|
+
return
|
|
229
187
|
}
|
|
230
188
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
this.player.pause()
|
|
189
|
+
if (!this.player) {
|
|
190
|
+
console.warn('Audio player not initialized')
|
|
234
191
|
return
|
|
235
192
|
}
|
|
236
193
|
|
|
237
|
-
|
|
238
|
-
|
|
194
|
+
try {
|
|
195
|
+
if (this.currentPlayingAttempt !== null && this.currentPlayingAttempt !== attemptNumber) {
|
|
196
|
+
this.player.remove()
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (this.currentPlayingAttempt === attemptNumber && isPlaying) {
|
|
200
|
+
this.player.pause()
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (this.currentPlayingAttempt === attemptNumber && !isPlaying) {
|
|
205
|
+
this.player.play()
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
this.player.replace(item.audioUri)
|
|
210
|
+
this.currentPlayingAttempt = attemptNumber
|
|
211
|
+
this.setCurrentAttempt(attemptNumber)
|
|
239
212
|
this.player.play()
|
|
240
|
-
|
|
241
|
-
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error('Error playing audio:', error)
|
|
215
|
+
this.setIsPlaying(false)
|
|
242
216
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
this.player.play()
|
|
248
|
-
} catch (error) {
|
|
249
|
-
console.error('Error playing audio:', error)
|
|
250
|
-
this.setIsPlaying(false)
|
|
217
|
+
let errorMessage = 'Failed to play audio'
|
|
218
|
+
if (error instanceof Error) {
|
|
219
|
+
errorMessage = error.message
|
|
220
|
+
}
|
|
251
221
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
222
|
+
this.collection.update(attemptNumber, {
|
|
223
|
+
audioError: errorMessage,
|
|
224
|
+
})
|
|
255
225
|
}
|
|
226
|
+
}),
|
|
227
|
+
})
|
|
256
228
|
|
|
257
|
-
this.collection.update(attemptNumber, {
|
|
258
|
-
audioError: errorMessage,
|
|
259
|
-
})
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Handle playback completion (called from hook)
|
|
264
229
|
public handlePlaybackComplete() {
|
|
265
230
|
this.currentPlayingAttempt = null
|
|
266
231
|
this.setCurrentAttempt(null)
|
|
267
232
|
}
|
|
268
233
|
|
|
269
|
-
// Public method to get transcript for an attempt
|
|
270
234
|
public getTranscript(attemptNumber: number): string | undefined {
|
|
271
235
|
return this.collection.get(attemptNumber)?.transcript
|
|
272
236
|
}
|
|
273
237
|
|
|
274
|
-
// Public method to check if transcript is loading
|
|
275
238
|
public isTranscriptLoading(attemptNumber: number): boolean {
|
|
276
239
|
return this.collection.get(attemptNumber)?.transcriptLoading || false
|
|
277
240
|
}
|
|
278
241
|
|
|
279
|
-
// Public method to check if audio is loading
|
|
280
242
|
public isAudioLoading(attemptNumber: number): boolean {
|
|
281
243
|
return this.collection.get(attemptNumber)?.audioLoading || false
|
|
282
244
|
}
|
|
283
245
|
|
|
284
|
-
// Public method to get all attempts
|
|
285
246
|
public getAllAttempts(): VoiceTranscriptionItem[] {
|
|
286
247
|
return this.collection.getAll()
|
|
287
248
|
}
|
|
288
249
|
|
|
289
|
-
// Cleanup method
|
|
290
250
|
public cleanup() {
|
|
291
|
-
// Stop and cleanup audio player
|
|
292
251
|
if (this.player) {
|
|
293
252
|
try {
|
|
294
253
|
this.player.remove()
|
|
295
254
|
} catch (error) {}
|
|
296
255
|
}
|
|
297
256
|
|
|
298
|
-
// Cleanup blob URLs to prevent memory leaks
|
|
299
257
|
this.collection.getAll().forEach((item) => {
|
|
300
258
|
if (item.audioUri && item.audioUri.startsWith('blob:')) {
|
|
301
259
|
try {
|
|
@@ -306,7 +264,6 @@ export class VoicePlayerModel {
|
|
|
306
264
|
}
|
|
307
265
|
})
|
|
308
266
|
|
|
309
|
-
// Abort any ongoing downloads
|
|
310
267
|
this.collection.getAll().forEach((item) => {
|
|
311
268
|
if (item.controller) {
|
|
312
269
|
this.downloader.abortDownload(item.controller)
|
|
@@ -324,12 +281,7 @@ export class VoicePlayerModel {
|
|
|
324
281
|
if (this.currentPlayingAttempt === attemptNumber && this.$isPlaying.getState()) {
|
|
325
282
|
this.pauseAudio()
|
|
326
283
|
} else {
|
|
327
|
-
this.
|
|
284
|
+
this.handlePlayAttempt(attemptNumber)
|
|
328
285
|
}
|
|
329
286
|
}
|
|
330
|
-
|
|
331
|
-
// Public method to check if a specific attempt is playing
|
|
332
|
-
public isAttemptPlaying(attemptNumber: number): boolean {
|
|
333
|
-
return this.currentPlayingAttempt === attemptNumber && this.$isPlaying.getState()
|
|
334
|
-
}
|
|
335
287
|
}
|