Supabase के साथ स्ट्रीमिंग और कैशिंग

Supabase Edge Functions के ज़रिए स्पीच जनरेट और स्ट्रीम करें। स्पीच को Supabase Storage में स्टोर करें और बिल्ट-इन CDN से रिस्पॉन्स कैश करें।

कैसे करें गाइड · मानता है कि आपने ElevenAPI क्विकस्टार्ट पूरा कर लिया है और आपका Supabase अकाउंट है।

परिचय

इस गाइड में आप सीखेंगे कि Supabase Edge Functions, Supabase Storage और ElevenLabs का इस्तेमाल करके स्पीच जनरेट, स्ट्रीम, स्टोर और कैश करने के लिए एज API कैसे बनाएं।

आवश्यकताएं

सेटअप

लोकल Supabase प्रोजेक्ट बनाएं

Supabase CLI इंस्टॉल करने के बाद, लोकली नया Supabase प्रोजेक्ट बनाने के लिए यह कमांड चलाएं:

supabase init

स्टोरेज बकेट कॉन्फ़िगर करें

config.toml फ़ाइल में यह कॉन्फ़िगरेशन जोड़कर आप Supabase CLI को अपने-आप स्टोरेज बकेट जनरेट करने के लिए कॉन्फ़िगर कर सकते हैं:

./supabase/config.toml
[storage.buckets.audio]
public = false
file_size_limit = "50MiB"
allowed_mime_types = ["audio/mp3"]
objects_path = "./audio"

supabase start चलाने पर यह आपके लोकल Supabase प्रोजेक्ट में नया स्टोरेज बकेट बनाएगा। अगर आप इसे अपने होस्ट किए गए Supabase प्रोजेक्ट में पुश करना चाहते हैं, तो supabase seed buckets --linked चला सकते हैं।

Supabase Edge Functions के लिए बैकग्राउंड टास्क कॉन्फ़िगर करें

लोकल डेवलपमेंट के दौरान Supabase Edge Functions में बैकग्राउंड टास्क इस्तेमाल करने के लिए, आपको config.toml फ़ाइल में यह कॉन्फ़िगरेशन जोड़ना होगा:

./supabase/config.toml
[edge_runtime]
policy = "per_worker"

per_worker पॉलिसी के साथ चलाने पर, Function में किए गए बदलाव अपने-आप रीलोड नहीं होंगे। आपको supabase functions serve चलाकर इसे मैन्युअली रीस्टार्ट करना होगा।

स्पीच जनरेशन के लिए Supabase Edge Function बनाएं

यह कमांड चलाकर नया Edge Function बनाएं:

supabase functions new text-to-speech

अगर आप VS Code या Cursor इस्तेमाल कर रहे हैं, तो जब CLI “Generate VS Code settings for Deno? [y/N]” पूछे, तो y चुनें!

एनवायरनमेंट वेरिएबल सेट अप करें

supabase/functions डायरेक्टरी में नई .env फ़ाइल बनाएं और ये वेरिएबल जोड़ें:

supabase/functions/.env
# Find / create an API key at https://elevenlabs.io/app/settings/api-keys
ELEVENLABS_API_KEY=your_api_key

डिपेंडेंसीज़

प्रोजेक्ट कुछ डिपेंडेंसीज़ इस्तेमाल करता है:

  • Supabase डेटाबेस के साथ इंटरैक्ट करने के लिए @supabase/supabase-js लाइब्रेरी।
  • टेक्स्ट टू स्पीच API के साथ इंटरैक्ट करने के लिए ElevenLabs JavaScript SDK।
  • रिक्वेस्ट पैरामीटर से हैश जनरेट करने के लिए ओपन-सोर्स object-hash।

Supabase Edge Function Deno runtime इस्तेमाल करता है, इसलिए आपको डिपेंडेंसीज़ इंस्टॉल करने की ज़रूरत नहीं है। इसके बजाय, आप npm: प्रीफ़िक्स के ज़रिए उन्हें import कर सकते हैं।

Supabase Edge Function का कोड लिखें

अपनी नई supabase/functions/text-to-speech/index.ts फ़ाइल में यह कोड जोड़ें:

supabase/functions/text-to-speech/index.ts
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from "jsr:@supabase/supabase-js@2";
import { ElevenLabsClient } from "npm:elevenlabs";
import * as hash from "npm:object-hash";
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
);
const elevenlabs = new ElevenLabsClient({
apiKey: Deno.env.get("ELEVENLABS_API_KEY"),
});
// Upload audio to Supabase Storage in a background task
async function uploadAudioToStorage(stream: ReadableStream, requestHash: string) {
const { data, error } = await supabase.storage
.from("audio")
.upload(`${requestHash}.mp3`, stream, {
contentType: "audio/mp3",
});
console.log("Storage upload result", { data, error });
}
Deno.serve(async (req) => {
// To secure your function for production, you can for example validate the request origin,
// or append a user access token and validate it with Supabase Auth.
console.log("Request origin", req.headers.get("host"));
const url = new URL(req.url);
const params = new URLSearchParams(url.search);
const text = params.get("text");
const voiceId = params.get("voiceId") ?? "JBFqnCBsd6RMkjVDRZzb";
const requestHash = hash.MD5({ text, voiceId });
console.log("Request hash", requestHash);
// Check storage for existing audio file
const { data } = await supabase.storage.from("audio").createSignedUrl(`${requestHash}.mp3`, 60);
if (data) {
console.log("Audio file found in storage", data);
const storageRes = await fetch(data.signedUrl);
if (storageRes.ok) return storageRes;
}
if (!text) {
return new Response(JSON.stringify({ error: "Text parameter is required" }), {
status: 400,
headers: { "Content-Type": "application/json" },
});
}
try {
console.log("ElevenLabs API call");
const response = await elevenlabs.textToSpeech.stream(voiceId, {
output_format: "mp3_44100_128",
model_id: "eleven_multilingual_v2",
text,
});
const stream = new ReadableStream({
async start(controller) {
for await (const chunk of response) {
controller.enqueue(chunk);
}
controller.close();
},
});
// Branch stream to Supabase Storage
const [browserStream, storageStream] = stream.tee();
// Upload to Supabase Storage in the background
EdgeRuntime.waitUntil(uploadAudioToStorage(storageStream, requestHash));
// Return the streaming response immediately
return new Response(browserStream, {
headers: {
"Content-Type": "audio/mpeg",
},
});
} catch (error) {
console.log("error", { error });
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { "Content-Type": "application/json" },
});
}
});

कोड को विस्तार से समझें

कोड में कुछ बातों पर ध्यान देना ज़रूरी है। आइए इसे चरण-दर-चरण समझते हैं।

1

आने वाली रिक्वेस्ट को हैंडल करें

आने वाली रिक्वेस्ट को हैंडल करने के लिए Deno.serve हैंडलर इस्तेमाल करें। डेमो में हम रिक्वेस्ट origin को वैलिडेट नहीं करते, लेकिन आप रिक्वेस्ट origin को वैलिडेट कर सकते हैं या यूज़र access token जोड़कर उसे Supabase Auth से वैलिडेट कर सकते हैं।

आने वाली रिक्वेस्ट से फ़ंक्शन text और voiceId पैरामीटर निकालता है। voiceId पैरामीटर वैकल्पिक है और डिफ़ॉल्ट रूप से “Allison” वॉइस के ElevenLabs ID पर सेट होता है।

object-hash लाइब्रेरी से फ़ंक्शन रिक्वेस्ट पैरामीटर का हैश जनरेट करता है। इस हैश का इस्तेमाल Supabase Storage में मौजूद ऑडियो फ़ाइलों को जांचने के लिए होता है।

Deno.serve(async (req) => {
// To secure your function for production, you can for example validate the request origin,
// or append a user access token and validate it with Supabase Auth.
console.log("Request origin", req.headers.get("host"));
const url = new URL(req.url);
const params = new URLSearchParams(url.search);
const text = params.get("text");
const voiceId = params.get("voiceId") ?? "JBFqnCBsd6RMkjVDRZzb";
const requestHash = hash.MD5({ text, voiceId });
console.log("Request hash", requestHash);
// ...
})
2

Supabase Storage में मौजूद ऑडियो फ़ाइल देखें

Supabase Storage में स्मार्ट CDN पहले से मौजूद है, जिससे आप अपनी फ़ाइलों को आसानी से कैश और सर्व कर सकते हैं।

यहां फ़ंक्शन Supabase Storage में मौजूदा ऑडियो फ़ाइल जांचता है। फ़ाइल मौजूद होने पर, फ़ंक्शन Supabase Storage से फ़ाइल लौटाता है।

const { data } = await supabase
.storage
.from("audio")
.createSignedUrl(`${requestHash}.mp3`, 60);
if (data) {
console.log("Audio file found in storage", data);
const storageRes = await fetch(data.signedUrl);
if (storageRes.ok) return storageRes;
}
3

स्पीच को स्ट्रीम के रूप में जनरेट करें और दो ब्रांच में बांटें

