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:
- reageerida-kõne-tuvastus: teek kõne tekstiks teisendamiseks, muutes selle Reacti komponentidele kättesaadavaks.
- regeneraatori tööaeg: raamatukogu tõrkeotsinguks
regeneratorRuntime
pole määratletud” viga, mis kuvatakse failis Next.js, kui kasutate kõnetuvastust - html-pdf-sõlm: raamatukogu HTML-lehe või avaliku URL-i teisendamiseks PDF-iks
- aksioosid: teek HTTP-päringute tegemiseks nii brauseris kui ka Node.js-is
- 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
faililcomponents
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
oncontentEditable
jasuppressContentEditableWarning
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
:
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.