Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 69 additions & 68 deletions app/controller/RecordController.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import os
import subprocess
import time
from typing import List

import requests
from boto3 import client

from fastapi import APIRouter, Request, UploadFile, File, Form
from boto3 import client
from pydub import AudioSegment
from pydub.playback import play

from app import s3Service
from app.dto.BasicTTSRequestDto import BasicTTSRequestDto
from app.dto.FirstTTSRequestDto import FirstTTSRequestDto
from app.dto.ExtraTTSRequestDto import ExtraTTSRequestDto

from app.dto.ScheduleSpeakRequestDto import ScheduleSpeakRequestDto
from app.dto.ScheduleTTSRequestDto import ScheduleTTSRequestDto
from app.service.elevenLabs import text_to_speech_file_save_AWS
from app.service.gpt import ChatgptAPI
from app.service.s3Service import download_from_s3
from app.utils import play_file

from app.elevenLabs import add_voice, text_to_speech_file_save_AWS, get_voice, delete_all_voice, text_to_speech_file
from app.s3Service import download_from_s3_links, download_from_s3

router = APIRouter(
prefix="/api/fastapi",
Expand All @@ -30,18 +37,6 @@
)


async def save_local_file(file: UploadFile) -> str:
"""하나의 파일을 로컬에 저장하고 경로를 반환합니다."""
audio_dir = "./audio"
if not os.path.exists(audio_dir):
os.makedirs(audio_dir)

local_file_path = os.path.join(audio_dir, file.filename)
with open(local_file_path, "wb") as f:
f.write(await file.read())
return local_file_path


async def save_local_files(files: List[UploadFile]) -> list:
"""업로드된 파일을 로컬에 저장하고 파일 경로를 반환합니다."""
audio_dir = "./audio"
Expand All @@ -56,76 +51,82 @@ async def save_local_files(files: List[UploadFile]) -> list:
return local_file_path_list


# 첫 로그인 시 목소리 녹음 api
# 첫 로그인 시 1분 목소리 녹음 api
@router.post("/voices")
async def getVoice(request: Request, user_id: int = Form(...), file: UploadFile = File(...)):
token = request.headers.get("Authorization").split(" ")[1]
local_file_path = await save_local_file(file)
name = str(user_id)
# voice_id = add_voice(name=name, local_file_paths=[local_file_path])
print(name)
async def getVoice(request: Request, files: List[UploadFile] = File(...)):
# token = request.headers.get("Authorization").split(" ")[1]
local_file_path_list = await save_local_files(files)
name = 'yjg'
voice_id = add_voice(name=name, local_file_paths=local_file_path_list)
print(voice_id)
# voice_url = s3Service.upload_to_s3(local_file_path)
os.remove(local_file_path)
# os.remove(local_file_path)

send_user_voice_id_to_spring(token=token, voice_id=yjg_voice_id)
# send_user_voice_file_to_spring(token=token, voice_url=voice_url)


@router.post("/schedules")
async def schedule_tts(request: Request, schedules: ScheduleTTSRequestDto):
@router.post("/save/basic-tts")
async def save_S3_basic_tts(request: Request, firstTTSRequestDtoList: FirstTTSRequestDto):
# token = request.headers.get("Authorization").split(" ")[1]
voice_id = schedules.voice_id

prompt = ChatgptAPI(schedules.schedule_text, schedules.alias)

# schedule_dict: {"저녁": "엄마~ 저녁 잘 챙겨 먹었어?", "운동": "오늘 운동했어? 건강 챙겨~!"}
schedule_dict = prompt.get_schedule_json()
# text가 어떤형식으로 올지 몰라서 일단 그대로 내보낸다고 가정 (변환시 지피티 사용)

# TTS 처리 (MP3 파일 생성 후 s3 저장)
response = {
schedules.schedule_id[i]: {
"keyword": schedules.schedule_text[i], # 키워드 직접 저장
"text": schedule_dict.get(schedules.schedule_text[i], ""), # 문장은 GPT 결과에서 매핑
"url": text_to_speech_file_save_AWS(
schedule_dict.get(schedules.schedule_text[i], ""),
yjg_voice_id
)
}
for i in range(len(schedules.schedule_id))
firstTTSRequestDtoList.basic_schedule_id[i]: text_to_speech_file_save_AWS(firstTTSRequestDtoList.basic_schedule_text[i],
yjg_voice_id)
for i in range(len(firstTTSRequestDtoList.basic_schedule_id))
}

