@@ -10,18 +10,16 @@ import { getAllSupportedFormats } from "@/lib/file-format-utils";
1010import { uploadLargeFile } from "@/lib/storage-service" ;
1111
1212interface UploadAudioProps {
13- onUpload : (
13+ readonly onUpload : (
1414 data :
1515 | FormData
1616 | { audioUrl : string ; originalFile ?: { name : string ; size : number } } ,
1717 options : { language : string ; diarize : boolean } ,
1818 ) => void ;
19- disabled ?: boolean ;
20- maxFileSize ?: number ;
21- onConversionStart ?: ( ) => void ;
22- onConversionComplete ?: ( ) => void ;
23- onConversionError ?: ( error : string ) => void ;
24- onApiResponse ?: ( response : { timestamp : Date ; data : unknown } ) => void ;
19+ readonly onConversionStart ?: ( ) => void ;
20+ readonly onConversionComplete ?: ( ) => void ;
21+ readonly onConversionError ?: ( error : string ) => void ;
22+ readonly onApiResponse ?: ( response : { timestamp : Date ; data : unknown } ) => void ;
2523}
2624
2725// --- URL Validation Helpers (can be moved to a util file if needed elsewhere) ---
@@ -86,7 +84,7 @@ export function UploadAudio({
8684 const handleFileChange = useCallback (
8785 ( e : React . ChangeEvent < HTMLInputElement > ) => {
8886 handleFileSelect ( e ) ; // Let the hook handle validation
89- if ( e . target . files && e . target . files [ 0 ] ) {
87+ if ( e . target . files ?. [ 0 ] ) {
9088 setFile ( e . target . files [ 0 ] ) ;
9189 setAudioUrl ( "" ) ; // Clear URL if a file is selected
9290 } else {
@@ -121,126 +119,144 @@ export function UploadAudio({
121119 [ ] ,
122120 ) ;
123121
124- const handleSubmit = useCallback ( async ( ) => {
125- if ( activeTab === "file" && file && ! fileError ) {
126- // Check if the file requires conversion
127- if ( requiresConversion ) {
128- try {
129- // Start conversion state IMMEDIATELY before any async operations
130- onConversionStart ?.( ) ;
131-
132- // Log conversion start
133- onApiResponse ?.( {
134- timestamp : new Date ( ) ,
135- data : {
136- message : `Starting conversion: ${ file . name } (${ file . type || "unknown type" } ) → MP3` ,
137- originalFile : file . name ,
138- originalFormat :
139- file . name . split ( "." ) . pop ( ) ?. toLowerCase ( ) || "unknown" ,
140- targetFormat : "mp3" ,
141- fileSize : file . size ,
142- } ,
143- } ) ;
144-
145- // Add a small delay to ensure state updates are processed
146- await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
147-
148- // First upload the file to Firebase to get a public URL
149- onApiResponse ?.( {
150- timestamp : new Date ( ) ,
151- data : {
152- message : `Uploading ${ file . name } to Firebase for conversion...` ,
153- } ,
154- } ) ;
155-
156- const uploadResult = await uploadLargeFile ( file ) ;
157-
158- onApiResponse ?.( {
159- timestamp : new Date ( ) ,
160- data : {
161- message : `File uploaded successfully` ,
162- firebaseUrl : uploadResult . url ,
163- firebasePath : uploadResult . path ,
164- } ,
165- } ) ;
166-
167- const fileExtension = file . name . split ( "." ) . pop ( ) ?. toLowerCase ( ) || "" ;
168-
169- // Call conversion endpoint with the uploaded file URL
170- onApiResponse ?.( {
171- timestamp : new Date ( ) ,
172- data : {
173- message : `Calling CloudConvert API for ${ fileExtension . toUpperCase ( ) } → MP3 conversion...` ,
174- } ,
175- } ) ;
122+ // Helper function to upload file to Firebase
123+ const uploadFileToFirebase = useCallback ( async ( file : File ) => {
124+ onApiResponse ?.( {
125+ timestamp : new Date ( ) ,
126+ data : {
127+ message : `Uploading ${ file . name } to Firebase for conversion...` ,
128+ } ,
129+ } ) ;
130+
131+ const uploadResult = await uploadLargeFile ( file ) ;
132+
133+ onApiResponse ?.( {
134+ timestamp : new Date ( ) ,
135+ data : {
136+ message : `File uploaded successfully` ,
137+ firebaseUrl : uploadResult . url ,
138+ firebasePath : uploadResult . path ,
139+ } ,
140+ } ) ;
141+
142+ return uploadResult ;
143+ } , [ onApiResponse ] ) ;
144+
145+ // Helper function to convert file using CloudConvert
146+ const convertFileToMp3 = useCallback ( async ( fileUrl : string , originalFile : File ) => {
147+ const fileExtension = originalFile . name . split ( "." ) . pop ( ) ?. toLowerCase ( ) || "" ;
148+
149+ onApiResponse ?.( {
150+ timestamp : new Date ( ) ,
151+ data : {
152+ message : `Calling CloudConvert API for ${ fileExtension . toUpperCase ( ) } → MP3 conversion...` ,
153+ } ,
154+ } ) ;
155+
156+ const response = await fetch ( "/api/convert/cloud" , {
157+ method : "POST" ,
158+ headers : {
159+ "Content-Type" : "application/json" ,
160+ } ,
161+ body : JSON . stringify ( {
162+ fileUrl,
163+ originalFormat : fileExtension ,
164+ targetFormat : "mp3" ,
165+ } ) ,
166+ } ) ;
167+
168+ if ( ! response . ok ) {
169+ throw new Error ( `Conversion failed: ${ response . statusText } ` ) ;
170+ }
176171
177- const response = await fetch ( "/api/convert/cloud" , {
178- method : "POST" ,
179- headers : {
180- "Content-Type" : "application/json" ,
181- } ,
182- body : JSON . stringify ( {
183- fileUrl : uploadResult . url ,
184- originalFormat : fileExtension ,
185- targetFormat : "mp3" ,
186- } ) ,
187- } ) ;
188-
189- if ( ! response . ok ) {
190- throw new Error ( `Conversion failed: ${ response . statusText } ` ) ;
191- }
192-
193- const conversionResult = await response . json ( ) ;
194-
195- onApiResponse ?.( {
196- timestamp : new Date ( ) ,
197- data : {
198- message : `CloudConvert API response received` ,
199- success : conversionResult . success ,
200- jobId : conversionResult . jobId ,
201- convertedUrl : conversionResult . convertedUrl
202- ? "URL generated"
203- : "No URL" ,
204- } ,
205- } ) ;
172+ const conversionResult = await response . json ( ) ;
206173
207- if ( ! conversionResult . success ) {
208- throw new Error ( conversionResult . error || "Conversion failed" ) ;
209- }
174+ onApiResponse ?.( {
175+ timestamp : new Date ( ) ,
176+ data : {
177+ message : `CloudConvert API response received` ,
178+ success : conversionResult . success ,
179+ jobId : conversionResult . jobId ,
180+ convertedUrl : conversionResult . convertedUrl ? "URL generated" : "No URL" ,
181+ } ,
182+ } ) ;
210183
211- onConversionComplete ?.( ) ;
184+ if ( ! conversionResult . success ) {
185+ throw new Error ( conversionResult . error || "Conversion failed" ) ;
186+ }
212187
213- onApiResponse ?.( {
214- timestamp : new Date ( ) ,
215- data : {
216- message : `Conversion completed successfully! Proceeding to transcription...` ,
217- convertedUrl : conversionResult . convertedUrl ,
218- } ,
219- } ) ;
188+ return conversionResult ;
189+ } , [ onApiResponse ] ) ;
190+
191+ // Helper function to handle conversion errors
192+ const handleConversionError = useCallback ( ( error : unknown ) => {
193+ const errorMessage = error instanceof Error ? error . message : "Conversion failed" ;
194+
195+ onApiResponse ?.( {
196+ timestamp : new Date ( ) ,
197+ data : {
198+ message : `Conversion failed: ${ errorMessage } ` ,
199+ error : errorMessage ,
200+ step : "conversion" ,
201+ } ,
202+ } ) ;
203+
204+ onConversionError ?.( errorMessage ) ;
205+ console . error ( "Conversion error:" , error ) ;
206+ } , [ onApiResponse , onConversionError ] ) ;
207+
208+ // Helper function to handle file conversion workflow
209+ const handleFileConversion = useCallback ( async ( file : File ) => {
210+ onConversionStart ?.( ) ;
211+
212+ // Log conversion start
213+ onApiResponse ?.( {
214+ timestamp : new Date ( ) ,
215+ data : {
216+ message : `Starting conversion: ${ file . name } (${ file . type || "unknown type" } ) → MP3` ,
217+ originalFile : file . name ,
218+ originalFormat : file . name . split ( "." ) . pop ( ) ?. toLowerCase ( ) || "unknown" ,
219+ targetFormat : "mp3" ,
220+ fileSize : file . size ,
221+ } ,
222+ } ) ;
223+
224+ // Add a small delay to ensure state updates are processed
225+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
226+
227+ // Upload file to Firebase
228+ const uploadResult = await uploadFileToFirebase ( file ) ;
229+
230+ // Convert file using CloudConvert
231+ const conversionResult = await convertFileToMp3 ( uploadResult . url , file ) ;
232+
233+ onConversionComplete ?.( ) ;
234+
235+ onApiResponse ?.( {
236+ timestamp : new Date ( ) ,
237+ data : {
238+ message : `Conversion completed successfully! Proceeding to transcription...` ,
239+ convertedUrl : conversionResult . convertedUrl ,
240+ } ,
241+ } ) ;
242+
243+ // Submit converted file for transcription
244+ onUpload (
245+ {
246+ audioUrl : conversionResult . convertedUrl ,
247+ originalFile : { name : file . name , size : file . size } ,
248+ } ,
249+ transcriptionOptions ,
250+ ) ;
251+ } , [ onConversionStart , onApiResponse , onConversionComplete , onUpload , transcriptionOptions , uploadFileToFirebase , convertFileToMp3 ] ) ;
220252
221- // Now submit the converted file URL for transcription WITH original file metadata
222- onUpload (
223- {
224- audioUrl : conversionResult . convertedUrl ,
225- originalFile : { name : file . name , size : file . size } ,
226- } ,
227- transcriptionOptions ,
228- ) ;
253+ const handleSubmit = useCallback ( async ( ) => {
254+ if ( activeTab === "file" && file && ! fileError ) {
255+ if ( requiresConversion ) {
256+ try {
257+ await handleFileConversion ( file ) ;
229258 } catch ( error ) {
230- const errorMessage =
231- error instanceof Error ? error . message : "Conversion failed" ;
232-
233- onApiResponse ?.( {
234- timestamp : new Date ( ) ,
235- data : {
236- message : `Conversion failed: ${ errorMessage } ` ,
237- error : errorMessage ,
238- step : "conversion" ,
239- } ,
240- } ) ;
241-
242- onConversionError ?.( errorMessage ) ;
243- console . error ( "Conversion error:" , error ) ;
259+ handleConversionError ( error ) ;
244260 }
245261 } else {
246262 // File doesn't need conversion, proceed normally
@@ -261,11 +277,9 @@ export function UploadAudio({
261277 audioUrl ,
262278 isUrlPotentiallyValid ,
263279 onUpload ,
264- onConversionStart ,
265- onConversionComplete ,
266- onConversionError ,
267- onApiResponse ,
268280 transcriptionOptions ,
281+ handleFileConversion ,
282+ handleConversionError ,
269283 ] ) ;
270284
271285 const handleResetFile = useCallback ( ( ) => {
0 commit comments