STS4とYoutube Data API v3を使って簡単なwebアプリを作成する

# 目次
[1.はじめに](#1-はじめに)
[2.概要](#2-概要)
[3.APIキーの発行](#3-APIキーの発行)
[4.STS4プロジェクトの作成](#4-STS4プロジェクトの作成)

# 1. はじめに

今回は、YouTube Data API v3とSpring Tool Suite 4(STS4)を使って、簡単なwebアプリを作成する手順を紹介します。

まずはじめに、使用しているソフトの一覧とそのバージョンを記載します。バージョンなどは以下の通りです。

・Spring Tool Suite 4(STS4) 4-4.17.0
・YouTube Data API v3-rev222-1.25.0
・Google Cloud Platform(GCP)

# 2. 概要
今回はYouTube Data API v3を使って、検索した動画の情報を一覧として表示する簡単なwebアプリを作成しました。以下の画像は完成した画面になります。
【完成画面】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329741/d2bf3c2a-22e6-ac18-cf7b-d70c9ab5b108.png)

このAPIは無料で使える代わりに、リクエストの数に上限があるので、デバッグなどをするときは残り回数に注意が必要になります（申請をすれば上限を引き上げることが可能です）。
リクエスト数の上限を開放する方法は今回は割愛します。

# 3. APIキーの発行
以下のリンクからGoogle Cloudにアクセスして、APIキーの発行とOAuthクライアントIDの発行を行います。
https://console.cloud.google.com/apis/dashboard

手順１.　Google Cloudにアクセスしてから、"プロジェクトの選択"というプルダウンを選択します。

手順２.  ポップアップ画面が表示されるので、右上の"新しいプロジェクト"を選択します。選択後、プロジェクト作成用の画面が表示されるので、適当なプロジェクト名を入力して、"作成"ボタンを押下します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329741/4bc0fc0e-d668-da6c-4475-e354d0d679f6.png)

手順３.  作成したプロジェクト名がプルダウンに表示されていることを確認してから、左のメニューバーの"認証情報"を選択。"認証情報を作成"から"APIキー"をクリックする。

APIキーが表示されれば、作成は完了です。
左のメニューバーの"認証情報"から作成したAPIキーを確認できます。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329741/977ea3b9-32b6-bd2a-a2b9-4f2d685e883c.png)

# 4. Youtube Data API v3の有効化
手順１.  "APIサービスの有効化"を押下します。押下後、APIライブラリ画面が表示されるので、検索バーから"youtube"と検索し、検索結果から"YouTube Data API v3"を選択します。

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329741/63754a2f-4224-2408-466c-690b4eab2dc1.png)

手順２.  "有効にする"を選択して、APIを有効化にします。

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329741/e43fbdc5-4514-7534-2217-a42d5ee95329.png)

# 4. STS4プロジェクトの作成

手順１.  新規プロジェクトを作成します（以下プロジェクトの構成例）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329741/d372002a-d8e7-7df7-0039-45de4da2949c.png)

手順２、pom.xmlを編集する。
※ここのバージョンがうまく設定できていないと、javaのコードがうまく動かないので注意が必要です。
```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.0.6</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	
	<groupId>com.hoge</groupId>
	<artifactId>HogeProject-01</artifactId>
	<packaging>jar</packaging>
	<version>0.0.1-SNAPSHOT</version>
	
	<name>HogeProject-01</name>
	<url>http://maven.apache.org</url>
	
	<description>Demo project for Spring Boot</description>
	
	<properties>
		<project.youtube.version>v3-rev222-1.25.0</project.youtube.version>
	    <project.http.version>1.25.0</project.http.version>
	    <project.oauth.version>1.25.0</project.oauth.version>
	    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>17</java.version>
	</properties>
	
	<repositories>
	    <repository>
	      <id>google-api-services</id>
	      <url>http://google-api-client-libraries.appspot.com/mavenrepo</url>
	    </repository>
  	</repositories>
  	
	<dependencies>
		<!-- YouTube Data V3 support -->
        <dependency>
	      <groupId>com.google.apis</groupId>
	      <artifactId>google-api-services-youtube</artifactId>
	      <version>${project.youtube.version}</version>
	    </dependency>
	
	    <dependency>
	      <groupId>com.google.http-client</groupId>
	      <artifactId>google-http-client-jackson2</artifactId>
	      <version>${project.http.version}</version>
	    </dependency>
	
	    <dependency>
	      <groupId>com.google.oauth-client</groupId>
	      <artifactId>google-oauth-client-jetty</artifactId>
	      <version>${project.oauth.version}</version>
	    </dependency>
	
	    <dependency>
	        <groupId>com.google.collections</groupId>
	        <artifactId>google-collections</artifactId>
	        <version>1.0</version>
	    </dependency>
	    <!-- YouTube Data V3 support -->
	    
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-websocket</artifactId>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity6</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
      　<!-- DBは今回は使用しない -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.31</version>
			<scope>runtime</scope>
		</dependency>
      　<!-- DBは今回は使用しない -->
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>exec-maven-plugin</artifactId>
				<version>1.2.1</version>
				<configuration>
          			<mainClass>com.hoge.api.SampleAPI</mainClass>
        		</configuration>
			</plugin>
			
			<!-- Forces Maven to use Java 1.6 -->
	      	<plugin>
		        <artifactId>maven-compiler-plugin</artifactId>
		        <version>2.3.2</version>
		        <configuration>
		            <source>1.6</source>
		            <target>1.6</target>
		            <compilerArgument></compilerArgument>
		        </configuration>
	        </plugin>
		</plugins>
	</build>

</project>
```

