Kõne teisendamine PDF-vormingusse NextJS ja ExpressJS PlatoBlockchain Data Intelligence abil. Vertikaalne otsing. Ai.

Kõne teisendamine PDF-iks NextJS-i ja ExpressJS-iga

Kuna kõneliidesed muutuvad üha populaarsemaks, tasub uurida mõningaid asju, mida saame kõne interaktsiooniga teha. Mis siis, kui saaksime midagi öelda ja lasta selle transkribeerida ja allalaaditava PDF-failina välja pumbata?

Noh, spoileri hoiatus: me absoluutselt võimalik tee seda! On teeke ja raamistikke, mida saame selle teoks tegemiseks kokku panna, ja seda me selles artiklis koos teemegi.

Need on tööriistad, mida me kasutame

Esiteks on need kaks suurt mängijat: Next.js ja Express.js.

Järgmine.js pakub Reactile lisafunktsioone, sealhulgas staatiliste saitide ehitamise põhifunktsioone. See on paljudele arendajatele mõeldud, kuna see pakub kohe karbist välja, nagu dünaamiline marsruutimine, pildi optimeerimine, sisseehitatud domeeni ja alamdomeeni marsruutimine, kiired värskendused, failisüsteemi marsruutimine ja API marsruudid ... palju-palju muid asju.

Meie puhul vajame selle jaoks kindlasti Next.js-i API marsruudid meie kliendiserveris. Soovime marsruuti, mis võtab tekstifaili, teisendab selle PDF-iks, kirjutab selle meie failisüsteemi ja saadab seejärel kliendile vastuse.

Express.js võimaldab meil käivitada väikese Node.js-i rakenduse marsruutimise, HTTP-abiliste ja mallidega. See on server meie enda API jaoks, mida me vajame andmete edastamisel ja sõelumisel.

Meil on veel mõned sõltuvused, mida hakkame kasutama:

  1. reageerida-kõne-tuvastus: teek kõne tekstiks teisendamiseks, muutes selle Reacti komponentidele kättesaadavaks.
  2. regeneraatori tööaeg: raamatukogu tõrkeotsinguksregeneratorRuntime pole määratletud” viga, mis kuvatakse failis Next.js, kui kasutate kõnetuvastust
  3. html-pdf-sõlm: raamatukogu HTML-lehe või avaliku URL-i teisendamiseks PDF-iks
  4. aksioosid: teek HTTP-päringute tegemiseks nii brauseris kui ka Node.js-is
  5. sarved: teek, mis võimaldab ressurssidevahelist jagamist

Seadistamine

Esimese asjana tahame luua kaks projektikausta, ühe kliendi ja teise serveri jaoks. Nimetage neid mis iganes soovite. Panen omale nime audio-to-pdf-client ja audio-to-pdf-server, Vastavalt.

Kiireim viis Next.js-iga kliendi poolel alustamiseks on selle alglaadimine loo-järgmine-rakendus. Niisiis, avage oma terminal ja käivitage oma kliendiprojekti kaustast järgmine käsk:

npx create-next-app client

Nüüd vajame oma Expressi serverit. Saame hakkama cd- sisestage serveri projekti kausta ja käivitage npm init käsk. A package.json Kui see on tehtud, luuakse fail serveri projekti kausta.

Peame ikkagi Expressi installima, nii et teeme seda nüüd koos npm install express. Nüüd saame luua uue index.js fail serveri projekti kausta ja visake see kood sinna:

const express = require("express")
const app = express()

app.listen(4000, () => console.log("Server is running on port 4000"))

Kas olete serveri käivitamiseks valmis?

node index.js

Edasi liikumiseks vajame veel paari kausta ja veel ühte faili:

  • Loo components kaust kliendiprojekti kaustas.
  • Loo SpeechToText.jsx failil components alamkaust.

Enne kui me edasi läheme, on meil väike koristamine teha. Täpsemalt peame asendama failis vaikekoodi pages/index.js faili sellega:

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="/et/favicon.ico" />
      </Head>

      <h1>Convert your speech to pdf</h1>

      <main>
        <SpeechToText />
      </main>
    </div>
  );
}

Imporditud SpeechToText komponent eksporditakse lõpuks components/SpeechToText.jsx.

Installime teised sõltuvused

Olgu, meie rakenduse esialgne seadistus on eemal. Nüüd saame installida teegid, mis käitlevad edastatud andmeid.

Saame oma kliendisõltuvusi installida:

npm install react-speech-recognition regenerator-runtime axios

Meie Expressi serverisõltuvused on järgmisena üleval, nii et lähme cd serveri projekti kausta ja installige need:

npm install html-pdf-node cors

Tõenäoliselt on õige aeg peatada ja veenduda, et meie projektikaustades olevad failid on korras. Siin peaks teil olema kliendiprojekti kaustas:

/audio-to-pdf-web-client
├─ /components
|  └── SpeechToText.jsx
├─ /pages
|  ├─ _app.js
|  └── index.js
└── /styles
    ├─globals.css
    └── Home.module.css

