diff --git a/src/dtos/aiSummaryDto.ts b/src/dtos/aiSummaryDto.ts index 9b562db..65fb91b 100644 --- a/src/dtos/aiSummaryDto.ts +++ b/src/dtos/aiSummaryDto.ts @@ -13,6 +13,16 @@ export interface EmotionItem { percentage: number; // 퍼센트 값 (0-100) } +/** + * 하이라이트(북마크) 항목 + */ +export interface HighlightItem { + timeline: string; // "13:28" 형식 + content: string; // 북마크 메시지 + userId: number; + nickname: string; +} + /** * AI 채팅 요약 응답 DTO */ @@ -21,7 +31,8 @@ export interface GenerateSummaryResponseDto { roomTitle: string; videoTitle: string; topicSummary: string; - emotionAnalysis: EmotionItem[]; // string에서 배열로 변경 + emotionAnalysis: EmotionItem[]; + highlights: HighlightItem[]; timestamp: string; } diff --git a/src/routes/aiSummaryRoutes.ts b/src/routes/aiSummaryRoutes.ts index d3ac203..42e65c1 100644 --- a/src/routes/aiSummaryRoutes.ts +++ b/src/routes/aiSummaryRoutes.ts @@ -50,10 +50,19 @@ const router = express.Router(); * example: 영상제목 * topicSummary: * type: string - * example: 전체 대화 주제 요약 + * example: 영상을 보며 즐거운 시간을 보냈네요! * emotionAnalysis: * type: array * description: 감정별 분석 결과 + * items: + * type: object + * properties: + * emotion: + * type: string + * example: 기쁨 + * percentage: + * type: number + * example: 40 * example: * - emotion: "기쁨" * percentage: 40 @@ -63,6 +72,37 @@ const router = express.Router(); * percentage: 20 * - emotion: "슬픔" * percentage: 10 + * highlights: + * type: array + * description: 북마크 하이라이트 리스트 + * items: + * type: object + * properties: + * timeline: + * type: string + * example: "13:28" + * content: + * type: string + * example: "재밌는 장면" + * userId: + * type: number + * example: 123 + * nickname: + * type: string + * example: "사용자닉네임" + * example: + * - timeline: "13:28" + * content: "재밌는 장면" + * userId: 123 + * nickname: "홍길동" + * - timeline: "27:18" + * content: "감동적인 부분" + * userId: 456 + * nickname: "김철수" + * - timeline: "1:02:35" + * content: "결정적 순간" + * userId: 789 + * nickname: "이영희" * timestamp: * type: string * format: date-time diff --git a/src/services/aiSummaryService.ts b/src/services/aiSummaryService.ts index bee4859..feb128b 100644 --- a/src/services/aiSummaryService.ts +++ b/src/services/aiSummaryService.ts @@ -13,6 +13,7 @@ import { ClaudeResponseDto, AISummaryFeedbackData, EmotionItem, + HighlightItem, } from '../dtos/aiSummaryDto.js'; const BEDROCK_MODEL_ID = 'anthropic.claude-3-5-sonnet-20240620-v1:0'; @@ -105,10 +106,47 @@ export class AiSummaryService { .join('\n') .slice(0, MAX_CONTENT_LENGTH); - // 6. Claude 3.5 Sonnet 모델 호출 + // 6. 북마크 조회 추가 + const bookmarks = await prisma.bookmark.findMany({ + where: { roomId: data.roomId }, + include: { + user: { + select: { + userId: true, + nickname: true, + }, + }, + }, + orderBy: { timeline: 'asc' }, // 시간순 정렬 + }); + + // 북마크를 하이라이트 형시ㄱ + const highlights: HighlightItem[] = bookmarks.map(bookmark => { + const totalSeconds = bookmark.timeline || 0; + const hours = Math.floor(totalSeconds / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const seconds = totalSeconds % 60; + + // HH:MM:SS 또는 MM:SS 형식으로 변환 + let timeline = ''; + if (hours > 0) { + timeline = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; + } else { + timeline = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; + } + + return { + timeline, + content: bookmark.content || '', + userId: bookmark.user.userId, + nickname: bookmark.user.nickname, + }; + }); + + // 7. Claude 3.5 Sonnet 모델 호출 (기존 코드와 동일) const summary = await this.callClaudeModel(chatContent, video.title); - // 7. 임시 summaryId 생성 + // 8. 임시 summaryId 생성 const summaryId = `summary_${data.roomId}_${randomUUID()}`; return { @@ -117,6 +155,7 @@ export class AiSummaryService { videoTitle: video.title, topicSummary: summary.topicSummary, emotionAnalysis: summary.emotionAnalysis, + highlights, // 북마크 리스트 timestamp: new Date().toISOString(), }; }