From bc5b41958c0f27d705ef19ef9de4f907b65e5a73 Mon Sep 17 00:00:00 2001 From: jeongchanmin Date: Wed, 10 Jul 2024 21:08:37 +0900 Subject: [PATCH 1/3] =?UTF-8?q?:recycle:=20[Refactor]=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ebextensions-dev/02-ffmpeg-package.config | 11 -- .../naverCloud/service/ClovaServiceImpl.java | 150 ------------------ 2 files changed, 161 deletions(-) delete mode 100644 .ebextensions-dev/02-ffmpeg-package.config delete mode 100644 src/main/java/ssuPlector/ai/naverCloud/service/ClovaServiceImpl.java diff --git a/.ebextensions-dev/02-ffmpeg-package.config b/.ebextensions-dev/02-ffmpeg-package.config deleted file mode 100644 index 3b3194a..0000000 --- a/.ebextensions-dev/02-ffmpeg-package.config +++ /dev/null @@ -1,11 +0,0 @@ -commands: - 01-wget: - command: "wget -O /tmp/ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz" - 02-mkdir: - command: "if [ ! -d /opt/ffmpeg ] ; then mkdir -p /opt/ffmpeg; fi" - 03-tar: - command: "tar xvf /tmp/ffmpeg.tar.xz -C /opt/ffmpeg" - 04-ln: - command: "if [[ ! -f /usr/bin/ffmpeg ]] ; then ln -sf /opt/ffmpeg/ffmpeg-4.2.2-amd64-static/ffmpeg /usr/bin/ffmpeg; fi" - 05-ln: - command: "if [[ ! -f /usr/bin/ffprobe ]] ; then ln -sf /opt/ffmpeg/ffmpeg-4.2.2-amd64-static/ffprobe /usr/bin/ffprobe; fi" diff --git a/src/main/java/ssuPlector/ai/naverCloud/service/ClovaServiceImpl.java b/src/main/java/ssuPlector/ai/naverCloud/service/ClovaServiceImpl.java deleted file mode 100644 index 895b050..0000000 --- a/src/main/java/ssuPlector/ai/naverCloud/service/ClovaServiceImpl.java +++ /dev/null @@ -1,150 +0,0 @@ -package ssuPlector.ai.naverCloud.service; - -import java.io.File; -import java.nio.file.Files; -import java.util.HashMap; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.client.WebClient; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import reactor.core.publisher.Mono; -import ssuPlector.global.exception.GlobalException; -import ssuPlector.global.response.code.GlobalErrorCode; - -@Component -public class ClovaServiceImpl implements ClovaService { - - private final ObjectMapper objectMapper; - private final WebClient webClient; - - @Value("${naver.cloud.id}") - String clientId; - - @Value("${naver.cloud.secret}") - String clientSecret; - - public ClovaServiceImpl(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - this.webClient = - WebClient.builder().baseUrl("https://naveropenapi.apigw.ntruss.com").build(); - } - - public String soundToText(File file) { - - try { - byte[] fileContent = Files.readAllBytes(file.toPath()); - String language = "Kor"; - - Mono responseMono = - webClient - .post() - .uri( - uriBuilder -> - uriBuilder - .path("/recog/v1/stt") - .queryParam("lang", language) - .build()) - .header("X-NCP-APIGW-API-KEY-ID", clientId) - .header("X-NCP-APIGW-API-KEY", clientSecret) - .contentType(MediaType.APPLICATION_OCTET_STREAM) - .bodyValue(fileContent) - .retrieve() - .bodyToMono(String.class) - .map(this::getTextFromResponse); - - return responseMono.block(); - } catch (Exception e) { - e.printStackTrace(); - throw new GlobalException(GlobalErrorCode._INTERNAL_SERVER_ERROR); - } - } - - private String getTextFromResponse(String responseStr) { - try { - return objectMapper.readTree(responseStr).get("text").asText(); - } catch (Exception e) { - e.printStackTrace(); - throw new GlobalException(GlobalErrorCode._INTERNAL_SERVER_ERROR); - } - } - - public String summarizeText(String text) { - - try { - Map requestBody = new HashMap<>(); - Map document = new HashMap<>(); - document.put("content", text); - Map option = new HashMap<>(); - option.put("language", "ko"); - option.put("model", "general"); - option.put("tone", "2"); // 정중체 - option.put("summaryCount", 10); // 요약문장 수 - requestBody.put("document", document); - requestBody.put("option", option); - - Mono responseMono = - webClient - .post() - .uri("/text-summary/v1/summarize") - .header("X-NCP-APIGW-API-KEY-ID", clientId) - .header("X-NCP-APIGW-API-KEY", clientSecret) - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(requestBody) - .retrieve() - .onStatus( - status -> - status.is4xxClientError() || status.is5xxServerError(), - clientResponse -> - clientResponse - .bodyToMono(String.class) - .flatMap( - errorBody -> { - if (errorBody.contains( - "E100")) { // E100: 유효한 문장이 - // 부족한 경우 - return Mono.error( - new GlobalException( - GlobalErrorCode - .INSUFFICIENT_VALID_SENTENCE)); - } else if (errorBody.contains( - "E003")) { // E003: 요청한 문장이 - // 너무 긴 경우 - return Mono.error( - new GlobalException( - GlobalErrorCode - .TEXT_QUOTA_EXCEEDED)); - } else if (errorBody.contains( - "E001")) { // E001: 빈 문장인 경우 - return Mono.error( - new GlobalException( - GlobalErrorCode - .EMPTY_TEXT)); - } else { - return Mono.error( - new GlobalException( - GlobalErrorCode - ._INTERNAL_SERVER_ERROR)); - } - })) - .bodyToMono(String.class) - .map(this::getSummaryFromResponse); - - return responseMono.block(); - } catch (Exception e) { - throw new GlobalException(GlobalErrorCode._INTERNAL_SERVER_ERROR); - } - } - - private String getSummaryFromResponse(String responseStr) { - try { - return objectMapper.readTree(responseStr).get("summary").asText(); - } catch (Exception e) { - throw new GlobalException(GlobalErrorCode._INTERNAL_SERVER_ERROR); - } - } -} From 1222b6826b9c379095c5fdcf09652b1054f0ab8d Mon Sep 17 00:00:00 2001 From: jeongchanmin Date: Wed, 10 Jul 2024 21:11:50 +0900 Subject: [PATCH 2/3] =?UTF-8?q?:recycle:=20[Refactor]=20ffmpeg=20->=20clov?= =?UTF-8?q?aSpeech=20=EB=B0=A9=EC=8B=9D=20=EC=9D=B4=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ClovaSpeechService.java | 8 ++ .../service/ClovaSpeechServiceImpl.java | 94 +++++++++++++++++++ .../ssuPlector/service/pm/PmServiceImpl.java | 52 +--------- src/main/resources/application.yml | 8 +- 4 files changed, 109 insertions(+), 53 deletions(-) create mode 100644 src/main/java/ssuPlector/ai/naverCloud/service/ClovaSpeechService.java create mode 100644 src/main/java/ssuPlector/ai/naverCloud/service/ClovaSpeechServiceImpl.java diff --git a/src/main/java/ssuPlector/ai/naverCloud/service/ClovaSpeechService.java b/src/main/java/ssuPlector/ai/naverCloud/service/ClovaSpeechService.java new file mode 100644 index 0000000..a7907d6 --- /dev/null +++ b/src/main/java/ssuPlector/ai/naverCloud/service/ClovaSpeechService.java @@ -0,0 +1,8 @@ +package ssuPlector.ai.naverCloud.service; + +import java.io.File; + +public interface ClovaSpeechService { + + String convert(File file); +} diff --git a/src/main/java/ssuPlector/ai/naverCloud/service/ClovaSpeechServiceImpl.java b/src/main/java/ssuPlector/ai/naverCloud/service/ClovaSpeechServiceImpl.java new file mode 100644 index 0000000..37f8d14 --- /dev/null +++ b/src/main/java/ssuPlector/ai/naverCloud/service/ClovaSpeechServiceImpl.java @@ -0,0 +1,94 @@ +package ssuPlector.ai.naverCloud.service; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.client.WebClient; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import reactor.core.publisher.Mono; +import ssuPlector.global.exception.GlobalException; +import ssuPlector.global.response.code.GlobalErrorCode; + +@Component +public class ClovaSpeechServiceImpl implements ClovaSpeechService { + + private final ObjectMapper objectMapper; + private final WebClient webClient; + + @Value("${naver.cloud.clovaSpeech.client-secret}") + String clientSecret; + + public ClovaSpeechServiceImpl(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + this.webClient = + WebClient.builder() + .baseUrl( + "https://clovaspeech-gw.ncloud.com/external/v1/8450/a2c84d3e94793711c615c18275ecf83ebe3be19059abd2d901513e1a749035da") + .build(); + } + + @Override + public String convert(File file) { + + try { + String language = "ko-KR"; + + Mono responseMono = + webClient + .post() + .uri("/recognizer/upload") + .header("X-CLOVASPEECH-API-KEY", clientSecret) + .contentType(MediaType.MULTIPART_FORM_DATA) + .bodyValue(generateMultipartBody(file, language)) + .retrieve() + .bodyToMono(String.class) + .map(this::getTextFromResponse); + + return responseMono.block(); + } catch (Exception e) { + throw new GlobalException(GlobalErrorCode._INTERNAL_SERVER_ERROR); + } + } + + private MultiValueMap generateMultipartBody(File file, String language) { + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("media", new FileSystemResource(file)); + + Map params = new HashMap<>(); + params.put("language", language); + params.put("completion", "sync"); + params.put("wordAlignment", true); + params.put("fullText", true); + params.put("noiseFiltering", true); + params.put("diarization", Map.of("enable", true)); + params.put("format", "JSON"); + + body.add("params", new HttpEntity<>(params, createJsonHeaders())); + return body; + } + + private HttpHeaders createJsonHeaders() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + return headers; + } + + private String getTextFromResponse(String responseStr) { + try { + return objectMapper.readTree(responseStr).get("text").asText(); + } catch (Exception e) { + throw new GlobalException(GlobalErrorCode._INTERNAL_SERVER_ERROR); + } + } +} diff --git a/src/main/java/ssuPlector/service/pm/PmServiceImpl.java b/src/main/java/ssuPlector/service/pm/PmServiceImpl.java index 16d2a3b..045d589 100644 --- a/src/main/java/ssuPlector/service/pm/PmServiceImpl.java +++ b/src/main/java/ssuPlector/service/pm/PmServiceImpl.java @@ -11,7 +11,7 @@ import org.springframework.web.multipart.MultipartFile; import lombok.RequiredArgsConstructor; -import ssuPlector.ai.naverCloud.service.ClovaService; +import ssuPlector.ai.naverCloud.service.ClovaSpeechService; import ssuPlector.ai.openAI.service.ChatGptService; import ssuPlector.domain.category.MeetingTodo; import ssuPlector.dto.request.PmDTO.PmRequestDTO; @@ -23,7 +23,7 @@ public class PmServiceImpl implements PmService { private final ChatGptService chatGptService; - private final ClovaService clovaService; + private final ClovaSpeechService clovaSpeechService; @Override public List recommendMeeting( @@ -93,59 +93,13 @@ public String summarize(MultipartFile file) { fos.write(file.getBytes()); // 파일에 바이트 배열 쓰기 fos.close(); // 출력 스트림 닫기 - List chunks = splitAudio(audioFile, 60); - - for (File chunk : chunks) { - String text = clovaService.soundToText(chunk); - totalText.append(text).append(" "); - chunk.delete(); - } + totalText.append(clovaSpeechService.convert(audioFile)); audioFile.delete(); } catch (Exception e) { - e.printStackTrace(); throw new GlobalException(GlobalErrorCode._INTERNAL_SERVER_ERROR); } return chatGptService.summarizeText(totalText.toString().trim()); } - - private List splitAudio(File inputFile, int chunkDurationInSeconds) throws Exception { - - List chunks = new ArrayList<>(); - String inputFilePath = inputFile.getAbsolutePath(); - String outputFilePattern = inputFilePath.replace(".m4a", "_chunk_%03d.m4a"); - - // ffmpeg을 실행하여 오디오 파일 분할 - ProcessBuilder pb = - new ProcessBuilder( - "ffmpeg", - "-hide_banner", - "-loglevel", - "error", - "-i", - inputFilePath, - "-f", - "segment", - "-segment_time", - String.valueOf(chunkDurationInSeconds), - "-c", - "copy", - outputFilePattern); - pb.redirectErrorStream(true); - pb.inheritIO(); - Process process = pb.start(); - process.waitFor(); - - // 생성된 분할 파일 수집 - int index = 0; - while (true) { - File chunk = new File(String.format(outputFilePattern, index)); - if (!chunk.exists()) break; - chunks.add(chunk); - index++; - } - - return chunks; - } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 04de255..5305556 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -62,8 +62,8 @@ kakao: redirect-uri: ${KAKAO_REDIRECT_URI} naver: cloud: - id: ${NAVER_CLOUD_ID} - secret: ${NAVER_CLOUD_SECRET} + clovaSpeech: + client-secret: ${NAVER_CLOVA_SPEECH_CLIENT_SECRET} openai: model: gpt-3.5-turbo api: @@ -121,8 +121,8 @@ kakao: redirect-uri: ${KAKAO_REDIRECT_URI} naver: cloud: - id: ${NAVER_CLOUD_ID} - secret: ${NAVER_CLOUD_SECRET} + clovaSpeech: + client-secret: ${NAVER_CLOVA_SPEECH_CLIENT_SECRET} openai: model: gpt-3.5-turbo api: From b6f43ef2149429506da3ef316d3e2fdf30f4ccfe Mon Sep 17 00:00:00 2001 From: jeongchanmin Date: Wed, 10 Jul 2024 21:12:17 +0900 Subject: [PATCH 3/3] =?UTF-8?q?:recycle:=20[Refactor]=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ssuPlector/ai/naverCloud/service/ClovaService.java | 10 ---------- .../ai/openAI/service/ChatGptServiceImpl.java | 2 -- .../service/developer/DeveloperServiceImpl.java | 4 +--- 3 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 src/main/java/ssuPlector/ai/naverCloud/service/ClovaService.java diff --git a/src/main/java/ssuPlector/ai/naverCloud/service/ClovaService.java b/src/main/java/ssuPlector/ai/naverCloud/service/ClovaService.java deleted file mode 100644 index 1e8b247..0000000 --- a/src/main/java/ssuPlector/ai/naverCloud/service/ClovaService.java +++ /dev/null @@ -1,10 +0,0 @@ -package ssuPlector.ai.naverCloud.service; - -import java.io.File; - -public interface ClovaService { - - String soundToText(File file); - - String summarizeText(String text); -} diff --git a/src/main/java/ssuPlector/ai/openAI/service/ChatGptServiceImpl.java b/src/main/java/ssuPlector/ai/openAI/service/ChatGptServiceImpl.java index 92922f4..1932750 100644 --- a/src/main/java/ssuPlector/ai/openAI/service/ChatGptServiceImpl.java +++ b/src/main/java/ssuPlector/ai/openAI/service/ChatGptServiceImpl.java @@ -37,7 +37,6 @@ public String recommendMeetingToDo(String query) { apiUrl, getHttpEntity(chatGptRequest), ChatGptResponse.class); return response.getChoices().get(0).getMessage().getContent(); } catch (Exception e) { - e.printStackTrace(); throw new GlobalException(GlobalErrorCode._INTERNAL_SERVER_ERROR); } } @@ -70,7 +69,6 @@ public String summarizeText(String text) { restTemplate.postForObject(apiUrl, request, ChatGptResponse.class); if (response == null) { - System.out.println("response is null"); throw new GlobalException(GlobalErrorCode._INTERNAL_SERVER_ERROR); } diff --git a/src/main/java/ssuPlector/service/developer/DeveloperServiceImpl.java b/src/main/java/ssuPlector/service/developer/DeveloperServiceImpl.java index 91a37eb..ccb0f42 100644 --- a/src/main/java/ssuPlector/service/developer/DeveloperServiceImpl.java +++ b/src/main/java/ssuPlector/service/developer/DeveloperServiceImpl.java @@ -135,8 +135,6 @@ public Long createDummyDeveloper( @Override public List searchDeveloper(String developerName) { List developers = developerRepository.searchDeveloper(developerName); - return developers.stream() - .map(DeveloperConverter::toDeveloperSearchDTO) - .toList(); + return developers.stream().map(DeveloperConverter::toDeveloperSearchDTO).toList(); } }