Ja siin peaks serveri projekti kaustas olema:

/audio-to-pdf-server
└── index.js

UI loomine

Noh, meie kõne PDF-vormingus poleks kuigi hea, kui sellega pole võimalik suhelda, nii et teeme selle jaoks Reacti komponendi, mida saame nimetada <SpeechToText>.

Saate täielikult kasutada oma märgistust. Siin on see, mida mul on, et anda teile aimu tükkidest, mida me kokku paneme:

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;

See komponent tagastab a Reaktsiooni fragment mis sisaldab HTML-i <``section``> element, mis sisaldab kolme divi:

  • .button-container sisaldab kahte nuppu, mida kasutatakse kõnetuvastuse käivitamiseks ja peatamiseks.
  • .words on contentEditable ja suppressContentEditableWarning atribuudid, et muuta see element redigeeritavaks ja keelata kõik Reacti hoiatused.
  • Teine .button-container sisaldab veel kahte nuppu, mida kasutatakse vastavalt kõne lähtestamiseks ja teisendamiseks PDF-vormingusse.

Stiil on hoopis teine ​​asi. Ma ei hakka seda siin käsitlema, kuid võite kasutada mõningaid minu kirjutatud stiile ka teie enda jaoks. styles/global.css faili.

Vaata täielikku CSS-i
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;
}

Seal olevaid CSS-i muutujaid kasutatakse nuppude taustavärvi reguleerimiseks.

Vaatame viimaseid muudatusi! Jookse npm run dev terminalis ja kontrollige neid.

Külastades peaksite seda brauseris nägema http://localhost:3000:

Kõne teisendamine PDF-iks NextJS-i ja ExpressJS-iga

Meie esimene kõne tekstiks teisendamine!

Esimese asjana tuleb importida vajalikud sõltuvused meie süsteemi <SpeechToText> komponent:

import React, { useRef, useState } from "react";
import SpeechRecognition, {
  useSpeechRecognition,
} from "react-speech-recognition";
import axios from "axios";

Seejärel kontrollime, kas brauser toetab kõnetuvastust, ja esitame teatise, kui seda ei toetata:

const speechRecognitionSupported =
  SpeechRecognition.browserSupportsSpeechRecognition();

if (!speechRecognitionSupported) {
  return <div>Your browser does not support speech recognition.</div>;
}

Järgmisena võtame välja transcript ja resetTranscript alates useSpeechRecognition() konks:

const { transcript, resetTranscript } = useSpeechRecognition();

Seda me vajame riigile, kes tegeleb listening:

const [listening, setListening] = useState(false);

Vajame ka a ref jaoks div koos contentEditable atribuut, siis peame lisama atribuudi ref omistada sellele ja läbida transcript as children:

const textBodyRef = useRef(null);

…ja:

<div
  className="words"
  contentEditable
  ref={textBodyRef}
  suppressContentEditableWarning={true}
  >
  {transcript}
</div>

Viimane asi, mida siin vajame, on funktsioon, mis käivitab kõnetuvastuse ja selle funktsiooni sidumiseks onClick meie nupu sündmusekuulaja. Nupp määrab kuulamise true ja paneb selle pidevalt tööle. Keelame nupu, kui see on selles olekus, et vältida täiendavate sündmuste käivitamist.

const startListening = () => {
  setListening(true);
  SpeechRecognition.startListening({
    continuous: true,
  });
};

…ja:

<button
  type="button"
  onClick={startListening}
  style={{ "--bgColor": "blue" }}
  disabled={listening}
>
  Start
</button>

Nupule klõpsamine peaks nüüd transkriptsiooni käivitama.

Veel funktsioone

OK, meil on komponent, mis suudab algus kuulates. Kuid nüüd vajame seda ka mõne muu asja tegemiseks, näiteks stopListening, resetText ja handleConversion. Teeme need funktsioonid.

const stopListening = () => {
  setListening(false);
  SpeechRecognition.stopListening();
};

const resetText = () => {
  stopListening();
  resetTranscript();
  textBodyRef.current.innerText = "";
};

const handleConversion = async () => {}

Kõik funktsioonid lisatakse an onClick sündmuste kuulaja vastavatel nuppudel:

<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 funktsioon on asünkroonne, sest lõpuks esitame API päringu. Nupul "Stopp" on keelatud atribuut, mis käivitub, kui kuulamine on vale.

Kui taaskäivitame serveri ja värskendame brauserit, saame nüüd brauseris kõne transkriptsiooni käivitada, peatada ja lähtestada.

Nüüd on meil vaja, et rakendus seda teeks salvestama mis tuvastas kõne, teisendades selle PDF-failiks. Selleks vajame Express.js serveripoolset teed.

API marsruudi seadistamine

Selle marsruudi eesmärk on võtta tekstifail, teisendada see PDF-iks, kirjutada see PDF meie failisüsteemi ja saata seejärel kliendile vastus.

Seadistamiseks avame server/index.js faili ja importige html-pdf-node ja fs sõltuvused, mida kasutatakse meie failisüsteemi kirjutamiseks ja avamiseks.

const HTMLToPDF = require("html-pdf-node");
const fs = require("fs");
const cors = require("cors)

Järgmisena paneme paika oma marsruudi:

app.use(cors())
app.use(express.json())

app.post("/", (req, res) => {
  // etc.
})

Seejärel jätkame kasutamiseks vajalike valikute määratlemisega html-pdf-node marsruudi sees:

let options = { format: "A4" };
let file = {
  content: `<html><body><pre style='font-size: 1.2rem'>${req.body.text}</pre></body></html>`,
};

. options objekt aktsepteerib paberi suuruse ja stiili määramiseks väärtust. Paberi suurused järgivad palju erinevat süsteemi kui need suurusühikud, mida me tavaliselt veebis kasutame. Näiteks, A4 on tüüpiline tähesuurus.

. file objekt aktsepteerib kas avaliku veebisaidi URL-i või HTML-i märgistust. HTML-lehe loomiseks kasutame html, body, pre HTML-i sildid ja tekstist req.body.

Saate rakendada mis tahes oma valitud stiili.

Järgmisena lisame a trycatch teekonnal ilmneda võivate vigade käsitlemiseks:

try {

} catch(error){
  console.log(error);
  res.status(500).send(error);
}

Järgmisena kasutame generatePdf alates html-pdf-node raamatukogu genereerimiseks a pdfBuffer (toores PDF-fail) meie failist ja looge kordumatu pdfName:

HTMLToPDF.generatePdf(file, options).then((pdfBuffer) => {
  // console.log("PDF Buffer:-", pdfBuffer);
  const pdfName = "./data/speech" + Date.now() + ".pdf";

  // Next code here
}

Sealt edasi kasutame failisüsteemi moodulit kliendirakendusele vastuse kirjutamiseks, lugemiseks ja (jah, lõpuks!) saatmiseks:

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

Teeme selle natuke lahti:

  • . writeFile failisüsteemi moodul aktsepteerib failinime, andmeid ja tagasihelistamisfunktsiooni, mis võib faili kirjutamisel probleemi korral tagastada veateate. Kui töötate CDN-iga, mis pakub vea lõpp-punkte, võite selle asemel kasutada neid.
  • . readFile failisüsteemi moodul aktsepteerib failinime ja tagasihelistamisfunktsiooni, mis on võimeline või tagastama lugemisvea ja ka lugemisandmed. Kui meil pole lugemisvigu ja loetud andmed on olemas, koostame ja saadame kliendile vastuse. Jällegi saab selle asendada teie CDN-i lõpp-punktidega, kui teil need on.
  • . res.setHeader("Content-Type", "application/pdf"); teatab brauserile, et saadame PDF-faili.
  • . res.setHeader("Content-Disposition", "attachment"); käsib brauseril muuta saadud andmed allalaaditavaks.

Kuna API marsruut on valmis, saame seda kasutada oma rakenduses aadressil http://localhost:4000. Saame jätkata oma taotluse kliendiosaga, et täita handleConversion funktsiooni.

Konversiooni käsitlemine

Enne kui saame alustada tööd a handleConversion funktsiooni, peame looma oleku, mis käsitleb meie API laadimis-, vea-, õnnestumis- ja muid sõnumeid. Me hakkame kasutama Reacti useState konks selle seadistamiseks:

const [response, setResponse] = useState({
  loading: false,
  message: "",
  error: false,
  success: false,
});

aasta handleConversion funktsiooni, kontrollime enne koodi käivitamist, millal veebileht on laaditud, ja veendume, et div koos editable atribuut ei ole tühi:

if (typeof window !== "undefined") {
const userText = textBodyRef.current.innerText;
  // console.log(textBodyRef.current.innerText);

  if (!userText) {
    alert("Please speak or write some text.");
    return;
  }
}

Jätkame, pakendades oma võimaliku API taotluse a trycatch, käsitledes võimalikke vigu ja värskendades vastuse olekut:

try {

} catch(error){
  setResponse({
    ...response,
    loading: false,
    error: true,
    message:
      "An unexpected error occurred. Text not converted. Please try again",
    success: false,
  });
}

Järgmisena määrame vastuse oleku jaoks mõned väärtused ja määrame ka konfiguratsiooni axios ja tee serverile postitustaotlus:

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
);

Kui oleme saanud eduka vastuse, määrame vastuse oleku sobivate väärtustega ja juhendame brauserit vastuvõetud PDF-faili alla laadima:

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();

Ja sisuRedigeeritava all saame kasutada järgmist div sõnumite kuvamiseks:

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

Lõplik kood

Olen GitHubis kõik kokku pakkinud, et saaksite vaadata nii serveri kui ka kliendi täielikku lähtekoodi.

Ajatempel:

Veel alates CSSi trikid