feat: word pronunciation supported

This commit is contained in:
Gabe Yuan
2024-03-25 18:14:12 +08:00
parent 1c77a289a6
commit a83039577c
7 changed files with 130 additions and 6 deletions

View File

@@ -16,6 +16,7 @@ import {
KV_SALT_SYNC,
URL_BAIDU_LANGDETECT,
URL_BAIDU_SUGGEST,
URL_BAIDU_TTS,
OPT_LANGS_BAIDU,
URL_TENCENT_TRANSMART,
OPT_LANGS_TENCENT,
@@ -95,6 +96,18 @@ export const apiBaiduSuggest = async (text) => {
return [];
};
/**
* 百度语音
* @param {*} text
* @param {*} lan
* @param {*} spd
* @returns
*/
export const apiBaiduTTS = (text, lan = "uk", spd = 3) => {
const url = `${URL_BAIDU_TTS}?${queryString.stringify({ lan, text, spd })}`;
return fetchPolyfill(url);
};
/**
* 腾讯语言识别
* @param {*} text

View File

@@ -83,6 +83,7 @@ export const URL_MICROSOFT_TRAN =
export const URL_MICROSOFT_AUTH = "https://edge.microsoft.com/translate/auth";
export const URL_BAIDU_LANGDETECT = "https://fanyi.baidu.com/langdetect";
export const URL_BAIDU_SUGGEST = "https://fanyi.baidu.com/sug";
export const URL_BAIDU_TTS = "https://fanyi.baidu.com/gettts";
export const URL_BAIDU_WEB = "https://fanyi.baidu.com/";
export const URL_BAIDU_TRANSAPI = "https://fanyi.baidu.com/transapi";
export const URL_BAIDU_TRANSAPI_V2 = "https://fanyi.baidu.com/v2transapi";

51
src/hooks/Audio.js Normal file
View File

@@ -0,0 +1,51 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { apiBaiduTTS } from "../apis";
/**
* 声音播放hook
* @param {*} src
* @returns
*/
export function useAudio(src) {
// const audioRef = useRef(new Audio(src));
const audioRef = useRef(null);
const [error, setError] = useState(null);
const [ready, setReady] = useState(false);
const [playing, setPlaying] = useState(false);
const play = useCallback(() => {
audioRef.current?.play();
}, []);
useEffect(() => {
if (!src) {
return;
}
const audio = new Audio(src);
audio.addEventListener("error", (err) => setError(err));
audio.addEventListener("canplaythrough", () => setReady(true));
audio.addEventListener("play", () => setPlaying(true));
audio.addEventListener("ended", () => setPlaying(false));
audioRef.current = audio;
}, [src]);
return {
error,
ready,
playing,
play,
};
}
export function useTextAudio(text, lan = "uk", spd = 3) {
const [src, setSrc] = useState("");
useEffect(() => {
(async () => {
const res = await apiBaiduTTS(text, lan, spd);
setSrc(res);
})();
}, [text, lan, spd]);
return useAudio(src);
}

View File

@@ -12,6 +12,7 @@ import {
import { isBg } from "./browser";
import { newCacheReq, newTransReq } from "./req";
import { kissLog } from "./log";
import { blobToBase64 } from "./utils";
const TIMEOUT = 5000;
@@ -163,6 +164,9 @@ export const fetchData = async (
const contentType = res.headers.get("Content-Type");
if (contentType?.includes("json")) {
return await res.json();
} else if (contentType?.includes("audio")) {
const blob = await res.blob();
return await blobToBase64(blob);
}
return await res.text();
};

View File

@@ -233,3 +233,16 @@ export const isValidWord = (str) => {
const regex = /^[a-zA-Z-]+$/;
return regex.test(str);
};
/**
* blob转为base64
* @param {*} blob
* @returns
*/
export const blobToBase64 = (blob) => {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
};

View File

@@ -0,0 +1,37 @@
import IconButton from "@mui/material/IconButton";
import VolumeUpIcon from "@mui/icons-material/VolumeUp";
import { useTextAudio } from "../../hooks/Audio";
export default function AudioBtn({ text, lan = "uk" }) {
const { error, ready, playing, play } = useTextAudio(text, lan);
if (error) {
return;
}
if (!ready) {
return (
<IconButton disabled>
<VolumeUpIcon />
</IconButton>
);
}
if (playing) {
return (
<IconButton color="primary">
<VolumeUpIcon />
</IconButton>
);
}
return (
<IconButton
onClick={() => {
play();
}}
>
<VolumeUpIcon />
</IconButton>
);
}

View File

@@ -2,10 +2,11 @@ import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack";
import FavBtn from "./FavBtn";
import Typography from "@mui/material/Typography";
import AudioBtn from "./AudioBtn";
const phonicMap = {
en_phonic: "英",
us_phonic: "美",
en_phonic: ["英", "uk"],
us_phonic: ["美", "en"],
};
export default function DictCont({ dictResult }) {
@@ -27,13 +28,17 @@ export default function DictCont({ dictResult }) {
</Stack>
<Typography component="div">
<Typography>
<Stack direction="row">
{dictResult.voice
?.map(Object.entries)
.map((item) => item[0])
.map(([key, val]) => `${phonicMap[key] || key} ${val}`)
.join(" ")}
</Typography>
.map(([key, val]) => (
<span>
<span>{`${phonicMap[key]?.[0] || key} ${val}`}</span>
<AudioBtn text={dictResult.src} lan={phonicMap[key]?.[1]} />
</span>
))}
</Stack>
<ul style={{ margin: "0.5em 0" }}>
{dictResult.content[0].mean.map(({ pre, cont }, idx) => (
<li key={idx}>