diff --git a/build.gradle b/build.gradle index c4c5f8b..77e42d2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,52 +1,68 @@ plugins { - id 'java' - id 'org.springframework.boot' version '3.2.4' - id 'io.spring.dependency-management' version '1.1.4' + id 'java' + id 'org.springframework.boot' version '3.2.4' + id 'io.spring.dependency-management' version '1.1.4' } group = 'fairytale' version = '0.0.1-SNAPSHOT' java { - sourceCompatibility = '17' + sourceCompatibility = '17' } // build 이름 변경 -jar { - archiveBaseName = 'fairytale' - version = '0.0.1-SNAPSHOT' - enabled = false +jar{ + archiveBaseName = 'fairytale' + version = '0.0.1-SNAPSHOT' + enabled = false } configurations { - compileOnly { - extendsFrom annotationProcessor - } + compileOnly { + extendsFrom annotationProcessor + } } repositories { - mavenCentral() + mavenCentral() } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-web' - //logging - implementation 'org.springframework.boot:spring-boot-starter-log4j2' - modules { - module("org.springframework.boot:spring-boot-starter-logging") { - replacedBy("org.springframework.boot:spring-boot-starter-log4j2") - } - } + //logging + implementation 'org.springframework.boot:spring-boot-starter-log4j2' + modules { + module("org.springframework.boot:spring-boot-starter-logging") { + replacedBy("org.springframework.boot:spring-boot-starter-log4j2") + } + } - // ElevenLabs API - implementation('net.andrewcpu:elevenlabs-api:2.7.8') + // ElevenLabs API + implementation ('net.andrewcpu:elevenlabs-api:2.7.8') - // AWS - implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + // AWS + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.mysql:mysql-connector-j' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + // Face Swap API + implementation 'com.squareup.okhttp3:okhttp:3.10.0' + implementation 'org.json:json:20200518' + + // Face Swap API + implementation 'com.squareup.okhttp3:okhttp:3.10.0' + implementation 'org.json:json:20200518' + + // Face Swap API + implementation 'com.squareup.okhttp3:okhttp:3.10.0' + implementation 'org.json:json:20200518' // Spring Security OAUTH 2.1 implementation 'org.springframework.boot:spring-boot-starter-security' @@ -62,5 +78,5 @@ dependencies { } tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() } diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/converter/FaceDetectConverter.java b/src/main/java/fairytale/tbd/domain/faceSwap/converter/FaceDetectConverter.java new file mode 100644 index 0000000..aa7266c --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/converter/FaceDetectConverter.java @@ -0,0 +1,6 @@ +package fairytale.tbd.domain.faceSwap.converter; + +import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectRequestDto; + +public class FaceDetectConverter { +} \ No newline at end of file diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/entity/CustomCharacter.java b/src/main/java/fairytale/tbd/domain/faceSwap/entity/CustomCharacter.java new file mode 100644 index 0000000..d9e8616 --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/entity/CustomCharacter.java @@ -0,0 +1,40 @@ +package fairytale.tbd.domain.faceSwap.entity; + +import fairytale.tbd.domain.fairytale.entity.Fairytale; +import fairytale.tbd.domain.user.entity.User; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CustomCharacter { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long customId; + + private String customURL; + + @OneToOne + @JoinColumn(name = "userId") + private User userId; + + + @ManyToOne + @JoinColumn(name = "fairytale_id") + private Fairytale fairytaleId; + + public void setUserId(User userId) { + this.userId = userId; + } + + public void setFairytaleId(Fairytale fairytaleId) { + this.fairytaleId = fairytaleId; + } +} diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/entity/OriginalCharacter.java b/src/main/java/fairytale/tbd/domain/faceSwap/entity/OriginalCharacter.java new file mode 100644 index 0000000..4bbca20 --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/entity/OriginalCharacter.java @@ -0,0 +1,40 @@ +package fairytale.tbd.domain.faceSwap.entity; + +import fairytale.tbd.domain.fairytale.entity.Fairytale; +import fairytale.tbd.domain.user.entity.User; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OriginalCharacter { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long originId; + + private String originalURL; + + @ManyToOne + @JoinColumn(name = "fairytale_id") + private Fairytale fairytaleId; + + @ManyToOne + @JoinColumn(name = "userId") + private User user; + + public void setUser(User user){ + this.user = user; + } + + public void setFairytaleId(Fairytale fairytaleId) { + this.fairytaleId = fairytaleId; + } + +} diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/entity/Uuid.java b/src/main/java/fairytale/tbd/domain/faceSwap/entity/Uuid.java new file mode 100644 index 0000000..849e973 --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/entity/Uuid.java @@ -0,0 +1,18 @@ +package fairytale.tbd.domain.faceSwap.entity; + +import jakarta.persistence.*; +import lombok.*; +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) + +public class Uuid { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long uuidId; + + @Column(unique = true) + private String uuid; +} \ No newline at end of file diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/repository/CustomCharacterRepository.java b/src/main/java/fairytale/tbd/domain/faceSwap/repository/CustomCharacterRepository.java new file mode 100644 index 0000000..d0c2368 --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/repository/CustomCharacterRepository.java @@ -0,0 +1,7 @@ +package fairytale.tbd.domain.faceSwap.repository; + +import fairytale.tbd.domain.faceSwap.entity.CustomCharacter; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CustomCharacterRepository extends JpaRepository { +} diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/repository/OriginalCharacterRepository.java b/src/main/java/fairytale/tbd/domain/faceSwap/repository/OriginalCharacterRepository.java new file mode 100644 index 0000000..8704481 --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/repository/OriginalCharacterRepository.java @@ -0,0 +1,7 @@ +package fairytale.tbd.domain.faceSwap.repository; + +import fairytale.tbd.domain.faceSwap.entity.OriginalCharacter; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface OriginalCharacterRepository extends JpaRepository { +} diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/repository/UuidRepository.java b/src/main/java/fairytale/tbd/domain/faceSwap/repository/UuidRepository.java new file mode 100644 index 0000000..3fe33c4 --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/repository/UuidRepository.java @@ -0,0 +1,7 @@ +package fairytale.tbd.domain.faceSwap.repository; + +import fairytale.tbd.domain.faceSwap.entity.Uuid; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UuidRepository extends JpaRepository { +} diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceDetectApiService.java b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceDetectApiService.java new file mode 100644 index 0000000..423ff2a --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceDetectApiService.java @@ -0,0 +1,10 @@ +package fairytale.tbd.domain.faceSwap.service; + +import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectRequestDto; +import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectResponseDto; + +import java.io.IOException; + +public interface FaceDetectApiService { + FaceDetectResponseDto getOptFromFaceDetectApi(FaceDetectRequestDto faceDetectRequestDto) throws IOException; +} diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceDetectApiServiceImpl.java b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceDetectApiServiceImpl.java new file mode 100644 index 0000000..4ffae6f --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceDetectApiServiceImpl.java @@ -0,0 +1,85 @@ +package fairytale.tbd.domain.faceSwap.service; + +import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectRequestDto; +import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectResponseDto; +import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapRequestDto; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; +import java.util.logging.LogManager; +import java.util.logging.Logger; + + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class FaceDetectApiServiceImpl implements FaceDetectApiService{ + + @Value("${face.akool.apikey}") + private static String apiKey; + + + @Override + public FaceDetectResponseDto getOptFromFaceDetectApi(FaceDetectRequestDto faceDetectRequestDto) throws IOException { + + + String landmarkStr = ""; + + OkHttpClient client = new OkHttpClient(); + + MediaType mediaType = MediaType.parse("application/json"); + + String requestBodyJson = "{\n" + + " \"single_face\": true, \n" + + " \"image_url\": \"" + faceDetectRequestDto.getImgURL() + "\"\n" + + "}"; + + RequestBody body = RequestBody.create(mediaType, requestBodyJson); + + Request request = new Request.Builder() + .url("https://sg3.akool.com/detect") + .method("POST", body) + .addHeader("Authorization", "Bearer " + apiKey) + .addHeader("Content-Type", "application/json") + .build(); + + try (Response response = client.newCall(request).execute()){ + + if(!response.isSuccessful()){ + throw new IOException("Unexpected code " + response); + } + + String responseData = response.body().string(); + + JSONObject jsonObject = new JSONObject(responseData); + + int errCode = jsonObject.getInt("error_code"); + String errorMsg = jsonObject.getString("error_msg"); + + if(errCode!=0 || !errorMsg.equals("SUCCESS")){ + throw new IOException("Error! \n" + + "error code : " + + errCode + "\n" + + "error massage : " + + errorMsg + "\n"); + } + + landmarkStr = jsonObject.getString("landmarks_str"); + } catch (IOException e) { + + e.printStackTrace(); + + } + + return FaceDetectResponseDto.builder() + .photoUrl(faceDetectRequestDto.getImgURL()) + .opt(landmarkStr) + .build(); + } +} diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapApiService.java b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapApiService.java new file mode 100644 index 0000000..47e4307 --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapApiService.java @@ -0,0 +1,8 @@ +package fairytale.tbd.domain.faceSwap.service; + +import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectResponseDto; +import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapResponseDto; + +public interface FaceSwapApiService { + /*FaceSwapResponseDto*/void getFaceSwapImg(FaceDetectResponseDto faceDetectResponseDto); +} diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapApiServiceImpl.java b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapApiServiceImpl.java new file mode 100644 index 0000000..ae433a8 --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapApiServiceImpl.java @@ -0,0 +1,24 @@ +package fairytale.tbd.domain.faceSwap.service; + +import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectResponseDto; +import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapRequestDto; +import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapResponseDto; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class FaceSwapApiServiceImpl implements FaceSwapApiService{ + + // 여기에서 originalCharacter, customCharacter 둘 다에 사용할 수 있게끔 한다. + @Override + @Transactional + public void getFaceSwapImg(FaceDetectResponseDto faceDetectResponseDto){ + FaceSwapRequestDto.FaceSwapRequest faceSwapRequest = new FaceSwapRequestDto.FaceSwapRequest(); + + } +} \ No newline at end of file diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/service/PhotoUploadService.java b/src/main/java/fairytale/tbd/domain/faceSwap/service/PhotoUploadService.java new file mode 100644 index 0000000..3f14b28 --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/service/PhotoUploadService.java @@ -0,0 +1,11 @@ +package fairytale.tbd.domain.faceSwap.service; + +import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectRequestDto; +import fairytale.tbd.domain.user.entity.User; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +public interface PhotoUploadService { + FaceDetectRequestDto savePhotos(User userId, MultipartFile photoUploadRequestDto) throws IOException; +} diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/service/PhotoUploadServiceImpl.java b/src/main/java/fairytale/tbd/domain/faceSwap/service/PhotoUploadServiceImpl.java new file mode 100644 index 0000000..dcb2d8d --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/service/PhotoUploadServiceImpl.java @@ -0,0 +1,46 @@ +package fairytale.tbd.domain.faceSwap.service; + +import fairytale.tbd.domain.faceSwap.entity.Uuid; +import fairytale.tbd.domain.faceSwap.repository.UuidRepository; +import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectRequestDto; +import fairytale.tbd.domain.user.entity.User; +import fairytale.tbd.global.aws.s3.AmazonS3Manager; +import org.springframework.transaction.annotation.Transactional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.UUID; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class PhotoUploadServiceImpl implements PhotoUploadService { + + private final UuidRepository uuidRepository; + private final AmazonS3Manager amazonS3Manager; + + @Override + @Transactional + public FaceDetectRequestDto savePhotos(User userId, MultipartFile file) throws IOException { + String imgURL = ""; + String uuid = UUID.randomUUID().toString(); + + try { + Uuid saveUuid = uuidRepository.save(Uuid.builder().uuid(uuid).build()); + byte[] imageData = file.getBytes(); + imgURL = amazonS3Manager.uploadFile(saveUuid.getUuid(), file); + } catch (IOException e) { + throw new RuntimeException(e); + } + + FaceDetectRequestDto faceDetectRequestDto = new FaceDetectRequestDto(); + faceDetectRequestDto.setImgURL(imgURL); + + return faceDetectRequestDto; + } + +} \ No newline at end of file diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/web/controller/FaceSwapRestController.java b/src/main/java/fairytale/tbd/domain/faceSwap/web/controller/FaceSwapRestController.java new file mode 100644 index 0000000..426fad9 --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/web/controller/FaceSwapRestController.java @@ -0,0 +1,56 @@ +package fairytale.tbd.domain.faceSwap.web.controller; + +import fairytale.tbd.domain.faceSwap.service.FaceDetectApiServiceImpl; +import fairytale.tbd.domain.faceSwap.service.FaceSwapApiServiceImpl; +import fairytale.tbd.domain.faceSwap.service.PhotoUploadServiceImpl; +import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectRequestDto; +import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectResponseDto; +import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapRequestDto; +import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapResponseDto; +import fairytale.tbd.domain.user.entity.User; +import fairytale.tbd.global.annotation.LoginUser; +import fairytale.tbd.global.response.ApiResponse; +import lombok.RequiredArgsConstructor; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@RestController +@RequestMapping("/faceSwap") +@RequiredArgsConstructor +public class FaceSwapRestController { + + private final Logger LOGGER = LogManager.getLogger(FaceSwapRestController.class); + private final PhotoUploadServiceImpl photoUploadService; + private final FaceDetectApiServiceImpl faceDetectApiService; + private final FaceSwapApiServiceImpl faceSwapApiService; + + @PostMapping("/uploadImg") + public ApiResponse uploadImg(@LoginUser User user, @ModelAttribute MultipartFile file) throws IOException{ + FaceDetectRequestDto faceDetectRequestDto = photoUploadService.savePhotos(user, file); + return ApiResponse.onSuccess(faceDetectRequestDto); + } + + + @PostMapping("/test") + public ApiResponse test(@RequestBody FaceDetectRequestDto faceDetectRequestDto){ + + LOGGER.info("Face detect request : {}", faceDetectRequestDto); + + + FaceDetectResponseDto optFromFaceDetectApi = null; + + try{ + optFromFaceDetectApi = faceDetectApiService.getOptFromFaceDetectApi(faceDetectRequestDto); + } + catch( Exception e){ + e.printStackTrace(); + } + + + return ApiResponse.onSuccess(optFromFaceDetectApi); + } +} \ No newline at end of file diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceDetectRequestDto.java b/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceDetectRequestDto.java new file mode 100644 index 0000000..2b563cd --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceDetectRequestDto.java @@ -0,0 +1,14 @@ +package fairytale.tbd.domain.faceSwap.web.dto; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public class FaceDetectRequestDto { + + private String imgURL; + +} \ No newline at end of file diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceDetectResponseDto.java b/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceDetectResponseDto.java new file mode 100644 index 0000000..d944b0c --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceDetectResponseDto.java @@ -0,0 +1,11 @@ +package fairytale.tbd.domain.faceSwap.web.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class FaceDetectResponseDto { + private String photoUrl; + private String opt; +} \ No newline at end of file diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceSwapRequestDto.java b/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceSwapRequestDto.java new file mode 100644 index 0000000..2a71abc --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceSwapRequestDto.java @@ -0,0 +1,54 @@ +package fairytale.tbd.domain.faceSwap.web.dto; + +import lombok.*; + +public class FaceSwapRequestDto { + + @Getter + @Setter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class FaceSwapRequest { + + private SourceImage sourseImage; + + private TargetImage targetImage; + + // 1 means open, 0 means close + private int faceEnhance; + + private String modifyImage; + + private String webHookUrl; + + } + + // Original Image + @Getter + @Setter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SourceImage { + + private String sourcePath; + + private String sourceOpts; + + } + + // User Image + @Getter + @Setter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class TargetImage { + + private String targetPath; + + private String targetOpts; + + } +} \ No newline at end of file diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceSwapResponseDto.java b/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceSwapResponseDto.java new file mode 100644 index 0000000..398f841 --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/web/dto/FaceSwapResponseDto.java @@ -0,0 +1,11 @@ +package fairytale.tbd.domain.faceSwap.web.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class FaceSwapResponseDto { + private Long fairytaleID; + +} \ No newline at end of file