diff --git a/build.gradle.kts b/build.gradle.kts index 47a6240..09f1dc4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,14 +1,16 @@ plugins { java - id("org.springframework.boot") version "3.2.3" - id("io.spring.dependency-management") version "1.1.4" + id("org.springframework.boot") version "3.3.3" + id("io.spring.dependency-management") version "1.1.6" } group = "com.ypm" version = "0.0.1-SNAPSHOT" java { - sourceCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } configurations { @@ -27,16 +29,23 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-oauth2-client") implementation("org.springframework.boot:spring-boot-starter-thymeleaf") + implementation("org.springframework.boot:spring-boot-starter-actuator") + + // Data Access + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + runtimeOnly("org.postgresql:postgresql") + // Dev developmentOnly("org.springframework.boot:spring-boot-devtools") + developmentOnly("org.springframework.boot:spring-boot-docker-compose") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") // YouTube Client implementation("com.google.apis:google-api-services-youtube:v3-rev20240310-2.0.0") - implementation("com.google.api-client:google-api-client:2.4.0") + implementation("com.google.api-client:google-api-client:2.6.0") implementation("com.google.http-client:google-http-client:1.44.1") - implementation("com.google.oauth-client:google-oauth-client-jetty:1.35.0") - implementation("com.google.code.gson:gson:2.10.1") + implementation("com.google.oauth-client:google-oauth-client-jetty:1.36.0") + implementation("com.google.code.gson:gson:2.10") // Lombok compileOnly("org.projectlombok:lombok") diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..91d7f0e --- /dev/null +++ b/compose.yaml @@ -0,0 +1,14 @@ +services: + postgres-db: + image: 'postgres:latest' + environment: + - POSTGRES_DB=ypm-db + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_USER=${POSTGRES_USERNAME} + ports: + - '5432:5432' + volumes: + - ypm-db:/var/lib/postgresql/data + +volumes: + ypm-db: diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd49..2c35211 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6e59778..09523c0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,21 +1,6 @@ -# -# Copyright 2012-2024 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4..f5feea6 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 6689b85..9b42019 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/src/main/java/com/ypm/config/youtube/YouTubeClientConfiguration.java b/src/main/java/com/ypm/config/youtube/YouTubeClientConfiguration.java index 5a5edeb..12932a6 100644 --- a/src/main/java/com/ypm/config/youtube/YouTubeClientConfiguration.java +++ b/src/main/java/com/ypm/config/youtube/YouTubeClientConfiguration.java @@ -14,7 +14,7 @@ public class YouTubeClientConfiguration { @Bean - public YouTube getYouTubeClient() throws GeneralSecurityException, IOException { + public YouTube youTubeClient() throws GeneralSecurityException, IOException { HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); GsonFactory jsonFactory = GsonFactory.getDefaultInstance(); diff --git a/src/main/java/com/ypm/constant/Part.java b/src/main/java/com/ypm/constant/Part.java index f756276..d3e46ca 100644 --- a/src/main/java/com/ypm/constant/Part.java +++ b/src/main/java/com/ypm/constant/Part.java @@ -1,7 +1,7 @@ package com.ypm.constant; public enum Part { - SNIPPET, STATUS; + SNIPPET, STATUS, CONTENT_DETAILS; @Override public String toString() { diff --git a/src/main/java/com/ypm/controller/LibraryImportController.java b/src/main/java/com/ypm/controller/LibraryImportController.java new file mode 100644 index 0000000..cbdd242 --- /dev/null +++ b/src/main/java/com/ypm/controller/LibraryImportController.java @@ -0,0 +1,32 @@ +package com.ypm.controller; + +import com.ypm.persistence.entity.VideoImport; +import com.ypm.service.youtube.ImportService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; + +@RestController +@RequestMapping("/import") +@RequiredArgsConstructor +public class LibraryImportController { + + private final ImportService importService; + + @PostMapping("/watch-later") + public ResponseEntity importWatchLaterLibrary(@RequestParam("file") MultipartFile file) throws IOException { + if (file.isEmpty()) { + return ResponseEntity.badRequest().build(); + } + + List savedVideos; + savedVideos = importService.importCsv(file); + + var responseBody = String.format("Saved %s videos", savedVideos.size()); + return ResponseEntity.ok().body(responseBody); + } +} diff --git a/src/main/java/com/ypm/controller/PlayListController.java b/src/main/java/com/ypm/controller/PlayListController.java index 8e45d61..d60ee61 100644 --- a/src/main/java/com/ypm/controller/PlayListController.java +++ b/src/main/java/com/ypm/controller/PlayListController.java @@ -5,9 +5,9 @@ import com.google.api.services.youtube.model.PlaylistSnippet; import com.ypm.dto.PlaylistDto; import com.ypm.dto.request.MergePlayListsRequest; -import com.ypm.service.PlayListService; +import com.ypm.service.youtube.PlayListService; import com.ypm.service.TokenService; -import com.ypm.service.VideoService; +import com.ypm.service.youtube.VideoService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; diff --git a/src/main/java/com/ypm/controller/VideoController.java b/src/main/java/com/ypm/controller/VideoController.java index f888a0c..dc655aa 100644 --- a/src/main/java/com/ypm/controller/VideoController.java +++ b/src/main/java/com/ypm/controller/VideoController.java @@ -1,17 +1,16 @@ package com.ypm.controller; +import com.ypm.dto.VideoDto; import com.ypm.service.TokenService; -import com.ypm.service.VideoService; +import com.ypm.service.youtube.VideoService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.io.IOException; +import java.util.List; @RestController @RequestMapping("/videos") @@ -21,6 +20,13 @@ public class VideoController { private final VideoService videosService; private final TokenService tokenService; + @PostMapping("/load") + public ResponseEntity> getVideoData(@RequestBody List videoIds) throws IOException { + var videoData = videosService.getVideoData(videoIds); + + return ResponseEntity.ok(videoData); + } + @DeleteMapping("/{videoId}") public ResponseEntity deleteVideos( @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authClient, diff --git a/src/main/java/com/ypm/dto/VideoDto.java b/src/main/java/com/ypm/dto/VideoDto.java new file mode 100644 index 0000000..5b0b227 --- /dev/null +++ b/src/main/java/com/ypm/dto/VideoDto.java @@ -0,0 +1,11 @@ +package com.ypm.dto; + +import java.util.List; + +public record VideoDto( + String id, + String title, + String description, + List tags, + String channelName) { +} diff --git a/src/main/java/com/ypm/dto/mapper/VideoMapper.java b/src/main/java/com/ypm/dto/mapper/VideoMapper.java new file mode 100644 index 0000000..d21b3a8 --- /dev/null +++ b/src/main/java/com/ypm/dto/mapper/VideoMapper.java @@ -0,0 +1,25 @@ +package com.ypm.dto.mapper; + +import com.google.api.services.youtube.model.Video; +import com.ypm.dto.VideoDto; + +import java.util.List; +import java.util.stream.Collectors; + +public class VideoMapper { + + public static List mapToVideoDto(List