|
@@ -319,12 +319,17 @@ public class XfVoiceCloneController extends BaseController {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private String synthesizeCloneAudio(JSONObject account, String assetId, String text, String engineVersion) throws Exception {
|
|
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);
|
|
CountDownLatch latch = new CountDownLatch(1);
|
|
|
AtomicReference<String> errRef = new AtomicReference<>();
|
|
AtomicReference<String> errRef = new AtomicReference<>();
|
|
|
ByteArrayOutputStream audioOutput = new ByteArrayOutputStream();
|
|
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() {
|
|
HTTP_CLIENT.newWebSocket(request, new WebSocketListener() {
|
|
|
@Override
|
|
@Override
|
|
|
public void onOpen(WebSocket webSocket, Response response) {
|
|
public void onOpen(WebSocket webSocket, Response response) {
|
|
@@ -438,7 +443,7 @@ public class XfVoiceCloneController extends BaseController {
|
|
|
return root;
|
|
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 date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneId.of("GMT")));
|
|
|
String signatureOrigin = "host: " + CLONE_TTS_HOST + "\n" +
|
|
String signatureOrigin = "host: " + CLONE_TTS_HOST + "\n" +
|
|
|
"date: " + date + "\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\"",
|
|
String authorizationOrigin = String.format("api_key=\"%s\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"%s\"",
|
|
|
apiKey, signature);
|
|
apiKey, signature);
|
|
|
String authorization = Base64.getEncoder().encodeToString(authorizationOrigin.getBytes(StandardCharsets.UTF_8));
|
|
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) +
|
|
"&date=" + urlEncode(date) +
|
|
|
"&host=" + urlEncode(CLONE_TTS_HOST);
|
|
"&host=" + urlEncode(CLONE_TTS_HOST);
|
|
|
|
|
+ return new CloneWebsocketAuth(url, date);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private String buildWebsocketFailureMessage(Throwable t, Response response) {
|
|
private String buildWebsocketFailureMessage(Throwable t, Response response) {
|
|
@@ -468,7 +474,12 @@ public class XfVoiceCloneController extends BaseController {
|
|
|
sb.append(", body=").append(bodyText);
|
|
sb.append(", body=").append(bodyText);
|
|
|
}
|
|
}
|
|
|
if (response.code() == 403) {
|
|
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())) {
|
|
} else if (t != null && StringUtils.isNotBlank(t.getMessage())) {
|
|
|
sb.append(": ").append(t.getMessage());
|
|
sb.append(": ").append(t.getMessage());
|
|
@@ -476,6 +487,16 @@ public class XfVoiceCloneController extends BaseController {
|
|
|
return sb.toString();
|
|
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) {
|
|
private Map<String, String> buildVoiceTrainHeaders(String apiKey, String appId, String token, String bodyText) {
|
|
|
Map<String, String> headers = new LinkedHashMap<>();
|
|
Map<String, String> headers = new LinkedHashMap<>();
|
|
|
String requestTime = String.valueOf(System.currentTimeMillis());
|
|
String requestTime = String.valueOf(System.currentTimeMillis());
|