音声インターフェイスがますます重要になってきているため、音声インタラクションでできることのいくつかを調べる価値があります。 たとえば、私たちが何かを言い、それを書き起こし、ダウンロード可能な PDF として送り出すことができたらどうでしょうか?
さて、ネタバレ注意:私たちは絶対に できる それを行う! それを実現するために組み合わせることができるライブラリーとフレームワークがあり、それがこの記事で一緒にやろうとしていることです。
これらは私たちが使用しているツールです
まず、Next.js と Express.js という XNUMX つの大きなプレーヤーがあります。
Next.js 静的サイトを構築するための主要な機能を含む、React への追加機能への取り組み。 動的ルーティング、画像の最適化、ビルトイン ドメインとサブドメインのルーティング、高速更新、ファイル システム ルーティング、API ルートなど、すぐに利用できる機能を備えているため、多くの開発者にとって頼りになるツールです。 他にもいろいろ.
私たちの場合、そのために Next.js が絶対に必要です。 API ルート クライアントサーバー上。 テキスト ファイルを受け取り、PDF に変換し、ファイル システムに書き込み、クライアントに応答を送信するルートが必要です。
Express.js ルーティング、HTTP ヘルパー、およびテンプレートを使用する小さな Node.js アプリを作成できます。 これは独自の API 用のサーバーであり、物事の間でデータを渡したり解析したりするときに必要になります。
使用する他の依存関係がいくつかあります。
- 反応音声認識: 音声をテキストに変換し、React コンポーネントで使用できるようにするためのライブラリ。
- 再生器-ランタイム: 「トラブルシューティング用ライブラリ」
regeneratorRuntime
反応音声認識の使用時に Next.js に表示される "は定義されていません" エラー - html-pdf-ノード:HTMLページや公開URLをPDFに変換するためのライブラリ
- アクシオス: ブラウザと Node.js の両方で HTTP リクエストを行うためのライブラリ
- ホルン: クロスオリジン リソース共有を可能にするライブラリ
セットアップ
最初に、クライアント用とサーバー用の XNUMX つのプロジェクト フォルダーを作成します。 好きな名前を付けてください。 私は自分の名前を付けています audio-to-pdf-client
および audio-to-pdf-server
それぞれ。
クライアント側で Next.js を使い始める最速の方法は、次のようにブートストラップすることです。 次のアプリを作成. そのため、ターミナルを開き、クライアント プロジェクト フォルダーから次のコマンドを実行します。
npx create-next-app client
次に、Express サーバーが必要です。 私たちはそれを手に入れることができます cd
- サーバー プロジェクト フォルダーに移動し、 npm init
指図。 NS package.json
ファイルは、完了するとサーバー プロジェクト フォルダーに作成されます。
Express を実際にインストールする必要があります。 npm install express
. これで、新しい index.js
ファイルをサーバー プロジェクト フォルダーに置き、このコードをそこにドロップします。
const express = require("express")
const app = express()
app.listen(4000, () => console.log("Server is running on port 4000"))
サーバーを実行する準備はできましたか?
node index.js
先に進むには、さらにいくつかのフォルダーと別のファイルが必要になります。
- 作る
components
クライアント プロジェクト フォルダー内のフォルダー。 - 作る
SpeechToText.jsx
内のファイルcomponents
サブフォルダ。
先に進む前に、少しクリーンアップを行う必要があります。 具体的には、デフォルトのコードを置き換える必要があります pages/index.js
これでファイル:
import Head from "next/head";
import SpeechToText from "../components/SpeechToText";
export default function Home() {
return (
<div className="home">
<Head>
<title>Audio To PDF</title>
<meta
name="description"
content="An app that converts audio to pdf in the browser"
/>
<link rel="icon" href="/ja/favicon.ico" />
</Head>
<h1>Convert your speech to pdf</h1>
<main>
<SpeechToText />
</main>
</div>
);
}
インポートされた SpeechToText
コンポーネントは最終的にエクスポートされます components/SpeechToText.jsx
.
他の依存関係をインストールしましょう
よし、これでアプリの初期設定は完了です。 これで、渡されるデータを処理するライブラリをインストールできます。
クライアントの依存関係を次の方法でインストールできます。
npm install react-speech-recognition regenerator-runtime axios
次に、Express サーバーの依存関係について説明します。 cd
サーバー プロジェクト フォルダーに移動し、それらをインストールします。
npm install html-pdf-node cors
一時停止して、プロジェクト フォルダー内のファイルが正常であることを確認する良い機会です。 この時点で、クライアント プロジェクト フォルダーには次のものが必要です。
/audio-to-pdf-web-client
├─ /components
| └── SpeechToText.jsx
├─ /pages
| ├─ _app.js
| └── index.js
└── /styles
├─globals.css
└── Home.module.css
そして、サーバー プロジェクト フォルダーに必要なものは次のとおりです。
/audio-to-pdf-server
└── index.js
UI の構築
さて、私たちの音声から PDF への変換は、それを操作する方法がなければ、それほど優れたものではありません。そのため、呼び出すことができる React コンポーネントを作成しましょう。 <SpeechToText>
.
独自のマークアップを完全に使用できます。 ここに、私たちがまとめている作品のアイデアをあなたに与えるために持っているものがあります:
import React from "react";
const SpeechToText = () => {
return (
<>
<section>
<div className="button-container">
<button type="button" style={{ "--bgColor": "blue" }}>
Start
</button>
<button type="button" style={{ "--bgColor": "orange" }}>
Stop
</button>
</div>
<div
className="words"
contentEditable
suppressContentEditableWarning={true}
></div>
<div className="button-container">
<button type="button" style={{ "--bgColor": "red" }}>
Reset
</button>
<button type="button" style={{ "--bgColor": "green" }}>
Convert to pdf
</button>
</div>
</section>
</>
);
};
export default SpeechToText;
このコンポーネントは 反応フラグメント HTML を含む <``section``>
XNUMX つの div を含む要素:
.button-container
音声認識の開始と停止に使用される XNUMX つのボタンが含まれています。.words
持っていますcontentEditable
およびsuppressContentEditableWarning
属性を使用してこの要素を編集可能にし、React からの警告を抑制します。- 別の
.button-container
音声のリセットと PDF への変換にそれぞれ使用される XNUMX つのボタンがあります。
スタイリングは全く別物です。 ここでは触れませんが、私が書いたいくつかのスタイルをあなた自身の出発点として使ってください。 styles/global.css
ファイルにソフトウェアを指定する必要があります。
CSS全体を表示
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
.home {
background-color: #333;
min-height: 100%;
padding: 0 1rem;
padding-bottom: 3rem;
}
h1 {
width: 100%;
max-width: 400px;
margin: auto;
padding: 2rem 0;
text-align: center;
text-transform: capitalize;
color: white;
font-size: 1rem;
}
.button-container {
text-align: center;
display: flex;
justify-content: center;
gap: 3rem;
}
button {
color: white;
background-color: var(--bgColor);
font-size: 1.2rem;
padding: 0.5rem 1.5rem;
border: none;
border-radius: 20px;
cursor: pointer;
}
button:hover {
opacity: 0.9;
}
button:active {
transform: scale(0.99);
}
.words {
max-width: 700px;
margin: 50px auto;
height: 50vh;
border-radius: 5px;
padding: 1rem 2rem 1rem 5rem;
background-image: -webkit-gradient(
linear,
0 0,
0 100%,
from(#d9eaf3),
color-stop(4%, #fff)
) 0 4px;
background-size: 100% 3rem;
background-attachment: scroll;
position: relative;
line-height: 3rem;
overflow-y: auto;
}
.success,
.error {
background-color: #fff;
margin: 1rem auto;
padding: 0.5rem 1rem;
border-radius: 5px;
width: max-content;
text-align: center;
display: block;
}
.success {
color: green;
}
.error {
color: red;
}
そこにある CSS 変数は、ボタンの背景色を制御するために使用されています。
最新の変更点を見てみましょう。 走る npm run dev
ターミナルでそれらをチェックアウトします。
アクセスすると、ブラウザにこれが表示されるはずです http://localhost:3000
:
初めてのスピーチからテキストへの変換!
最初に取るべきアクションは、必要な依存関係を私たちの <SpeechToText>
成分:
import React, { useRef, useState } from "react";
import SpeechRecognition, {
useSpeechRecognition,
} from "react-speech-recognition";
import axios from "axios";
次に、ブラウザーで音声認識がサポートされているかどうかを確認し、サポートされていない場合は通知を表示します。
const speechRecognitionSupported =
SpeechRecognition.browserSupportsSpeechRecognition();
if (!speechRecognitionSupported) {
return <div>Your browser does not support speech recognition.</div>;
}
次は抽出してみましょう transcript
および resetTranscript
useSpeechRecognition()
針:
const { transcript, resetTranscript } = useSpeechRecognition();
これは、処理する状態に必要なものです listening
:
const [listening, setListening] = useState(false);
また、 ref
div
contentEditable
属性を追加する必要があります ref
それに属性を付けて渡す transcript
as children
:
const textBodyRef = useRef(null);
…と:
<div
className="words"
contentEditable
ref={textBodyRef}
suppressContentEditableWarning={true}
>
{transcript}
</div>
ここで最後に必要なのは、音声認識をトリガーする関数と、その関数を onClick
ボタンのイベントリスナー。 ボタンはリッスンを設定します true
継続的に実行します。 ボタンがその状態にある間はボタンを無効にして、追加のイベントを発生させないようにします。
const startListening = () => {
setListening(true);
SpeechRecognition.startListening({
continuous: true,
});
};
…と:
<button
type="button"
onClick={startListening}
style={{ "--bgColor": "blue" }}
disabled={listening}
>
Start
</button>
ボタンをクリックすると、文字起こしが開始されます。
その他の機能
OK、コンポーネントができました。 start 聞いている。 しかし、次のような他のいくつかのことを行うためにも必要です。 stopListening
, resetText
および handleConversion
. それらの関数を作ってみましょう。
const stopListening = () => {
setListening(false);
SpeechRecognition.stopListening();
};
const resetText = () => {
stopListening();
resetTranscript();
textBodyRef.current.innerText = "";
};
const handleConversion = async () => {}
各機能は、 onClick
適切なボタンのイベント リスナー:
<button
type="button"
onClick={stopListening}
style={{ "--bgColor": "orange" }}
disabled={listening === false}
>
Stop
</button>
<div className="button-container">
<button
type="button"
onClick={resetText}
style={{ "--bgColor": "red" }}
>
Reset
</button>
<button
type="button"
style={{ "--bgColor": "green" }}
onClick={handleConversion}
>
Convert to pdf
</button>
</div>
handleConversion
最終的に API リクエストを行うため、関数は非同期です。 「Stop」ボタンには、listening が false の場合にトリガーされる disabled 属性があります。
サーバーを再起動してブラウザーを更新すると、ブラウザーで音声文字起こしを開始、停止、およびリセットできるようになります。
今、私たちが必要としているのは、アプリが 書き写す PDFファイルに変換することで音声を認識しました。 そのためには、Express.js からのサーバー側のパスが必要です。
API ルートの設定
このルートの目的は、テキスト ファイルを取得して PDF に変換し、その PDF をファイル システムに書き込み、クライアントに応答を送信することです。
セットアップするには、 server/index.js
ファイルをインポートして html-pdf-node
および fs
ファイルシステムの書き込みとオープンに使用される依存関係。
const HTMLToPDF = require("html-pdf-node");
const fs = require("fs");
const cors = require("cors)
次に、ルートを設定します。
app.use(cors())
app.use(express.json())
app.post("/", (req, res) => {
// etc.
})
次に、使用するために必要なオプションを定義します html-pdf-node
ルート内:
let options = { format: "A4" };
let file = {
content: `<html><body><pre style='font-size: 1.2rem'>${req.body.text}</pre></body></html>`,
};
options
オブジェクトは、用紙サイズとスタイルを設定する値を受け入れます。 用紙サイズは、Web で通常使用するサイズ単位とは大きく異なるシステムに従います。 例えば、 A4は一般的なレターサイズです.
file
オブジェクトは、公開 Web サイトの URL または HTML マークアップのいずれかを受け入れます。 HTML ページを生成するために、 html
, body
, pre
HTML タグと req.body
.
任意のスタイルを適用できます。
次に、追加します trycatch
途中でポップアップする可能性のあるエラーを処理するには:
try {
} catch(error){
console.log(error);
res.status(500).send(error);
}
次に、 generatePdf
html-pdf-node
生成するライブラリ pdfBuffer
(生の PDF ファイル) からファイルを作成し、独自の pdfName
:
HTMLToPDF.generatePdf(file, options).then((pdfBuffer) => {
// console.log("PDF Buffer:-", pdfBuffer);
const pdfName = "./data/speech" + Date.now() + ".pdf";
// Next code here
}
そこから、filesystem モジュールを使用して書き込み、読み取り、(はい、ついに!) クライアント アプリに応答を送信します。
fs.writeFile(pdfName, pdfBuffer, function (writeError) {
if (writeError) {
return res
.status(500)
.json({ message: "Unable to write file. Try again." });
}
fs.readFile(pdfName, function (readError, readData) {
if (!readError && readData) {
// console.log({ readData });
res.setHeader("Content-Type", "application/pdf");
res.setHeader("Content-Disposition", "attachment");
res.send(readData);
return;
}
return res
.status(500)
.json({ message: "Unable to write file. Try again." });
});
});
それを少し分解してみましょう:
-
writeFile
filesystem モジュールは、ファイル名、データ、およびファイルへの書き込みに問題がある場合にエラー メッセージを返すコールバック関数を受け入れます。 エラー エンドポイントを提供する CDN を使用している場合は、代わりにそれらを使用できます。 -
readFile
filesystem モジュールは、ファイル名と、読み取りエラーと読み取りデータを返すことができるコールバック関数を受け入れます。 読み取りエラーがなく、読み取りデータが存在すると、応答を作成してクライアントに送信します。 繰り返しますが、CDN のエンドポイントがある場合は、これを CDN のエンドポイントに置き換えることができます。 -
res.setHeader("Content-Type", "application/pdf");
PDFファイルを送信していることをブラウザに伝えます。 -
res.setHeader("Content-Disposition", "attachment");
受信したデータをダウンロード可能にするようブラウザに指示します。
API ルートの準備が整ったので、次のアプリで使用できます。 http://localhost:4000
. アプリケーションのクライアント部分に進み、 handleConversion
機能。
変換の処理
作業を開始する前に handleConversion
関数を使用するには、読み込み、エラー、成功、およびその他のメッセージに対する API 要求を処理する状態を作成する必要があります。 React を使用します useState
それを設定するためのフック:
const [response, setResponse] = useState({
loading: false,
message: "",
error: false,
success: false,
});
handleConversion
関数を使用して、コードを実行する前に Web ページがいつ読み込まれたかを確認し、 div
editable
属性が空ではありません:
if (typeof window !== "undefined") {
const userText = textBodyRef.current.innerText;
// console.log(textBodyRef.current.innerText);
if (!userText) {
alert("Please speak or write some text.");
return;
}
}
最終的な API リクエストを trycatch
、発生する可能性のあるエラーを処理し、応答状態を更新します。
try {
} catch(error){
setResponse({
...response,
loading: false,
error: true,
message:
"An unexpected error occurred. Text not converted. Please try again",
success: false,
});
}
次に、応答状態の値をいくつか設定し、構成も設定します axios
サーバーへの投稿リクエストを行います。
setResponse({
...response,
loading: true,
message: "",
error: false,
success: false,
});
const config = {
headers: {
"Content-Type": "application/json",
},
responseType: "blob",
};
const res = await axios.post(
"http://localhost:4000",
{
text: textBodyRef.current.innerText,
},
config
);
正常な応答を取得したら、適切な値で応答状態を設定し、ブラウザーに受信した PDF をダウンロードするように指示します。
setResponse({
...response,
loading: false,
error: false,
message:
"Conversion was successful. Your download will start soon...",
success: true,
});
// convert the received data to a file
const url = window.URL.createObjectURL(new Blob([res.data]));
// create an anchor element
const link = document.createElement("a");
// set the href of the created anchor element
link.href = url;
// add the download attribute, give the downloaded file a name
link.setAttribute("download", "yourfile.pdf");
// add the created anchor tag to the DOM
document.body.appendChild(link);
// force a click on the link to start a simulated download
link.click();
そして、contentEditable の下で以下を使用できます。 div
メッセージの表示用:
<div>
{response.success && <i className="success">{response.message}</i>}
{response.error && <i className="error">{response.message}</i>}
</div>
最終コード
サーバーとクライアントの両方の完全なソース コードを確認できるように、すべてを GitHub にパッケージ化しました。