📌 고정 게시글

📢 공지합니다

이 게시글은 메인 페이지에 항상 고정되어 표시됩니다.

최코딩의 개발

스프링과 파이썬 스크립트 연결(TTS 만들기) 본문

개발 팁

스프링과 파이썬 스크립트 연결(TTS 만들기)

seung_ho_choi.s 2024. 6. 11. 13:39
728x90

이번시간에는 스프링과 파이썬 스크립트를 잇는 작업을 실시하겠다. 

 

 

서론

먼저 스프링은 자바 진영이어서 파이썬에서 만든 기능을 쓸 수 없다... .

사용할려면 API를 통해 이어야 되는데 어떻게 할지 막막했다. 

 

일단 필자는 위 사진과 같이 확성기 아이콘을 누르면 아래 뉴스 본문을 읽어주게 하는 것이 목표다.

즉 스프링에서 확성기 아이콘을 누르면 AJAX를 통해 실시간으로 뉴스 본문의 데이터가 이동하고 

해당 데이터는 TTS 파이썬 스크립트로 이동하면 된다. 

 

코드

<script>
    var speaker = false;

    $(document).ready(function () {
        $('#executeScript').click(function () {
            if (!speaker) {
                // speaker가 false일 때만 실행되도록
                speaker = true;
                $.ajax({
                    type: "POST",
                    url: "/run-tts",
                    data: {
                        "newsId": $("#originalContent").text()
                    },
                    success: function (data) {
                        alert("TTS 종료합니다.")
                    }
                });
            } else {
                // speaker가 이미 true인 경우에 실행되도록
                speaker = false;
                // speaker를 false로 변경할 때 해당 URL을 끊습니다.
                $.ajax({
                    type: "POST",
                    url: "/cancel-tts",
                    success: function (data) {
                        // 성공적으로 취소되었을 때 실행되는 코드를 작성합니다.

                    },
                    error: function (xhr, status, error) {
                        // 오류가 발생했을 때 실행되는 코드를 작성합니다.
                        console.error("TTS 취소 요청 중 오류가 발생하였습니다.", error);
                    }
                });
            }
        });
    });


</script>

 

확성기 아이콘을 누를때 실행되는 JS이다. 

일단 if에서는 확성기 아이콘을 누를때 /run-tts로 이동하게 되고 else에서는 확성기 아이콘을 한번 더 누를때 종료해야되므로 /cancel-tts로 이동하게 된다.  

 

<script>
    // 페이지 이동을 감지하는 이벤트 핸들러
    window.addEventListener('beforeunload', function (event) {
        // AJAX 요청을 보냅니다.
        $.ajax({
            type: "POST",
            url: "/cancel-tts2",
            async: true,
            success: function (data) {
                console.log("TTS 취소 요청이 성공적으로 전송되었습니다.");
            },
            error: function (xhr, status, error) {
                console.error("TTS 취소 요청 중 오류가 발생하였습니다.", error);
            }
        });

    });

</script>

 

추가로 페이지를 이동할때는 당연히 TTS가 종료되어야 하므로 위 코드와 같이 페이지 이동이 감지되면 

/cancel-tts2로 이동하게 된다. 물론 /cancel-tts2 와 /cancel-tts 둘의 기능은 똑같다. 그냥 구분하기 위해 이름만 바꿔놓았다.

 

자 이제 핵심코드들이다. 

private Process ttsProcess; // TTS 스크립트를 실행할 프로세스

 

일단 먼저 컨트롤러에서 위와 같이 변수를 설정해두어야 된다. 프로세스가 있어야지 TTS를 취소할 수 있기 문이다. 

 

