Skip to content

Commit

Permalink
Merge pull request #3
Browse files Browse the repository at this point in the history
Set up access token utilization by yt client
  • Loading branch information
leingenm authored Mar 22, 2024
2 parents 16f90de + f5ea223 commit ba35f6a
Show file tree
Hide file tree
Showing 15 changed files with 266 additions and 97 deletions.
58 changes: 32 additions & 26 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,46 +1,52 @@
plugins {
java
id("org.springframework.boot") version "3.2.3"
id("io.spring.dependency-management") version "1.1.4"
java
id("org.springframework.boot") version "3.2.3"
id("io.spring.dependency-management") version "1.1.4"
}

group = "com.ypm"
version = "0.0.1-SNAPSHOT"

java {
sourceCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_17
}

configurations {
compileOnly {
extendsFrom(configurations.annotationProcessor.get())
}
compileOnly {
extendsFrom(configurations.annotationProcessor.get())
}
}

repositories {
mavenCentral()
mavenCentral()
}

dependencies {
// Spring Boot
implementation("org.springframework.boot:spring-boot-starter-web")
developmentOnly("org.springframework.boot:spring-boot-devtools")

// YouTube Client
implementation("com.google.apis:google-api-services-youtube:v3-rev20240310-2.0.0")
implementation("com.google.api-client:google-api-client:2.3.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.api-client:google-api-client-jackson2:1.28.1")

// Lombok
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")

// Test
testImplementation("org.springframework.boot:spring-boot-starter-test")
// Spring Boot
implementation("org.springframework.boot:spring-boot-starter-web")
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")

developmentOnly("org.springframework.boot:spring-boot-devtools")
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.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")

// Lombok
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")

// Test
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
}

tasks.withType<Test> {
useJUnitPlatform()
useJUnitPlatform()
}
1 change: 0 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
# 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ public class YoutubePlaylistManagerApplication {
public static void main(String[] args) {
SpringApplication.run(YoutubePlaylistManagerApplication.class, args);
}

}
23 changes: 23 additions & 0 deletions src/main/java/com/ypm/config/security/SecurityConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.ypm.config.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.oauth2Login(config -> {
config.defaultSuccessUrl("/auth/success", true);
config.failureUrl("/auth/error");
})
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
package com.ypm.config;
package com.ypm.config.youtube;

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.youtube.YouTube;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import java.io.IOException;
import java.security.GeneralSecurityException;

@Configuration
@Lazy
public class YouTubeClientConfiguration {

@Value("${youtube.application.name}")
private String applicationName;

@Bean
public YouTube getYouTubeClient() throws GeneralSecurityException, IOException {
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
GsonFactory jsonFactory = GsonFactory.getDefaultInstance();

return new YouTube.Builder(httpTransport, jsonFactory, null)
.setApplicationName(applicationName)
.setApplicationName("YouTube Playlists Helper")
.build();
}
}
34 changes: 34 additions & 0 deletions src/main/java/com/ypm/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.ypm.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {
@GetMapping("/success")
public ModelAndView success() {
ModelAndView modelAndView = new ModelAndView("auth");
modelAndView.addObject("type", "success");
modelAndView.addObject("title", "Login Successful");
modelAndView.addObject("message",
"Welcome back! You have successfully logged in.");

return modelAndView;
}

@GetMapping("/error")
public ModelAndView error() {
ModelAndView modelAndView = new ModelAndView("auth");
modelAndView.addObject("type", "error");
modelAndView.addObject("title", "Login Error");
modelAndView.addObject("message",
"Sorry, there was an error with your login credentials. Please try again.");

return modelAndView;
}
}
40 changes: 40 additions & 0 deletions src/main/java/com/ypm/controller/PlayListController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.ypm.controller;

import com.google.api.services.youtube.model.Playlist;
import com.google.api.services.youtube.model.VideoSnippet;
import com.ypm.service.YouTubeService;
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.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.List;

@RestController
@RequestMapping("/playlist")
@RequiredArgsConstructor
public class PlayListController {
private final YouTubeService youTubeService;

@GetMapping("/list")
public ResponseEntity<List<Playlist>> getPlayLists(
@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authentication) throws IOException {

String accessToken = authentication.getAccessToken().getTokenValue();
return ResponseEntity.ok(youTubeService.getMyPlayLists(accessToken));
}

@GetMapping("/{playlistId}/videos")
public ResponseEntity<List<VideoSnippet>> getVideos(
@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authentication,
@PathVariable String playlistId) throws IOException {

String accessToken = authentication.getAccessToken().getTokenValue();
return ResponseEntity.ok(youTubeService.getPlayListVideos(accessToken, playlistId));
}
}
37 changes: 0 additions & 37 deletions src/main/java/com/ypm/controller/PlaylistsController.java

This file was deleted.

41 changes: 41 additions & 0 deletions src/main/java/com/ypm/service/YouTubeService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.ypm.service;

import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.model.Playlist;
import com.google.api.services.youtube.model.VideoSnippet;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;

@Service
@RequiredArgsConstructor
public class YouTubeService {

private final YouTube youTubeClient;

public List<Playlist> getMyPlayLists(String accessToken) throws IOException {
var request = youTubeClient.playlists().list(List.of("snippet"));
var response = request
.setAccessToken(accessToken)
.setMine(true)
.execute();

return response.getItems();
}

public List<VideoSnippet> getPlayListVideos(String accessToken, String playListId) throws IOException {
var request = youTubeClient.playlistItems().list(List.of("snippet"));
var response = request
.setAccessToken(accessToken)
.setPlaylistId(playListId)
.execute();

return response
.getItems()
.stream()
.map(item -> new VideoSnippet().setTitle(item.getSnippet().getTitle()))
.toList();
}
}
33 changes: 23 additions & 10 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
spring:

# Secret data
# Secrets
config:
import: classpath:application.secrets.yml

# Spring specific
# Spring
application:
name: youtube-playlist-manager
security:
oauth2:
client:
registration:
google:
client-name: Google
client-id: ${google.application.client-id}
client-secret: ${google.application.client-secret}
scope: profile, email, https://www.googleapis.com/auth/youtube.readonly
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8080/login/oauth2/code/google
provider:
google:
authorization-uri: https://accounts.google.com/o/oauth2/v2/auth
token-uri: https://www.googleapis.com/oauth2/v4/token
user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
jwk-set-uri: https://www.googleapis.com/oauth2/v3/certs

# Custom
youtube:
application:
name: ${google.application.name}
api:
key: ${google.api.key}
channel:
id: ${google.channel.id}
# Logging
logging:
level:
org.springframework.security: trace
30 changes: 30 additions & 0 deletions src/main/resources/static/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}

.container {
text-align: center;
background-color: #ffffff;
padding: 20px;
border-radius: 8px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}

h1.success {
color: #008000;
}

h1.error {
color: #ff0000;
}

p {
color: #666666;
}
15 changes: 15 additions & 0 deletions src/main/resources/templates/auth.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Message</title>
<link rel="stylesheet" type="text/css" href="/style.css">
</head>
<body>
<div class="container">
<h1 th:class="${type}" th:text="${title}"></h1>
<p th:text="${message}"></p>
</div>
</body>
</html>
Loading

0 comments on commit ba35f6a

Please sign in to comment.