return response


@router.post("/schedules-speak")
async def speak_schedule(request: Request, scheduleSpeakRequestDto: ScheduleSpeakRequestDto):
@router.post("/basic-tts")
async def speak_schedule_tts(request: Request, basicTTSRequestDto: BasicTTSRequestDto):
# token = request.headers.get("Authorization").split(" ")[1]
local_file_path = download_from_s3(scheduleSpeakRequestDto.schedule_voice_Url)
local_file_path = download_from_s3(basicTTSRequestDto.schedule_voice_Url)
print(f"Downloaded file path: {local_file_path}")

# target_time에 맞춰서 TTS 파일 재생
play_file.play_at_target_time(scheduleSpeakRequestDto.target_time, local_file_path)
# 블루투스 헤드셋 또는 기본 스피커로 출력
os.system("pactl list sinks | grep 'bluez_sink'") # 블루투스 출력 장치 확인
os.system("pactl set-default-sink `pactl list sinks short | grep bluez_sink | awk '{print $2}'`") # 기본 출력 변경

return {"message": "TTS completed and played on speaker"}
# 로컬 파일을 직접 재생
subprocess.run(["mpg321", local_file_path])

return {"message": "TTS completed and played on Bluetooth headset or speaker"}

def send_user_voice_file_to_spring(token: str, voice_url: str):
headers = {
"Authorization": f"Bearer {token}"
}
data = {
"voiceUrl": voice_url
}
requests.post("http://localhost:8080/api/spring/records/voices", headers=headers, json=data)
# requests.post("https://peachmentor.com/api/spring/records/voices", headers=headers, json=data)

@router.post("/extra-tts")
async def speak_schedule_tts(request: Rlocalhostquest, extraTTSRequestDto: ExtraTTSRequestDto):
# token = request.headers.get("Authorization").split(" ")[1]
schedule_text = extraTTSRequestDto.schedule_text

#진짜 실제로 쓸 코드
local_file_path = text_to_speech_file(schedule_text, yjg_voice_id)

# 테스트하면서 AWS에 올려놓으려고 남긴 코드
url = text_to_speech_file_save_AWS(schedule_text, yjg_voice_id)
local_file_path = download_from_s3(url)

# local_file_path = os.getcwd()+"/test_audio/test8.mp3" # test
# 블루투스 헤드셋 또는 기본 스피커로 출력
os.system("pactl list sinks | grep 'bluez_sink'") # 블루투스 출력 장치 확인
os.system("pactl set-default-sink `pactl list sinks short | grep bluez_sink | awk '{print $2}'`") # 기본 출력 변경

def send_user_voice_id_to_spring(token: str, voice_id: str):
# 로컬 파일을 직접 재생
subprocess.run(["/usr/bin/mpg321", local_file_path])
# subprocess.run(["ffplay", "-nodisp", "-autoexit", local_file_path],
# stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # 윈도우용
return {"message": "TTS completed and played on Bluetooth headset or speaker"}


def send_user_voice_file_to_spring(token: str, voice_url: str):
headers = {
"Authorization": f"Bearer {token}"
}
data = {
"voiceId": voice_id
"voiceUrl": voice_url
}
requests.post("http://localhost:8080/api/spring/records/voices", headers=headers, json=data)
requests.post("http://springboot:8080/api/spring/records/voices", headers=headers, json=data)
# requests.post("https://peachmentor.com/api/spring/records/voices", headers=headers, json=data)


Expand All @@ -137,15 +138,15 @@ def send_user_speech_file_to_spring(token: str, before_audio_link: str, answerId
"beforeAudioLink": before_audio_link,
"answerId": answerId
}
requests.post("http://localhost:8080/api/spring/records/speeches", headers=headers, json=data)
requests.post("http://springboot:8080/api/spring/records/speeches", headers=headers, json=data)
# requests.post("https://peachmentor.com/api/spring/records/speeches", headers=headers, json=data)


def receive_self_feedback(token: str) -> str:
headers = {
"Authorization": f"Bearer {token}"
}
response = requests.get("http://localhost:8080/api/spring/self-feedbacks/latest-feedbacks", headers=headers)
response = requests.get("http://springboot:8080/api/spring/self-feedbacks/latest-feedbacks", headers=headers)
# response = requests.get("https://peachmentor.com/api/spring/self-feedbacks/latest-feedbacks", headers=headers)

feedback_data = response.json().get('result', {})
Expand All @@ -165,7 +166,7 @@ def send_statistics_to_spring(token: str, gantourCount: int, silentTime: float,
"silentTime": silentTime,
"answerId": answerId
}
requests.post("http://localhost:8080/api/spring/statistics", headers=headers, json=data)
requests.post("http://springboot:8080/api/spring/statistics", headers=headers, json=data)
# requests.post("https://peachmentor.com/api/spring/statistics", headers=headers, json=data)

# # 질문 답변에 대한 insight 제공 api
Expand Down Expand Up @@ -232,4 +233,4 @@ def send_statistics_to_spring(token: str, gantourCount: int, silentTime: float,
# analysis_gpt = AnalysisAssistant(questions=analysisRequestDto.questions, beforeScripts=analysisRequestDto.beforeScripts)
# analysis = analysis_gpt.get_analysis()
#
# return {"analysisText": analysis} # 데이터를 JSON 객체로 감쌈
# return {"analysisText": analysis} # 데이터를 JSON 객체로 감쌈
Binary file added audio/99ef70a1-0fe0-4b82-af32-9e32cbea7800.mp3
Binary file not shown.
Binary file added audio/d9da92b8-6a16-4886-bee1-2222a98a8cf3.mp3
Binary file not shown.
Binary file added audio/test8.mp3
Binary file not shown.
74 changes: 67 additions & 7 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,71 @@
import RPi.GPIO as GPIO
import time
from datetime import datetime

import uvicorn
# ───────────────────────────────
# PIR 센서 관련
# ───────────────────────────────
PIR_PIN = 17 # GPIO17

def detect_motion():
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIR_PIN, GPIO.IN)

print("PIR 센서 디버깅 시작 (Ctrl+C 종료)")
prev_state = None

try:
while True:
signal = GPIO.input(PIR_PIN)

if signal != prev_state:
timestamp = datetime.now().strftime("%H:%M:%S")
state_str = "감지됨 (HIGH)" if signal else " 없음 (LOW)"
print(f"[{timestamp}] 상태 변경 ▶ {state_str}")
prev_state = signal

time.sleep(0.1)
except KeyboardInterrupt:
print("⛔ 종료 중...")
GPIO.cleanup()


# ───────────────────────────────
# DHT11 센서 관련 (5회 재시도 버전)
# ───────────────────────────────
import adafruit_dht
import board

def read_dht11():
print("🌡️ DHT11 센서 측정 시작...")
dhtDevice = adafruit_dht.DHT11(board.D4) # GPIO4 (멀티보드 IO4)

for i in range(5): # 최대 5번 재시도
try:
print(f"📡 시도 {i + 1} ...")
temperature = dhtDevice.temperature
humidity = dhtDevice.humidity

if temperature is not None and humidity is not None:
print(f"✅ 온도: {temperature}°C")
print(f"✅ 습도: {humidity}%")
break
else:
print("⚠️ 센서로부터 데이터를 읽을 수 없습니다.")
except RuntimeError as error:
print(f"⚠️ 에러 발생: {error.args[0]}")
except Exception as error:
print(f"❌ 심각한 오류: {error}")
break
time.sleep(2) # 재시도 간 간격

# 종료 함수는 비활성화 (라이브러리 오류 방지)
# dhtDevice.exit()


# ───────────────────────────────
# 메인 함수
# ───────────────────────────────
if __name__ == "__main__":
uvicorn.run(
app="app.main:app",
host="localhost",
# host="0.0.0.0",
port=8000,
)
# detect_motion() # PIR 센서 테스트 시 주석 해제
read_dht11() # 현재는 DHT11만 테스트