ElevenLabs API की स्ट्रीमिंग क्षमताओं से फ़ंक्शन एक स्ट्रीम जनरेट करता है। इसका फ़ायदा यह है कि बड़े टेक्स्ट के लिए भी आप तुरंत यूज़र को ऑडियो स्ट्रीम करना शुरू कर सकते हैं, और बैकग्राउंड में स्ट्रीम को Supabase Storage में अपलोड कर सकते हैं।

इससे सबसे बेहतर यूज़र अनुभव मिलता है और बड़े टेक्स्ट ब्लॉक भी जादुई रूप से तेज़ लगते हैं। यहां जादू लाइन 17 पर होता है, जहां stream.tee() मेथड readable stream को दो ब्रांच में बांटता है: एक ब्राउज़र के लिए और एक Supabase Storage के लिए।

try {
const response = await elevenlabs.textToSpeech.stream(voiceId, {
output_format: "mp3_44100_128",
model_id: "eleven_multilingual_v2",
text,
});
const stream = new ReadableStream({
async start(controller) {
for await (const chunk of response) {
controller.enqueue(chunk);
}
controller.close();
},
});
// Branch stream to Supabase Storage
const [browserStream, storageStream] = stream.tee();
// Upload to Supabase Storage in the background
EdgeRuntime.waitUntil(uploadAudioToStorage(storageStream, requestHash));
// Return the streaming response immediately
return new Response(browserStream, {
headers: {
"Content-Type": "audio/mpeg",
},
});
} catch (error) {
console.log("error", { error });
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { "Content-Type": "application/json" },
});
}
4

बैकग्राउंड में ऑडियो स्ट्रीम को Supabase Storage में अपलोड करें

पिछले चरण में लाइन 20 पर मौजूद EdgeRuntime.waitUntil मेथड, uploadAudioToStorage फ़ंक्शन के ज़रिए बैकग्राउंड में ऑडियो स्ट्रीम को Supabase Storage में अपलोड करने के लिए इस्तेमाल होता है। इससे फ़ंक्शन तुरंत ब्राउज़र को स्ट्रीमिंग रिस्पॉन्स लौटा सकता है, जबकि ऑडियो Supabase Storage में अपलोड हो रहा होता है।

स्टोरेज ऑब्जेक्ट बन जाने के बाद, अगली बार जब आपके यूज़र उन्हीं पैरामीटर के साथ रिक्वेस्ट करेंगे, तो फ़ंक्शन Supabase Storage CDN से ऑडियो फ़ाइल लौटाएगा।

// Upload audio to Supabase Storage in a background task
async function uploadAudioToStorage(
stream: ReadableStream,
requestHash: string,
) {
const { data, error } = await supabase.storage
.from("audio")
.upload(`${requestHash}.mp3`, stream, {
contentType: "audio/mp3",
});
console.log("Storage upload result", { data, error });
}

लोकली चलाएं

फ़ंक्शन को लोकली चलाने के लिए ये कमांड चलाएं:

supabase start

लोकल Supabase स्टैक के चालू हो जाने के बाद, फ़ंक्शन शुरू करने और लॉग देखने के लिए यह कमांड चलाएं:

supabase functions serve

आज़माएं

फ़ंक्शन को काम करते सुनने के लिए http://127.0.0.1:54321/functions/v1/text-to-speech?text=hello%20world पर जाएं।

इसके बाद, अपने लोकल Supabase Storage बकेट में ऑडियो फ़ाइल देखने के लिए http://127.0.0.1:54323/project/default/storage/buckets/audio पर जाएं।

Supabase पर डिप्लॉय करें

अगर आपने पहले से नहीं किया है, तो database.new पर नया Supabase अकाउंट बनाएं और लोकल प्रोजेक्ट को अपने Supabase अकाउंट से लिंक करें:

supabase link

हो जाने पर, फ़ंक्शन डिप्लॉय करने के लिए यह कमांड चलाएं:

supabase functions deploy

फ़ंक्शन सीक्रेट्स सेट करें

अब जब आपके सभी सीक्रेट्स लोकली सेट हो गए हैं, तो अपने Supabase प्रोजेक्ट में सीक्रेट्स सेट करने के लिए यह कमांड चलाएं:

supabase secrets set --env-file supabase/functions/.env

फ़ंक्शन टेस्ट करें

फ़ंक्शन को इस तरह डिज़ाइन किया गया है कि इसे सीधे <audio> एलिमेंट के स्रोत के रूप में इस्तेमाल किया जा सकता है।

<audio
src="https://${SUPABASE_PROJECT_REF}.supabase.co/functions/v1/text-to-speech?text=Hello%2C%20world!&voiceId=JBFqnCBsd6RMkjVDRZzb"
controls
/>

अगले चरण