تبدیل گفتار به PDF با NextJS و ExpressJS PlatoBlockchain Intelligence. جستجوی عمودی Ai.

تبدیل گفتار به PDF با NextJS و ExpressJS

با تبدیل شدن بیشتر رابط‌های گفتاری، ارزش بررسی برخی از کارهایی که می‌توانیم با تعاملات گفتاری انجام دهیم را دارد. مثلاً، چه می‌شود اگر بتوانیم چیزی بگوییم و آن را رونویسی کنیم و به‌عنوان PDF قابل دانلود منتشر کنیم؟

خوب، هشدار اسپویلر: ما کاملاً می توان انجام این کار کتابخانه‌ها و چارچوب‌هایی وجود دارند که می‌توانیم آن را با هم ترکیب کنیم و این کاری است که در این مقاله با هم انجام خواهیم داد.

اینها ابزارهایی هستند که ما استفاده می کنیم

اول از همه، اینها دو بازیکن بزرگ هستند: Next.js و Express.js.

Next.js ویژگی‌های اضافی React، از جمله ویژگی‌های کلیدی برای ساختن سایت‌های استاتیک را اعمال می‌کند. این برای بسیاری از توسعه دهندگان به دلیل چیزهایی است که مستقیماً ارائه می دهد، مانند مسیریابی پویا، بهینه سازی تصویر، مسیریابی داخلی و زیر دامنه، تازه سازی سریع، مسیریابی سیستم فایل و مسیرهای API... خیلی چیزهای دیگر.

در مورد ما، ما قطعا به Next.js برای آن نیاز داریم مسیرهای API در سرور مشتری ما ما مسیری می خواهیم که یک فایل متنی را بگیرد، آن را به PDF تبدیل کند، آن را در سیستم فایل ما بنویسد، سپس پاسخی را برای مشتری ارسال کند.

Express.js به ما این امکان را می دهد تا برنامه کوچک Node.js را با مسیریابی، راهنماهای HTTP و قالب بندی دریافت کنیم. این یک سرور برای API خودمان است، چیزی که هنگام ارسال و تجزیه داده ها بین چیزها به آن نیاز داریم.

ما وابستگی های دیگری داریم که از آنها استفاده خواهیم کرد:

  1. واکنش - گفتار - تشخیص: کتابخانه ای برای تبدیل گفتار به متن و در دسترس قرار دادن آن برای اجزای React.
  2. Regenerator-Runtime: کتابخانه ای برای عیب یابی "regeneratorRuntime خطای is not defined” که هنگام استفاده از react-speech-recognition در Next.js نشان داده می شود.
  3. html-pdf-node: کتابخانه ای برای تبدیل صفحه HTML یا URL عمومی به PDF
  4. آکسیوس: کتابخانه ای برای ایجاد درخواست های HTTP هم در مرورگر و هم در Node.js
  5. شاخ: کتابخانه ای که امکان به اشتراک گذاری منابع متقاطع را می دهد

راه اندازی

اولین کاری که می خواهیم انجام دهیم این است که دو پوشه پروژه ایجاد کنیم، یکی برای مشتری و دیگری برای سرور. نام آنها را هر چه دوست دارید بگذارید. اسم خودم را می گذارم audio-to-pdf-client و audio-to-pdf-serverبود.

سریعترین راه برای شروع با Next.js در سمت مشتری، بوت استرپ کردن آن است ایجاد برنامه بعدی. بنابراین، ترمینال خود را باز کنید و دستور زیر را از پوشه پروژه مشتری خود اجرا کنید:

npx create-next-app client

اکنون ما به سرور Express خود نیاز داریم. ما می توانیم آن را دریافت کنیم cd- وارد پوشه پروژه سرور شده و آن را اجرا کنید npm init فرمان آ 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="/fa/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

ساخت رابط کاربری

خوب، اگر راهی برای تعامل با آن وجود نداشته باشد، گفتار به 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;

این جزء a را برمی گرداند قطعه واکنش نشان می دهد که حاوی یک HTML است <``section``> عنصری که شامل سه div است:

  • .button-container شامل دو دکمه است که برای شروع و توقف تشخیص گفتار استفاده می شود.
  • .words است contentEditable و suppressContentEditableWarning ویژگی هایی که این عنصر را قابل ویرایش می کند و هرگونه هشدار React را سرکوب می کند.
  • دیگر .button-container دو دکمه دیگر را نگه می دارد که به ترتیب برای بازنشانی و تبدیل گفتار به PDF استفاده می شود.

استایل کردن یک چیز دیگر است. من در اینجا وارد آن نمی شوم، اما شما می توانید از برخی از سبک هایی که من نوشتم به عنوان نقطه شروع برای سبک خود استفاده کنید 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:

تبدیل گفتار به PDF با NextJS و ExpressJS

اولین تبدیل گفتار به متن ما!

اولین اقدامی که باید انجام داد این است که وابستگی های لازم را به ما وارد کنیم <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>

اکنون با کلیک بر روی دکمه، رونویسی شروع می شود.

توابع بیشتر

خوب، بنابراین ما یک جزء داریم که می تواند شروع استماع. اما اکنون برای انجام چند کار دیگر نیز به آن نیاز داریم، مانند 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>

La handleConversion تابع ناهمزمان است زیرا در نهایت یک درخواست API ایجاد خواهیم کرد. دکمه "توقف" دارای ویژگی غیرفعال است که در صورت نادرست بودن گوش دادن فعال می شود.

اگر سرور را دوباره راه اندازی کنیم و مرورگر را تازه کنیم، اکنون می توانیم رونویسی گفتار خود را در مرورگر شروع، متوقف و بازنشانی کنیم.

اکنون چیزی که ما نیاز داریم این است که برنامه بتواند رونویسی کردن که گفتار را با تبدیل آن به یک فایل 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>`,
};

La options شی مقداری را برای تنظیم اندازه و سبک کاغذ می پذیرد. اندازه‌های کاغذ از سیستم بسیار متفاوتی نسبت به واحدهای اندازه‌گیری که معمولاً در وب استفاده می‌کنیم پیروی می‌کنند. مثلا، A4 اندازه حروف معمولی است.

La file شی یا 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
}

از آنجا، ما از ماژول سیستم فایل برای نوشتن، خواندن و (بله، در نهایت!) پاسخ به برنامه مشتری استفاده می کنیم:

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." });
  });
});

بیایید آن را کمی تفکیک کنیم:

  • La writeFile ماژول سیستم فایل یک نام فایل، داده و یک تابع فراخوانی را می پذیرد که در صورت بروز مشکل در نوشتن فایل، می تواند پیام خطا را برگرداند. اگر با CDN کار می کنید که نقاط پایانی خطا را ارائه می دهد، می توانید به جای آن از آنها استفاده کنید.
  • La readFile ماژول سیستم فایل یک نام فایل و یک تابع فراخوانی را می پذیرد که می تواند خطای خواندن و همچنین داده های خوانده شده را برگرداند. هنگامی که خطای خواندن نداریم و داده های خوانده شده وجود دارد، پاسخی را ساخته و برای مشتری ارسال می کنیم. مجدداً، اگر آنها را دارید، می توان آن را با نقاط پایانی CDN خود جایگزین کرد.
  • La res.setHeader("Content-Type", "application/pdf"); به مرورگر می گوید که ما در حال ارسال یک فایل PDF هستیم.
  • La 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 عملکرد، ما بررسی می کنیم که صفحه وب چه زمانی قبل از اجرای کد ما بارگذاری شده است و مطمئن می شویم که این مورد 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();

و می توانیم در زیر محتوای قابل ویرایش از موارد زیر استفاده کنیم div برای نمایش پیام ها:

<div>
  {response.success && <i className="success">{response.message}</i>}
  {response.error && <i className="error">{response.message}</i>}
</div>

کد نهایی

من همه چیز را در GitHub بسته بندی کرده ام تا بتوانید کد منبع کامل را برای سرور و مشتری بررسی کنید.

تمبر زمان:

بیشتر از ترفندهای CSS