feat(读书听书准备): 读书听书准备

This commit is contained in:
ovo 2024-12-24 16:29:26 +08:00
parent 2423e27b34
commit 17c571c61d
11 changed files with 258 additions and 3 deletions

View File

@ -323,6 +323,12 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
<build>

View File

@ -5,9 +5,12 @@ import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@Slf4j
@SpringBootApplication
@MapperScan("com.guwan.backend.mapper")
@EnableFeignClients
public class BackendApplication {
public static void main(String[] args) {

View File

@ -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();
// }
// }
//}

View File

@ -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);
}

View File

@ -1,6 +1,9 @@
package com.guwan.backend.controller;
import cn.hutool.json.JSONObject;
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.pojo.entity.BookContent;
import com.guwan.backend.mongodb.EveryReadDetailOfMongodb;
@ -34,6 +37,11 @@ public class CommonController {
private final MinioUtil minioUtil;
private final VoiceServiceClient voiceServiceClient;
private final SimpleTTSClient simpleTTSClient;
private final BookContentService bookContentService;
@ -199,9 +207,11 @@ public class CommonController {
@GetMapping("/getBookCommentByPath")
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<>();
@ -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);
}
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,15 @@
package com.guwan.backend.pojo.enums;
public enum VoiceModels {
ZHHKHIUGAAINEURAL,
ZHHKHIUMAANNEURAL,
ZHHKWANLUNGNEURAL,
ZHCNXIAOXIAONEURAL,
/**
* 晓伊
*/
ZHCNXIAOYINEURAL
}

View File

@ -1,6 +1,7 @@
package com.guwan.backend.util;
import io.minio.*;
import io.minio.errors.MinioException;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
@ -12,7 +13,10 @@ import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
@ -160,6 +164,11 @@ public class MinioUtil {
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
// public void init() {

View File

@ -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);
}
}

View File

@ -111,7 +111,7 @@
// 先清空内容,显示加载状态
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();
if (data && data.data) {
const volumeTitle = data.data.volume || 'Unknown Volume';

View File

@ -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);
}
}