feat: word pronunciation supported
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
51
src/hooks/Audio.js
Normal 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);
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
};
|
||||
|
||||
37
src/views/Selection/AudioBtn.js
Normal file
37
src/views/Selection/AudioBtn.js
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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}>
|
||||
|
||||
Reference in New Issue
Block a user