diff --git a/flx-ai/src/main/java/com/pjilisense/flxai/controller/MyDigitalHumanController.java b/flx-ai/src/main/java/com/pjilisense/flxai/controller/MyDigitalHumanController.java index d52322a..3a4ba76 100644 --- a/flx-ai/src/main/java/com/pjilisense/flxai/controller/MyDigitalHumanController.java +++ b/flx-ai/src/main/java/com/pjilisense/flxai/controller/MyDigitalHumanController.java @@ -62,21 +62,26 @@ public class MyDigitalHumanController { @ApiOperation("保存") public Result save(@RequestBody MyDigitalHumanDTO dto){ - // - MyDigitalHumanDTO ret = myDigitalHumanService.genVideo(dto); - //myDigitalHumanService.save(dto); - - return new Result().ok(ret); + try{ + // + MyDigitalHumanDTO ret = myDigitalHumanService.genVideo(dto); + //myDigitalHumanService.save(dto); + return new Result().ok(ret); + }catch(RuntimeException e){ + return new Result().error(e.getMessage()); + } } @PutMapping @ApiOperation("修改") public Result update(@RequestBody MyDigitalHumanDTO dto){ - - MyDigitalHumanDTO ret = myDigitalHumanService.genVideo(dto); - //myDigitalHumanService.update(dto); - - return new Result().ok(ret); + try{ + MyDigitalHumanDTO ret = myDigitalHumanService.genVideo(dto); + //myDigitalHumanService.update(dto); + return new Result().ok(ret); + }catch(RuntimeException e){ + return new Result().error(e.getMessage()); + } } @DeleteMapping diff --git a/flx-ai/src/main/java/com/pjilisense/flxai/dto/DigitalImgDTO.java b/flx-ai/src/main/java/com/pjilisense/flxai/dto/DigitalImgDTO.java index 283e3e9..e5d7066 100644 --- a/flx-ai/src/main/java/com/pjilisense/flxai/dto/DigitalImgDTO.java +++ b/flx-ai/src/main/java/com/pjilisense/flxai/dto/DigitalImgDTO.java @@ -31,6 +31,4 @@ public class DigitalImgDTO implements Serializable { @ApiModelProperty(value = "图片类型1数字人形象2数字人场景3数字人背景4音色形象") private String imgType; - @ApiModelProperty(value = "音频在服务器的目录") - private String voiceServerDir; } \ No newline at end of file diff --git a/flx-ai/src/main/java/com/pjilisense/flxai/dto/MyDigitalHumanDTO.java b/flx-ai/src/main/java/com/pjilisense/flxai/dto/MyDigitalHumanDTO.java index 7a27ab4..fd7c9d7 100644 --- a/flx-ai/src/main/java/com/pjilisense/flxai/dto/MyDigitalHumanDTO.java +++ b/flx-ai/src/main/java/com/pjilisense/flxai/dto/MyDigitalHumanDTO.java @@ -46,5 +46,6 @@ public class MyDigitalHumanDTO implements Serializable { @ApiModelProperty(value = "声音存放目录") private String voicedir; - + @ApiModelProperty(value = "声音文本") + private String text; } \ No newline at end of file diff --git a/flx-ai/src/main/java/com/pjilisense/flxai/dto/VoiceRefFilesDTO.java b/flx-ai/src/main/java/com/pjilisense/flxai/dto/VoiceRefFilesDTO.java index e04def0..b368923 100644 --- a/flx-ai/src/main/java/com/pjilisense/flxai/dto/VoiceRefFilesDTO.java +++ b/flx-ai/src/main/java/com/pjilisense/flxai/dto/VoiceRefFilesDTO.java @@ -34,5 +34,6 @@ public class VoiceRefFilesDTO implements Serializable { @ApiModelProperty(value = "语言") private String language; - + @ApiModelProperty(value = "音频在服务器的目录") + private String voiceServerDir; } \ No newline at end of file diff --git a/flx-ai/src/main/java/com/pjilisense/flxai/entity/DigitalImgEntity.java b/flx-ai/src/main/java/com/pjilisense/flxai/entity/DigitalImgEntity.java index 7a0dce5..4bd7491 100644 --- a/flx-ai/src/main/java/com/pjilisense/flxai/entity/DigitalImgEntity.java +++ b/flx-ai/src/main/java/com/pjilisense/flxai/entity/DigitalImgEntity.java @@ -33,9 +33,5 @@ public class DigitalImgEntity { */ private String imgType; - /** - * 音频在服务器的目录 - */ - private String voiceServerDir; } \ No newline at end of file diff --git a/flx-ai/src/main/java/com/pjilisense/flxai/entity/VoiceRefFilesEntity.java b/flx-ai/src/main/java/com/pjilisense/flxai/entity/VoiceRefFilesEntity.java index 84caae0..ac498f7 100644 --- a/flx-ai/src/main/java/com/pjilisense/flxai/entity/VoiceRefFilesEntity.java +++ b/flx-ai/src/main/java/com/pjilisense/flxai/entity/VoiceRefFilesEntity.java @@ -35,4 +35,8 @@ public class VoiceRefFilesEntity { * 语言 */ private String language; + /** + * 音频在服务器的目录 + */ + private String voiceServerDir; } \ No newline at end of file diff --git a/flx-ai/src/main/java/com/pjilisense/flxai/service/impl/MyDigitalHumanServiceImpl.java b/flx-ai/src/main/java/com/pjilisense/flxai/service/impl/MyDigitalHumanServiceImpl.java index ef3cc2d..68c5601 100644 --- a/flx-ai/src/main/java/com/pjilisense/flxai/service/impl/MyDigitalHumanServiceImpl.java +++ b/flx-ai/src/main/java/com/pjilisense/flxai/service/impl/MyDigitalHumanServiceImpl.java @@ -4,12 +4,20 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.pjilisense.flxai.base.service.impl.CrudServiceImpl; import com.pjilisense.flxai.dao.MyDigitalHumanDao; import com.pjilisense.flxai.dto.MyDigitalHumanDTO; +import com.pjilisense.flxai.dto.VoiceRefFilesDTO; import com.pjilisense.flxai.entity.MyDigitalHumanEntity; import com.pjilisense.flxai.service.MyDigitalHumanService; import cn.hutool.core.util.StrUtil; +import com.pjilisense.flxai.service.VoiceRefFilesService; +import com.pjilisense.flxai.utils.FileUtil; +import com.pjilisense.flxai.utils.TtsUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.Map; +import java.util.UUID; /** * ${comments} @@ -20,6 +28,15 @@ import java.util.Map; @Service public class MyDigitalHumanServiceImpl extends CrudServiceImpl implements MyDigitalHumanService { + @Autowired + VoiceRefFilesService voiceRefFilesService; + + @Autowired + TtsUtils ttsUtils; + + @Autowired + FileUtil fileUtil; + @Override public QueryWrapper getWrapper(Map params){ String id = (String)params.get("id"); @@ -34,7 +51,24 @@ public class MyDigitalHumanServiceImpl extends CrudServiceImpl0){ update(dto); } else { diff --git a/flx-ai/src/main/java/com/pjilisense/flxai/utils/OkHttpUtils.java b/flx-ai/src/main/java/com/pjilisense/flxai/utils/OkHttpUtils.java new file mode 100644 index 0000000..63f6480 --- /dev/null +++ b/flx-ai/src/main/java/com/pjilisense/flxai/utils/OkHttpUtils.java @@ -0,0 +1,346 @@ +package com.pjilisense.flxai.utils; + +import com.alibaba.fastjson.JSON; +import okhttp3.*; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URLEncoder; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +public class OkHttpUtils { + private static volatile OkHttpClient okHttpClient = null; + private static volatile Semaphore semaphore = null; + private Map headerMap; + private Map paramMap; + private String json; + private String url; + private Request.Builder request; + + /** + * 初始化okHttpClient,并且允许https访问 + */ + private OkHttpUtils() { + if (okHttpClient == null) { + synchronized (OkHttpUtils.class) { + if (okHttpClient == null) { + TrustManager[] trustManagers = buildTrustManagers(); + okHttpClient = new OkHttpClient.Builder() + .connectTimeout(500, TimeUnit.SECONDS) + .writeTimeout(500, TimeUnit.SECONDS) + .readTimeout(500, TimeUnit.SECONDS) + .sslSocketFactory(createSSLSocketFactory(trustManagers), (X509TrustManager) trustManagers[0]) + .hostnameVerifier((hostName, session) -> true) + .retryOnConnectionFailure(true) + .build(); + addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"); + } + } + } + } + + /** + * 用于异步请求时,控制访问线程数,返回结果 + * + * @return + */ + private static Semaphore getSemaphoreInstance() { + //只能1个线程同时访问 + synchronized (OkHttpUtils.class) { + if (semaphore == null) { + semaphore = new Semaphore(0); + } + } + return semaphore; + } + + /** + * 创建OkHttpUtils + * + * @return + */ + public static OkHttpUtils builder() { + return new OkHttpUtils(); + } + + /** + * 添加url + * + * @param url + * @return + */ + public OkHttpUtils url(String url) { + this.url = url; + return this; + } + + /** + * 添加参数 + * + * @param key 参数名 + * @param value 参数值 + * @return + */ + public OkHttpUtils addParam(String key, Object value) { + if (paramMap == null) { + paramMap = new LinkedHashMap<>(16); + } + paramMap.put(key, value); + return this; + } + + /** + * 添加参数 + * + * @param json json + * @return + */ + public OkHttpUtils addParam(String json) { + this.json = json; + return this; + } + + /** + * 添加请求头 + * + * @param key 参数名 + * @param value 参数值 + * @return + */ + public OkHttpUtils addHeader(String key, String value) { + if (headerMap == null) { + headerMap = new LinkedHashMap<>(16); + } + headerMap.put(key, value); + return this; + } + + /** + * 初始化get方法 + * + * @return + */ + public OkHttpUtils get() { + request = new Request.Builder().get(); + StringBuilder urlBuilder = new StringBuilder(url); + if (paramMap != null) { + urlBuilder.append("?"); + try { + for (Map.Entry entry : paramMap.entrySet()) { + urlBuilder.append(URLEncoder.encode(entry.getKey(), "utf-8")). + append("="). + append(URLEncoder.encode(String.valueOf(entry.getValue()), "utf-8")). + append("&"); + } + } catch (Exception e) { + e.printStackTrace(); + } + urlBuilder.deleteCharAt(urlBuilder.length() - 1); + } + request.url(urlBuilder.toString()); + return this; + } + + /** + * 初始化post方法 + * + * @param isJsonPost true等于json的方式提交数据,类似postman里post方法的raw + * false等于普通的表单提交 + * @return + */ + public OkHttpUtils post(boolean isJsonPost) { + RequestBody requestBody = null; + if (isJsonPost) { + String json = ""; + if (paramMap != null) { + json = JSON.toJSONString(paramMap); + } else { + json = this.json; + } + requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json); + } else { + FormBody.Builder formBody = new FormBody.Builder(); + if (paramMap != null) { + for (String key : paramMap.keySet()) { + formBody.add(key, String.valueOf(paramMap.get(key))); + } + } + requestBody = formBody.build(); + } +// } + request = new Request.Builder().post(requestBody).url(url); + return this; + } + + /** + * 同步请求 + * + * @return + */ + public String sync() { + setHeader(request); + try { + Response response = okHttpClient.newCall(request.build()).execute(); + assert response.body() != null; + String retVal =response.body().string(); + response.close(); + return retVal; + } catch (IOException e) { + e.printStackTrace(); + return "请求失败:" + e.getMessage(); + } + } + + /** + * 同步请求 + * + * @return + */ + public boolean syncwav(String destFile) { + setHeader(request); + try { + Response response = okHttpClient.newCall(request.build()).execute(); + assert response.body() != null; + ResponseBody body = response.body(); + java.io.InputStream inputStream = body.byteStream(); + byte[] bytes = new byte[8192]; + int readCount=-1; + File f= new File(destFile); + if(f.exists()) { + f.delete(); + } + FileOutputStream fso = new FileOutputStream(f); + while((readCount=inputStream.read(bytes))!=-1){ + fso.write(bytes,0,readCount); + } + fso.close(); + response.close(); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + /** + * 异步请求,有返回值 + */ + public String async() { + StringBuilder buffer = new StringBuilder(""); + setHeader(request); + okHttpClient.newCall(request.build()).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + buffer.append("请求出错:").append(e.getMessage()); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + assert response.body() != null; + buffer.append(response.body().string()); + getSemaphoreInstance().release(); + } + }); + try { + getSemaphoreInstance().acquire(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return buffer.toString(); + } + + /** + * 异步请求,带有接口回调 + * + * @param callBack + */ + public void async(ICallBack callBack) { + setHeader(request); + okHttpClient.newCall(request.build()).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + callBack.onFailure(call, e.getMessage()); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + assert response.body() != null; + callBack.onSuccessful(call, response.body().string()); + } + }); + } + + /** + * 为request添加请求头 + * + * @param request + */ + private void setHeader(Request.Builder request) { + if (headerMap != null) { + try { + for (Map.Entry entry : headerMap.entrySet()) { + request.addHeader(entry.getKey(), entry.getValue()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + + /** + * 生成安全套接字工厂,用于https请求的证书跳过 + * + * @return + */ + private static SSLSocketFactory createSSLSocketFactory(TrustManager[] trustAllCerts) { + SSLSocketFactory ssfFactory = null; + try { + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new SecureRandom()); + ssfFactory = sc.getSocketFactory(); + } catch (Exception e) { + e.printStackTrace(); + } + return ssfFactory; + } + + private static TrustManager[] buildTrustManagers() { + return new TrustManager[]{ + new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } + }; + } + + /** + * 自定义一个接口回调 + */ + public interface ICallBack { + + void onSuccessful(Call call, String data); + + void onFailure(Call call, String errorMsg); + + } +} \ No newline at end of file diff --git a/flx-ai/src/main/java/com/pjilisense/flxai/utils/TtsUtils.java b/flx-ai/src/main/java/com/pjilisense/flxai/utils/TtsUtils.java new file mode 100644 index 0000000..6506d9d --- /dev/null +++ b/flx-ai/src/main/java/com/pjilisense/flxai/utils/TtsUtils.java @@ -0,0 +1,103 @@ +package com.pjilisense.flxai.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Repository; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Map; + +@Repository +public class TtsUtils { + + @Value("${tts.url.server}") + private String serverUrl;//http://192.168.200.152:8080/v1/tts + + @Value("${tts.url.path}") + private String path; + public boolean sendText(String text,String voiceId,String destFile) { + try { + long ts = System.currentTimeMillis() / 1000; + long start = System.currentTimeMillis(); + boolean result = OkHttpUtils.builder() + .url(serverUrl+path) + .addParam(genBodyJson(text,voiceId)) + .addHeader("timestamp", String.valueOf(ts)) + .addHeader("Content-Type", " application/json;charset=utf-8") + .post(true).syncwav(destFile); + System.out.println("result="+result); + long end = System.currentTimeMillis(); + return result; + }catch(Exception e){ + return false; + } + } + public String genBodyJson(String text,String voiceId) { + LinkedHashMap postParam = new LinkedHashMap<>(); + postParam.put("text", text); + postParam.put("chunk_length", 200); + postParam.put("format", "wav"); + postParam.put("references", new ArrayList()); + postParam.put("reference_id", voiceId); + postParam.put("seed", null); + postParam.put("use_memory_cache", "never"); + postParam.put("normalize", true); + postParam.put("opus_bitrate", -1000); + postParam.put("latency", "normal"); + postParam.put("streaming", false); + postParam.put("max_new_tokens", 1024); + postParam.put("top_p", 0.7); + postParam.put("repetition_penalty", 1.2); + postParam.put("temperature", 0.7); + String json = JSON.toJSONString(postParam); +// parser.add_argument( +// "--reference_audio", +// "-ra", +// type=str, +// nargs="+", +// default=None, +// help="Path to the audio file", +// ) +// parser.add_argument( +// "--reference_text", +// "-rt", +// type=str, +// nargs="+", +// default=None, +// help="Reference text for voice synthesis", +// ) +// parser.add_argument( +// "--output", +// "-o", +// type=str, +// default="generated_audio", +// help="Output audio file name", +// ) + return json; + } + public String hmacSHA256(String appKey, String data) { + try { + // Create HMAC-SHA256 key from the given secret + SecretKeySpec secretKeySpec = new SecretKeySpec(appKey.getBytes(), "HmacSHA256"); + // Get an instance of Mac object implementing HMAC-SHA256 + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(secretKeySpec); + // Calculate the HMAC value + byte[] hmacBytes = mac.doFinal(data.getBytes()); + // Convert result into a hexadecimal string + StringBuilder sb = new StringBuilder(hmacBytes.length * 2); + for (byte b : hmacBytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new RuntimeException("Failed to calculate HMAC-SHA256", e); + } + } +} diff --git a/flx-ai/src/main/resources/application.yml b/flx-ai/src/main/resources/application.yml index b263511..a739679 100644 --- a/flx-ai/src/main/resources/application.yml +++ b/flx-ai/src/main/resources/application.yml @@ -68,3 +68,7 @@ file: ## Linux系统下访问路径 uploadLinux: /user/img/ uploadUrl: /upload/ +tts: + url: + server: http://192.168.200.152:8080 + path: /v1/tts \ No newline at end of file diff --git a/flx-ai/src/main/resources/mapper/postgres/DigitalImgDao.xml b/flx-ai/src/main/resources/mapper/postgres/DigitalImgDao.xml index 2588a5d..47d6723 100644 --- a/flx-ai/src/main/resources/mapper/postgres/DigitalImgDao.xml +++ b/flx-ai/src/main/resources/mapper/postgres/DigitalImgDao.xml @@ -8,7 +8,7 @@ - + diff --git a/flx-ai/src/main/resources/mapper/postgres/VoiceRefFilesDao.xml b/flx-ai/src/main/resources/mapper/postgres/VoiceRefFilesDao.xml index e860c06..0cba2f6 100644 --- a/flx-ai/src/main/resources/mapper/postgres/VoiceRefFilesDao.xml +++ b/flx-ai/src/main/resources/mapper/postgres/VoiceRefFilesDao.xml @@ -9,13 +9,15 @@ + - - - - - + + + + + + \ No newline at end of file