Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Cast API 머지 #46

Merged
merged 49 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0bc3fa5
fix: Cast 엔티티 수정
ja7811 Jul 20, 2024
c0cd105
add: tts 기능 구현 용 파일 추가
yuuddin Jul 24, 2024
333bbb5
fix: 파일 구조 오류 수정
yuuddin Jul 24, 2024
330db76
fix: tts 로직 임시 구현(파싱 로직 구현 후 refactoring 예정)
yuuddin Jul 27, 2024
0586b1b
add: dto로 요청 값 받기
yuuddin Jul 27, 2024
6c65904
Merge branch 'feat/cast-api/11' into feat/tts/13
yuuddin Jul 27, 2024
177dd86
feat: 스크립트 생성 기능 (#21)
ja7811 Jul 27, 2024
aff518b
Merge branch 'feat/cast-api/11' into feat/tts/13
yuuddin Jul 27, 2024
5829b10
Merge pull request #20 from umc-owncast/feat/tts/13
yuuddin Jul 27, 2024
ea3ab1a
add: @구분자를 통한 parsing 로직 구현
yuuddin Jul 28, 2024
80c83cd
add: script parsing 후 time point marking 후 ssml요청 로직 구현
yuuddin Jul 28, 2024
995df8c
add: ssml 결과물(mp3) 파일 프로젝트 내부(임시)에 저장 로직 구현
yuuddin Jul 28, 2024
6dcab33
fix: 코드 정리
yuuddin Jul 28, 2024
97ec99d
add: sentence에 timepoint column 추가
yuuddin Jul 28, 2024
7e5868c
Merge pull request #23 from umc-owncast/feat/parsing/22
yuuddin Jul 28, 2024
d87b137
update: mp3 파일 무시
ja7811 Jul 28, 2024
8a8fc7d
add: script 번역 기능 구현 (#28)
yuuddin Aug 4, 2024
08a1772
feat: S3 관련 파일 업로드 설정 (#34)
yuuddin Aug 6, 2024
08c899f
feat: 캐스트 재생 (스트리밍) (#33)
ja7811 Aug 6, 2024
df5bb53
add : sentence entity 저장 로직
Aug 7, 2024
9bac90e
add : voice code enum 처리
Aug 7, 2024
cfaaf27
[FEATURE] Voice Enum 처리 & Sentence save 로직 구현 (#36)
yuuddin Aug 8, 2024
f544418
add: 필요한 DTO/서비스/리포지토리 선언
ja7811 Aug 8, 2024
c9e525e
add: Cast 필요 메소드
ja7811 Aug 8, 2024
5e37a1e
feat: cast api 구현
ja7811 Aug 9, 2024
c9824e3
fix: Cast에 Sentence 매핑 추가 + 연관관계 편의 메소드
ja7811 Aug 9, 2024
e442991
fix: 컬렉션 초기화 안되는 문제
ja7811 Aug 9, 2024
e9cd15f
update: API가 엔티티 대신 dto 반환하도록 변경
ja7811 Aug 9, 2024
401e186
add: DTO 클래스 추가
ja7811 Aug 9, 2024
4333195
feat: 캐스트 스크립트 검색 API 추가
ja7811 Aug 9, 2024
99df443
Merge branch 'feat/cast-api-completion' of https://github.com/umc-own…
Aug 10, 2024
dddc129
feat: Cast API 구현 (#37)
ja7811 Aug 10, 2024
74fffc9
fix : cast 생성 로직 수정
Aug 10, 2024
a978d52
add : cast audi length 입력
Aug 10, 2024
7eca235
fix: multipartfile 입력 형태 수정
Aug 10, 2024
05d44db
fix: 코드 정리
Aug 10, 2024
1e8557e
fix: CastScriptDTO에 스크립트가 비는 문제 해결
ja7811 Aug 10, 2024
238bc4f
add: StringUtil 추가
ja7811 Aug 10, 2024
2c4ffce
add: deleteCast 추가, setCastImage 추가
ja7811 Aug 10, 2024
4536427
fix: update 조건 수정
ja7811 Aug 10, 2024
c75d73a
fix: CastPlaylist 동일한 행 존재하면 기각
ja7811 Aug 10, 2024
0b6b663
fix: Long playlistId 검사조건 @NotNull로 변경
ja7811 Aug 10, 2024
0277b92
update: 수정, 저장, 삭제 API 추가 + @RequestPart 오류 수정 + stream에 @CrossOrigin 추가
ja7811 Aug 10, 2024
6355c88
fix: stream 기능 업데이트
ja7811 Aug 10, 2024
35d6d2f
fix: cast-api 브랜치와 머지
ja7811 Aug 10, 2024
28d8d12
feat: Cast API 완성 (#42)
ja7811 Aug 11, 2024
96021af
Merge branch 'feat/cast-api/11' of https://github.com/umc-owncast/Own…
ja7811 Aug 11, 2024
6e46154
Merge branch 'develop' into feat/cast-api/11
ja7811 Aug 11, 2024
0fbdb0a
fix: 머지 중 오류 변경
ja7811 Aug 11, 2024
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ out/
.vscode/

### 추가로 무시할 파일 ###
*.yml
*.yml
*.mp3
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ dependencies {
// OpenAI API 라이브러리
implementation 'com.theokanning.openai-gpt3-java:service:0.18.2'
implementation 'com.google.code.gson:gson:2.9.0'

//jwt
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
Expand Down
1 change: 0 additions & 1 deletion src/main/java/com/umc/owncast/OwncastApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

@EnableJpaAuditing
@SpringBootApplication
@EnableJpaAuditing
public class OwncastApplication {

public static void main(String[] args) {
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/umc/owncast/common/config/AppConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.umc.owncast.common.config;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/umc/owncast/common/config/AwsS3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.umc.owncast.common.config;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AwsS3Config {
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;

@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;

@Value("${cloud.aws.region.static}")
private String region;

@Bean
public AmazonS3Client amazonS3Client() {
AWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey);
return (AmazonS3Client) AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCreds))
.build();
}
}
17 changes: 16 additions & 1 deletion src/main/java/com/umc/owncast/common/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.umc.owncast.common.config;

import com.umc.owncast.common.jwt.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springdoc.webmvc.core.service.RequestService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
Expand All @@ -17,6 +17,12 @@
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;


@Configuration
@EnableWebSecurity //기본적인 웹보안 활성화
Expand Down Expand Up @@ -56,4 +62,13 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}

CorsConfigurationSource apiConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*")); // 이후 수정
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
10 changes: 8 additions & 2 deletions src/main/java/com/umc/owncast/common/entity/BaseTimeEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@
import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class) // Auditing 기능 포함
@EntityListeners(AuditingEntityListener.class)
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public abstract class BaseTimeEntity {

@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;

@LastModifiedDate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ public enum ErrorCode implements BaseErrorCode {
OUTPUT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "AUTH_5000", "서버 출력에 오류가 있습니다. 관리자에게 문의하세요"),

BOOKMARK_NOT_EXIST(HttpStatus.BAD_REQUEST, "BOOKMARK4001", "존재하지 않는 북마크입니다."),
BOOKMARK_ALREADY_EXIST(HttpStatus.BAD_REQUEST, "BOOKMARK4002", "이미 존재하는 북마크입니다.")
BOOKMARK_ALREADY_EXIST(HttpStatus.BAD_REQUEST, "BOOKMARK4002", "이미 존재하는 북마크입니다."),


// 캐스트 관련 에러
REQUEST_TIMEOUT(HttpStatus.REQUEST_TIMEOUT, "CAST4001", "캐스트 생성시간이 너무 오래 걸립니다."),

// 기타 에러는 아래에 추가
;

Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/umc/owncast/common/util/StringUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.umc.owncast.common.util;

import java.util.Objects;

public class StringUtil {
public static boolean isBlank(String s){
return Objects.isNull(s) || s.isBlank();
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
package com.umc.owncast.domain.cast.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.theokanning.openai.completion.chat.ChatCompletionRequest;
import com.umc.owncast.domain.cast.service.ChatGPTPromptGenerator;
import com.umc.owncast.domain.cast.service.KeywordService;
import com.umc.owncast.common.response.ApiResponse;
import com.umc.owncast.domain.cast.dto.CastSaveDTO;
import com.umc.owncast.domain.cast.dto.CastUpdateDTO;
import com.umc.owncast.domain.cast.dto.KeywordCastCreationDTO;
import com.umc.owncast.domain.cast.dto.ScriptCastCreationDTO;
import com.umc.owncast.domain.cast.service.CastService;
import com.umc.owncast.domain.cast.service.ScriptService;
import com.umc.owncast.domain.cast.service.StreamService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.umc.owncast.domain.cast.service.KeywordService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.w3c.dom.stylesheets.LinkStyle;

import java.util.List;

Expand All @@ -21,6 +32,108 @@
@RequestMapping("/api/cast")
public class CastController {
private final KeywordService keywordService;
private final CastService castService;
private final ScriptService scriptService;
private final StreamService streamService;

/* * * * * * * * * * * * * *
* 테스트용 메소드 (나중에 삭제) *
* * * * * * * * * * * * * **/

@PostMapping("/script-test")
@Operation(summary = "스크립트 생성 API (ScriptService 테스트용)")
public String createScript(@Valid @RequestBody KeywordCastCreationDTO castRequest){
System.out.println(castRequest);
return scriptService.createScript(castRequest);
}

/*cast 저장 전 api
@PostMapping("/temporary")
@Operation(summary = "스크립트 생성 api. 저장 버튼 전 화면 입니다.")
public void createCast(@Valid @RequestBody KeywordCastCreationDTO castRequest){
castService.createCast(castRequest);
}*/

/*@GetMapping("/stream-test")
@CrossOrigin(origins = "*") // TODO 프론트 url로 대체
@Operation(summary = "스트리밍 테스트. 테스트용 음악 파일을 스트리밍 합니다")
public Object streamTest(@RequestHeader HttpHeaders headers) throws IOException {
System.out.println("Stream test");
return streamService.stream("test.mp3", headers);
}

@GetMapping("/stream/{filename}")
@CrossOrigin(origins = "*")
@Operation(summary = "filename을 스트리밍합니다")
public Object streamTest(@RequestHeader HttpHeaders headers,
@PathVariable(name = "filename") String filename) throws IOException {
return streamService.stream(filename, headers);
}*/


/* * * * * * * *
* API 용 메소드 *
* * * * * * * **/

/* Cast 생성 API (keyword) */
@PostMapping("/keyword")
@Operation(summary = "키워드로 캐스트를 생성하는 API")
public ApiResponse<Object> createCastByKeyword(@Valid @RequestBody KeywordCastCreationDTO castRequest){
return castService.createCastByKeyword(castRequest);
}

/* Cast 생성 API (script) */
@PostMapping("/script")
@Operation(summary = "스크립트로 캐스트를 생성하는 API.")
public ApiResponse<Object> createCastByScript(@Valid @RequestBody ScriptCastCreationDTO castRequest){
return castService.createCastByScript(castRequest);
}

/* Cast 저장 API */
@PostMapping(value = "/{castId}", consumes = {MediaType.APPLICATION_JSON_VALUE , MediaType.MULTIPART_FORM_DATA_VALUE})
@Operation(summary = "캐스트 저장 API (저장 화면에서 호출)")
public ApiResponse<Object> saveCast(@PathVariable("castId") Long castId,
@Valid @RequestPart(value = "saveInfo") CastSaveDTO saveRequest,
@RequestPart(value = "image", required = false) MultipartFile image){
System.out.println("CastController: save()");
System.out.println(saveRequest);
System.out.println(image);
return castService.saveCast(castId, saveRequest, image);
}

/* Cast 재생 API */
@GetMapping("/{castId}/audio")
@Operation(summary = "캐스트 재생 API")
@CrossOrigin
public ResponseEntity<UrlResource> streamCast(@PathVariable("castId") Long castId,
@RequestHeader HttpHeaders headers){
return castService.streamCast(castId, headers);
}

/* Cast 스크립트 가져오는 API */
@GetMapping("/{castId}/scripts")
@Operation(summary = "캐스트 스크립트 가져오기 API")
public ApiResponse<Object> fetchCastScripts(@PathVariable("castId") Long castId){
return castService.fetchCastScript(castId);
}

/* Cast 수정 API */
@PatchMapping("/{castId}")
@Operation(summary = "캐스트 수정 API")
public ApiResponse<Object> updateCast(@PathVariable("castId") Long castId,
@Valid @RequestPart(value = "updateInfo") CastUpdateDTO updateRequest,
@RequestPart(value = "image", required = false) MultipartFile image){
// TODO 캐스트 생성자 혹은 관리자여야 함
return castService.updateCast(castId, updateRequest, image);
}

/* Cast 삭제 API */
@DeleteMapping("/{castId}")
@Operation(summary = "캐스트 삭제 API")
public ApiResponse<Object> deleteCast(@PathVariable("castId") Long castId){
// TODO 캐스트 생성자 혹은 관리자여야 함
return castService.deleteCast(castId);
}

@GetMapping("/home")
@Operation(summary = "홈 화면 키워드 6개 받아오기")
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/com/umc/owncast/domain/cast/dto/CastDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.umc.owncast.domain.cast.dto;

import com.umc.owncast.domain.cast.entity.Cast;
import com.umc.owncast.domain.enums.Formality;
import com.umc.owncast.domain.sentence.dto.SentenceResponseDTO;
import lombok.*;

import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class CastDTO {
private Long id;
private String title;
private String imagePath;
private String audioLength;
private String voice;
private Formality formality;
private Boolean isPublic;
private Long hits;
private List<SentenceResponseDTO> sentences;

public CastDTO(Cast cast){
id = cast.getId();
title = cast.getTitle();
imagePath = cast.getImagePath();
audioLength = cast.getAudioLength();
voice = cast.getVoice();
formality = cast.getFormality();
isPublic = cast.getIsPublic();
hits = cast.getHits();
sentences = cast.getSentences().stream()
.map(SentenceResponseDTO::new)
.toList();
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/umc/owncast/domain/cast/dto/CastSaveDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.umc.owncast.domain.cast.dto;

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.*;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class CastSaveDTO {
// 제목 커버이미지 카테고리 공개 여부
@NotEmpty
private String title;

@NotNull
private Long playlistId;

private Boolean isPublic;
}
24 changes: 24 additions & 0 deletions src/main/java/com/umc/owncast/domain/cast/dto/CastScriptDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.umc.owncast.domain.cast.dto;

import com.umc.owncast.domain.cast.entity.Cast;
import com.umc.owncast.domain.sentence.dto.SentenceResponseDTO;
import lombok.*;

import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class CastScriptDTO {
private Long id;
private List<SentenceResponseDTO> sentences;

public CastScriptDTO(Cast cast){
id = cast.getId();
sentences = cast.getSentences().stream()
.map(SentenceResponseDTO::new)
.toList();
}
}
Loading
Loading