feat: support cloned voice deletion
This commit is contained in:
@@ -18,6 +18,12 @@ type clonedVoiceBinding struct {
|
||||
Explicit bool
|
||||
}
|
||||
|
||||
type DeletedClonedVoiceResult struct {
|
||||
Voice store.ClonedVoice `json:"voice"`
|
||||
Upstream map[string]any `json:"upstream,omitempty"`
|
||||
RequestID string `json:"requestId,omitempty"`
|
||||
}
|
||||
|
||||
func validateVoiceCloneRequest(body map[string]any) error {
|
||||
voiceID := firstNonEmptyString(stringFromMap(body, "voice_id"), stringFromMap(body, "voiceId"))
|
||||
if !validMiniMaxVoiceID(voiceID) {
|
||||
@@ -184,6 +190,62 @@ func (s *Service) touchClonedVoiceUsage(ctx context.Context, user *auth.User, bo
|
||||
_ = s.store.TouchClonedVoiceUsage(ctx, voice.ID)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteClonedVoice(ctx context.Context, user *auth.User, rawID string) (DeletedClonedVoiceResult, error) {
|
||||
id := strings.TrimSpace(rawID)
|
||||
if id == "" {
|
||||
return DeletedClonedVoiceResult{}, &clients.ClientError{Code: "bad_request", Message: "voice id is required", StatusCode: 400, Retryable: false}
|
||||
}
|
||||
clonedVoiceID := ""
|
||||
voiceID := id
|
||||
if looksLikeUUID(id) {
|
||||
clonedVoiceID = id
|
||||
voiceID = ""
|
||||
}
|
||||
voice, found, err := s.store.FindClonedVoiceForUser(ctx, user, clonedVoiceID, voiceID)
|
||||
if err != nil {
|
||||
return DeletedClonedVoiceResult{}, err
|
||||
}
|
||||
if !found {
|
||||
return DeletedClonedVoiceResult{}, &clients.ClientError{Code: "cloned_voice_not_found", Message: "cloned voice not found", StatusCode: 404, Retryable: false}
|
||||
}
|
||||
candidate, ok, err := s.store.GetRuntimeModelCandidateForVoiceCloneDeletion(ctx, voice.PlatformModelID, voice.PlatformID)
|
||||
if err != nil {
|
||||
return DeletedClonedVoiceResult{}, err
|
||||
}
|
||||
if !ok {
|
||||
return DeletedClonedVoiceResult{}, &clients.ClientError{Code: "cloned_voice_platform_unavailable", Message: "cloned voice platform binding is unavailable", StatusCode: 400, Retryable: false}
|
||||
}
|
||||
requestHTTPClient, err := s.httpClientForCandidate(candidate, false)
|
||||
if err != nil {
|
||||
return DeletedClonedVoiceResult{}, err
|
||||
}
|
||||
deleter, ok := s.clientFor(candidate, false).(clients.VoiceCloneDeleter)
|
||||
if !ok {
|
||||
return DeletedClonedVoiceResult{}, &clients.ClientError{Code: "unsupported_operation", Message: "voice clone deletion is not supported by this provider", StatusCode: 400, Retryable: false}
|
||||
}
|
||||
upstream, err := deleter.DeleteVoiceClone(ctx, clients.VoiceCloneDeleteRequest{
|
||||
VoiceID: voice.VoiceID,
|
||||
Candidate: candidate,
|
||||
HTTPClient: requestHTTPClient,
|
||||
})
|
||||
if err != nil {
|
||||
return DeletedClonedVoiceResult{}, err
|
||||
}
|
||||
deleted, found, err := s.store.DeleteClonedVoiceForUser(ctx, user, voice.ID, voice.VoiceID)
|
||||
if err != nil {
|
||||
return DeletedClonedVoiceResult{}, err
|
||||
}
|
||||
if !found {
|
||||
deleted = voice
|
||||
deleted.Status = "deleted"
|
||||
}
|
||||
return DeletedClonedVoiceResult{
|
||||
Voice: deleted,
|
||||
Upstream: upstream.Result,
|
||||
RequestID: upstream.RequestID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func firstAudioURLFromResult(result map[string]any) string {
|
||||
items, _ := result["data"].([]any)
|
||||
for _, raw := range items {
|
||||
|
||||
Reference in New Issue
Block a user