@PostMapping("/run-tts")
@ResponseBody
public void postTTSScript(@RequestParam("newsId") String content) {
    try {
        // 파이썬 스크립트를 실행하는 명령어
        String[] command = {"python", "C:/Users/chltm/PycharmProjects/amcn_AI/tts/tts.py", content};

        // 프로세스 빌더를 사용하여 명령어 실행
        ProcessBuilder processBuilder = new ProcessBuilder(command);
        ttsProcess = processBuilder.start();

        // 실행 결과를 읽어오기 위해 BufferedReader 사용
        BufferedReader reader = new BufferedReader(new InputStreamReader(ttsProcess.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null) {
            log.info(line);
        }

        // 에러 출력 읽기
        BufferedReader errorReader = new BufferedReader(new InputStreamReader(ttsProcess.getErrorStream()));
        while ((line = errorReader.readLine()) != null) {
           log.info(line);
        }

        // 프로세스 종료 상태 확인
        int exitCode = ttsProcess.waitFor();
        log.info(String.valueOf(exitCode));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

 

자 여기가 핵심코드들이다. TTS 아이콘을 누르면 실행되는 rest api이다. 

// 파이썬 스크립트를 실행하는 명령어
String[] command = {"python", "C:/Users/chltm/PycharmProjects/amcn_AI/tts/tts.py", content};

// 프로세스 빌더를 사용하여 명령어 실행
ProcessBuilder processBuilder = new ProcessBuilder(command);
ttsProcess = processBuilder.start();

 

이 코드들 빼고 나머지는 필요가 없다. 아래 코드들은 그냥 log 정보를 얻기 위해서이지 실행하는데에 있어서는 필요가 없다. 

이젠 저 코드에서 content 즉 넘어온 뉴스 본문데이터가 내 로컬에 저장되어있는 tts.py로 이동하게 된다. 

def speak(content):
    engine = pyttsx3.init()
    engine.setProperty('rate', 230)
    engine.say(content)
    engine.runAndWait()

if __name__ == "__main__":
    if len(sys.argv) > 1:
        content = sys.argv[1]
        speak(content)
    else:
        print("No content provided")

 

여기가 해당 tts.py 스크립트이다. 여기서 목소리가 나오게 되고 저 라이브러리를 사용하기 위해서는

import sys
import pyttsx3

pip install 해서 위 2가지를 설치해주면된다. 

 

// POST 요청을 받아 파이썬 스크립트 실행 취소
@PostMapping("/cancel-tts")
@ResponseBody
public void cancelTTSScript() {
    if (ttsProcess != null) {
        ttsProcess.destroy(); // 파이썬 스크립트 실행 프로세스를 종료
        log.info("TTS script execution canceled.");
    } else {

        log.info("No TTS script is currently running.");
    }
}

// POST 요청을 받아 파이썬 스크립트 실행 취소
@PostMapping("/cancel-tts2")
@ResponseBody
public void cancelTTSScriptOther() {
    log.info("하이");
    if (ttsProcess != null) {
        ttsProcess.destroy(); // 파이썬 스크립트 실행 프로세스를 종료
        log.info("TTS script execution canceled.");
    } else {
        log.info("No TTS script is currently running.");
    }
}

 

자 이제 아이콘을 한번 더 눌러 취소하거나 다른 페이지로 이동할 때 취소하면 위 해당 url로 넘어오게 되고 

프로세스를 종료하기 위해 destory를 사용하면 TTS가 종료가 되는것을 확인할 수 있다.

 

결론

물론 쟝고나 플라스크를 해서 통신해도 되고 자이썬을 이용해서 통신해도 되지만 위 방법이 제일 깔끔해서 위와 같이 코드를 작성했다. 

@PostMapping("/run-tts")
@ResponseBody
public void postTTSScriptV2(@RequestParam("newsId") String content) {
    try {
        RestTemplate restTemplate = new RestTemplate();
        String url = "http://127.0.0.1:8000/tts";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        JSONObject request = new JSONObject();
        request.put("content", content);

        HttpEntity<String> entity = new HttpEntity<>(request.toString(), headers);

        ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);

        System.out.println("Response: " + response.getBody());
    } catch (Exception e) {
        e.printStackTrace();
    }
}
from fastapi import FastAPI
import asyncio
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
from gtts import gTTS
import playsound
import pyttsx3
app = FastAPI()



@app.get("/")
async def root():
    return {"message": "Hello World"}


@app.post('/tts')
async def tts(request: Request):
    try:
        content = await request.json()
        if 'content' in content:
            # TTS 엔진 초기화
            engine = pyttsx3.init()
            engine.setProperty('rate', 230)
            engine.say(content)
            engine.runAndWait()
            # print(content)
            # tts = gTTS(text=content['content'], lang='ko')
            # tts.save("gg.mp3")
            # playsound.playsound('gg')
            # print("===")
            return JSONResponse({"status": "success"}, status_code=200)
        else:
            raise HTTPException(status_code=400, detail="No content provided")
    except Exception as e:
        return JSONResponse({"status": "error", "message": str(e)}, status_code=500)


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

 

위 코드는 스프링과 플라스크 서버간의 통신 방법 코드이다. 

 

 

느낀점

애초에 언어가 다른 자바와 파이썬을 상호작용하는 것의 어려움이 있었지만 나름 쉬웠고 할만했다. 

728x90