diff --git a/.gitignore b/.gitignore index 47e46733..d2f225f8 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,10 @@ out/ CLAUDE.md db_dev.mv.db db_dev.trace.db + +cookies.txt + +.DS_Store + +### QueryDSL Q클래스 ### +**/generated/ \ No newline at end of file diff --git a/back/.env.default b/back/.env.default index 47d2f175..da8cf60b 100644 --- a/back/.env.default +++ b/back/.env.default @@ -1 +1 @@ -CUSTOM__JWT__SECRET_KEY=NEED_TO_SET \ No newline at end of file +CUSTOM__JWT__SECRET_KEY=abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789 diff --git a/back/build.gradle.kts b/back/build.gradle.kts index 9ba7ee2a..ed959ef9 100644 --- a/back/build.gradle.kts +++ b/back/build.gradle.kts @@ -1,10 +1,12 @@ +import org.gradle.kotlin.dsl.implementation + plugins { java id("org.springframework.boot") version "3.5.5" id("io.spring.dependency-management") version "1.1.7" } -group = "com.ll" +group = "com" version = "0.0.1-SNAPSHOT" description = "back" @@ -25,21 +27,45 @@ repositories { } dependencies { + // JWT implementation("io.jsonwebtoken:jjwt-api:0.12.6") runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.6") runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.6") + + // Swagger implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9") + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.13") + + // Spring Boot implementation("org.springframework.boot:spring-boot-starter-actuator") implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-web") + implementation ("org.springframework.boot:spring-boot-starter-data-redis") + + // QueryDSL + implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta") + annotationProcessor("com.querydsl:querydsl-apt:5.0.0:jakarta") + annotationProcessor("jakarta.persistence:jakarta.persistence-api:3.1.0") + annotationProcessor("jakarta.annotation:jakarta.annotation-api:2.1.1") + + // Lombok compileOnly("org.projectlombok:lombok") - developmentOnly("org.springframework.boot:spring-boot-devtools") - runtimeOnly("com.h2database:h2") annotationProcessor("org.projectlombok:lombok") + + // DB + runtimeOnly("com.h2database:h2") + runtimeOnly("com.mysql:mysql-connector-j") + + // DevTools + developmentOnly("org.springframework.boot:spring-boot-devtools") + + // Test testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.security:spring-security-test") testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + implementation ("software.amazon.awssdk:s3:2.25.0") } tasks.withType { diff --git a/back/src/main/java/com/back/domain/file/controller/VideoController.java b/back/src/main/java/com/back/domain/file/controller/VideoController.java new file mode 100644 index 00000000..cdbe3a6e --- /dev/null +++ b/back/src/main/java/com/back/domain/file/controller/VideoController.java @@ -0,0 +1,33 @@ +package com.back.domain.file.controller; + +import com.back.domain.file.service.VideoService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.net.URL; +import java.util.List; +import java.util.Map; + +@RestController +@RequiredArgsConstructor +public class VideoController { + + private final VideoService videoService; + + // 업로드용 Presigned URL + @GetMapping("/videos/upload-url") + public URL getUploadUrl(@RequestParam String fileName) { + return videoService.generateUploadUrl("test-bucket", fileName); + } + + // DASH 스트리밍용 URL + @GetMapping("/videos/dash-urls") + public Map getDashUrls( + @RequestParam String mpdFile, + @RequestParam List segmentFiles + ) { + return videoService.generateDashUrls("test-bucket", mpdFile, segmentFiles); + } +} \ No newline at end of file diff --git a/back/src/main/java/com/back/domain/file/entity/Video.java b/back/src/main/java/com/back/domain/file/entity/Video.java new file mode 100644 index 00000000..d4557dfe --- /dev/null +++ b/back/src/main/java/com/back/domain/file/entity/Video.java @@ -0,0 +1,66 @@ +package com.back.domain.file.entity; + +import com.back.global.jpa.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Video extends BaseEntity { + @Column(unique = true) + private String uuid; + + @JdbcTypeCode(SqlTypes.JSON) + @Column(name = "transcoding_results") + private String transcodingResults; + + private String originalPath; + + private Integer views; + + private String originalFileName; + + private Integer duration; + + private Long fileSize; + + @Builder(access = AccessLevel.PRIVATE) + private Video(String uuid, String transcodingResults, String originalPath, Integer views, String originalFileName, Integer duration, Long fileSize) { + this.uuid = uuid; + this.transcodingResults = transcodingResults; + this.originalPath = originalPath; + this.views = views; + this.originalFileName = originalFileName; + this.duration = duration; + this.fileSize = fileSize; + } + + public static Video create(String uuid, String transcodingResults, String originalPath, String originalFileName, Integer duration, Long fileSize) { + if (uuid == null || uuid.isBlank()) { + throw new IllegalArgumentException("uuid cannot be null or empty"); + } + if (originalPath == null || originalPath.isBlank()) { + throw new IllegalArgumentException("originalPath cannot be null or empty"); + } + if (originalFileName == null || originalFileName.isBlank()) { + throw new IllegalArgumentException("originalFileName cannot be null or empty"); + } + + return Video.builder() + .uuid(uuid) + .transcodingResults(transcodingResults) + .originalPath(originalPath) + .views(0) + .originalFileName(originalFileName) + .duration(duration) + .fileSize(fileSize) + .build(); + } +} diff --git a/back/src/main/java/com/back/domain/file/repository/VideoRepository.java b/back/src/main/java/com/back/domain/file/repository/VideoRepository.java new file mode 100644 index 00000000..02e6431e --- /dev/null +++ b/back/src/main/java/com/back/domain/file/repository/VideoRepository.java @@ -0,0 +1,12 @@ +package com.back.domain.file.repository; + +import com.back.domain.file.entity.Video; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface VideoRepository extends JpaRepository { + Optional