feat: voice clone (#47)

This commit is contained in:
Lighthousexx 2025-09-09 10:12:00 +08:00 committed by GitHub
parent cd4e1f1b11
commit 86111526b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 56 additions and 52 deletions

View file

@ -25,15 +25,15 @@ public interface VoiceCloneApi {
* synthesize speech matching the characteristics of the provided sample.
* @param request voice clone creation parameters including voice name, sample text,
* target text, and audio file reference
* @return voice clone creation result with voice ID and preview file information
* @return voice clone creation result with voice and preview file information
*/
@POST("voice/clone")
Single<VoiceCloneResult> cloneVoice(@Body VoiceCloneRequest request);
/**
* Deletes an existing voice clone by voice ID. Permanently removes the voice model
* and associated data from the system. This operation cannot be undone.
* @param request voice deletion parameters containing the voice ID to delete
* Deletes an existing voice clone by voice. Permanently removes the voice model and
* associated data from the system. This operation cannot be undone.
* @param request voice deletion parameters containing the voice to delete
* @return voice deletion result with confirmation and deletion timestamp
*/
@POST("voice/delete")
@ -41,8 +41,8 @@ public interface VoiceCloneApi {
/**
* Retrieves a list of available voice clones with optional filtering. Returns
* metadata and details for voice clones including voice IDs, names, types, download
* URLs, and creation timestamps.
* metadata and details for voice clones including voice, names, types, download URLs,
* and creation timestamps.
* @param voiceType optional voice type filter
* @param voiceName optional voice name filter
* @return list of voice clone data with comprehensive metadata
@ -52,7 +52,7 @@ public interface VoiceCloneApi {
/**
* Retrieves a list of available voice clones with optional filtering and Request-Id
* header. Returns metadata and details for voice clones including voice IDs, names,
* header. Returns metadata and details for voice clones including voice, names,
* types, download URLs, and creation timestamps.
* @param voiceType optional voice type filter
* @param voiceName optional voice name filter

View file

@ -20,15 +20,19 @@ public class VoiceCloneRequest extends CommonRequest implements ClientRequest<Vo
private String voiceName;
/** Text content corresponding to the sample audio */
@JsonProperty("voice_text_input")
private String voiceTextInput;
@JsonProperty("text")
private String text;
/** Target text for preview audio */
@JsonProperty("voice_text_output")
private String voiceTextOutput;
@JsonProperty("input")
private String input;
/** File ID of the audio file */
@JsonProperty("file_id")
private String fileId;
/** Model */
@JsonProperty("model")
private String model;
}

View file

@ -8,7 +8,7 @@ import lombok.NoArgsConstructor;
/**
* Result data for voice cloning operations. This class contains the voice cloning result
* information including voice ID, audio file ID, and file purpose.
* information including voice, audio file ID, and file purpose.
*/
@Data
@Builder
@ -16,9 +16,9 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class VoiceCloneResult {
/** Voice ID */
@JsonProperty("voice_id")
private String voiceId;
/** Voice */
@JsonProperty("voice")
private String voice;
/** Audio preview file ID */
@JsonProperty("file_id")

View file

@ -15,8 +15,8 @@ public interface VoiceCloneService {
VoiceCloneResponse cloneVoice(VoiceCloneRequest request);
/**
* Deletes an existing voice clone by voice ID.
* @param request the voice deletion request containing the voice ID to delete
* Deletes an existing voice clone by voice.
* @param request the voice deletion request containing the voice to delete
* @return VoiceDeleteResponse containing the deletion result and timestamp
*/
VoiceDeleteResponse deleteVoice(VoiceDeleteRequest request);

View file

@ -58,8 +58,8 @@ public class VoiceCloneServiceImpl implements VoiceCloneService {
if (request.getVoiceName() == null || request.getVoiceName().trim().isEmpty()) {
throw new IllegalArgumentException("voice name cannot be null or empty");
}
if (request.getVoiceTextInput() == null || request.getVoiceTextInput().trim().isEmpty()) {
throw new IllegalArgumentException("voice text input cannot be null or empty");
if (request.getInput() == null || request.getInput().trim().isEmpty()) {
throw new IllegalArgumentException("input cannot be null or empty");
}
if (request.getFileId() == null || request.getFileId().trim().isEmpty()) {
throw new IllegalArgumentException("file ID cannot be null or empty");
@ -70,8 +70,8 @@ public class VoiceCloneServiceImpl implements VoiceCloneService {
if (request == null) {
throw new IllegalArgumentException("request cannot be null");
}
if (request.getVoiceId() == null || request.getVoiceId().trim().isEmpty()) {
throw new IllegalArgumentException("voice ID cannot be null or empty");
if (request.getVoice() == null || request.getVoice().trim().isEmpty()) {
throw new IllegalArgumentException("voice cannot be null or empty");
}
}

View file

@ -8,13 +8,13 @@ import java.util.Date;
/**
* Voice data entity containing voice information. This class contains voice details
* including voice ID, name, type, download URL, and creation time.
* including voice, name, type, download URL, and creation time.
*/
@Data
public class VoiceData {
@JsonProperty("voice_id")
private String voiceId;
@JsonProperty("voice")
private String voice;
@JsonProperty("voice_name")
private String voiceName;

View file

@ -8,13 +8,13 @@ import lombok.EqualsAndHashCode;
/**
* Request parameters for voice deletion API. This class contains the necessary parameters
* for deleting a voice, specifically the voice ID.
* for deleting a voice, specifically the voice.
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class VoiceDeleteRequest extends CommonRequest implements ClientRequest<VoiceDeleteRequest> {
@JsonProperty("voice_id")
private String voiceId;
@JsonProperty("voice")
private String voice;
}

View file

@ -12,7 +12,7 @@ import java.util.Date;
/**
* Result data for voice deletion operations. This class contains the deletion result
* information including voice ID and deletion timestamp.
* information including voice and deletion timestamp.
*/
@Data
@Builder
@ -20,11 +20,11 @@ import java.util.Date;
@NoArgsConstructor
public class VoiceDeleteResult {
@JsonProperty("voice_id")
private String voiceId;
@JsonProperty("voice")
private String voice;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonProperty("delete_time")
private Date deleteTime;
@JsonProperty("update_time")
private Date updateTime;
}

View file

@ -15,8 +15,6 @@ import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.*;
/**
@ -73,9 +71,10 @@ public class VoiceCloneServiceTest {
// Now we can use the uploaded file ID to create a voice clone
VoiceCloneRequest request = new VoiceCloneRequest();
request.setVoiceName("Test");
request.setVoiceTextInput("This is sample text for voice cloning training");
request.setVoiceTextOutput("This is target text for voice preview generation");
request.setText("This is sample text for voice cloning training");
request.setInput("This is target text for voice preview generation");
request.setFileId(fileId); // Use the actual file ID from upload
request.setModel("CogTTS-clone");
// Execute voice cloning
VoiceCloneResponse response = voiceCloneService.cloneVoice(request);
@ -112,10 +111,10 @@ public class VoiceCloneServiceTest {
@DisplayName("Test voice deletion")
@EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
void testDeleteVoice() throws JsonProcessingException {
// Create a voice deletion request with a test voice ID
// Create a voice deletion request with a test voice
// In a real scenario, this would be an ID from a previously created voice clone
VoiceDeleteRequest request = new VoiceDeleteRequest();
request.setVoiceId("Test voice Id");
request.setVoice("Test voice");
// Execute the voice deletion
VoiceDeleteResponse response = voiceCloneService.deleteVoice(request);

View file

@ -75,17 +75,18 @@ public class VoiceCloneExample {
// Step 2: Create voice clone using the uploaded file
VoiceCloneRequest request = new VoiceCloneRequest();
request.setVoiceName("My Custom Voice");
request.setVoiceTextInput("Hello, this is a sample text for voice cloning training");
request.setVoiceTextOutput("Welcome to our voice synthesis system");
request.setText("Hello, this is a sample text for voice cloning training");
request.setInput("Welcome to our voice synthesis system");
request.setFileId(fileId);
request.setRequestId("clone-request-" + System.currentTimeMillis());
request.setModel("CogTTS-clone");
System.out.println("Creating voice clone...");
VoiceCloneResponse response = voiceCloneService.cloneVoice(request);
if (response.isSuccess()) {
System.out.println("Voice clone created successfully!");
System.out.println("Voice ID: " + response.getData().getVoiceId());
System.out.println("Voice: " + response.getData().getVoice());
} else {
System.err.println("Voice clone creation failed: " + response.getMsg());
if (response.getError() != null) {
@ -118,7 +119,7 @@ public class VoiceCloneExample {
if (response.getData().getVoiceList() != null && !response.getData().getVoiceList().isEmpty()) {
System.out.println("Found " + response.getData().getVoiceList().size() + " voices:");
for (VoiceData voice : response.getData().getVoiceList()) {
System.out.println("- Voice ID: " + voice.getVoiceId());
System.out.println("- Voice: " + voice.getVoice());
System.out.println(" Name: " + voice.getVoiceName());
System.out.println(" Type: " + voice.getVoiceType());
if (voice.getDownloadUrl() != null) {
@ -142,24 +143,24 @@ public class VoiceCloneExample {
/**
* Example of deleting a voice clone
* Note: Replace "your-voice-id" with an actual voice ID from your account
* Note: Replace "your-voice" with an actual voice from your account
*/
private static void deleteVoiceExample(VoiceCloneService voiceCloneService) {
try {
// Note: This is just an example - replace with actual voice ID
String voiceIdToDelete = "your-voice-id-here";
// Note: This is just an example - replace with actual voice
String voiceToDelete = "your-voice-here";
VoiceDeleteRequest request = new VoiceDeleteRequest();
request.setVoiceId(voiceIdToDelete);
request.setVoice(voiceToDelete);
request.setRequestId("delete-request-" + System.currentTimeMillis());
System.out.println("Deleting voice clone with ID: " + voiceIdToDelete);
System.out.println("Deleting voice: " + voiceToDelete);
VoiceDeleteResponse response = voiceCloneService.deleteVoice(request);
if (response.isSuccess()) {
System.out.println("Voice clone deleted successfully!");
if (response.getData().getDeleteTime() != null) {
System.out.println("Deletion time: " + response.getData().getDeleteTime());
if (response.getData().getUpdateTime() != null) {
System.out.println("Deletion time: " + response.getData().getUpdateTime());
}
} else {
System.err.println("Voice deletion failed: " + response.getMsg());
@ -168,9 +169,9 @@ public class VoiceCloneExample {
}
}
} catch (Exception e) {
// Expected to fail with example voice ID
System.out.println("Note: This example uses a placeholder voice ID and is expected to fail.");
System.out.println("Replace 'your-voice-id-here' with an actual voice ID to test deletion.");
// Expected to fail with example voice
System.out.println("Note: This example uses a placeholder voice and is expected to fail.");
System.out.println("Replace 'your-voice-here' with an actual voice to test deletion.");
}
}
}