feat: voiceclone implement comprehensive voice cloning service with full API support (#38)

Co-authored-by: tomsun28 <tomsun28@outlook.com>
This commit is contained in:
Lighthousexx 2025-08-27 11:36:59 +08:00 committed by GitHub
parent 6430107d74
commit ce66a0142c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 789 additions and 1 deletions

View file

@ -23,6 +23,8 @@ import ai.z.openapi.service.videos.VideosService;
import ai.z.openapi.service.videos.VideosServiceImpl; import ai.z.openapi.service.videos.VideosServiceImpl;
import ai.z.openapi.service.assistant.AssistantService; import ai.z.openapi.service.assistant.AssistantService;
import ai.z.openapi.service.assistant.AssistantServiceImpl; import ai.z.openapi.service.assistant.AssistantServiceImpl;
import ai.z.openapi.service.voiceclone.VoiceCloneService;
import ai.z.openapi.service.voiceclone.VoiceCloneServiceImpl;
import ai.z.openapi.core.config.ZaiConfig; import ai.z.openapi.core.config.ZaiConfig;
import ai.z.openapi.core.model.BiFlowableClientResponse; import ai.z.openapi.core.model.BiFlowableClientResponse;
import ai.z.openapi.core.model.ClientRequest; import ai.z.openapi.core.model.ClientRequest;
@ -98,6 +100,9 @@ public abstract class AbstractAiClient extends AbstractClientBaseService {
/** Assistant service for AI assistant functionality */ /** Assistant service for AI assistant functionality */
private AssistantService assistantService; private AssistantService assistantService;
/** Voice clone service for voice cloning operations */
private VoiceCloneService voiceCloneService;
/** /**
* Constructs a new AbstractAiClient with the specified configuration. * Constructs a new AbstractAiClient with the specified configuration.
* @param config the configuration object containing API keys, timeouts, and other * @param config the configuration object containing API keys, timeouts, and other
@ -238,6 +243,18 @@ public abstract class AbstractAiClient extends AbstractClientBaseService {
return assistantService; return assistantService;
} }
/**
* Returns the voice clone service for voice cloning operations. This service handles
* voice cloning creation, deletion, and listing functionality.
* @return the VoiceCloneService instance (lazily initialized)
*/
public synchronized VoiceCloneService voiceClone() {
if (voiceCloneService == null) {
this.voiceCloneService = new VoiceCloneServiceImpl(this);
}
return voiceCloneService;
}
// ==================== Utility Methods ==================== // ==================== Utility Methods ====================
/** /**

View file

@ -0,0 +1,66 @@
package ai.z.openapi.api.voiceclone;
import ai.z.openapi.service.voiceclone.VoiceCloneRequest;
import ai.z.openapi.service.voiceclone.VoiceCloneResult;
import ai.z.openapi.service.voiceclone.VoiceDeleteRequest;
import ai.z.openapi.service.voiceclone.VoiceDeleteResult;
import ai.z.openapi.service.voiceclone.VoiceListResult;
import io.reactivex.rxjava3.core.Single;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.POST;
import retrofit2.http.Query;
/**
* Voice Clone API for voice cloning and management operations. Provides endpoints for
* creating voice clones from audio samples, deleting existing voice clones, and listing
* available voice clones.
*/
public interface VoiceCloneApi {
/**
* Creates a new voice clone from provided audio sample and parameters. Uses advanced
* neural voice cloning technology to generate a custom voice model that can
* 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
*/
@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
* @return voice deletion result with confirmation and deletion timestamp
*/
@POST("voice/delete")
Single<VoiceDeleteResult> deleteVoice(@Body VoiceDeleteRequest request);
/**
* 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.
* @param voiceType optional voice type filter
* @param voiceName optional voice name filter
* @return list of voice clone data with comprehensive metadata
*/
@GET("voice/list")
Single<VoiceListResult> listVoices(@Query("voiceType") String voiceType, @Query("voiceName") String voiceName);
/**
* 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,
* types, download URLs, and creation timestamps.
* @param voiceType optional voice type filter
* @param voiceName optional voice name filter
* @param requestId unique request identifier for tracking and monitoring
* @return list of voice clone data with comprehensive metadata
*/
@GET("voice/list")
Single<VoiceListResult> listVoices(@Query("voiceType") String voiceType, @Query("voiceName") String voiceName,
@Header("Request-Id") String requestId);
}

View file

@ -2,7 +2,8 @@ package ai.z.openapi.service.file;
public enum UploadFilePurpose { public enum UploadFilePurpose {
BATCH("batch"), FILE_EXTRACT("file-extract"), CODE_INTERPRETER("code-interpreter"), AGENT("agent"); BATCH("batch"), FILE_EXTRACT("file-extract"), CODE_INTERPRETER("code-interpreter"), AGENT("agent"),
VOICE_CLONE_INPUT("voice-clone-input");
private final String value; private final String value;

View file

@ -0,0 +1,34 @@
package ai.z.openapi.service.voiceclone;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.service.CommonRequest;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* Request parameters for voice cloning API. This class contains all the necessary
* parameters for voice cloning operations, including voice name, sample audio text,
* target preview text, and audio file information.
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class VoiceCloneRequest extends CommonRequest implements ClientRequest<VoiceCloneRequest> {
/** Voice name */
@JsonProperty("voice_name")
private String voiceName;
/** Text content corresponding to the sample audio */
@JsonProperty("voice_text_input")
private String voiceTextInput;
/** Target text for preview audio */
@JsonProperty("voice_text_output")
private String voiceTextOutput;
/** File ID of the audio file */
@JsonProperty("file_id")
private String fileId;
}

View file

@ -0,0 +1,35 @@
package ai.z.openapi.service.voiceclone;
import ai.z.openapi.core.model.ClientResponse;
import ai.z.openapi.service.batches.BatchPage;
import ai.z.openapi.service.model.ChatError;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* Response wrapper for voice cloning API operations. This class contains the standard
* response structure including status code, message, success flag, result data, and error
* information.
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VoiceCloneResponse implements ClientResponse<VoiceCloneResult> {
private int code;
private String msg;
private boolean success;
private VoiceCloneResult data;
private ChatError error;
}

View file

@ -0,0 +1,31 @@
package ai.z.openapi.service.voiceclone;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
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.
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class VoiceCloneResult {
/** Voice ID */
@JsonProperty("voice_id")
private String voiceId;
/** Audio preview file ID */
@JsonProperty("file_id")
private String fileId;
/** File purpose */
@JsonProperty("file_purpose")
private String filePurpose;
}

View file

@ -0,0 +1,32 @@
package ai.z.openapi.service.voiceclone;
/**
* Voice clone service interface for voice cloning operations. This service provides
* methods for creating, deleting, and listing voice clones.
*/
public interface VoiceCloneService {
/**
* Creates a new voice clone based on the provided audio sample and parameters.
* @param request the voice clone creation request containing voice name, sample audio
* text, target text, and audio file information
* @return VoiceCloneResponse containing the voice clone result and metadata
*/
VoiceCloneResponse cloneVoice(VoiceCloneRequest request);
/**
* Deletes an existing voice clone by voice ID.
* @param request the voice deletion request containing the voice ID to delete
* @return VoiceDeleteResponse containing the deletion result and timestamp
*/
VoiceDeleteResponse deleteVoice(VoiceDeleteRequest request);
/**
* Retrieves a list of available voice clones with optional filtering.
* @param request the voice list request containing optional voice type and name
* filters
* @return VoiceListResponse containing the filtered list of voice data
*/
VoiceListResponse listVoice(VoiceListRequest request);
}

View file

@ -0,0 +1,78 @@
package ai.z.openapi.service.voiceclone;
import ai.z.openapi.AbstractAiClient;
import ai.z.openapi.api.voiceclone.VoiceCloneApi;
import ai.z.openapi.service.deserialize.MessageDeserializeFactory;
import ai.z.openapi.utils.RequestSupplier;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
/**
* Voice clone service implementation for voice cloning operations. This service provides
* methods for creating, deleting, and listing voice clones using the VoiceClone API
* endpoints.
*/
public class VoiceCloneServiceImpl implements VoiceCloneService {
protected static final ObjectMapper mapper = MessageDeserializeFactory.defaultObjectMapper();
private final AbstractAiClient zAiClient;
private final VoiceCloneApi voiceCloneApi;
public VoiceCloneServiceImpl(AbstractAiClient zAiClient) {
this.zAiClient = zAiClient;
this.voiceCloneApi = zAiClient.retrofit().create(VoiceCloneApi.class);
}
@Override
public VoiceCloneResponse cloneVoice(VoiceCloneRequest request) {
validateCreateVoiceParams(request);
RequestSupplier<VoiceCloneRequest, VoiceCloneResult> supplier = voiceCloneApi::cloneVoice;
return this.zAiClient.executeRequest(request, supplier, VoiceCloneResponse.class);
}
@Override
public VoiceDeleteResponse deleteVoice(VoiceDeleteRequest request) {
validateDeleteVoiceParams(request);
RequestSupplier<VoiceDeleteRequest, VoiceDeleteResult> supplier = voiceCloneApi::deleteVoice;
return this.zAiClient.executeRequest(request, supplier, VoiceDeleteResponse.class);
}
@Override
public VoiceListResponse listVoice(VoiceListRequest request) {
RequestSupplier<VoiceListRequest, VoiceListResult> supplier = (params) -> {
String voiceType = params != null ? params.getVoiceType() : null;
String voiceName = params != null ? params.getVoiceName() : null;
String requestId = params != null ? params.getRequestId() : "";
return voiceCloneApi.listVoices(voiceType, voiceName, requestId);
};
return this.zAiClient.executeRequest(request, supplier, VoiceListResponse.class);
}
private void validateCreateVoiceParams(VoiceCloneRequest request) {
if (request == null) {
throw new IllegalArgumentException("request cannot be null");
}
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.getFileId() == null || request.getFileId().trim().isEmpty()) {
throw new IllegalArgumentException("file ID cannot be null or empty");
}
}
private void validateDeleteVoiceParams(VoiceDeleteRequest request) {
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");
}
}
}

View file

@ -0,0 +1,32 @@
package ai.z.openapi.service.voiceclone;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
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.
*/
@Data
public class VoiceData {
@JsonProperty("voice_id")
private String voiceId;
@JsonProperty("voice_name")
private String voiceName;
@JsonProperty("voice_type")
private String voiceType;
@JsonProperty("download_url")
private String downloadUrl;
@JsonProperty("create_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}

View file

@ -0,0 +1,20 @@
package ai.z.openapi.service.voiceclone;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.service.CommonRequest;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* Request parameters for voice deletion API. This class contains the necessary parameters
* for deleting a voice, specifically the voice ID.
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class VoiceDeleteRequest extends CommonRequest implements ClientRequest<VoiceDeleteRequest> {
@JsonProperty("voice_id")
private String voiceId;
}

View file

@ -0,0 +1,31 @@
package ai.z.openapi.service.voiceclone;
import ai.z.openapi.core.model.ClientResponse;
import ai.z.openapi.service.model.ChatError;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Response wrapper for voice deletion API operations. This class contains the standard
* response structure including status code, message, success flag, result data, and error
* information.
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VoiceDeleteResponse implements ClientResponse<VoiceDeleteResult> {
private int code;
private String msg;
private boolean success;
private VoiceDeleteResult data;
private ChatError error;
}

View file

@ -0,0 +1,30 @@
package ai.z.openapi.service.voiceclone;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* Result data for voice deletion operations. This class contains the deletion result
* information including voice ID and deletion timestamp.
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class VoiceDeleteResult {
@JsonProperty("voice_id")
private String voiceId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonProperty("delete_time")
private Date deleteTime;
}

View file

@ -0,0 +1,25 @@
package ai.z.openapi.service.voiceclone;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.service.CommonRequest;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* Request parameters for voice list API. This class contains optional filter parameters
* for retrieving voice clones by type and name.
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class VoiceListRequest extends CommonRequest implements ClientRequest<VoiceListRequest> {
/** Voice type filter"PRIVATE" or "OFFICIAL" or null */
@JsonProperty("voice_type")
private String voiceType;
/** Voice name filter */
@JsonProperty("voice_name")
private String voiceName;
}

View file

@ -0,0 +1,31 @@
package ai.z.openapi.service.voiceclone;
import ai.z.openapi.core.model.ClientResponse;
import ai.z.openapi.service.model.ChatError;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Response wrapper for voice list API operations. This class contains the standard
* response structure including status code, message, success flag, result data, and error
* information.
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VoiceListResponse implements ClientResponse<VoiceListResult> {
private int code;
private String msg;
private boolean success;
private VoiceListResult data;
private ChatError error;
}

View file

@ -0,0 +1,19 @@
package ai.z.openapi.service.voiceclone;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* Result data for voice list operations. This class contains the list of voice data
* returned from voice listing API calls.
*/
@Data
public class VoiceListResult {
@JsonProperty("voice_list")
private List<VoiceData> voiceList;
}

View file

@ -0,0 +1,130 @@
package ai.z.openapi.service.voiceclone;
import ai.z.openapi.ZaiClient;
import ai.z.openapi.core.config.ZaiConfig;
import ai.z.openapi.service.file.FileApiResponse;
import ai.z.openapi.service.file.FileService;
import ai.z.openapi.service.file.FileUploadParams;
import ai.z.openapi.service.file.UploadFilePurpose;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
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.*;
/**
* Test class for VoiceCloneService functionality. Tests voice cloning operations
* including file upload, voice creation, deletion, and listing. Requires ZAI_API_KEY
* environment variable to be set for integration tests.
*/
@DisplayName("VoiceCloneService Tests")
public class VoiceCloneServiceTest {
private static final Logger logger = LoggerFactory.getLogger(VoiceCloneServiceTest.class);
private static final ObjectMapper mapper = new ObjectMapper();
private VoiceCloneService voiceCloneService;
private FileService fileService;
@BeforeEach
void setUp() {
ZaiConfig zaiConfig = new ZaiConfig();
String apiKey = zaiConfig.getApiKey();
if (apiKey == null) {
zaiConfig.setApiKey("id.test-api-key");
}
ZaiClient client = new ZaiClient(zaiConfig);
voiceCloneService = client.voiceClone();
fileService = client.files();
}
@Test
@DisplayName("Test voice clone creation with file upload")
@EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
void testCloneVoice() throws JsonProcessingException {
// Step 1: Upload the voice input audio file
// First, we need to upload the voice sample audio file to get a file ID
String voiceInputFilePath = "src/test/resources/voice_clone_input.mp3";
FileUploadParams uploadRequest = FileUploadParams.builder()
.filePath(voiceInputFilePath)
.purpose(UploadFilePurpose.VOICE_CLONE_INPUT.value())
.requestId("voice-clone-test-" + System.currentTimeMillis())
.build();
FileApiResponse uploadResponse = fileService.uploadFile(uploadRequest);
assertNotNull(uploadResponse, "File upload response should not be null");
assertTrue(uploadResponse.isSuccess(), "File upload should be successful");
assertNotNull(uploadResponse.getData(), "Uploaded file data should not be null");
String fileId = uploadResponse.getData().getId();
logger.info("Voice input file uploaded successfully with ID: {}", fileId);
// Step 2: Create voice clone using the uploaded file
// 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.setFileId(fileId); // Use the actual file ID from upload
// Execute voice cloning
VoiceCloneResponse response = voiceCloneService.cloneVoice(request);
// Verify the voice cloning response
assertNotNull(response, "Voice clone response should not be null");
assertTrue(response.isSuccess(), "Voice cloning should be successful");
assertNotNull(response.getData(), "Voice clone data should not be null");
logger.info("Voice clone created successfully: {}", mapper.writeValueAsString(response));
}
@Test
@DisplayName("Test voice list")
@EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
void testListVoice() throws JsonProcessingException {
// Create a voice list request with filtering parameters
// This will retrieve voices filtered by type and name pattern
VoiceListRequest request = new VoiceListRequest();
request.setVoiceType("PRIVATE"); // Filter for custom voice clones
request.setVoiceName("Test"); // Filter by voice name pattern
// Execute the voice listing
VoiceListResponse response = voiceCloneService.listVoice(request);
// Verify the listing response
logger.info("Voice list response: {}", mapper.writeValueAsString(response));
assertNotNull(response, "Voice list response should not be null");
assertTrue(response.isSuccess(), "Voice listing should be successful");
assertNotNull(response.getData(), "Voice list data should not be null");
logger.info("Voice list retrieved successfully: {}", mapper.writeValueAsString(response));
}
@Test
@DisplayName("Test voice deletion")
@EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
void testDeleteVoice() throws JsonProcessingException {
// Create a voice deletion request with a test voice ID
// In a real scenario, this would be an ID from a previously created voice clone
VoiceDeleteRequest request = new VoiceDeleteRequest();
request.setVoiceId("Test voice Id");
// Execute the voice deletion
VoiceDeleteResponse response = voiceCloneService.deleteVoice(request);
// Verify the deletion response
assertNotNull(response, "Voice deletion response should not be null");
assertTrue(response.isSuccess(), "Voice deletion should be successful");
assertNotNull(response.getData(), "Voice deletion data should not be null");
logger.info("Voice deleted successfully: {}", mapper.writeValueAsString(response));
}
}

Binary file not shown.

View file

@ -0,0 +1,176 @@
package ai.z.openapi.samples;
import ai.z.openapi.ZaiClient;
import ai.z.openapi.service.file.FileApiResponse;
import ai.z.openapi.service.file.FileService;
import ai.z.openapi.service.file.FileUploadParams;
import ai.z.openapi.service.file.UploadFilePurpose;
import ai.z.openapi.service.voiceclone.*;
import java.nio.file.Paths;
/**
* Voice Clone Example
* Demonstrates how to use ZaiClient for voice cloning operations including:
* - Uploading voice samples
* - Creating voice clones
* - Listing existing voices
* - Deleting voice clones
*/
public class VoiceCloneExample {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
// export ZAI_API_KEY=your.api_key
ZaiClient client = ZaiClient.builder().build();
// Or set API Key via code
// ZaiClient client = ZaiClient.builder()
// .apiKey("your.api_key")
// .build();
VoiceCloneService voiceCloneService = client.voiceClone();
FileService fileService = client.files();
try {
// Example 1: Create a voice clone
System.out.println("=== Voice Clone Creation Example ===");
createVoiceCloneExample(voiceCloneService, fileService);
// Example 2: List existing voices
System.out.println("\n=== Voice List Example ===");
listVoicesExample(voiceCloneService);
// Example 3: Delete a voice clone
System.out.println("\n=== Voice Deletion Example ===");
deleteVoiceExample(voiceCloneService);
} catch (Exception e) {
System.err.println("Exception occurred: " + e.getMessage());
e.printStackTrace();
}
}
/**
* Example of creating a voice clone with file upload
*/
private static void createVoiceCloneExample(VoiceCloneService voiceCloneService, FileService fileService) {
try {
// Step 1: Upload the voice input audio file
String voiceInputFilePath = Paths.get("samples", "resources", "voice_clone_input.mp3").toString();
FileUploadParams uploadRequest = FileUploadParams.builder()
.filePath(voiceInputFilePath)
.purpose(UploadFilePurpose.VOICE_CLONE_INPUT.value())
.requestId("voice-clone-example-" + System.currentTimeMillis())
.build();
System.out.println("Uploading voice input file...");
FileApiResponse uploadResponse = fileService.uploadFile(uploadRequest);
if (uploadResponse.isSuccess()) {
String fileId = uploadResponse.getData().getId();
System.out.println("Voice input file uploaded successfully with ID: " + fileId);
// 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.setFileId(fileId);
request.setRequestId("clone-request-" + System.currentTimeMillis());
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());
} else {
System.err.println("Voice clone creation failed: " + response.getMsg());
if (response.getError() != null) {
System.err.println("Error details: " + response.getError().getMessage());
}
}
} else {
System.err.println("File upload failed: " + uploadResponse.getMsg());
}
} catch (Exception e) {
System.err.println("Error in voice clone creation: " + e.getMessage());
}
}
/**
* Example of listing existing voice clones
*/
private static void listVoicesExample(VoiceCloneService voiceCloneService) {
try {
// List all private voices
VoiceListRequest request = new VoiceListRequest();
request.setVoiceType("PRIVATE"); // Filter for custom voice clones
request.setRequestId("list-request-" + System.currentTimeMillis());
System.out.println("Retrieving voice list...");
VoiceListResponse response = voiceCloneService.listVoice(request);
if (response.isSuccess()) {
System.out.println("Voice list retrieved successfully!");
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(" Name: " + voice.getVoiceName());
System.out.println(" Type: " + voice.getVoiceType());
if (voice.getDownloadUrl() != null) {
System.out.println(" Download URL: " + voice.getDownloadUrl());
}
if (voice.getCreateTime() != null) {
System.out.println(" Created: " + voice.getCreateTime());
}
System.out.println();
}
} else {
System.out.println("No voices found.");
}
} else {
System.err.println("Voice list retrieval failed: " + response.getMsg());
}
} catch (Exception e) {
System.err.println("Error in voice list retrieval: " + e.getMessage());
}
}
/**
* Example of deleting a voice clone
* Note: Replace "your-voice-id" with an actual voice ID 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";
VoiceDeleteRequest request = new VoiceDeleteRequest();
request.setVoiceId(voiceIdToDelete);
request.setRequestId("delete-request-" + System.currentTimeMillis());
System.out.println("Deleting voice clone with ID: " + voiceIdToDelete);
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());
}
} else {
System.err.println("Voice deletion failed: " + response.getMsg());
if (response.getError() != null) {
System.err.println("Error details: " + response.getError().getMessage());
}
}
} 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.");
}
}
}