diff --git a/src/apis/index.js b/src/apis/index.js
index ab07fcc..cf3795f 100644
--- a/src/apis/index.js
+++ b/src/apis/index.js
@@ -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
diff --git a/src/config/index.js b/src/config/index.js
index 2d22dee..3f00333 100644
--- a/src/config/index.js
+++ b/src/config/index.js
@@ -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";
diff --git a/src/hooks/Audio.js b/src/hooks/Audio.js
new file mode 100644
index 0000000..f9f2587
--- /dev/null
+++ b/src/hooks/Audio.js
@@ -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);
+}
diff --git a/src/libs/fetch.js b/src/libs/fetch.js
index 15215f8..c5598e0 100644
--- a/src/libs/fetch.js
+++ b/src/libs/fetch.js
@@ -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();
};
diff --git a/src/libs/utils.js b/src/libs/utils.js
index 4a0f9e5..77700db 100644
--- a/src/libs/utils.js
+++ b/src/libs/utils.js
@@ -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);
+ });
+};
diff --git a/src/views/Selection/AudioBtn.js b/src/views/Selection/AudioBtn.js
new file mode 100644
index 0000000..510aaeb
--- /dev/null
+++ b/src/views/Selection/AudioBtn.js
@@ -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 (
+
+
+
+ );
+ }
+
+ if (playing) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+ {
+ play();
+ }}
+ >
+
+
+ );
+}
diff --git a/src/views/Selection/DictCont.js b/src/views/Selection/DictCont.js
index f8b37d5..dbaa545 100644
--- a/src/views/Selection/DictCont.js
+++ b/src/views/Selection/DictCont.js
@@ -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 }) {
-
+
{dictResult.voice
?.map(Object.entries)
.map((item) => item[0])
- .map(([key, val]) => `${phonicMap[key] || key} ${val}`)
- .join(" ")}
-
+ .map(([key, val]) => (
+
+ {`${phonicMap[key]?.[0] || key} ${val}`}
+
+
+ ))}
+
{dictResult.content[0].mean.map(({ pre, cont }, idx) => (
-