-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #65 from tukcomCD2024/feat#56/Change-elevenlabs-to…
…-WAS-server Feat#56/change elevenlabs to was server
- Loading branch information
Showing
11 changed files
with
352 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
backend/memetory/src/main/java/com/example/memetory/domain/voice/controller/VoiceApi.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.example.memetory.domain.voice.controller; | ||
|
||
import com.example.memetory.domain.voice.dto.request.GenerateVoiceRequestDto; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.Parameter; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
import io.swagger.v3.oas.annotations.security.SecurityRequirement; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import org.springframework.http.ResponseEntity; | ||
|
||
import java.io.IOException; | ||
|
||
@Tag(name = "Voice") | ||
public interface VoiceApi { | ||
|
||
@Operation( | ||
summary = "목소리 추출", | ||
description = "S3_URL에 있는 음성파일에서 목소리를 추출한다.", | ||
security = {@SecurityRequirement(name = "access_token")} | ||
) | ||
@ApiResponses(value = { | ||
@ApiResponse( | ||
responseCode = "201", | ||
description = "목소리 추출!" | ||
) | ||
}) | ||
ResponseEntity<Object> register( | ||
@Parameter(hidden = true) String email, | ||
GenerateVoiceRequestDto generateVoiceRequestDto | ||
) throws IOException; | ||
} |
67 changes: 67 additions & 0 deletions
67
.../memetory/src/main/java/com/example/memetory/domain/voice/controller/VoiceController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package com.example.memetory.domain.voice.controller; | ||
|
||
import com.example.memetory.domain.voice.dto.VoiceServiceDto; | ||
import com.example.memetory.domain.voice.dto.request.GenerateVoiceRequestDto; | ||
import com.example.memetory.domain.voice.dto.response.GenerateVoiceResponseDto; | ||
import com.example.memetory.domain.voice.service.VoiceService; | ||
import com.example.memetory.global.annotation.LoginMemberEmail; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.util.MultiValueMap; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import org.springframework.web.reactive.function.BodyInserters; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
import reactor.core.publisher.Mono; | ||
|
||
import java.io.*; | ||
|
||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/voice") | ||
public class VoiceController implements VoiceApi { | ||
|
||
private final VoiceService voiceService; | ||
|
||
@Value("${elevenlabs.api.url.add}") | ||
private String apiUrl; | ||
|
||
@Value("${elevenlabs.api.key}") | ||
private String apiKey; | ||
|
||
@PostMapping | ||
@Override | ||
public ResponseEntity<Object> register(@LoginMemberEmail String email, @RequestBody GenerateVoiceRequestDto generateVoiceRequestDto) throws IOException { | ||
|
||
// s3 url을 받아서 음성파일을 가져오는 ServiceDto 로직 1개 | ||
VoiceServiceDto voiceServiceDtoS3 = generateVoiceRequestDto.toServiceDtoS3(email); | ||
MultiValueMap<String, Object> formData = voiceService.generateVoice(voiceServiceDtoS3); | ||
|
||
return WebClient | ||
.create(apiUrl) | ||
.post() | ||
.header("xi-api-key", apiKey) | ||
.contentType(MediaType.MULTIPART_FORM_DATA) | ||
.body(BodyInserters.fromMultipartData(formData)) | ||
.retrieve() | ||
.bodyToMono(GenerateVoiceResponseDto.class) | ||
.flatMap(response -> { | ||
// elevenlabs API 호출이 완료 되면 실행할 로직 | ||
VoiceServiceDto voiceServiceDtoElevenlabs = | ||
generateVoiceRequestDto.toServiceDtoElevenlabs(email, response.getElevenlabsVoiceId()); | ||
voiceService.register(voiceServiceDtoElevenlabs); | ||
return Mono.just(ResponseEntity.status(HttpStatus.CREATED).build()); | ||
}) | ||
.onErrorResume(error -> { | ||
System.out.println(("An error occurred while processing the request: {}" + error.getMessage())); | ||
return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()); | ||
}) | ||
.block(); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
backend/memetory/src/main/java/com/example/memetory/domain/voice/dto/VoiceServiceDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.example.memetory.domain.voice.dto; | ||
|
||
import com.example.memetory.domain.member.entity.Member; | ||
import com.example.memetory.domain.voice.entity.Voice; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
@Builder | ||
@AllArgsConstructor | ||
public class VoiceServiceDto { | ||
|
||
private String email; | ||
private String s3Key; | ||
private String name; | ||
private String description; | ||
private String elevenlabsVoiceId; | ||
|
||
public Voice toEntity(Member member) { | ||
return Voice.builder() | ||
.member(member) | ||
.elevenlabsVoiceId(elevenlabsVoiceId) | ||
.build(); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
.../src/main/java/com/example/memetory/domain/voice/dto/request/GenerateVoiceRequestDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.example.memetory.domain.voice.dto.request; | ||
|
||
import com.example.memetory.domain.voice.dto.VoiceServiceDto; | ||
import lombok.AccessLevel; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class GenerateVoiceRequestDto { | ||
private String s3Key; | ||
private String name; | ||
private String description; | ||
|
||
public VoiceServiceDto toServiceDtoS3(String email) { | ||
return VoiceServiceDto.builder() | ||
.email(email) | ||
.s3Key(s3Key) | ||
.name(name) | ||
.description(description) | ||
.build(); | ||
} | ||
|
||
public VoiceServiceDto toServiceDtoElevenlabs(String email, String elevenlabsId) { | ||
return VoiceServiceDto.builder() | ||
.email(email) | ||
.elevenlabsVoiceId(elevenlabsId) | ||
.build(); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
...rc/main/java/com/example/memetory/domain/voice/dto/response/GenerateVoiceResponseDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.example.memetory.domain.voice.dto.response; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import lombok.AccessLevel; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
|
||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PRIVATE) | ||
public class GenerateVoiceResponseDto { | ||
|
||
@JsonProperty("voice_id") | ||
private String elevenlabsVoiceId; | ||
|
||
} |
32 changes: 32 additions & 0 deletions
32
backend/memetory/src/main/java/com/example/memetory/domain/voice/entity/Voice.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.example.memetory.domain.voice.entity; | ||
|
||
import com.example.memetory.domain.member.entity.Member; | ||
import jakarta.persistence.*; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Entity | ||
public class Voice { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
@Column(name = "voice_id") | ||
private Long id; | ||
|
||
@Column(name = "elevenlabs_voice_id") | ||
private String elevenlabsVoiceId; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "member_id") | ||
private Member member; | ||
|
||
@Builder | ||
public Voice(String elevenlabsVoiceId, Member member) { | ||
this.elevenlabsVoiceId = elevenlabsVoiceId; | ||
this.member = member; | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
.../memetory/src/main/java/com/example/memetory/domain/voice/repository/VoiceRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.example.memetory.domain.voice.repository; | ||
|
||
import com.example.memetory.domain.voice.entity.Voice; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface VoiceRepository extends JpaRepository<Voice, Long> { | ||
} |
108 changes: 108 additions & 0 deletions
108
backend/memetory/src/main/java/com/example/memetory/domain/voice/service/VoiceService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package com.example.memetory.domain.voice.service; | ||
|
||
import com.amazonaws.services.s3.AmazonS3Client; | ||
import com.amazonaws.services.s3.model.S3Object; | ||
import com.example.memetory.domain.member.entity.Member; | ||
import com.example.memetory.domain.member.service.MemberService; | ||
import com.example.memetory.domain.voice.dto.VoiceServiceDto; | ||
import com.example.memetory.domain.voice.entity.Voice; | ||
import com.example.memetory.domain.voice.repository.VoiceRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.core.io.FileSystemResource; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import org.springframework.util.LinkedMultiValueMap; | ||
import org.springframework.util.MultiValueMap; | ||
|
||
import java.io.*; | ||
|
||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class VoiceService { | ||
|
||
private final VoiceRepository voiceRepository; | ||
private final MemberService memberService; | ||
private final AmazonS3Client amazonS3Client; | ||
|
||
@Value("${cloud.aws.s3.bucket}") | ||
private String bucketName; | ||
|
||
@Transactional | ||
public void register(VoiceServiceDto voiceServiceDto) { | ||
Member foundMember = memberService.findByEmail(voiceServiceDto.getEmail()); | ||
Voice newVoice = voiceServiceDto.toEntity(foundMember); | ||
|
||
voiceRepository.save(newVoice); | ||
} | ||
|
||
// 목소리 생성 | ||
public MultiValueMap<String, Object> generateVoice(VoiceServiceDto voiceServiceDto) throws IOException { | ||
S3Object s3Object = getS3File(voiceServiceDto); | ||
|
||
return createFormData(s3Object, voiceServiceDto); | ||
} | ||
|
||
// S3 파일 가져오기 | ||
private S3Object getS3File(VoiceServiceDto voiceServiceDto) { | ||
return amazonS3Client.getObject(bucketName, voiceServiceDto.getS3Key()); | ||
} | ||
|
||
// Elevenlabs로 보낼 formData 생성 | ||
private MultiValueMap<String, Object> createFormData(S3Object s3Object, VoiceServiceDto voiceServiceDto) throws IOException { | ||
// S3Object로부터 데이터를 읽어오기 위한 InputStream 생성 | ||
InputStream inputStream = s3Object.getObjectContent(); | ||
|
||
// 데이터를 임시로 저장할 ByteArrayOutputStream 생성 | ||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); | ||
|
||
// 입력 스트림에서 데이터를 읽어와서 ByteArrayOutputStream에 쓰기 | ||
byte[] buffer = new byte[1024]; | ||
int bytesRead; | ||
while ((bytesRead = inputStream.read(buffer)) != -1) { | ||
byteArrayOutputStream.write(buffer, 0, bytesRead); | ||
} | ||
|
||
// ByteArrayOutputStream에 저장된 데이터를 byte 배열로 변환 | ||
byte[] data = byteArrayOutputStream.toByteArray(); | ||
|
||
// byte 배열을 파일 시스템 리소스로 변환 | ||
FileSystemResource fileSystemResource = createFileSystemResource(data, voiceServiceDto.getS3Key()); | ||
|
||
// Elevenlabs API 호출을 위한 폼 데이터 MultiValueMap 생성 | ||
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>(); | ||
formData.add("files", fileSystemResource); | ||
formData.add("name", voiceServiceDto.getName()); | ||
formData.add("description", voiceServiceDto.getDescription()); | ||
|
||
// 입력 스트림 닫기 | ||
try { | ||
inputStream.close(); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
|
||
// S3Object 닫기 | ||
if (s3Object != null) { | ||
try { | ||
s3Object.close(); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
return formData; | ||
} | ||
|
||
private FileSystemResource createFileSystemResource(byte[] data, String s3Key) throws IOException { | ||
// 임시 파일 생성 | ||
File tempFile = File.createTempFile(s3Key, ""); | ||
try (FileOutputStream fos = new FileOutputStream(tempFile)) { | ||
// 데이터를 파일에 쓰기 | ||
fos.write(data); | ||
} | ||
// 파일 시스템 리소스 생성하여 반환 | ||
return new FileSystemResource(tempFile); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
backend/memetory/src/main/java/com/example/memetory/global/config/S3Config.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.example.memetory.global.config; | ||
|
||
import com.amazonaws.auth.AWSStaticCredentialsProvider; | ||
import com.amazonaws.auth.BasicAWSCredentials; | ||
import com.amazonaws.services.s3.AmazonS3Client; | ||
import com.amazonaws.services.s3.AmazonS3ClientBuilder; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration | ||
public class S3Config { | ||
@Value("${cloud.aws.credentials.access-key}") | ||
private String accessKey; | ||
@Value("${cloud.aws.credentials.secret-key}") | ||
private String secretKey; | ||
@Value("${cloud.aws.region.static}") | ||
private String region; | ||
|
||
@Bean | ||
public AmazonS3Client amazonS3Client() { | ||
BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey); | ||
return (AmazonS3Client) AmazonS3ClientBuilder.standard() | ||
.withRegion(region) | ||
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) | ||
.build(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters