|
1 | 1 | <script lang="ts"> |
2 | 2 | import { browser } from '$app/environment'; |
3 | | - import { invalidate } from '$app/navigation'; |
4 | | - import { page } from '$app/stores'; |
5 | 3 | import { onMount } from 'svelte'; |
6 | 4 | import WaveSurfer from 'wavesurfer.js'; |
7 | 5 | import RecordPlugin from 'wavesurfer.js/dist/plugins/record.esm.js'; |
|
13 | 11 | import ButtonStop from './ButtonStop.svelte'; |
14 | 12 |
|
15 | 13 | interface RecorderProps { |
| 14 | + deleteRecording: () => void; |
| 15 | + recordingBlob?: Blob | MediaSource; |
| 16 | + recordingFileName?: string; |
| 17 | + recordingUrl?: string; |
| 18 | + saveRecording: () => void; |
16 | 19 | scrollingWaveform?: boolean; |
17 | 20 | } |
18 | 21 |
|
19 | | - let { scrollingWaveform = true }: RecorderProps = $props(); |
| 22 | + let { |
| 23 | + deleteRecording, |
| 24 | + recordingBlob = $bindable(), |
| 25 | + recordingFileName = $bindable(''), |
| 26 | + recordingUrl = $bindable(''), |
| 27 | + saveRecording, |
| 28 | + scrollingWaveform = true |
| 29 | + }: RecorderProps = $props(); |
20 | 30 |
|
21 | 31 | let defaultDeviceId: string | undefined = $state(undefined); |
22 | 32 | let isPaused = $state(false); |
23 | 33 | let isRecording = $state(false); |
24 | 34 | let isStopped = $state(false); |
25 | 35 | let progress = $state('00:00'); |
26 | 36 | let record: RecordPlugin = $state({} as RecordPlugin); |
27 | | - let recordingUrl = $state(''); |
28 | 37 | let progressColor = $state( |
29 | 38 | browser && window.matchMedia('(prefers-color-scheme: dark)').matches |
30 | 39 | ? '#ffffff' |
|
75 | 84 | }); |
76 | 85 | } |
77 | 86 |
|
78 | | - async function deleteRecording() { |
| 87 | + async function handleDeleteRecording() { |
79 | 88 | if (recordingUrl) { |
80 | | - URL.revokeObjectURL(recordingUrl); |
81 | | - recordingUrl = ''; |
| 89 | + deleteRecording(); |
| 90 | +
|
82 | 91 | progress = '00:00'; |
83 | 92 | wavesurfer.empty(); |
84 | 93 | } |
85 | 94 | } |
86 | 95 |
|
| 96 | + async function handleSaveRecording() { |
| 97 | + saveRecording(); |
| 98 | +
|
| 99 | + progress = '00:00'; |
| 100 | + wavesurfer.empty(); |
| 101 | + } |
| 102 | +
|
87 | 103 | async function startPlayback() { |
88 | 104 | wavesurfer.playPause(); |
89 | 105 | } |
|
135 | 151 | } |
136 | 152 | } |
137 | 153 |
|
138 | | - async function saveRecording() { |
139 | | - if (recordingUrl) { |
140 | | - const fileName = `${new Date().toISOString()}-${crypto.randomUUID()}.webm`; |
141 | | - const response = await fetch(recordingUrl); |
142 | | - const blob = await response.blob(); |
143 | | - const userId = $page.data.user?.id; |
144 | | -
|
145 | | - if (!userId) { |
146 | | - alert('You must be logged in to save recordings'); |
147 | | - return; |
148 | | - } |
149 | | -
|
150 | | - const { error: uploadError } = await $page.data.supabase.storage |
151 | | - .from('user_recordings_audio_files') |
152 | | - .upload(`${userId}/${fileName}`, blob, { |
153 | | - contentType: 'audio/webm' |
154 | | - }); |
155 | | -
|
156 | | - if (uploadError) { |
157 | | - console.error('Error uploading file:', uploadError.message); |
158 | | - return; |
159 | | - } |
160 | | -
|
161 | | - const transcriptionResult = await getTranscriptionFromBackend(blob); |
162 | | - const transcription = transcriptionResult ? transcriptionResult.text : ''; |
163 | | -
|
164 | | - const { error: insertError } = await $page.data.supabase.from('user_recordings').insert([ |
165 | | - { |
166 | | - id: crypto.randomUUID(), |
167 | | - created_at: new Date().toISOString(), |
168 | | - user_id: userId, |
169 | | - audio_file_name: fileName, |
170 | | - transcription: transcription |
171 | | - } |
172 | | - ]); |
173 | | -
|
174 | | - if (insertError) { |
175 | | - console.error('Error inserting record into database:', insertError.message); |
176 | | - return; |
177 | | - } |
178 | | -
|
179 | | - progress = '00:00'; |
180 | | - recordingUrl = ''; |
181 | | - wavesurfer.empty(); |
182 | | -
|
183 | | - await invalidate('get_user_recordings'); |
184 | | - } |
185 | | - } |
186 | | -
|
187 | | - async function getTranscriptionFromBackend(audioBlob: Blob): Promise<{ text: string } | null> { |
188 | | - try { |
189 | | - const formData = new FormData(); |
190 | | - const newBlob = new Blob([audioBlob], { type: 'audio/webm' }); |
191 | | - formData.append('audio', newBlob, 'audio.webm'); |
192 | | -
|
193 | | - const response = await fetch('/api/transcribe', { |
194 | | - method: 'POST', |
195 | | - body: formData |
196 | | - }); |
197 | | -
|
198 | | - if (!response.ok) { |
199 | | - throw new Error('Error in transcription request'); |
200 | | - } |
201 | | -
|
202 | | - const result = await response.json(); |
203 | | -
|
204 | | - return result; |
205 | | - } catch (error) { |
206 | | - console.error('Error fetching transcription:', error); |
207 | | - return null; |
208 | | - } |
209 | | - } |
210 | | -
|
211 | 154 | function updateProgress(time: number) { |
212 | 155 | const formattedTime = [ |
213 | 156 | Math.floor((time % 3600000) / 60000), // minutes |
|
252 | 195 | </div> |
253 | 196 |
|
254 | 197 | <div class="recording-actions"> |
255 | | - <Button label="Save & transcribe recording" onclick={saveRecording} /> |
| 198 | + <Button label="Save recording" onclick={handleSaveRecording} /> |
256 | 199 |
|
257 | | - <Button kind="danger" label="Delete recording" onclick={deleteRecording} /> |
| 200 | + <Button kind="danger" label="Delete recording" onclick={handleDeleteRecording} /> |
258 | 201 | </div> |
259 | 202 | {:else} |
260 | 203 | <ButtonRecord onclick={startRecording} /> |
|
0 commit comments