手順３.  Controllerクラスを作成します。
```Java:ViewController.java
package com.hoge.controller;

import com.hoge.api.SampleAPI;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import jakarta.servlet.http.HttpServletRequest;

@Controller
public class ViewController {
	
	@GetMapping("/")
	public String homeGet(Model model, HttpServletRequest request) {
		try {
			model.addAttribute("video", new SampleAPI().getSearchList());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "view.html";
	}
}
```

手順４.  動画の情報を取得するためのSampleAPIクラスを作成します。
```Java:SampleAPI.java
package com.hoge.api;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.YouTube.Search;
import com.google.api.services.youtube.model.SearchListResponse;
import com.hoge.model.VideoBean;

public class SampleAPI {
	/** Global instance of YouTube object to make all API requests. */
	private static YouTube youtube;
	private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
    private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
    private static final String PROPERTIES_FILENAME = "youtube.properties";
    
    /** HTTPリクエストの初期化クラス */
    private static final HttpRequestInitializer HTTP_REQUEST_INITIALIZER = new HttpRequestInitializer() {
        public void initialize(HttpRequest request) throws IOException {}
    };
    
    // 検索したい動画情報の取得
    public List<VideoBean> getSearchList() throws Exception {

        List<VideoBean> list = new ArrayList<>();
        
        try {
			// youtubeオブジェクト生成
			youtube = new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY, HTTP_REQUEST_INITIALIZER).setApplicationName("youtube-web2023").build();
			  
			// apikeyをセットする
			Properties properties = new Properties();
   
			// プロパティファイルに定義したAPIキーを設定する
	        InputStream in = Search.class.getResourceAsStream("/" + PROPERTIES_FILENAME);
	        properties.load(in);
            String apiKey = properties.getProperty("youtube.apikey");
            
            // 検索結果のbean
        	SearchListResponse res = new SearchListResponse();
            YouTube.Search.List search = youtube.search().list("snippet")
            		.setQ("三毛猫") // 検索したいキーワードを入力する
            		.setKey(apiKey);
            
            // VideoBeanにデータを格納する
		    do {
		    	res = search
			    	.setMaxResults(50L) // 最大50件
			    	.setPageToken(res.getNextPageToken())
			    	.execute();
		    	
	            for (int i = 0; i < res.getItems().size(); i++) {
	            	VideoBean bean = new VideoBean();
	            	bean.setVideoTitle(res.getItems().get(i).getSnippet().getTitle()); // 動画タイトル
	            	bean.setThumbnail((res.getItems().get(i).getSnippet().getThumbnails().getDefault().getUrl())); // サムネイル画像)
	            	list.add(bean);
            }
		    } while (res.getNextPageToken() != null); // 51件以降のデータも格納する
		    
        } catch (GoogleJsonResponseException e) {
        	e.printStackTrace();
            System.err.println("There was a service error: " + e.getDetails().getCode() + " : " + e.getDetails().getMessage());
            
        } catch (Throwable t) {
            t.printStackTrace();
        }
        //登録しているチャンネルのidを返却する
		return list;
    }
}
```

手順５.  "youtube.properties"ファイルを作成し、以下の内容で編集します。先ほど手順２・３で作成したJavaファイルと同じパッケージ配下に格納します。
```properties:youtube.properties
youtube.apikey=ここに作成したapiキーを貼り付け
```

手順６.  templatesに表示用のhtmlクラスを作成します。
```html:view.html
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
</head>
<body>
	<table border="1" class="nara-table" style="table-layout: fixed;">
		<thead>
			<tr>
				<th style="background:#AD002D;font-weight: bold;color:#fff;">動画のタイトル</th>
				<th style="background:#AD002D;font-weight: bold;color:#fff;">サムネイル</th>
			</tr>
		</thead>
		<th:block th:each="v : ${video}">
			<tbody>
			  	<tr>
				    <td th:text="${v.videoTitle}"></td>
				    <td><img th:src="@{${v.thumbnail}}" /></td>
			  	</tr>
			  </tbody>
		</th:block>
	</table>
</body>
</html>
```

手順６.  今回は、動画のタイトルとサムネイルの情報が欲しいので、必要な情報のみを格納するbeanを作成します。
```Java:VideoBean.java
package com.hoge.model;

public class VideoBean {
	
	private String videoTitle = "";
	private String thumbnail = "";
 
	public String getVideoTitle() {
		return videoTitle;
	}
	public void setVideoTitle(String videoTitle) {
		this.videoTitle = videoTitle;
	}
	public String getThumbnail() {
		return thumbnail;
	}
	public void setThumbnail(String thumbnail) {
		this.thumbnail = thumbnail;
	}
}
```