feat(读书听书准备): 读书听书准备
This commit is contained in:
parent
2423e27b34
commit
17c571c61d
6
pom.xml
6
pom.xml
|
@ -323,6 +323,12 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
|
<version>4.1.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -5,9 +5,12 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@MapperScan("com.guwan.backend.mapper")
|
@MapperScan("com.guwan.backend.mapper")
|
||||||
|
@EnableFeignClients
|
||||||
public class BackendApplication {
|
public class BackendApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.guwan.backend.client;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
@FeignClient(name = "ttsService", url = "http://127.0.0.1:8534")
|
||||||
|
public interface SimpleTTSClient {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping(value = "/v1/audio/speech")
|
||||||
|
byte[] saveAudio(String jsonInputString);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//public class SimpleAudioSaver {
|
||||||
|
//
|
||||||
|
// public static void main(String[] args) {
|
||||||
|
// String urlString = "http://localhost:8534/v1/audio/speech"; // API URL
|
||||||
|
// String jsonInputString = "{\"input\": \"想听个啥123\", \"voice\": \"zh-CN-XiaoxiaoNeural\", \"style\": \"\", \"rate\": 0, \"pitch\": 0}";
|
||||||
|
//
|
||||||
|
// saveAudio(urlString, jsonInputString);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static void saveAudio(String urlString, String jsonInputString) {
|
||||||
|
// try {
|
||||||
|
// // 创建 URL 对象
|
||||||
|
// URL url = new URL(urlString);
|
||||||
|
// // 打开连接
|
||||||
|
// HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
// // 设置请求方式为 POST
|
||||||
|
// connection.setRequestMethod("POST");
|
||||||
|
// connection.setDoOutput(true);
|
||||||
|
// connection.setRequestProperty("Content-Type", "application/json");
|
||||||
|
// connection.setRequestProperty("Accept", "audio/mpeg");
|
||||||
|
//
|
||||||
|
// // 发送请求体
|
||||||
|
// connection.getOutputStream().write(jsonInputString.getBytes("UTF-8"));
|
||||||
|
//
|
||||||
|
// // 处理响应
|
||||||
|
// if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||||
|
// // 获取输入流
|
||||||
|
// InputStream inputStream = new BufferedInputStream(connection.getInputStream());
|
||||||
|
// // 保存文件
|
||||||
|
// try (FileOutputStream outputStream = new FileOutputStream("output.mp3")) {
|
||||||
|
// byte[] buffer = new byte[1024];
|
||||||
|
// int bytesRead;
|
||||||
|
// while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
|
// outputStream.write(buffer, 0, bytesRead);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// System.out.println("Audio saved as output.mp3");
|
||||||
|
// } else {
|
||||||
|
// System.err.println("Failed to get audio. Response code: " + connection.getResponseCode());
|
||||||
|
// }
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.guwan.backend.client;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语音转文本
|
||||||
|
*/
|
||||||
|
@FeignClient(name = "voiceService", url = "http://127.0.0.1:8532")
|
||||||
|
public interface VoiceServiceClient {
|
||||||
|
|
||||||
|
@PostMapping(value = "/v1/audio/transcriptions", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
|
JSONObject voiceToText(@RequestPart("file") MultipartFile file,
|
||||||
|
@RequestPart("model") String model);
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
package com.guwan.backend.controller;
|
package com.guwan.backend.controller;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
import com.guwan.backend.annotation.OperationLog;
|
import com.guwan.backend.annotation.OperationLog;
|
||||||
|
import com.guwan.backend.client.SimpleTTSClient;
|
||||||
|
import com.guwan.backend.client.VoiceServiceClient;
|
||||||
import com.guwan.backend.common.Result;
|
import com.guwan.backend.common.Result;
|
||||||
import com.guwan.backend.pojo.entity.BookContent;
|
import com.guwan.backend.pojo.entity.BookContent;
|
||||||
import com.guwan.backend.mongodb.EveryReadDetailOfMongodb;
|
import com.guwan.backend.mongodb.EveryReadDetailOfMongodb;
|
||||||
|
@ -34,6 +37,11 @@ public class CommonController {
|
||||||
|
|
||||||
private final MinioUtil minioUtil;
|
private final MinioUtil minioUtil;
|
||||||
|
|
||||||
|
private final VoiceServiceClient voiceServiceClient;
|
||||||
|
private final SimpleTTSClient simpleTTSClient;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private final BookContentService bookContentService;
|
private final BookContentService bookContentService;
|
||||||
|
|
||||||
|
@ -199,9 +207,11 @@ public class CommonController {
|
||||||
@GetMapping("/getBookCommentByPath")
|
@GetMapping("/getBookCommentByPath")
|
||||||
public ResponseEntity<Map<String, Object>> getBookCommentByPath(@RequestParam("id") Long id) {
|
public ResponseEntity<Map<String, Object>> getBookCommentByPath(@RequestParam("id") Long id) {
|
||||||
// 从数据库中获取评论内容
|
// 从数据库中获取评论内容
|
||||||
String comments = bookContentService.getById(id).getSectionContent();
|
//String comments = bookContentService.getById(id).getSectionContent();
|
||||||
|
|
||||||
BookContent byId = bookContentService.getById(id);
|
BookContent byId = bookContentService.lambdaQuery()
|
||||||
|
.eq(BookContent::getBookName, "大爱仙尊")
|
||||||
|
.eq(BookContent::getSectionId, id).one();
|
||||||
|
|
||||||
// 构造返回数据
|
// 构造返回数据
|
||||||
Map<String, Object> response = new HashMap<>();
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
@ -252,4 +262,47 @@ public class CommonController {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* model 默认值 /model/faster-whisper-small/
|
||||||
|
*/
|
||||||
|
@PostMapping("/convertVoiceToText")
|
||||||
|
public Result convertVoiceToText(MultipartFile file, String model) {
|
||||||
|
|
||||||
|
if (model == null){
|
||||||
|
model = "/model/faster-whisper-small/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.success(voiceServiceClient.voiceToText(file, model));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/simpleTTS")
|
||||||
|
public Result simpleTTS(String input, String voiceModel) {
|
||||||
|
|
||||||
|
|
||||||
|
if (voiceModel == null) {
|
||||||
|
voiceModel = "zh-CN-XiaoxiaoNeural";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 JSONObject 构造JSON
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
jsonObject.put("input", input);
|
||||||
|
jsonObject.put("voice", voiceModel);
|
||||||
|
|
||||||
|
String jsonInputString = jsonObject.toString();
|
||||||
|
System.out.println(jsonInputString);
|
||||||
|
|
||||||
|
|
||||||
|
// jsonInputString = "{\"input\": \"想听个啥123\", \"voice\": \"zh-CN-XiaoxiaoNeural\", \"style\": \"\", \"rate\": 0, \"pitch\": 0}";
|
||||||
|
|
||||||
|
|
||||||
|
String url = minioUtil.getUrl(minioUtil.getFileUrl("temp",
|
||||||
|
minioUtil.uploadByteArray(
|
||||||
|
simpleTTSClient.saveAudio(jsonInputString), "mp3")));
|
||||||
|
|
||||||
|
|
||||||
|
return Result.success(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.guwan.backend.pojo.enums;
|
||||||
|
|
||||||
|
public enum VoiceModels {
|
||||||
|
|
||||||
|
ZHHKHIUGAAINEURAL,
|
||||||
|
ZHHKHIUMAANNEURAL,
|
||||||
|
ZHHKWANLUNGNEURAL,
|
||||||
|
ZHCNXIAOXIAONEURAL,
|
||||||
|
/**
|
||||||
|
* 女 晓伊
|
||||||
|
*/
|
||||||
|
ZHCNXIAOYINEURAL
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package com.guwan.backend.util;
|
package com.guwan.backend.util;
|
||||||
|
|
||||||
import io.minio.*;
|
import io.minio.*;
|
||||||
|
import io.minio.errors.MinioException;
|
||||||
import io.minio.http.Method;
|
import io.minio.http.Method;
|
||||||
import io.minio.messages.DeleteError;
|
import io.minio.messages.DeleteError;
|
||||||
import io.minio.messages.DeleteObject;
|
import io.minio.messages.DeleteObject;
|
||||||
|
@ -12,7 +13,10 @@ import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -160,6 +164,11 @@ public class MinioUtil {
|
||||||
return url.substring(0, url.indexOf("?"));
|
return url.substring(0, url.indexOf("?"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUrlByName(String name) {
|
||||||
|
//TODO 加个全局
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成文件名
|
* 生成文件名
|
||||||
|
@ -185,6 +194,37 @@ public class MinioUtil {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public String uploadByteArray(byte[] data, String suffix) {
|
||||||
|
|
||||||
|
String uuid = UUIDUtil.uuid();
|
||||||
|
|
||||||
|
String fileName = uuid + "." +suffix;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用 byte[] 创建 InputStream
|
||||||
|
InputStream byteArrayInputStream = new ByteArrayInputStream(data);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
minioClient.putObject(
|
||||||
|
PutObjectArgs.builder()
|
||||||
|
.bucket("temp")
|
||||||
|
.object(fileName) // 存储在 MinIO 中的对象名称
|
||||||
|
.stream(byteArrayInputStream, data.length, -1)
|
||||||
|
.contentType("application/octet-stream") // 根据数据类型设置
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
log.info("File uploaded successfully to MinIO!");
|
||||||
|
return fileName;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 初始化时创建视频桶
|
// 初始化时创建视频桶
|
||||||
// @PostConstruct
|
// @PostConstruct
|
||||||
// public void init() {
|
// public void init() {
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.guwan.backend.util;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class UUIDUtil {
|
||||||
|
public static String uuid(){
|
||||||
|
return UUID.randomUUID().toString().replace("-","");
|
||||||
|
}
|
||||||
|
public static void main(String args[]){
|
||||||
|
String uuid = UUIDUtil.uuid();
|
||||||
|
System.out.println(uuid);
|
||||||
|
}
|
||||||
|
}
|
|
@ -111,7 +111,7 @@
|
||||||
// 先清空内容,显示加载状态
|
// 先清空内容,显示加载状态
|
||||||
document.getElementById("comments").innerHTML = "Loading...";
|
document.getElementById("comments").innerHTML = "Loading...";
|
||||||
|
|
||||||
const response = await fetch(`/common/getBookCommentByPath?id=${bookId}`);
|
const response = await fetch(`/api/common/getBookCommentByPath?id=${bookId}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data && data.data) {
|
if (data && data.data) {
|
||||||
const volumeTitle = data.data.volume || 'Unknown Volume';
|
const volumeTitle = data.data.volume || 'Unknown Volume';
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.guwan.backend;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class CommaToNewline {
|
||||||
|
|
||||||
|
public static void replaceCommasWithNewlines(String filePath) {
|
||||||
|
try {
|
||||||
|
// 读取文件内容到字符串
|
||||||
|
String content = new String(Files.readAllBytes(Paths.get(filePath)));
|
||||||
|
|
||||||
|
// 将逗号替换为换行符
|
||||||
|
content = content.replace(",", "\n");
|
||||||
|
|
||||||
|
// 将修改后的内容写回文件
|
||||||
|
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
|
||||||
|
writer.write(content);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 假设有一个文件路径
|
||||||
|
String filePath = "D:\\00_桌面\\123.txt";
|
||||||
|
replaceCommasWithNewlines(filePath);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue