Skip to content

Commit 844156d

Browse files
author
2ewyeonwoo3
committed
Merge branch 'prod_aws' of https://github.com/stonylion/Backend into prod_aws
2 parents 94fe65a + d0d9e78 commit 844156d

3 files changed

Lines changed: 126 additions & 14 deletions

File tree

Dstonylion/AI/views.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os, json, base64, re, random, uuid, tempfile
33
from uuid import uuid4
44
from pathlib import Path
5+
from urllib.parse import urlparse
56

67
import boto3
78
from botocore.exceptions import ClientError
@@ -514,7 +515,8 @@ def get(self, request, story_id, page_id):
514515
return Response({"error": "해당 페이지의 삽화가 없습니다."},
515516
status=status.HTTP_404_NOT_FOUND)
516517

517-
s3_key = illustration.image.name # 실제 S3 내부 경로
518+
file_url = illustration.image.url
519+
s3_key = s3_key = urlparse(file_url).path.lstrip('/')
518520
bucket = settings.AWS_STORAGE_BUCKET_NAME
519521
region = settings.AWS_S3_REGION_NAME
520522

@@ -526,22 +528,26 @@ def get(self, request, story_id, page_id):
526528
region_name=region
527529
)
528530

531+
filename = f"story_{story_id}_page_{page_id}.png"
532+
529533
try:
530534
presigned_url = s3_client.generate_presigned_url(
531535
ClientMethod='get_object',
532-
Params={'Bucket': bucket, 'Key': s3_key},
536+
Params={'Bucket': bucket, 'Key': s3_key, 'ResponseContentDisposition': f'attachment; filename="{filename}"'},
533537
ExpiresIn=60 * 10 # 10분 유효
534538
)
535539
except Exception as e:
536540
return Response({"error": f"URL 생성 실패: {str(e)}"},
537541
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
542+
543+
print("DEBUG s3 key:", s3_key)
538544

539545
return Response({
540546
"story_id": story_id,
541547
"page_id": page_id,
542548
"download_url": presigned_url
543549
}, status=200)
544-
550+
545551
HARD_MIN_USER_TOKENS = 200
546552
FINALIZE_SUFFIX = "이제 결말을 확장해도 될까?"
547553

Dstonylion/story/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
urlpatterns = [
55
path('options/', StoryOptionSaveView.as_view()),
6-
path("draft/", StoryDraftUpdateView.as_view()),
6+
path("draft/", StoryDraftUpdateView.as_view()),
7+
path('draft_stt/', StoryDraftAudioAppendView.as_view()),
78
path('morals/', MoralThemeListView.as_view()),
89
path('story-morals/', StoryMoralSaveView.as_view()),
910
path('story-morals/recommend/', RecommendMoralView.as_view()),

Dstonylion/story/views.py

Lines changed: 115 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import redis, random, os, json, re
1+
import redis, random, os, json, re, tempfile
22
import openai
3+
from openai import OpenAI
4+
from pathlib import Path
35
from django.conf import settings
46
from django.core.files import File
57
from django.shortcuts import render
68
from rest_framework.views import APIView
79
from rest_framework.response import Response
810
from rest_framework.permissions import IsAuthenticated
11+
from rest_framework.parsers import MultiPartParser, FormParser
912

1013
from .models import *
1114
from .serializers import *
@@ -28,6 +31,8 @@
2831

2932
load_dotenv(settings.BASE_DIR/ ".env")
3033
# openai.api_key = os.getenv("OPENAI_API_KEY")
34+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
35+
client = OpenAI(api_key=OPENAI_API_KEY)
3136

3237
User = get_user_model()
3338

@@ -57,20 +62,89 @@ def post(self, request):
5762
port=getattr(settings, "REDIS_PORT", 6379),
5863
db=0,
5964
decode_responses=True,
60-
ssl=True,
65+
ssl=False,
6166
)
6267
redis_client.hset(f"story_option:{request.user.id}", mapping={"runtime": runtime, "age_group": age_group})
6368

6469
return Response({"next": "/story/record/"}, status=status.HTTP_200_OK)
65-
70+
71+
def append_draft(user_id, new_text):
72+
redis_client = redis.StrictRedis(
73+
host=getattr(settings, "REDIS_HOST", "localhost"),
74+
port=getattr(settings, "REDIS_PORT", 6379),
75+
db=0,
76+
decode_responses=True,
77+
ssl=False,
78+
)
79+
80+
key = f"story_draft:{user_id}"
81+
existing = redis_client.get(key) or ""
82+
83+
# 기존 draft 뒤에 자연스럽게 이어 붙이기
84+
if existing:
85+
updated = existing.rstrip() + " " + new_text.strip()
86+
else:
87+
updated = new_text.strip()
88+
89+
redis_client.set(key, updated)
90+
return updated
91+
92+
class StoryDraftAudioAppendView(APIView):
93+
permission_classes = [IsAuthenticated]
94+
parser_classes = [MultiPartParser, FormParser]
95+
96+
def post(self, request):
97+
audio = request.FILES.get("audio")
98+
if not audio:
99+
return Response({"error": "audio 파일이 필요합니다."}, status=400)
100+
101+
# 1) 파일 임시 저장
102+
suffix = Path(audio.name).suffix or ".mp3"
103+
with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp:
104+
for chunk in audio.chunks():
105+
tmp.write(chunk)
106+
tmp_path = tmp.name
107+
108+
# 2) Whisper-1 호출
109+
try:
110+
with open(tmp_path, "rb") as f:
111+
transcript = client.audio.transcriptions.create(
112+
model="whisper-1",
113+
file=f
114+
)
115+
text = transcript.text.strip()
116+
except Exception as e:
117+
return Response({"error": f"STT 실패: {str(e)}"}, status=500)
118+
119+
# 3) Redis draft에 append
120+
redis_client = redis.StrictRedis(
121+
host=getattr(settings, "REDIS_HOST", "localhost"),
122+
port=getattr(settings, "REDIS_PORT", 6379),
123+
db=0,
124+
decode_responses=True,
125+
)
126+
127+
key = f"story_draft:{request.user.id}"
128+
existing = redis_client.get(key) or ""
129+
130+
updated = (existing + " " + text).strip()
131+
132+
redis_client.set(key, updated)
133+
134+
return Response({
135+
"message": "음성이 변환되어 draft에 추가되었습니다.",
136+
"recognized_text": text,
137+
"draft": updated
138+
}, status=200)
139+
66140
class StoryDraftUpdateView(APIView):
67141
"""
68142
사용자가 텍스트 입력으로 최종 입력한 내용을 Redis에 저장하는 API
69143
POST /api/story/draft/
70144
"""
71145
permission_classes = [IsAuthenticated]
72146

73-
def post(self, request):
147+
'''def post(self, request):
74148
text = request.data.get("text")
75149
76150
if not text:
@@ -85,7 +159,7 @@ def post(self, request):
85159
port=getattr(settings, "REDIS_PORT", 6379),
86160
db=0,
87161
decode_responses=True,
88-
ssl=True,
162+
ssl=False,
89163
)
90164
91165
redis_key = f"story_draft:{request.user.id}"
@@ -96,7 +170,38 @@ def post(self, request):
96170
return Response(
97171
{"message": "draft 업데이트 완료되었습니다."},
98172
status=200
173+
)'''
174+
175+
def put(self, request):
176+
text = request.data.get("text", "").strip()
177+
178+
if text is None:
179+
return Response({"error": "text는 필수 항목입니다."}, status=400)
180+
181+
redis_client = redis.StrictRedis(
182+
host=getattr(settings, "REDIS_HOST", "localhost"),
183+
port=getattr(settings, "REDIS_PORT", 6379),
184+
db=0,
185+
decode_responses=True,
186+
)
187+
188+
redis_client.set(f"story_draft:{request.user.id}", text)
189+
190+
return Response({
191+
"message": "Draft 전체가 저장되었습니다.",
192+
"draft": text
193+
}, status=200)
194+
195+
def get(self, request):
196+
redis_client = redis.StrictRedis(
197+
host=getattr(settings, "REDIS_HOST", "localhost"),
198+
port=getattr(settings, "REDIS_PORT", 6379),
199+
db=0,
200+
decode_responses=True,
201+
ssl=False,
99202
)
203+
draft = redis_client.get(f"story_draft:{request.user.id}") or ""
204+
return Response({"draft": draft}, status=200)
100205

101206

102207
DEFAULT_MORALS = [
@@ -239,7 +344,7 @@ def post(self, request):
239344
port=getattr(settings, "REDIS_PORT", 6379),
240345
db=0,
241346
decode_responses=True,
242-
ssl=True,
347+
ssl=False,
243348
)
244349
user_id = request.user.id
245350
redis_key = f"story_morals:{user_id}"
@@ -282,7 +387,7 @@ def post(self, request):
282387
port=getattr(settings, "REDIS_PORT", 6379),
283388
db=0,
284389
decode_responses=True,
285-
ssl=True,
390+
ssl=False,
286391
)
287392

288393
user_id = request.user.id
@@ -393,7 +498,7 @@ def post(self, request):
393498
port=getattr(settings, "REDIS_PORT", 6379),
394499
db=0,
395500
decode_responses=True,
396-
ssl=True,
501+
ssl=False,
397502
)
398503

399504
keys = [
@@ -839,7 +944,7 @@ def get(self, request):
839944
}
840945

841946
return Response(response, status=200)
842-
947+
843948
class StoryNDWReportView(APIView):
844949
permission_classes = [IsAuthenticated]
845950

@@ -915,4 +1020,4 @@ def post(self, request):
9151020
"result": result,
9161021
"rationale": rationale,
9171022
"report": report
918-
})
1023+
})

0 commit comments

Comments
 (0)