NextJS と ExpressJS PlatoBlockchain Data Intelligence を使用して音声を PDF に変換します。 垂直検索。 あい。

NextJS と ExpressJS を使用して音声を PDF に変換する

音声インターフェイスがますます重要になってきているため、音声インタラクションでできることのいくつかを調べる価値があります。 たとえば、私たちが何かを言い、それを書き起こし、ダウンロード可能な PDF として送り出すことができたらどうでしょうか?

さて、ネタバレ注意:私たちは絶対に できる それを行う! それを実現するために組み合わせることができるライブラリーとフレームワークがあり、それがこの記事で一緒にやろうとしていることです。

これらは私たちが使用しているツールです

まず、Next.js と Express.js という XNUMX つの大きなプレーヤーがあります。

Next.js 静的サイトを構築するための主要な機能を含む、React への追加機能への取り組み。 動的ルーティング、画像の最適化、ビルトイン ドメインとサブドメインのルーティング、高速更新、ファイル システム ルーティング、API ルートなど、すぐに利用できる機能を備えているため、多くの開発者にとって頼りになるツールです。 他にもいろいろ.

私たちの場合、そのために Next.js が絶対に必要です。 API ルート クライアントサーバー上。 テキスト ファイルを受け取り、PDF に変換し、ファイル システムに書き込み、クライアントに応答を送信するルートが必要です。

Express.js ルーティング、HTTP ヘルパー、およびテンプレートを使用する小さな Node.js アプリを作成できます。 これは独自の API 用のサーバーであり、物事の間でデータを渡したり解析したりするときに必要になります。

使用する他の依存関係がいくつかあります。

  1. 反応音声認識: 音声をテキストに変換し、React コンポーネントで使用できるようにするためのライブラリ。
  2. 再生器-ランタイム: 「トラブルシューティング用ライブラリ」regeneratorRuntime 反応音声認識の使用時に Next.js に表示される "は定義されていません" エラー
  3. html-pdf-ノード:HTMLページや公開URLをPDFに変換するためのライブラリ
  4. アクシオス: ブラウザと Node.js の両方で HTTP リクエストを行うためのライブラリ
  5. ホルン: クロスオリジン リソース共有を可能にするライブラリ

セットアップ

最初に、クライアント用とサーバー用の 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:

NextJS と ExpressJS を使用して音声を PDF に変換する

初めてのスピーチからテキストへの変換!

最初に取るべきアクションは、必要な依存関係を私たちの <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 にパッケージ化しました。

タイムスタンプ:

より多くの CSSトリック