Skip to content

Commit e864016

Browse files
committed
feat: add Firebase integration for file storage and management
1 parent abfd594 commit e864016

File tree

10 files changed

+1150
-56
lines changed

10 files changed

+1150
-56
lines changed

netlify/functions/cleanup.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import dotenv from 'dotenv';
2+
import { initializeApp } from 'firebase/app';
3+
import { getStorage, ref, deleteObject } from 'firebase/storage';
4+
import { getFirebaseConfig } from './firebase-config.js';
5+
dotenv.config();
6+
7+
export async function handler(event, context) {
8+
if (event.httpMethod !== 'POST') {
9+
return {
10+
statusCode: 405,
11+
body: JSON.stringify({ error: 'Method not allowed' })
12+
};
13+
}
14+
15+
try {
16+
const { filePath } = JSON.parse(event.body);
17+
18+
if (!filePath) {
19+
return {
20+
statusCode: 400,
21+
body: JSON.stringify({ error: 'Missing file path' })
22+
};
23+
}
24+
25+
// Initialize Firebase
26+
const app = initializeApp(getFirebaseConfig());
27+
const storage = getStorage(app);
28+
29+
// Create a reference to the file
30+
const fileRef = ref(storage, filePath);
31+
32+
// Delete the file
33+
await deleteObject(fileRef);
34+
35+
return {
36+
statusCode: 200,
37+
body: JSON.stringify({ message: 'File deleted successfully' })
38+
};
39+
} catch (error) {
40+
console.error('Error deleting file:', error);
41+
return {
42+
statusCode: 500,
43+
body: JSON.stringify({ error: error.message || 'Unknown error' })
44+
};
45+
}
46+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export function getFirebaseConfig() {
2+
return {
3+
apiKey: process.env.VITE_FIREBASE_API_KEY,
4+
authDomain: process.env.VITE_FIREBASE_AUTH_DOMAIN,
5+
projectId: process.env.VITE_FIREBASE_PROJECT_ID,
6+
storageBucket: process.env.VITE_FIREBASE_STORAGE_BUCKET,
7+
messagingSenderId: process.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
8+
appId: process.env.VITE_FIREBASE_APP_ID
9+
};
10+
}

netlify/functions/transcribe.js

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,65 @@
1-
21
import dotenv from 'dotenv';
2+
import { initializeApp } from 'firebase/app';
3+
import { getStorage, ref, uploadBytes, getDownloadURL } from 'firebase/storage';
4+
import { getFirebaseConfig } from './firebase-config.js';
35
dotenv.config();
46

7+
// Initialize Firebase when needed
8+
let firebaseApp;
9+
let storage;
10+
11+
function initializeFirebase() {
12+
if (!firebaseApp) {
13+
firebaseApp = initializeApp(getFirebaseConfig());
14+
storage = getStorage(firebaseApp);
15+
}
16+
return { firebaseApp, storage };
17+
}
18+
19+
// Generate a unique filename with timestamp and random string
20+
const generateUniqueFilename = (originalName) => {
21+
const timestamp = Date.now();
22+
const randomString = Math.random().toString(36).substring(2, 8);
23+
const fileExtension = originalName?.split('.').pop() || 'audio';
24+
return `audio_${timestamp}_${randomString}.${fileExtension}`;
25+
};
26+
27+
// Function to upload base64 data to Firebase Storage
28+
async function uploadBase64ToFirebase(base64Data, mimeType = 'audio/mpeg') {
29+
const { storage } = initializeFirebase();
30+
31+
// Strip the data URL prefix if present
32+
const base64WithoutPrefix = base64Data.replace(/^data:.*;base64,/, '');
33+
34+
// Convert base64 to a Blob
35+
const byteCharacters = atob(base64WithoutPrefix);
36+
const byteArrays = [];
37+
38+
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
39+
const slice = byteCharacters.slice(offset, offset + 512);
40+
const byteNumbers = new Array(slice.length);
41+
for (let i = 0; i < slice.length; i++) {
42+
byteNumbers[i] = slice.charCodeAt(i);
43+
}
44+
const byteArray = new Uint8Array(byteNumbers);
45+
byteArrays.push(byteArray);
46+
}
47+
48+
const blob = new Blob(byteArrays, { type: mimeType });
49+
50+
// Create a reference with a unique filename
51+
const filename = generateUniqueFilename('upload');
52+
const fileRef = ref(storage, `temp_audio/${filename}`);
53+
54+
// Upload the blob
55+
const snapshot = await uploadBytes(fileRef, blob);
56+
console.log('Uploaded a blob to Firebase Storage');
57+
58+
// Get download URL
59+
const downloadURL = await getDownloadURL(snapshot.ref);
60+
return { url: downloadURL, ref: `temp_audio/${filename}` };
61+
}
62+
563
export async function handler(event, context) {
664
if (event.httpMethod !== 'POST') {
765
return {
@@ -11,17 +69,41 @@ export async function handler(event, context) {
1169
}
1270

1371
try {
14-
const { audioData, options } = JSON.parse(event.body);
72+
const { audioData, audioUrl, options } = JSON.parse(event.body);
1573

16-
// Format input parameters like in your server
74+
// Format input parameters
1775
const inputParams = {
1876
task: options.task || 'transcribe',
19-
audio: audioData,
2077
batch_size: options.batch_size || 64,
2178
return_timestamps: options.return_timestamps || true,
2279
diarize: options.diarize || false,
2380
};
2481

82+
// Set audio source - check file size by base64 length
83+
let finalAudioUrl = audioUrl;
84+
85+
// If no URL is provided but we have base64 data, check if it's large
86+
if (!audioUrl && audioData) {
87+
// Estimate size of base64 (rough estimate: base64 size * 0.75 = binary size)
88+
const estimatedSizeInMB = (audioData.length * 0.75) / (1024 * 1024);
89+
90+
if (estimatedSizeInMB > 1) {
91+
// Large file detected, upload to Firebase
92+
console.log(`Large audio file detected (${estimatedSizeInMB.toFixed(2)}MB), uploading to Firebase`);
93+
const uploadResult = await uploadBase64ToFirebase(audioData);
94+
finalAudioUrl = uploadResult.url;
95+
console.log('Uploaded to Firebase, URL:', finalAudioUrl);
96+
} else {
97+
// Small file, use base64 directly
98+
inputParams.audio = audioData;
99+
}
100+
}
101+
102+
// If we have a URL (either provided or from Firebase upload), use it
103+
if (finalAudioUrl) {
104+
inputParams.audio = finalAudioUrl;
105+
}
106+
25107
// Only include language if it's not "None" (auto-detect)
26108
if (options.language !== "None") {
27109
inputParams.language = options.language;
@@ -55,11 +137,17 @@ export async function handler(event, context) {
55137
};
56138
}
57139

140+
// If we used Firebase, include the reference in the response for later cleanup
141+
if (finalAudioUrl && finalAudioUrl !== audioUrl) {
142+
data.firebaseRef = `Used Firebase Storage for large file`;
143+
}
144+
58145
return {
59146
statusCode: 200,
60147
body: JSON.stringify(data)
61148
};
62149
} catch (error) {
150+
console.error('Error in transcribe function:', error);
63151
return {
64152
statusCode: 500,
65153
body: JSON.stringify({ error: error.message || 'Unknown error' })

0 commit comments

Comments
 (0)