本篇要解決的問題
之前寫過二篇開源的語音辨識功能:
免費開源的語音辨識功能:Google Colab + Whisper large v3
免費開源的語音辨識功能:Google Colab + Faster Whisper
這篇算是第三篇,是這幾天想調整一下 Cloudflare 上的設定時,看到有多了 Workers AI 的功能,點一點後意外發現的。
原本很開心的以為終於有個好操作的免費版可以使用,但實際使用時,發現 Workers AI 對檔案大小有限制,而且是超過 2MB 就會直接跳「AiError」不給辨識。
不能超過 2MB 的檔案?
想了一想,應該就只有短影音之類的了,所以覺得用 Workers AI 來語音辨識好像不怎麼實用。
只是都已經研究出使用方式了,就還是整理為本篇筆記文,期待以後會再放寬檔案大小的限制。
註冊 Cloudflare 帳號
Cloudflare 是佛心來的,免費帳號就可以擁有很多功能,包含今天這篇 Workers AI。
進到官方網站後,點右上角的「註冊」按鈕,就可以免費註冊:
https://www.cloudflare.com/zh-tw/
開通 Speech to Text App 功能
註冊成功後,左側選單點擊「AI > Workers AI」,接著右側點擊「從 Worker 範本建立」:
點擊從 Worker 範本建立
可以看到 Workers AI 的範本有很多,有興趣的朋友可以玩玩其他的。
本篇我們要使用的是語音轉文字,所以點擊「Speech to Text App」:
Speech to Text App
點擊後,會看見頁面上 Cloudflare 已經提供了需要的檔案,基本的程式碼也寫出來了。
這一步需要做的,就是修改名稱,然後按下「部署」:
修改名稱,點擊部署
名稱會影響的是後續我們調用 API 時的 URL,可以取一個自己能辨識的。
Cloudflare 部署進度很快,不用 10 秒就會部署完成,成功後會看到以下畫面:
部署完成
修改程式碼
Workers AI 給的程式碼是基本的使用方式,我們要調整成我們好用的。
本篇,August 會把程式碼調整成前端可以用 API 的方式來取得辨識的結果。
以下程式碼,是 ChatGPT + Claude AI 提供的程式碼,August 再稍為修改一下而成的,上圖中點擊「編輯代碼」後,把以下程式碼複製、貼上去後,再按鈕部署,這步驟就完成了:
const CORS_HEADERS = {
'Access-Control-Allow-Origin': '*', // 這邊可以限制網域
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
export default {
async fetch(request, env) {
if (request.method === 'OPTIONS') {
return new Response(null, { headers: CORS_HEADERS });
}
if (request.method !== 'POST') {
return new Response('Method Not Allowed', {
status: 405,
headers: CORS_HEADERS,
});
}
const contentType = request.headers.get('Content-Type');
if (!contentType || !contentType.includes('multipart/form-data')) {
return new Response(JSON.stringify({ error: 'Invalid Content-Type' }), {
status: 400,
headers: { ...CORS_HEADERS, 'Content-Type': 'application/json' },
});
}
try {
const formData = await request.formData();
const file = formData.get('file');
if (!file) {
return new Response(JSON.stringify({ error: 'No file uploaded' }), {
status: 400,
headers: { ...CORS_HEADERS, 'Content-Type': 'application/json' },
});
}
const blob = await file.arrayBuffer();
const inputs = {
audio: [...new Uint8Array(blob)],
};
const response = await env.AI.run('@cf/openai/whisper', inputs);
return new Response(JSON.stringify(response), {
headers: { ...CORS_HEADERS, 'Content-Type': 'application/json' },
});
} catch (error) {
return new Response(JSON.stringify({ error: error }), {
status: 500,
headers: { ...CORS_HEADERS, 'Content-Type': 'application/json' },
});
}
}
};
'Access-Control-Allow-Origin': '*'
這行記得修改,可以限制我們自己的網域才能使用,*
代表全宇宙都可以使用。為了本篇的示範方便,August 這邊才寫為 *
。
最後的畫面會像這樣:
更新程式碼後部署
取得 API URL
Cloudflare 部署 Workers AI 後,在我們第一部修改名稱時,就會看到這個對外的網址,如果忘記了,可以點擊畫面中的「workers.dev」取得:
取得 API URL
點擊後會新開一個頁籤,這個頁籤的網址就是我們下一步調用 API 時的 URL。
前端建立頁面調用 API
前端的工,就是放一個 input type="file"
,再放一個 button
執行點擊後調用 API,收到回應後再把回應值塞到指定的 div
裡。
之前寫語音辨識為文字的筆記文,有人留言說需要字幕檔的方式,所以以下的程式碼也有加上「下載為字幕檔」的功能。
當然,有了 ChatGPT 的時代,很多程式碼都不用自己從 0 到 1 了,以下程式碼是 ChatGPT 生成一版後,August 再稍微調整的:
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>語音轉文字</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/dark.css">
</head>
<body>
<h1>語音轉文字</h1>
<input type="file" id="fileInput" accept="audio/*,video/*" required>
<button id="submit" type="button">上傳並轉換</button>
<div class="output-section">
<h2>原始辨識結果</h2>
<pre id="originalOutput"></pre>
</div>
<div class="output-section">
<h2>字幕檔 (SRT)</h2>
<pre id="srtOutput"></pre>
<button id="downloadButton" style="display: none;">下載字幕檔</button>
</div>
<script>
document.getElementById('submit').addEventListener('click', async (event) => {
event.preventDefault();
const fileInput = document.getElementById('fileInput');
if (fileInput.files.length === 0) {
alert('請選擇一個音頻檔案');
return;
}
const formData = new FormData();
formData.append('file', fileInput.files[0]);
const uri = 'https://xxx.xxx.xxx'; // 替換成自己的 URL
const response = await fetch(uri, {
method: 'POST',
body: formData
});
if (response.ok) {
const data = await response.json();
if (data.vtt) {
document.getElementById('originalOutput').innerText = data.text.replace(/ /g, ',');
const srtContent = vttToSrt(data.vtt);
document.getElementById('srtOutput').innerText = srtContent;
document.getElementById('downloadButton').style.display = 'block';
document.getElementById('downloadButton').addEventListener('click', () => {
const blob = new Blob([srtContent], { type: 'text/srt' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'subtitles.srt';
a.click();
URL.revokeObjectURL(url);
});
} else {
document.getElementById('originalOutput').innerText = '無法取得字幕檔案';
document.getElementById('srtOutput').innerText = '';
document.getElementById('downloadButton').style.display = 'none';
}
} else {
document.getElementById('originalOutput').innerText = '語音轉文字失敗,請檢查伺服器設置。';
document.getElementById('srtOutput').innerText = '';
document.getElementById('downloadButton').style.display = 'none';
}
});
function vttToSrt(vtt) {
const lines = vtt.split('\n');
let srt = '';
let counter = 1;
for (let i = 0; i < lines.length; i++) {
if (lines[i].includes('-->')) {
srt += `${counter}\n`;
srt += lines[i].replace('.', ',') + '\n';
counter++;
} else {
srt += lines[i] + '\n';
}
}
return srt;
}
</script>
</body>
</html>
複製貼上後,需要手動修改的是這行:
const uri = 'https://xxx.xxx.xxx';
換成我們在上一步,從 Cloudflare Workers AI 取得的 URL 即可。
頁面打開來,會長得像這樣:
頁面樣子
要注意一下,這邊沒有寫 loading 效果,所以當選好了檔案,點擊「上傳並轉換」後,實際上背後已經在調用 API 了,請自己開啟 Chrome 的 Network 面版查看。
成功的話頁面上會秀出辨識結果。
失敗的話,要從 Console 面版去看錯誤訊息,通常失敗的原因就是檔案大小超過 2MB。
Top comments (0)