Skip to content

Commit

Permalink
Fix: Youtube API로 유튜브 링크 추출 방식 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
suminnnnn committed Oct 13, 2024
1 parent 3b49dd9 commit ac211b3
Showing 1 changed file with 70 additions and 37 deletions.
107 changes: 70 additions & 37 deletions src/main/java/cmc/blink/domain/link/business/LinkService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
import cmc.blink.global.exception.LinkException;
import cmc.blink.global.exception.constant.ErrorCode;
import cmc.blink.global.util.opengraph.OpenGraph;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.YouTubeRequestInitializer;
import com.google.api.services.youtube.model.Video;
import com.google.api.services.youtube.model.VideoListResponse;
import lombok.RequiredArgsConstructor;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
Expand All @@ -27,6 +32,7 @@
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
Expand All @@ -36,12 +42,8 @@
import org.springframework.web.util.HtmlUtils;

import java.io.IOException;
import java.net.ProtocolException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
Expand All @@ -57,13 +59,17 @@ public class LinkService {
private final LinkFolderQueryAdapter linkFolderQueryAdapter;
private final LinkFolderCommandAdapter linkFolderCommandAdapter;

@Value("${gcp.api-key}")
private String apiKey;

private static final List<String> USER_AGENT_LIST = Arrays.asList(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 13_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15"
);

private static final Logger logger = LoggerFactory.getLogger(LinkService.class);
private com.google.api.client.json.JsonFactory JsonFactory;

@Transactional
public LinkResponse.LinkCreateDto saveLink(LinkRequest.LinkCreateDto createDto, User user) throws Exception {
Expand Down Expand Up @@ -190,54 +196,81 @@ private LinkResponse.LinkInfo fetchLinkInfo(String url) throws Exception {

private LinkResponse.LinkInfo fetchYoutubeLinkInfo(String url) {
try {
String userAgent = getRandomUserAgent();
// 유튜브 영상의 ID를 추출
String videoId = extractVideoId(url);

// YouTube Data API 클라이언트 생성
YouTube youtube = new YouTube.Builder(
new com.google.api.client.http.javanet.NetHttpTransport(),
GsonFactory.getDefaultInstance(),
null
).setYouTubeRequestInitializer(new YouTubeRequestInitializer(apiKey))
.setApplicationName("youtube-data-api-example")
.build();

// 동영상 정보 요청
YouTube.Videos.List videoRequest = youtube.videos()
.list("snippet")
.setId(videoId);

// API 응답
VideoListResponse response = videoRequest.execute();
List<Video> videoList = response.getItems();

if (videoList.isEmpty()) {
throw new LinkException(ErrorCode.INVALID_LINK_URL);
}

int timeout = new Random().nextInt(2000) + 1000;
Video video = videoList.get(0);

Document doc = Jsoup.connect(url)
.header("authority", "www.youtube.com")
.header("method", "GET")
.header("path", "/?app=desktop&hl=ko&gl=KR")
.header("scheme", "https")
.header("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
// .header("accept-encoding", "gzip, deflate, br, zstd")
.header("accept-language", "ko,en-US;q=0.9,en;q=0.8")
.header("user-agent", userAgent)
.timeout(new Random().nextInt(2000) + 1000)
.ignoreContentType(true)
.get();
String title = video.getSnippet().getTitle();

System.out.println("\n\n\n\n\ndoc = " + doc);
String channelTitle = video.getSnippet().getChannelTitle();

String title = doc.select("meta[property=og:title]").attr("content");
if (title.isEmpty()) {
title = doc.title();
}
String description = video.getSnippet().getDescription();

String type = "YouTube";
String thumbnailUrl = video.getSnippet().getThumbnails().getDefault().getUrl();

String channelTitle = doc.select("meta[itemprop='author']").attr("content");
if (channelTitle.isEmpty()) {
channelTitle = doc.select("link[itemprop='name']").attr("content");
}

String description = doc.select("meta[property=og:description]").attr("content");
if (description.isEmpty()) {
description = doc.select("meta[name=description]").attr("content");
}
String type = "Youtube";

String contents = String.format("%s | %s", channelTitle, description);

String imageUrl = doc.select("meta[property=og:image]").attr("content");

return LinkMapper.toLinkInfo(title, type, contents, imageUrl);
return LinkMapper.toLinkInfo(title, type, contents, thumbnailUrl);
} catch (Exception e) {
e.printStackTrace();
return null;
// throw new LinkException(ErrorCode.LINK_SCRAPED_FAILED);
}
}

private String extractVideoId(String url) throws LinkException {
try {
if (url.contains("youtu.be/")) {
return url.substring(url.lastIndexOf("/") + 1).split("\\?")[0];
}

URL videoUrl = new URL(url);
String query = videoUrl.getQuery();

if (query != null) {
String[] queryParams = query.split("&");
for (String param : queryParams) {
if (param.startsWith("v=")) {
return param.split("=")[1];
}
}
}

String path = videoUrl.getPath();
if (path.contains("/embed/")) {
return path.substring(path.lastIndexOf("/embed/") + 7);
}

} catch (Exception e) {
throw new LinkException(ErrorCode.INVALID_LINK_URL);
}
throw new LinkException(ErrorCode.INVALID_LINK_URL);
}

private LinkResponse.LinkInfo fetchInstagramLinkInfo(String url) {
try {
Expand Down

0 comments on commit ac211b3

Please sign in to comment.