yzx před 6 dny
rodič
revize
9306198e9e

+ 26 - 5
ruoyi-admin/src/main/java/com/ruoyi/aicall/controller/XfVoiceCloneController.java

@@ -319,12 +319,17 @@ public class XfVoiceCloneController extends BaseController {
     }
 
     private String synthesizeCloneAudio(JSONObject account, String assetId, String text, String engineVersion) throws Exception {
-        String authUrl = buildCloneWebsocketUrl(account.getString("apiKey"), account.getString("apiSecret"));
+        CloneWebsocketAuth auth = buildCloneWebsocketAuth(account.getString("apiKey"), account.getString("apiSecret"));
         CountDownLatch latch = new CountDownLatch(1);
         AtomicReference<String> errRef = new AtomicReference<>();
         ByteArrayOutputStream audioOutput = new ByteArrayOutputStream();
 
-        Request request = new Request.Builder().url(authUrl).build();
+        Request request = new Request.Builder()
+                .url(auth.url)
+                .addHeader("Host", CLONE_TTS_HOST)
+                .addHeader("Date", auth.date)
+                .addHeader("X-Date", auth.date)
+                .build();
         HTTP_CLIENT.newWebSocket(request, new WebSocketListener() {
             @Override
             public void onOpen(WebSocket webSocket, Response response) {
@@ -438,7 +443,7 @@ public class XfVoiceCloneController extends BaseController {
         return root;
     }
 
-    private String buildCloneWebsocketUrl(String apiKey, String apiSecret) throws Exception {
+    private CloneWebsocketAuth buildCloneWebsocketAuth(String apiKey, String apiSecret) throws Exception {
         String date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneId.of("GMT")));
         String signatureOrigin = "host: " + CLONE_TTS_HOST + "\n" +
                 "date: " + date + "\n" +
@@ -449,9 +454,10 @@ public class XfVoiceCloneController extends BaseController {
         String authorizationOrigin = String.format("api_key=\"%s\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"%s\"",
                 apiKey, signature);
         String authorization = Base64.getEncoder().encodeToString(authorizationOrigin.getBytes(StandardCharsets.UTF_8));
-        return CLONE_TTS_WS_URL + "?authorization=" + urlEncode(authorization) +
+        String url = CLONE_TTS_WS_URL + "?authorization=" + urlEncode(authorization) +
                 "&date=" + urlEncode(date) +
                 "&host=" + urlEncode(CLONE_TTS_HOST);
+        return new CloneWebsocketAuth(url, date);
     }
 
     private String buildWebsocketFailureMessage(Throwable t, Response response) {
@@ -468,7 +474,12 @@ public class XfVoiceCloneController extends BaseController {
                 sb.append(", body=").append(bodyText);
             }
             if (response.code() == 403) {
-                sb.append("。请确认当前 app-id/api-key/api-secret 是否属于“讯飞一句话复刻”服务应用,且该应用已开通 voice_clone/一句话复刻权限;普通在线TTS应用凭证无法调用该接口。");
+                if (StringUtils.containsIgnoreCase(bodyText, "valid date or x-date")
+                        || StringUtils.containsIgnoreCase(bodyText, "HMAC signature cannot be verified")) {
+                    sb.append("。当前更像是 WebSocket 时间头/签名校验失败,请先确认部署机器系统时间准确,并已同步标准时间;本次请求已自动补充 Date/X-Date 头,如仍失败,再检查当前 app-id/api-key/api-secret 是否属于“一句话复刻/voice_clone”服务应用。");
+                } else {
+                    sb.append("。请确认当前 app-id/api-key/api-secret 是否属于“讯飞一句话复刻”服务应用,且该应用已开通 voice_clone/一句话复刻权限;普通在线TTS应用凭证无法调用该接口。");
+                }
             }
         } else if (t != null && StringUtils.isNotBlank(t.getMessage())) {
             sb.append(": ").append(t.getMessage());
@@ -476,6 +487,16 @@ public class XfVoiceCloneController extends BaseController {
         return sb.toString();
     }
 
+    private static class CloneWebsocketAuth {
+        private final String url;
+        private final String date;
+
+        private CloneWebsocketAuth(String url, String date) {
+            this.url = url;
+            this.date = date;
+        }
+    }
+
     private Map<String, String> buildVoiceTrainHeaders(String apiKey, String appId, String token, String bodyText) {
         Map<String, String> headers = new LinkedHashMap<>();
         String requestTime = String.valueOf(System.currentTimeMillis());