diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index ed19bf6..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(./gradlew.bat clean test:*)", - "Bash(curl:*)", - "Bash(./gradlew.bat test:*)", - "Bash(./gradlew.bat:*)", - "Bash(find:*)", - "Bash(claude mcp:*)", - "WebFetch(domain:github.com)", - "mcp__serena__get_symbols_overview", - "mcp__serena__list_dir", - "mcp__serena__get_current_config" - ], - "deny": [], - "ask": [] - } -} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3e0e505 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.gradle/ +.idea/ +.claude/ +backup/ +bin/ +build/ \ No newline at end of file diff --git a/.gradle/8.1.1/checksums/checksums.lock b/.gradle/8.1.1/checksums/checksums.lock deleted file mode 100644 index a744523..0000000 Binary files a/.gradle/8.1.1/checksums/checksums.lock and /dev/null differ diff --git a/.gradle/8.1.1/dependencies-accessors/dependencies-accessors.lock b/.gradle/8.1.1/dependencies-accessors/dependencies-accessors.lock deleted file mode 100644 index 8debb61..0000000 Binary files a/.gradle/8.1.1/dependencies-accessors/dependencies-accessors.lock and /dev/null differ diff --git a/.gradle/8.1.1/dependencies-accessors/gc.properties b/.gradle/8.1.1/dependencies-accessors/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/.gradle/8.1.1/executionHistory/executionHistory.bin b/.gradle/8.1.1/executionHistory/executionHistory.bin deleted file mode 100644 index d5cac28..0000000 Binary files a/.gradle/8.1.1/executionHistory/executionHistory.bin and /dev/null differ diff --git a/.gradle/8.1.1/executionHistory/executionHistory.lock b/.gradle/8.1.1/executionHistory/executionHistory.lock deleted file mode 100644 index 6f2a668..0000000 Binary files a/.gradle/8.1.1/executionHistory/executionHistory.lock and /dev/null differ diff --git a/.gradle/8.1.1/fileChanges/last-build.bin b/.gradle/8.1.1/fileChanges/last-build.bin deleted file mode 100644 index f76dd23..0000000 Binary files a/.gradle/8.1.1/fileChanges/last-build.bin and /dev/null differ diff --git a/.gradle/8.1.1/fileHashes/fileHashes.bin b/.gradle/8.1.1/fileHashes/fileHashes.bin deleted file mode 100644 index b1fe9c0..0000000 Binary files a/.gradle/8.1.1/fileHashes/fileHashes.bin and /dev/null differ diff --git a/.gradle/8.1.1/fileHashes/fileHashes.lock b/.gradle/8.1.1/fileHashes/fileHashes.lock deleted file mode 100644 index b6c9750..0000000 Binary files a/.gradle/8.1.1/fileHashes/fileHashes.lock and /dev/null differ diff --git a/.gradle/8.1.1/fileHashes/resourceHashesCache.bin b/.gradle/8.1.1/fileHashes/resourceHashesCache.bin deleted file mode 100644 index b0f826d..0000000 Binary files a/.gradle/8.1.1/fileHashes/resourceHashesCache.bin and /dev/null differ diff --git a/.gradle/8.1.1/gc.properties b/.gradle/8.1.1/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock deleted file mode 100644 index 631f447..0000000 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and /dev/null differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties deleted file mode 100644 index 27e6c46..0000000 --- a/.gradle/buildOutputCleanup/cache.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Thu Nov 06 15:53:40 KST 2025 -gradle.version=8.1.1 diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin deleted file mode 100644 index 0d50483..0000000 Binary files a/.gradle/buildOutputCleanup/outputFiles.bin and /dev/null differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe deleted file mode 100644 index 2e9fd3b..0000000 Binary files a/.gradle/file-system.probe and /dev/null differ diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index cf04665..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -video-url-analyzer \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b589d56..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index ce1c62c..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index de0c428..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/backup/src/main/java/com/caliverse/video/analyzer/VideoAnalyzer.java b/backup/src/main/java/com/caliverse/video/analyzer/VideoAnalyzer.java deleted file mode 100644 index 39cf49d..0000000 --- a/backup/src/main/java/com/caliverse/video/analyzer/VideoAnalyzer.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.caliverse.video.analyzer; - -import com.caliverse.video.analyzer.platform.PlatformAnalyzer; -import com.caliverse.video.analyzer.platform.PlatformAnalyzerFactory; -import com.caliverse.video.analyzer.service.VideoAnalyzerService; -import com.caliverse.video.analyzer.model.VideoAnalysisResult; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 비디오 URL 분석 기능을 제공하는 메인 클래스입니다. - * - *

이 클래스는 비디오 URL을 분석하여 다음 사항을 확인합니다: - *

- * - *

지원하는 플랫폼: - *

- * - *

사용 예제

- *
{@code
- * VideoAnalyzer analyzer = new VideoAnalyzer();
- * VideoAnalysisResult result = analyzer.analyzeUrl("https://tv.naver.com/v/84373511");
- *
- * if (result.isSuccess()) {
- *     System.out.println("재생 가능: " + result.getReason());
- * } else {
- *     System.out.println("재생 불가: " + result.getReason());
- * }
- * }
- * - * @since 1.0.0 - * @see VideoAnalysisResult - */ -public class VideoAnalyzer { - - private static final Logger logger = LoggerFactory.getLogger(VideoAnalyzer.class); - - private final VideoAnalyzerService analyzerService; - private final PlatformAnalyzerFactory platformFactory; - - /** - * VideoAnalyzer 인스턴스를 생성합니다. - * - *

생성 시 내부적으로 분석 서비스와 플랫폼별 분석기를 초기화합니다. - */ - public VideoAnalyzer() { - this.analyzerService = new VideoAnalyzerService(); - this.platformFactory = new PlatformAnalyzerFactory(analyzerService); - } - - /** - * 비디오 URL을 분석하여 재생 가능 여부를 판단합니다. - * - *

특정 플랫폼(네이버TV 등)의 URL인 경우 해당 플랫폼에 맞는 분석을 수행하며, - * 일반 URL인 경우 표준 비디오 분석을 수행합니다. - * - *

분석 항목: - *

- * - *

성공 조건: 모든 항목이 재생 가능한 경우
- * 실패 조건: 하나라도 재생 불가능한 경우 (상세 이유 제공) - * - * @param url 분석할 비디오 URL (http:// 또는 https:// 형식) - * @return 분석 결과를 담은 {@link VideoAnalysisResult} 객체 - * @see VideoAnalysisResult#isSuccess() - * @see VideoAnalysisResult#getReason() - * - * @example - *

{@code
-     * // 네이버TV URL 분석
-     * VideoAnalysisResult result1 = analyzer.analyzeUrl("https://tv.naver.com/v/84373511");
-     *
-     * // 일반 비디오 URL 분석
-     * VideoAnalysisResult result2 = analyzer.analyzeUrl("https://example.com/video.mp4");
-     * }
- */ - public VideoAnalysisResult analyzeUrl(String url) { - // 플랫폼별 분석기 찾기 - PlatformAnalyzer analyzer = platformFactory.findAnalyzer(url); - - if (analyzer != null) { - return analyzer.analyze(url); - } else { - // 일반 비디오 URL로 처리 - return analyzerService.analyzeVideo(url); - } - } - - /** - * 현재 지원하는 플랫폼 목록을 반환합니다. - * - *

각 플랫폼별로 특화된 URL 분석 로직이 제공됩니다. - * - * @return 지원하는 플랫폼 이름 배열 (예: ["네이버TV"]) - *

- * @example - *

{@code
-     * String[] platforms = analyzer.getSupportedPlatforms();
-     * System.out.println("지원 플랫폼: " + String.join(", ", platforms));
-     * // 출력: 지원 플랫폼: 네이버TV
-     * }
- */ - public String[] getSupportedPlatforms() { - return platformFactory.getAllAnalyzers().stream() - .map(PlatformAnalyzer::getPlatformName) - .toArray(String[]::new); - } - - - /** - * 명령줄에서 테스트 실행을 위한 메인 메서드 - */ - public static void main(String[] args) { - if (args.length < 1) { - VideoAnalyzer analyzer = new VideoAnalyzer(); - logger.info("사용법: java -jar video-url-analyzer.jar "); - logger.info("지원하는 플랫폼: {}", String.join(", ", analyzer.getSupportedPlatforms())); - logger.info("예시:"); - logger.info(" 네이버 TV: https://tv.naver.com/v/85477455"); - logger.info(" 일반 비디오: https://example.com/video.mp4"); - System.exit(1); - } - - String url = args[0]; - VideoAnalyzer analyzer = new VideoAnalyzer(); - - logger.info("=== URL 분석 결과 ==="); - VideoAnalysisResult result = analyzer.analyzeUrl(url); - logger.info("{}", result); - } - -} diff --git a/backup/src/main/java/com/caliverse/video/analyzer/entity/UrlType.java b/backup/src/main/java/com/caliverse/video/analyzer/entity/UrlType.java deleted file mode 100644 index 370ccf2..0000000 --- a/backup/src/main/java/com/caliverse/video/analyzer/entity/UrlType.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.caliverse.video.analyzer.entity; - -public enum UrlType { - YOUTUBE, - BLOB, - STREAMING, - DIRECT_FILE, - NAVER_TV, - UNKNOWN - -} diff --git a/backup/src/main/java/com/caliverse/video/analyzer/model/VideoAnalysisResult.java b/backup/src/main/java/com/caliverse/video/analyzer/model/VideoAnalysisResult.java deleted file mode 100644 index 999627e..0000000 --- a/backup/src/main/java/com/caliverse/video/analyzer/model/VideoAnalysisResult.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.caliverse.video.analyzer.model; - -import com.caliverse.video.global.common.Messages; - -/** - * 비디오 URL 분석 결과를 담는 모델 클래스입니다. - * - *

이 클래스는 비디오 URL 분석의 최종 결과를 나타냅니다. - * 모든 검사 항목이 통과하면 {@code success = true}, 하나라도 실패하면 - * {@code success = false}와 함께 상세한 실패 이유를 제공합니다. - * - *

분석 항목

- * - * - *

사용 예제

- *
{@code
- * VideoAnalyzer analyzer = new VideoAnalyzer();
- * VideoAnalysisResult result = analyzer.analyzeUrl(url);
- *
- * if (result.isSuccess()) {
- *     System.out.println("재생 가능: " + result.getReason());
- *     // 출력: "재생 가능: 모든 검사 통과"
- * } else {
- *     System.out.println("재생 불가");
- *     System.out.println(result.getReason());
- *     // 출력 예시:
- *     // 재생 불가 사유:
- *     // - 보호된 URL: DRM 보호 키워드 감지: widevine
- *     // - 3D 비디오: 3D 비디오 헤더 감지
- * }
- * }
- * - * @since 1.0.0 - * @see com.caliverse.video.analyzer.VideoAnalyzer#analyzeUrl(String) - */ -public class VideoAnalysisResult { - private final boolean success; - private final String reason; - - private VideoAnalysisResult(boolean success, String reason) { - this.success = success; - this.reason = reason; - } - - /** - * 성공 결과 객체를 생성합니다. - * - *

모든 검사 항목이 통과했을 때 사용되며, reason은 "모든 검사 통과"로 설정됩니다. - * - * @return 성공 결과 객체 - */ - public static VideoAnalysisResult success() { - return new VideoAnalysisResult(true, Messages.SUCCESS_ALL_CHECKS_PASSED); - } - - /** - * 실패 결과 객체를 생성합니다. - * - *

하나 이상의 검사 항목이 실패했을 때 사용되며, - * 상세한 실패 이유를 reason에 포함합니다. - * - * @param reason 실패 이유 (개행 문자로 구분된 상세 설명 가능) - * @return 실패 결과 객체 - */ - public static VideoAnalysisResult failure(String reason) { - return new VideoAnalysisResult(false, reason); - } - - /** - * 분석 결과가 성공인지 여부를 반환합니다. - * - *

성공 조건: - *

- * - * @return 모든 검사가 통과하면 {@code true}, 하나라도 실패하면 {@code false} - */ - public boolean isSuccess() { - return success; - } - - /** - * 분석 결과의 이유를 반환합니다. - * - *

성공한 경우: "모든 검사 통과"
- * 실패한 경우: 상세한 실패 이유 (각 항목별로 구분) - * - *

실패 이유 예시: - *

-     * 재생 불가 사유:
-     * - 보호된 URL: DRM 보호 키워드 감지: widevine
-     * - 재생 불가: 지원되지 않는 포맷 또는 Content-Type: video/x-flv
-     * 
- * - * @return 분석 결과 이유 (null이 아닌 문자열) - */ - public String getReason() { - return reason; - } - - @Override - public String toString() { - if (success) { - return "VideoAnalysisResult{ 성공: " + reason + " }"; - } else { - return "VideoAnalysisResult{ 실패: " + reason + " }"; - } - } -} diff --git a/backup/src/main/java/com/caliverse/video/analyzer/platform/PlatformAnalyzer.java b/backup/src/main/java/com/caliverse/video/analyzer/platform/PlatformAnalyzer.java deleted file mode 100644 index 15e2a42..0000000 --- a/backup/src/main/java/com/caliverse/video/analyzer/platform/PlatformAnalyzer.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.caliverse.video.analyzer.platform; - -import com.caliverse.video.analyzer.model.VideoAnalysisResult; - -public interface PlatformAnalyzer { - /** - * 해당 플랫폼의 URL인지 확인합니다. - * - * @param url 확인할 URL - * @return 해당 플랫폼 URL이면 true, 아니면 false - */ - boolean canHandle(String url); - - /** - * 플랫폼 이름을 반환합니다. - * - * @return 플랫폼 이름 - */ - String getPlatformName(); - - /** - * 플랫폼별 URL 분석을 수행합니다. - * - * @param url 분석할 URL - * @return 분석 결과 - */ - VideoAnalysisResult analyze(String url); - -} diff --git a/backup/src/main/java/com/caliverse/video/analyzer/platform/PlatformAnalyzerFactory.java b/backup/src/main/java/com/caliverse/video/analyzer/platform/PlatformAnalyzerFactory.java deleted file mode 100644 index a6cd0b7..0000000 --- a/backup/src/main/java/com/caliverse/video/analyzer/platform/PlatformAnalyzerFactory.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.caliverse.video.analyzer.platform; - -import com.caliverse.video.analyzer.platform.impl.NaverTvPlatformAnalyzer; -import com.caliverse.video.analyzer.service.VideoAnalyzerService; - -import java.util.ArrayList; -import java.util.List; - -public class PlatformAnalyzerFactory { - private final List analyzers; - - public PlatformAnalyzerFactory(VideoAnalyzerService videoAnalyzerService) { - this.analyzers = new ArrayList<>(); - - // 플랫폼 분석기들을 등록 - analyzers.add(new NaverTvPlatformAnalyzer(videoAnalyzerService)); - - // 향후 다른 플랫폼들도 여기에 추가 - // analyzers.add(new VimeoPlatformAnalyzer(videoAnalyzerService)); - // analyzers.add(new DaumTvPlatformAnalyzer(videoAnalyzerService)); - } - - /** - * URL에 맞는 플랫폼 분석기를 찾습니다. - * - * @param url 분석할 URL - * @return 해당 플랫폼 분석기, 없으면 null - */ - public PlatformAnalyzer findAnalyzer(String url) { - for (PlatformAnalyzer analyzer : analyzers) { - if (analyzer.canHandle(url)) { - return analyzer; - } - } - return null; - } - - /** - * 등록된 모든 플랫폼 분석기 목록을 반환합니다. - * - * @return 플랫폼 분석기 목록 - */ - public List getAllAnalyzers() { - return new ArrayList<>(analyzers); - } - - /** - * 새로운 플랫폼 분석기를 추가합니다. - * - * @param analyzer 추가할 분석기 - */ - public void addAnalyzer(PlatformAnalyzer analyzer) { - analyzers.add(analyzer); - } - -} diff --git a/backup/src/main/java/com/caliverse/video/analyzer/platform/impl/NaverTvPlatformAnalyzer.java b/backup/src/main/java/com/caliverse/video/analyzer/platform/impl/NaverTvPlatformAnalyzer.java deleted file mode 100644 index 89af613..0000000 --- a/backup/src/main/java/com/caliverse/video/analyzer/platform/impl/NaverTvPlatformAnalyzer.java +++ /dev/null @@ -1,525 +0,0 @@ -package com.caliverse.video.analyzer.platform.impl; - -import com.caliverse.video.analyzer.platform.PlatformAnalyzer; -import com.caliverse.video.analyzer.service.VideoAnalyzerService; -import com.caliverse.video.analyzer.model.VideoAnalysisResult; -import com.caliverse.video.global.common.CommonConstants; -import com.caliverse.video.global.common.Messages; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.http.HttpEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.regex.Matcher; - -public class NaverTvPlatformAnalyzer implements PlatformAnalyzer { - - private static final Logger logger = LoggerFactory.getLogger(NaverTvPlatformAnalyzer.class); - - private final ObjectMapper objectMapper; - private final VideoAnalyzerService videoAnalyzerService; - - public NaverTvPlatformAnalyzer(VideoAnalyzerService videoAnalyzerService) { - this.objectMapper = new ObjectMapper(); - this.videoAnalyzerService = videoAnalyzerService; - } - - @Override - public boolean canHandle(String url) { - if (url == null) return false; - String lowerUrl = url.toLowerCase(); - return lowerUrl.contains(CommonConstants.DOMAIN_NAVER_TV) || lowerUrl.contains(CommonConstants.DOMAIN_NAVER); - } - - @Override - public String getPlatformName() { - return CommonConstants.FLATFORM_NAME_NAVER_TV; - } - - @Override - public VideoAnalysisResult analyze(String url) { - try { - // 1. HTML 콘텐츠 가져오기 - String htmlContent = fetchHtmlContent(url); - - // 2. __NEXT_DATA__ 파싱 - JsonNode nextDataNode = extractNextData(htmlContent); - if (nextDataNode == null) { - return VideoAnalysisResult.failure(Messages.NAVER_TV_PARSING_FAILED); - } - - // 3. props.pageProps 접근 - JsonNode pageProps = nextDataNode.path("props").path("pageProps"); - if (pageProps.isMissingNode()) { - return VideoAnalysisResult.failure(Messages.NAVER_TV_PARSING_FAILED); - } - - // 4. 콘텐츠 타입별 처리 - String videoUrl = null; - - // 4-1. vodInfo 확인 - JsonNode vodInfo = pageProps.path("vodInfo"); - if (!vodInfo.isMissingNode()) { - videoUrl = handleVodInfo(vodInfo); - } - - // 4-2. clipInfo 확인 - if (videoUrl == null) { - JsonNode clipInfo = pageProps.path("clipInfo"); - if (!clipInfo.isMissingNode()) { - videoUrl = handleClipInfo(clipInfo); - } - } - - // 4-3. liveInfo 확인 - if (videoUrl == null) { - JsonNode liveInfo = pageProps.path("liveInfo"); - if (!liveInfo.isMissingNode()) { - videoUrl = handleLiveInfo(liveInfo); - } - } - - if (videoUrl == null) { - return VideoAnalysisResult.failure(Messages.NAVER_TV_API_PARSING_FAILED); - } - - // 5. 제한사항 확인 - String restrictionReason = checkRestrictions(videoUrl); - if (restrictionReason != null) { - return VideoAnalysisResult.failure(Messages.format(Messages.NAVER_TV_RESTRICTION, restrictionReason)); - } - - // 6. 비디오 분석 - return videoAnalyzerService.analyzeVideo(videoUrl); - - } catch (Exception e) { - return VideoAnalysisResult.failure(Messages.format(Messages.NAVER_TV_ANALYSIS_ERROR, e.getMessage())); - } - } - - private String fetchHtmlContent(String url) throws IOException { - try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - HttpGet request = new HttpGet(url); - request.setHeader(CommonConstants.REQUEST_HEADER_NAME_USER_AGENT, CommonConstants.REQUEST_HEADER_USER_AGENT); - - try (CloseableHttpResponse response = httpClient.execute(request)) { - HttpEntity entity = response.getEntity(); - return EntityUtils.toString(entity, StandardCharsets.UTF_8); - } - } - } - - /** - * HTML에서 __NEXT_DATA__ 스크립트를 파싱합니다. - */ - private JsonNode extractNextData(String htmlContent) { - try { - Matcher matcher = CommonConstants.NAVER_NEXT_DATA_PATTERN.matcher(htmlContent); - if (matcher.find()) { - String jsonData = matcher.group(1); - return objectMapper.readTree(jsonData); - } - return null; - } catch (Exception e) { - logger.warn("__NEXT_DATA__ 파싱 실패: {}", e.getMessage()); - return null; - } - } - - /** - * vodInfo 처리: videoId + inKey로 VOD API 호출 - * 구조: vodInfo.clip.videoId, vodInfo.play.inKey - */ - private String handleVodInfo(JsonNode vodInfo) throws IOException { - String videoId = vodInfo.path("clip").path("videoId").asText(null); - String inKey = vodInfo.path("play").path("inKey").asText(null); - - if (videoId != null && inKey != null) { - String apiUrl = String.format(CommonConstants.NAVER_VOD_API_URL, - videoId, URLEncoder.encode(inKey, StandardCharsets.UTF_8)); - String apiResponse = fetchApiResponse(apiUrl); - return extractVideoUrl(apiResponse); - } - return null; - } - - /** - * clipInfo 처리: videoId만으로 Short API 호출 - */ - private String handleClipInfo(JsonNode clipInfo) throws IOException { - String videoId = clipInfo.path("videoId").asText(null); - - if (videoId != null) { - String apiUrl = String.format(CommonConstants.NAVER_SHORT_API_URL, videoId); - String apiResponse = fetchApiResponse(apiUrl); - return extractVideoUrlFromShortApi(apiResponse); - } - return null; - } - - /** - * liveInfo 처리: playbackBody에서 직접 m3u8 URL 추출 - */ - private String handleLiveInfo(JsonNode liveInfo) { - try { - String playbackBody = liveInfo.path("playbackBody").asText(null); - if (playbackBody == null) { - return null; - } - - // playbackBody는 JSON 문자열이므로 다시 파싱 - JsonNode playbackNode = objectMapper.readTree(playbackBody); - - // media 배열에서 HLS 프로토콜의 path 찾기 - JsonNode mediaArray = playbackNode.path("media"); - if (mediaArray.isArray()) { - for (JsonNode media : mediaArray) { - String protocol = media.path("protocol").asText(""); - if ("HLS".equals(protocol)) { - String path = media.path("path").asText(null); - if (path != null && !path.isEmpty()) { - // \\u 이스케이프 처리 - path = unescapeUnicode(path); - logger.debug("liveInfo에서 m3u8 URL 추출 성공"); - return path; - } - } - } - } - - return null; - } catch (Exception e) { - logger.warn("liveInfo playbackBody 파싱 실패: {}", e.getMessage()); - return null; - } - } - - /** - * 유니코드 이스케이프 시퀀스 처리 - */ - private String unescapeUnicode(String text) { - if (text == null) { - return null; - } - return text.replace("\\u003d", "=") - .replace("\\u0026", "&") - .replace("\\u003c", "<") - .replace("\\u003e", ">"); - } - - private String fetchApiResponse(String apiUrl) throws IOException { - try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - HttpGet request = new HttpGet(apiUrl); - request.setHeader(CommonConstants.REQUEST_HEADER_NAME_USER_AGENT, CommonConstants.REQUEST_HEADER_USER_AGENT); - request.setHeader(CommonConstants.REQUEST_HEADER_NAME_REFERER, CommonConstants.REQUEST_HEADER_NAVER_REFERER); - - try (CloseableHttpResponse response = httpClient.execute(request)) { - if (response.getStatusLine().getStatusCode() != 200) { - throw new IOException("API 호출 실패"); - } - HttpEntity entity = response.getEntity(); - return EntityUtils.toString(entity, StandardCharsets.UTF_8); - } - } - } - - private String extractVideoUrl(String apiResponse) throws IOException { - try { - JsonNode rootNode = objectMapper.readTree(apiResponse); - - // 1. MPD 구조 탐색: MPD -> Period -> AdaptationSet -> Representation - JsonNode mpdNode = rootNode.path("MPD"); - if (mpdNode.isArray() && !mpdNode.isEmpty()) { - JsonNode firstMpd = mpdNode.get(0); - JsonNode periodNode = firstMpd.path("Period"); - - if (periodNode.isArray() && !periodNode.isEmpty()) { - JsonNode firstPeriod = periodNode.get(0); - JsonNode adaptationSetNode = firstPeriod.path("AdaptationSet"); - - if (adaptationSetNode.isArray() && !adaptationSetNode.isEmpty()) { - for (int i = 0; i < adaptationSetNode.size(); i++) { - JsonNode adaptationSet = adaptationSetNode.get(i); - JsonNode representationNode = adaptationSet.path("Representation"); - - if (representationNode.isArray() && !representationNode.isEmpty()) { - for (int j = 0; j < representationNode.size(); j++) { - JsonNode representation = representationNode.get(j); - String videoUrl = extractBaseUrlFromRepresentation(representation); - - if (videoUrl != null) { -// logger.debug("비디오 URL 추출 성공 (AdaptationSet[{}], Representation[{}])", i, j); - return videoUrl; - } - } - } - - String adaptationSetUrl = extractUrlFromAdaptationSet(adaptationSet); - if (adaptationSetUrl != null) { -// logger.debug("비디오 URL 추출 성공 (AdaptationSet[{}] 레벨)", i); - return adaptationSetUrl; - } - } - } - } - } - - // 2. 레거시 구조 확인 (이전 API 버전 호환성) - JsonNode legacyRepresentations = rootNode.path("Representation"); - if (legacyRepresentations.isArray() && legacyRepresentations.size() > 0) { - for (int i = 0; i < legacyRepresentations.size(); i++) { - JsonNode representation = legacyRepresentations.get(i); - String videoUrl = extractBaseUrlFromRepresentation(representation); - - if (videoUrl != null) { - logger.debug("비디오 URL 추출 성공 (레거시 Representation[{}])", i); - return videoUrl; - } - } - } - - // 3. 다른 가능한 경로들 확인 - String alternativeUrl = findAlternativeVideoUrl(rootNode); - if (alternativeUrl != null) { - logger.debug("비디오 URL 추출 성공 (대안 경로)"); - return alternativeUrl; - } - - logger.warn("비디오 URL을 찾을 수 없습니다. JSON 구조를 확인해주세요."); - return null; - - } catch (Exception e) { - throw new IOException("JSON 파싱 오류: " + e.getMessage(), e); - } - - } - - private String checkRestrictions(String videoUrl) { - try { - try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - HttpGet request = new HttpGet(videoUrl); - request.setHeader(CommonConstants.REQUEST_HEADER_NAME_USER_AGENT, CommonConstants.REQUEST_HEADER_USER_AGENT); - request.setHeader(CommonConstants.REQUEST_HEADER_NAME_REFERER, CommonConstants.REQUEST_HEADER_NAVER_REFERER); - - try (CloseableHttpResponse response = httpClient.execute(request)) { - int statusCode = response.getStatusLine().getStatusCode(); - - if (statusCode == 403) return "접근 금지 (403) - CORS 또는 권한 제한"; - if (statusCode == 401) return "인 필요 (401)"; - if (statusCode >= 400) return "HTTP 오류 (" + statusCode + ")"; - - // CORS 헤더 확인 - if (response.containsHeader("Access-Control-Allow-Origin")) { - String allowOrigin = response.getFirstHeader("Access-Control-Allow-Origin").getValue(); - if (!allowOrigin.equals("*") && !allowOrigin.contains("tv.naver.com")) { - return "CORS 제한 - 특정 도메인에서만 접근 가능"; - } - } - - return null; // 제한 없음 - } - } - } catch (IOException e) { - return "네트워크 연결 오류: " + e.getMessage(); - } - } - - /** - * Representation 노드에서 BaseURL을 추출합니다. - */ - private String extractBaseUrlFromRepresentation(JsonNode representation) { - if (representation == null) return null; - - // BaseURL 배열 형태 - JsonNode baseUrls = representation.path("BaseURL"); - if (baseUrls.isArray() && !baseUrls.isEmpty()) { - String url = baseUrls.get(0).asText(); - if (url != null && !url.trim().isEmpty() && isValidVideoUrl(url)) { - return url; - } - } - - // BaseURL 단일 문자열 형태 - if (!baseUrls.isMissingNode() && baseUrls.isTextual()) { - String url = baseUrls.asText(); - if (url != null && !url.trim().isEmpty() && isValidVideoUrl(url)) { - return url; - } - } - - return null; - } - - /** - * AdaptationSet 레벨에서 URL을 찾습니다. - */ - private String extractUrlFromAdaptationSet(JsonNode adaptationSet) { - if (adaptationSet == null) return null; - - // AdaptationSet의 다양한 속성들 확인 - JsonNode baseUrl = adaptationSet.path("BaseURL"); - if (!baseUrl.isMissingNode()) { - if (baseUrl.isArray() && !baseUrl.isEmpty()) { - String url = baseUrl.get(0).asText(); - if (isValidVideoUrl(url)) return url; - } else if (baseUrl.isTextual()) { - String url = baseUrl.asText(); - if (isValidVideoUrl(url)) return url; - } - } - - // SegmentTemplate이나 다른 방식으로 URL 구성이 필요한 경우 - JsonNode segmentTemplate = adaptationSet.path("SegmentTemplate"); - if (!segmentTemplate.isMissingNode()) { - // SegmentTemplate 기반 URL 구성 로직 - return buildUrlFromSegmentTemplate(segmentTemplate); - } - - return null; - } - - /** - * 대안적인 비디오 URL 경로를 찾습니다. - */ - private String findAlternativeVideoUrl(JsonNode rootNode) { - // 1. 직접적인 URL 필드들 확인 - String[] possibleUrlFields = {"videoUrl", "streamUrl", "playbackUrl", "mediaUrl", "url"}; - - for (String field : possibleUrlFields) { - JsonNode urlNode = rootNode.path(field); - if (!urlNode.isMissingNode() && urlNode.isTextual()) { - String url = urlNode.asText(); - if (isValidVideoUrl(url)) return url; - } - } - - // 2. 중첩된 구조에서 URL 찾기 - JsonNode sources = rootNode.path("sources"); - if (sources.isArray() && !sources.isEmpty()) { - for (JsonNode source : sources) { - String url = source.path("src").asText(); - if (isValidVideoUrl(url)) return url; - } - } - - // 3. 다른 가능한 구조들... - // 필요시 추가 경로들을 여기에 구현 - - return null; - } - - /** - * SegmentTemplate에서 URL을 구성합니다. - */ - private String buildUrlFromSegmentTemplate(JsonNode segmentTemplate) { - // SegmentTemplate 방식은 복잡하므로 현재는 기본 구현만 - JsonNode media = segmentTemplate.path("@media"); -// JsonNode initialization = segmentTemplate.path("@initialization"); - - if (!media.isMissingNode() && media.isTextual()) { - String mediaTemplate = media.asText(); - // 템플릿 변수들을 실제 값으로 치환하는 로직 필요 - // 현재는 간단히 템플릿 문자열만 반환 - if (mediaTemplate.contains("http")) { - return mediaTemplate; - } - } - - return null; - } - - /** - * 유효한 비디오 URL인지 확인합니다. - */ - private boolean isValidVideoUrl(String url) { - if (url == null || url.trim().isEmpty()) { - return false; - } - - // 기본적인 URL 형식 확인 - if (!url.startsWith("http://") && !url.startsWith("https://")) { - return false; - } - - // 비디오 파일 확장자나 스트리밍 패턴 확인 - String lowerUrl = url.toLowerCase(); - return lowerUrl.contains(".mp4") || - lowerUrl.contains(".m3u8") || - lowerUrl.contains(".mov") || - lowerUrl.contains(".m4v") || - lowerUrl.contains("stream") || - lowerUrl.contains("video"); - } - - /** - * 짧은 URL API 응답에서 비디오 URL을 추출합니다. - * 응답 구조: {"card":{"content":{"vod":{"playback":{"videos":{"list":[{"source":"URL"}]}}}}}} - */ - private String extractVideoUrlFromShortApi(String apiResponse) throws IOException { - try { - JsonNode rootNode = objectMapper.readTree(apiResponse); - - // card.content.vod.playback.videos.list[0].source 경로로 접근 - JsonNode cardNode = rootNode.path("card"); - if (cardNode.isMissingNode()) { - logger.warn("짧은 URL API 응답에 'card' 노드가 없습니다."); - return null; - } - - JsonNode contentNode = cardNode.path("content"); - if (contentNode.isMissingNode()) { - logger.warn("짧은 URL API 응답에 'content' 노드가 없습니다."); - return null; - } - - JsonNode vodNode = contentNode.path("vod"); - if (vodNode.isMissingNode()) { - logger.warn("짧은 URL API 응답에 'vod' 노드가 없습니다."); - return null; - } - - JsonNode playbackNode = vodNode.path("playback"); - if (playbackNode.isMissingNode()) { - logger.warn("짧은 URL API 응답에 'playback' 노드가 없습니다."); - return null; - } - - JsonNode videosNode = playbackNode.path("videos"); - if (videosNode.isMissingNode()) { - logger.warn("짧은 URL API 응답에 'videos' 노드가 없습니다."); - return null; - } - - JsonNode listNode = videosNode.path("list"); - if (listNode.isArray() && !listNode.isEmpty()) { - JsonNode firstVideo = listNode.get(0); - JsonNode sourceNode = firstVideo.path("source"); - - if (!sourceNode.isMissingNode() && sourceNode.isTextual()) { - String videoUrl = sourceNode.asText(); - if (isValidVideoUrl(videoUrl)) { - logger.debug("짧은 URL API에서 비디오 URL 추출 성공"); - return videoUrl; - } - } - } - - logger.warn("짧은 URL API 응답에서 비디오 URL을 찾을 수 없습니다."); - return null; - - } catch (Exception e) { - throw new IOException("짧은 URL API JSON 파싱 오류: " + e.getMessage(), e); - } - } - - -} diff --git a/backup/src/main/java/com/caliverse/video/analyzer/service/VideoAnalyzerService.java b/backup/src/main/java/com/caliverse/video/analyzer/service/VideoAnalyzerService.java deleted file mode 100644 index 41bda1c..0000000 --- a/backup/src/main/java/com/caliverse/video/analyzer/service/VideoAnalyzerService.java +++ /dev/null @@ -1,293 +0,0 @@ -package com.caliverse.video.analyzer.service; - -import com.caliverse.video.analyzer.entity.UrlType; -import com.caliverse.video.analyzer.model.VideoAnalysisResult; -import com.caliverse.video.global.common.Messages; -import com.caliverse.video.global.util.HttpUtils; -import com.caliverse.video.global.util.UrlParser; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -/** - * 비디오 URL을 분석하는 핵심 서비스 클래스입니다. - */ -public class VideoAnalyzerService { - - private final HttpUtils httpUtils; - private final UrlParser urlParser; - - /** - * 체크 결과를 담는 내부 클래스 - */ - private static class CheckResult { - final boolean result; - final String reason; - - CheckResult(boolean result, String reason) { - this.result = result; - this.reason = reason; - } - } - - // AVPlayer에서 지원하는 비디오 포맷 목록 - private static final List SUPPORTED_FORMATS = Arrays.asList( - "mp4", "mov", "m4v", "m3u8", "hls" - ); - - // DRM 관련 키워드 - private static final List DRM_KEYWORDS = Arrays.asList( - "drm", "widevine", "playready", "fairplay", "encrypted", "protection" - ); - - public VideoAnalyzerService() { - this.httpUtils = new HttpUtils(); - this.urlParser = new UrlParser(); - } - - /** - * URL이 보호된 URL인지 분석합니다 (DRM, CORS, Signed URL). - */ - private CheckResult checkProtection(String videoUrl) { - if (videoUrl == null || videoUrl.trim().isEmpty()) { - return new CheckResult(false, Messages.ERROR_INVALID_URL); - } - - // URL 유형별로 분기 처리 - UrlType urlType = determineUrlType(videoUrl); - - switch (urlType) { - case YOUTUBE: // 서명 정보가 계속 필요하기때문에 불가능 - return new CheckResult(false, Messages.PROTECTION_YOUTUBE_URL); - case NAVER_TV: - return new CheckResult(false, ""); - case BLOB: - return checkBlobProtection(videoUrl); - case STREAMING: - return checkStreamingProtection(videoUrl); - case DIRECT_FILE: - return checkDirectFileProtection(videoUrl); - default: - return checkGenericProtection(videoUrl); - } - } - - /** - * URL이 보호된 URL인지 분석합니다 (하위 호환성을 위한 메소드). - * @deprecated checkProtection 사용을 권장합니다 - */ - @Deprecated - public boolean isProtectedUrl(String videoUrl) { - return checkProtection(videoUrl).result; - } - - /** - * URL 유형을 판별합니다. - */ - private UrlType determineUrlType(String videoUrl) { - String lowerUrl = videoUrl.toLowerCase(); - - if (lowerUrl.contains("youtube.com") || lowerUrl.contains("youtu.be")) { - return UrlType.YOUTUBE; - } else if (lowerUrl.contains("naver.com") || lowerUrl.contains("naver")) { - return UrlType.NAVER_TV; - } else if (lowerUrl.startsWith("blob:")) { - return UrlType.BLOB; - } else if (lowerUrl.contains(".m3u8") || lowerUrl.contains("hls") || - lowerUrl.contains("dash") || lowerUrl.contains("stream")) { - return UrlType.STREAMING; - } else if (lowerUrl.matches(".*\\.(mp4|mov|m4v|avi|mkv|webm)($|\\?.*)")) { - return UrlType.DIRECT_FILE; - } else { - return UrlType.UNKNOWN; - } - } - - /** - * Blob URL의 보호 상태를 확인합니다. - */ - private CheckResult checkBlobProtection(String videoUrl) { - // Blob URL은 브라우저에서 생성되므로 일반적으로 보호되지 않음 - // 단, 원본 소스가 보호된 경우일 수 있음 - return new CheckResult(false, Messages.PROTECTION_BLOB_URL); - } - - /** - * 스트리밍 URL의 보호 상태를 확인합니다. - */ - private CheckResult checkStreamingProtection(String videoUrl) { - String lowerUrl = videoUrl.toLowerCase(); - - // 1. DRM 관련 키워드 확인 - for (String keyword : DRM_KEYWORDS) { - if (lowerUrl.contains(keyword)) { - return new CheckResult(true, Messages.format(Messages.PROTECTION_DRM_KEYWORD_DETECTED, keyword)); - } - } - - // 2. 토큰 기반 인증 확인 - if (urlParser.hasSignatureParameters(videoUrl)) { - return new CheckResult(true, Messages.PROTECTION_SIGNED_URL); - } - - // 3. HTTP 헤더 확인 - try { - if (httpUtils.hasSecurityHeaders(videoUrl)) { - return new CheckResult(true, Messages.PROTECTION_SECURITY_HEADER_DETECTED); - } - return new CheckResult(false, Messages.PROTECTION_NOT_PROTECTED_STREAMING); - } catch (IOException e) { - return new CheckResult(true, Messages.format(Messages.PROTECTION_CONNECTION_FAILED, e.getMessage())); - } - } - - /** - * 직접 파일 URL의 보호 상태를 확인합니다. - */ - private CheckResult checkDirectFileProtection(String videoUrl) { - String lowerUrl = videoUrl.toLowerCase(); - - // 1. DRM 관련 키워드 확인 - for (String keyword : DRM_KEYWORDS) { - if (lowerUrl.contains(keyword)) { - return new CheckResult(true, Messages.format(Messages.PROTECTION_DRM_KEYWORD_DETECTED, keyword)); - } - } - - // 2. Signed URL 검사 - if (urlParser.hasSignatureParameters(videoUrl)) { - return new CheckResult(true, Messages.PROTECTION_SIGNED_URL); - } - - // 3. HTTP 헤더를 통한 보안 검사 - try { - if (httpUtils.hasSecurityHeaders(videoUrl)) { - return new CheckResult(true, Messages.PROTECTION_SECURITY_HEADER_DETECTED); - } - return new CheckResult(false, Messages.PROTECTION_NOT_PROTECTED_DIRECT_FILE); - } catch (IOException e) { - return new CheckResult(true, Messages.format(Messages.PROTECTION_CONNECTION_FAILED, e.getMessage())); - } - } - - /** - * 일반적인 URL의 보호 상태를 확인합니다. - */ - private CheckResult checkGenericProtection(String videoUrl) { - // 기존의 일반적인 보호 URL 검사 로직 - return checkDirectFileProtection(videoUrl); - } - - /** - * URL이 3D 비디오인지 분석합니다. - */ - private CheckResult check3DVideo(String videoUrl) { - if (videoUrl == null || videoUrl.trim().isEmpty()) { - return new CheckResult(false, Messages.ERROR_INVALID_URL); - } - - // 필요시 헤더나 메타데이터 확인 - try { - if (httpUtils.checkFor3DHeaders(videoUrl)) { - return new CheckResult(true, Messages.THREE_D_HEADER_DETECTED); - } - return new CheckResult(false, Messages.THREE_D_NOT_DETECTED); - } catch (IOException e) { - return new CheckResult(false, Messages.format(Messages.THREE_D_CONNECTION_FAILED, e.getMessage())); - } - } - - /** - * URL이 3D 비디오인지 분석합니다 (하위 호환성을 위한 메소드). - * @deprecated check3DVideo 사용을 권장합니다 - */ - @Deprecated - public boolean is3DVideo(String videoUrl) { - return check3DVideo(videoUrl).result; - } - - /** - * URL이 AVPlayer에서 재생 가능한지 분석합니다. - */ - private CheckResult checkPlayability(String videoUrl) { - if (videoUrl == null || videoUrl.trim().isEmpty()) { - return new CheckResult(false, Messages.ERROR_INVALID_URL); - } - - // 1. 포맷 검사 (파일 확장자) - String extension = urlParser.getExtension(videoUrl); - if (extension != null && SUPPORTED_FORMATS.contains(extension.toLowerCase())) { - return new CheckResult(true, Messages.format(Messages.PLAYABILITY_SUPPORTED_FORMAT, extension)); - } - - // 2. 스트리밍 URL 패턴 검사 - if (urlParser.isStreamingUrl(videoUrl)) { - return new CheckResult(true, Messages.PLAYABILITY_SUPPORTED_STREAMING); - } - - // 3. 필요시 콘텐츠 타입 헤더 확인 - try { - String contentType = httpUtils.getContentType(videoUrl); - if (contentType != null && ( - contentType.contains("video/") || - contentType.contains("application/x-mpegURL") || - contentType.contains("application/vnd.apple.mpegurl") - )) { - return new CheckResult(true, Messages.format(Messages.PLAYABILITY_SUPPORTED_CONTENT_TYPE, contentType)); - } - return new CheckResult(false, Messages.format(Messages.PLAYABILITY_UNSUPPORTED_FORMAT, contentType)); - } catch (IOException e) { - return new CheckResult(false, Messages.format(Messages.PLAYABILITY_CONNECTION_FAILED, e.getMessage())); - } - } - - /** - * URL이 AVPlayer에서 재생 가능한지 분석합니다 (하위 호환성을 위한 메소드). - * @deprecated checkPlayability 사용을 권장합니다 - */ - @Deprecated - public boolean isPlayableOnAVPlayer(String videoUrl) { - return checkPlayability(videoUrl).result; - } - - /** - * 모든 분석을 한번에 수행하고 결과를 반환합니다. - * 보호되지 않고, 2D 비디오이고, AVPlayer에서 재생 가능하면 성공입니다. - */ - public VideoAnalysisResult analyzeVideo(String videoUrl) { - CheckResult protectionResult = checkProtection(videoUrl); - CheckResult threeDResult = check3DVideo(videoUrl); - CheckResult playabilityResult = checkPlayability(videoUrl); - - // 실패 이유 수집 - StringBuilder failureReasons = new StringBuilder(); - - // 보호된 URL이면 실패 - if (protectionResult.result) { - failureReasons.append(Messages.ANALYSIS_PROTECTED_URL_PREFIX) - .append(protectionResult.reason).append("\n"); - } - - // 3D 비디오이면 실패 -// if (threeDResult.result) { -// failureReasons.append(Messages.ANALYSIS_THREE_D_VIDEO_PREFIX) -// .append(threeDResult.reason).append("\n"); -// } - - // AVPlayer에서 재생 불가능하면 실패 - if (!playabilityResult.result) { - failureReasons.append(Messages.ANALYSIS_NOT_PLAYABLE_PREFIX) - .append(playabilityResult.reason).append("\n"); - } - - // 실패 이유가 있으면 실패, 없으면 성공 - if (!failureReasons.isEmpty()) { - return VideoAnalysisResult.failure( - Messages.ANALYSIS_FAILURE_HEADER + "\n" + failureReasons.toString().trim() - ); - } else { - return VideoAnalysisResult.success(); - } - } -} diff --git a/backup/src/main/java/com/caliverse/video/global/common/CommonConstants.java b/backup/src/main/java/com/caliverse/video/global/common/CommonConstants.java deleted file mode 100644 index bd9c171..0000000 --- a/backup/src/main/java/com/caliverse/video/global/common/CommonConstants.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.caliverse.video.global.common; - -import java.util.regex.Pattern; - -public class CommonConstants { - // 도메인 - public static final String DOMAIN_NAVER_TV = "naver.com"; - public static final String DOMAIN_NAVER = "naver.me"; - public static final String FLATFORM_NAME_NAVER_TV = "네이버TV"; - - // 정규표현식 - public static final Pattern NAVER_NEXT_DATA_PATTERN = Pattern.compile(""); - public static final Pattern NAVER_VIDEO_ID_PATTERN = Pattern.compile("\"videoId\"\\s*:\\s*\"([^\"]+)\""); - public static final Pattern NAVER_IN_KEY_PATTERN = Pattern.compile("\"inKey\"\\s*:\\s*\"([^\"]+)\""); - - // API URL - public static final String NAVER_VOD_API_URL = "https://apis.naver.com/neonplayer/vodplay/v3/playback/%s?key=%s"; - public static final String NAVER_SHORT_API_URL = "https://api-videohub.naver.com/shortformhub/feeds/v7/card?serviceType=NTV&seedMediaId=%s&mediaType=VOD"; - - //헤더 - public static final String REQUEST_HEADER_NAME_USER_AGENT = "User-Agent"; - public static final String REQUEST_HEADER_NAME_REFERER = "Referer"; - - public static final String REQUEST_HEADER_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"; - public static final String REQUEST_HEADER_NAVER_REFERER = "https://tv.naver.com/"; -} diff --git a/backup/src/main/java/com/caliverse/video/global/common/Messages.java b/backup/src/main/java/com/caliverse/video/global/common/Messages.java deleted file mode 100644 index b89f330..0000000 --- a/backup/src/main/java/com/caliverse/video/global/common/Messages.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.caliverse.video.global.common; - -/** - * 분석 결과 메시지를 관리하는 상수 클래스입니다. - */ -public class Messages { - - // ========== 일반 메시지 ========== - public static final String SUCCESS_ALL_CHECKS_PASSED = "모든 검사 통과"; - public static final String ERROR_INVALID_URL = "유효하지 않은 URL"; - - // ========== 보호 URL 체크 메시지 ========== - public static final String PROTECTION_YOUTUBE_URL = "YouTube URL (서명 정보 필요)"; - public static final String PROTECTION_BLOB_URL = "Blob URL (브라우저에서 생성된 URL)"; - public static final String PROTECTION_DRM_KEYWORD_DETECTED = "DRM 보호 키워드 감지: %s"; - public static final String PROTECTION_SIGNED_URL = "서명된 URL (토큰 기반 인증)"; - public static final String PROTECTION_SECURITY_HEADER_DETECTED = "보안 헤더 감지"; - public static final String PROTECTION_NOT_PROTECTED_STREAMING = "보호되지 않은 스트리밍 URL"; - public static final String PROTECTION_NOT_PROTECTED_DIRECT_FILE = "보호되지 않은 직접 파일 URL"; - public static final String PROTECTION_CONNECTION_FAILED = "연결 실패 (보호된 것으로 간주): %s"; - - // ========== 3D 비디오 체크 메시지 ========== - public static final String THREE_D_HEADER_DETECTED = "3D 비디오 헤더 감지"; - public static final String THREE_D_NOT_DETECTED = "일반 2D 비디오"; - public static final String THREE_D_CONNECTION_FAILED = "연결 실패 (2D로 간주): %s"; - - // ========== 재생 가능성 체크 메시지 ========== - public static final String PLAYABILITY_SUPPORTED_FORMAT = "지원되는 포맷: %s"; - public static final String PLAYABILITY_SUPPORTED_STREAMING = "지원되는 스트리밍 URL"; - public static final String PLAYABILITY_SUPPORTED_CONTENT_TYPE = "지원되는 Content-Type: %s"; - public static final String PLAYABILITY_UNSUPPORTED_FORMAT = "지원되지 않는 포맷 또는 Content-Type: %s"; - public static final String PLAYABILITY_CONNECTION_FAILED = "연결 실패 (재생 불가능으로 간주): %s"; - - // ========== 전체 분석 결과 메시지 ========== - public static final String ANALYSIS_FAILURE_HEADER = "재생 불가 사유:"; - public static final String ANALYSIS_PROTECTED_URL_PREFIX = "- 보호된 URL: "; - public static final String ANALYSIS_THREE_D_VIDEO_PREFIX = "- 3D 비디오: "; - public static final String ANALYSIS_NOT_PLAYABLE_PREFIX = "- 재생 불가: "; - - // ========== 네이버TV 관련 메시지 ========== - public static final String NAVER_TV_PARSING_FAILED = "네이버TV URL 파싱 실패: videoId 또는 inKey를 찾을 수 없습니다."; - public static final String NAVER_TV_API_PARSING_FAILED = "네이버TV API 응답 파싱 실패: 비디오 URL을 찾을 수 없습니다."; - public static final String NAVER_TV_RESTRICTION = "네이버TV 제한사항: %s"; - public static final String NAVER_TV_ANALYSIS_ERROR = "네이버TV 분석 중 오류 발생: %s"; - - private Messages() { - // 인스턴스화 방지 - } - - /** - * 포맷 문자열을 사용하는 메시지를 생성합니다. - * - * @param format 포맷 문자열 - * @param args 포맷 인자 - * @return 포맷팅된 메시지 - */ - public static String format(String format, Object... args) { - return String.format(format, args); - } -} diff --git a/backup/src/main/java/com/caliverse/video/global/util/HttpUtils.java b/backup/src/main/java/com/caliverse/video/global/util/HttpUtils.java deleted file mode 100644 index 0be0b2b..0000000 --- a/backup/src/main/java/com/caliverse/video/global/util/HttpUtils.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.caliverse.video.global.util; - -import org.apache.http.Header; -import org.apache.http.HttpHeaders; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; - -/** - * HTTP 요청 관련 유틸리티 클래스입니다. - */ -public class HttpUtils { - - private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class); - private static final int CONNECTION_TIMEOUT = 5000; // 5초 - - /** - * URL의 HTTP 헤더에서 보안 관련 헤더가 있는지 확인합니다. - * - * @param url 확인할 URL - * @return 보안 헤더가 있으면 true, 없으면 false - * @throws IOException HTTP 요청 실패 시 - */ - public boolean hasSecurityHeaders(String url) throws IOException { - HttpHead request = new HttpHead(url); - RequestConfig config = RequestConfig.custom() - .setConnectTimeout(CONNECTION_TIMEOUT) - .setSocketTimeout(CONNECTION_TIMEOUT) - .build(); - request.setConfig(config); - - try (CloseableHttpClient httpClient = HttpClients.createDefault(); - CloseableHttpResponse response = httpClient.execute(request)) { - - // CORS 헤더 확인 - if (response.containsHeader("Access-Control-Allow-Origin")) { - return true; - } - - // 인증 관련 헤더 확인 - if (response.containsHeader("WWW-Authenticate") || - response.containsHeader("Authorization")) { - return true; - } - - // DRM 관련 헤더 확인 - if (response.containsHeader("X-DRM-Type") || - response.containsHeader("X-Content-Protection")) { - return true; - } - - // 기타 보안 헤더 확인 - for (Header header : response.getAllHeaders()) { - String headerName = header.getName().toLowerCase(); - String headerValue = header.getValue().toLowerCase(); - - if (headerName.contains("token") || - headerName.contains("auth") || - headerName.contains("drm") || - headerValue.contains("token") || - headerValue.contains("protection")) { - return true; - } - } - - return false; - } - } - - /** - * URL의 HTTP 헤더에서 3D 비디오 관련 정보가 있는지 확인합니다. - * - * @param url 확인할 URL - * @return 3D 비디오 헤더가 있으면 true, 없으면 false - * @throws IOException HTTP 요청 실패 시 - */ - public boolean checkFor3DHeaders(String url) throws IOException { - HttpHead request = new HttpHead(url); - // User-Agent 헤더 추가 (일반적인 브라우저로 위장) - request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"); - - // 기타 일반적인 브라우저 헤더들 추가 - request.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/json"); - request.setHeader("Accept-Language", "ko-KR,ko;q=0.9,en;q=0.8"); - request.setHeader("Accept-Encoding", "gzip, deflate, br"); - request.setHeader("Connection", "keep-alive"); - request.setHeader("Upgrade-Insecure-Requests", "1"); - request.setHeader("Referer", "https://tv.naver.com"); - - RequestConfig config = RequestConfig.custom() - .setConnectTimeout(CONNECTION_TIMEOUT) - .setSocketTimeout(CONNECTION_TIMEOUT) - .build(); - request.setConfig(config); - - try (CloseableHttpClient httpClient = HttpClients.createDefault(); - CloseableHttpResponse response = httpClient.execute(request)) { - - int statusCode = response.getStatusLine().getStatusCode(); - - if (statusCode >= 400) { - logger.warn("HTTP Error {} for URL: {}", statusCode, url); - return false; // 에러 발생 시 보안 헤더 없다고 간주 - } - - // 3D 관련 커스텀 헤더 확인 - if (response.containsHeader("X-Video-Type")) { - String videoType = response.getFirstHeader("X-Video-Type").getValue(); - if (videoType.toLowerCase().contains("3d") || - videoType.toLowerCase().contains("stereoscopic")) { - return true; - } - } - - // 기타 헤더에서 3D 관련 키워드 확인 - for (Header header : response.getAllHeaders()) { - String headerValue = header.getValue().toLowerCase(); - if (headerValue.contains("3d") || - headerValue.contains("stereoscopic")) { - return true; - } - } - - // 콘텐츠 타입 헤더 확인 - if (response.containsHeader(HttpHeaders.CONTENT_TYPE)) { - String contentType = response.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue(); - return contentType.contains("3d") || contentType.contains("stereoscopic"); - } - - return false; - } - } - - /** - * URL의 HTTP 헤더에서 Content-Type을 가져옵니다. - * - * @param url 확인할 URL - * @return Content-Type 문자열, 헤더가 없거나 요청 실패 시 null - * @throws IOException HTTP 요청 실패 시 - */ - public String getContentType(String url) throws IOException { - HttpHead request = new HttpHead(url); - RequestConfig config = RequestConfig.custom() - .setConnectTimeout(CONNECTION_TIMEOUT) - .setSocketTimeout(CONNECTION_TIMEOUT) - .build(); - request.setConfig(config); - - try (CloseableHttpClient httpClient = HttpClients.createDefault(); - CloseableHttpResponse response = httpClient.execute(request)) { - - if (response.containsHeader(HttpHeaders.CONTENT_TYPE)) { - return response.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue(); - } - - return null; - } - } -} diff --git a/backup/src/main/java/com/caliverse/video/global/util/OEmbedFetcher.java b/backup/src/main/java/com/caliverse/video/global/util/OEmbedFetcher.java deleted file mode 100644 index c50e75b..0000000 --- a/backup/src/main/java/com/caliverse/video/global/util/OEmbedFetcher.java +++ /dev/null @@ -1,575 +0,0 @@ -package com.caliverse.video.global.util; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - -/** - * oEmbed API를 활용하여 영상 메타데이터를 가져오는 유틸리티 클래스입니다. - * - *

지원 플랫폼: - *

    - *
  • 네이버TV
  • - *
  • YouTube
  • - *
  • Vimeo
  • - *
- */ -public class OEmbedFetcher { - - private static final Logger logger = LoggerFactory.getLogger(OEmbedFetcher.class); - private final ObjectMapper objectMapper; - - // oEmbed 엔드포인트 매핑 - private static final Map OEMBED_ENDPOINTS = new HashMap<>(); - - static { - OEMBED_ENDPOINTS.put("naver.com", "https://tv.naver.com/oembed"); - OEMBED_ENDPOINTS.put("naver.me", "https://tv.naver.com/oembed"); - OEMBED_ENDPOINTS.put("youtube.com", "https://www.youtube.com/oembed"); - OEMBED_ENDPOINTS.put("youtu.be", "https://www.youtube.com/oembed"); - OEMBED_ENDPOINTS.put("vimeo.com", "https://vimeo.com/api/oembed.json"); - } - - public OEmbedFetcher() { - this.objectMapper = new ObjectMapper(); - } - - /** - * oEmbed API를 통해 영상 메타데이터를 가져옵니다. - * - * @param videoUrl 영상 URL - * @return oEmbed 응답 데이터 - * @throws IOException API 요청 실패 시 - */ - public OEmbedResponse fetchOEmbedData(String videoUrl) throws IOException { - // 단축 URL(naver.me)인 경우 실제 URL로 변환 - String resolvedUrl = resolveShortUrl(videoUrl); - logger.debug("원본 URL: {}, 해결된 URL: {}", videoUrl, resolvedUrl); - - String oembedEndpoint = getOEmbedEndpoint(resolvedUrl); - - if (oembedEndpoint == null) { - throw new IllegalArgumentException("지원하지 않는 플랫폼입니다: " + resolvedUrl); - } - - String oembedUrl = buildOEmbedUrl(oembedEndpoint, resolvedUrl); - logger.debug("oEmbed URL: {}", oembedUrl); - - String jsonResponse = fetchOEmbedJson(oembedUrl); - OEmbedResponse response = parseOEmbedResponse(jsonResponse); - // 네이버TV 쇼츠의 경우 oEmbed가 빈 응답을 반환하면 직접 메타데이터 구성 - if (isEmptyResponse(response) && isNaverTvShortUrl(resolvedUrl)) { - response = buildNaverTvShortResponse(resolvedUrl); - logger.debug("네이버TV 쇼츠 URL에 대한 fallback 응답 생성"); - } - - return response; - } - - /** - * 단축 URL을 실제 URL로 변환합니다. - * naver.me와 같은 단축 URL의 경우 HTTP 리다이렉트를 따라가서 최종 URL을 반환합니다. - * - * @param url 원본 URL - * @return 해결된 URL (단축 URL이 아니면 원본 URL 반환) - * @throws IOException 리다이렉트 처리 실패 시 - */ - private String resolveShortUrl(String url) throws IOException { - // naver.me 단축 URL인지 확인 - if (!url.toLowerCase().contains("naver.me")) { - return url; - } - - try (CloseableHttpClient httpClient = HttpClients.custom() - .disableRedirectHandling() // 자동 리다이렉트 비활성화 - .build()) { - - HttpGet request = new HttpGet(url); - request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); - - try (CloseableHttpResponse response = httpClient.execute(request)) { - int statusCode = response.getStatusLine().getStatusCode(); - - // 리다이렉트 상태 코드 확인 (301, 302, 303, 307, 308) - if (statusCode >= 300 && statusCode < 400) { - String location = response.getFirstHeader("Location").getValue(); - if (location != null && !location.isEmpty()) { - logger.debug("단축 URL 리다이렉트: {} -> {}", url, location); - return location; - } - } - - // 리다이렉트가 없으면 원본 URL 반환 - return url; - } - } - } - - /** - * URL에서 적절한 oEmbed 엔드포인트를 찾습니다. - */ - private String getOEmbedEndpoint(String videoUrl) { - String lowerUrl = videoUrl.toLowerCase(); - - for (Map.Entry entry : OEMBED_ENDPOINTS.entrySet()) { - if (lowerUrl.contains(entry.getKey())) { - return entry.getValue(); - } - } - - return null; - } - - /** - * oEmbed API URL을 생성합니다. - */ - private String buildOEmbedUrl(String endpoint, String videoUrl) { - try { - String encodedUrl = URLEncoder.encode(videoUrl, StandardCharsets.UTF_8.toString()); - return endpoint + "?url=" + encodedUrl + "&format=json"; - } catch (Exception e) { - throw new RuntimeException("URL 인코딩 실패", e); - } - } - - /** - * oEmbed API를 호출하여 JSON 응답을 가져옵니다. - */ - private String fetchOEmbedJson(String oembedUrl) throws IOException { - try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - HttpGet request = new HttpGet(oembedUrl); - request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); - request.setHeader("Accept", "application/json"); - - try (CloseableHttpResponse response = httpClient.execute(request)) { - int statusCode = response.getStatusLine().getStatusCode(); - - if (statusCode != 200) { - throw new IOException("oEmbed API 호출 실패. 상태 코드: " + statusCode); - } - - return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); - } - } - } - - /** - * oEmbed JSON 응답을 파싱합니다. - */ - private OEmbedResponse parseOEmbedResponse(String jsonResponse) throws IOException { - try { - JsonNode rootNode = objectMapper.readTree(jsonResponse); - - OEmbedResponse response = new OEmbedResponse(); - response.setType(getStringValue(rootNode, "type")); - response.setVersion(getStringValue(rootNode, "version")); - response.setTitle(getStringValue(rootNode, "title")); - response.setAuthorName(getStringValue(rootNode, "author_name")); - response.setAuthorUrl(getStringValue(rootNode, "author_url")); - response.setProviderName(getStringValue(rootNode, "provider_name")); - response.setProviderUrl(getStringValue(rootNode, "provider_url")); - response.setThumbnailUrl(getStringValue(rootNode, "thumbnail_url")); - response.setThumbnailWidth(getIntValue(rootNode, "thumbnail_width")); - response.setThumbnailHeight(getIntValue(rootNode, "thumbnail_height")); - response.setWidth(getIntValue(rootNode, "width")); - response.setHeight(getIntValue(rootNode, "height")); - response.setHtml(getStringValue(rootNode, "html")); - - // 원본 JSON도 저장 - response.setRawJson(jsonResponse); - - return response; - } catch (Exception e) { - throw new IOException("oEmbed 응답 파싱 실패: " + e.getMessage(), e); - } - } - - private String getStringValue(JsonNode node, String fieldName) { - JsonNode fieldNode = node.get(fieldName); - return fieldNode != null && !fieldNode.isNull() ? fieldNode.asText() : null; - } - - private Integer getIntValue(JsonNode node, String fieldName) { - JsonNode fieldNode = node.get(fieldName); - return fieldNode != null && !fieldNode.isNull() ? fieldNode.asInt() : null; - } - - /** - * oEmbed 응답이 비어있는지 확인합니다. - */ - private boolean isEmptyResponse(OEmbedResponse response) { - return response != null && - response.getType() == null && - response.getTitle() == null && - (response.getWidth() == null || response.getWidth() == 0) && - (response.getHeight() == null || response.getHeight() == 0); - } - - /** - * 네이버TV 쇼츠 URL인지 확인합니다. - */ - private boolean isNaverTvShortUrl(String url) { - return url != null && url.toLowerCase().contains("tv.naver.com/h/"); - } - - /** - * 네이버TV 쇼츠 URL에서 ID를 추출하고 기본 메타데이터를 생성합니다. - */ - private OEmbedResponse buildNaverTvShortResponse(String url) { - try { - // URL에서 ID 추출: https://tv.naver.com/h/87349660 -> 87349660 - String id = extractNaverTvId(url); - if (id == null) { - return null; - } - - // HTML 페이지에서 메타데이터 추출 - MetaData metaData = extractMetaDataFromHtml(url); - - // embed URL 생성 - String embedUrl = "https://tv.naver.com/embed/" + id + "?autoPlay=true"; - String html = ""; - - OEmbedResponse response = new OEmbedResponse(); - response.setType("video"); - response.setVersion("1.0"); - response.setProviderName("NAVERTV"); - response.setProviderUrl("https://tv.naver.com"); - response.setWidth(544); - response.setHeight(306); - response.setHtml(html); - - // HTML에서 추출한 메타데이터 설정 - if (metaData != null) { - response.setTitle(metaData.title); - response.setAuthorName(metaData.authorName); - response.setThumbnailUrl(metaData.thumbnailUrl); - if (metaData.thumbnailUrl != null) { - // 네이버TV 썸네일은 일반적으로 16:9 비율 - response.setThumbnailWidth(720); - response.setThumbnailHeight(405); - } - } - - // JSON 구성 - String rawJson = buildShortResponseJson(response, embedUrl); - response.setRawJson(rawJson); - - return response; - } catch (Exception e) { - logger.warn("네이버TV 쇼츠 응답 생성 실패: {}", e.getMessage()); - return null; - } - } - - /** - * HTML 페이지에서 Open Graph 메타데이터를 추출합니다. - */ - private MetaData extractMetaDataFromHtml(String url) { - try { - String htmlContent = fetchHtmlContent(url); - MetaData metaData = new MetaData(); - - // og:description에서 제목 추출 - metaData.title = extractMetaTag(htmlContent, "og:description"); - - // og:title에서 작성자 이름 추출 - metaData.authorName = extractMetaTag(htmlContent, "og:title"); - - // og:image에서 썸네일 URL 추출 - metaData.thumbnailUrl = extractMetaTag(htmlContent, "og:image"); - - logger.debug("쇼츠 메타데이터 추출 - 제목: {}, 작성자: {}", metaData.title, metaData.authorName); - - return metaData; - } catch (Exception e) { - logger.warn("HTML 메타데이터 추출 실패: {}", e.getMessage()); - return null; - } - } - - /** - * HTML에서 특정 meta 태그의 content 값을 추출합니다. - */ - private String extractMetaTag(String htmlContent, String property) { - try { - // 패턴 매칭 - String pattern = " 87349660 - */ - private String extractNaverTvId(String url) { - try { - // /h/ 또는 /v/ 패턴에서 ID 추출 - String[] patterns = {"/h/", "/v/", "/l/"}; - - for (String pattern : patterns) { - int index = url.indexOf(pattern); - if (index != -1) { - String remaining = url.substring(index + pattern.length()); - // ? 또는 / 이전까지가 ID - int endIndex = remaining.indexOf('?'); - if (endIndex == -1) { - endIndex = remaining.indexOf('/'); - } - if (endIndex == -1) { - return remaining; - } else { - return remaining.substring(0, endIndex); - } - } - } - - return null; - } catch (Exception e) { - logger.warn("네이버TV ID 추출 실패: {}", e.getMessage()); - return null; - } - } - - /** - * 메타데이터를 담는 내부 클래스입니다. - */ - private static class MetaData { - String title; - String authorName; - String thumbnailUrl; - } - - /** - * oEmbed 응답 데이터를 담는 클래스입니다. - */ - public static class OEmbedResponse { - private String type; - private String version; - private String title; - private String authorName; - private String authorUrl; - private String providerName; - private String providerUrl; - private String thumbnailUrl; - private Integer thumbnailWidth; - private Integer thumbnailHeight; - private Integer width; - private Integer height; - private String html; - private String rawJson; - - // Getters and Setters - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getAuthorName() { - return authorName; - } - - public void setAuthorName(String authorName) { - this.authorName = authorName; - } - - public String getAuthorUrl() { - return authorUrl; - } - - public void setAuthorUrl(String authorUrl) { - this.authorUrl = authorUrl; - } - - public String getProviderName() { - return providerName; - } - - public void setProviderName(String providerName) { - this.providerName = providerName; - } - - public String getProviderUrl() { - return providerUrl; - } - - public void setProviderUrl(String providerUrl) { - this.providerUrl = providerUrl; - } - - public String getThumbnailUrl() { - return thumbnailUrl; - } - - public void setThumbnailUrl(String thumbnailUrl) { - this.thumbnailUrl = thumbnailUrl; - } - - public Integer getThumbnailWidth() { - return thumbnailWidth; - } - - public void setThumbnailWidth(Integer thumbnailWidth) { - this.thumbnailWidth = thumbnailWidth; - } - - public Integer getThumbnailHeight() { - return thumbnailHeight; - } - - public void setThumbnailHeight(Integer thumbnailHeight) { - this.thumbnailHeight = thumbnailHeight; - } - - public Integer getWidth() { - return width; - } - - public void setWidth(Integer width) { - this.width = width; - } - - public Integer getHeight() { - return height; - } - - public void setHeight(Integer height) { - this.height = height; - } - - public String getHtml() { - return html; - } - - public void setHtml(String html) { - this.html = html; - } - - public String getRawJson() { - return rawJson; - } - - public void setRawJson(String rawJson) { - this.rawJson = rawJson; - } - - @Override - public String toString() { - return "OEmbedResponse{" + - "type='" + type + '\'' + - ", version='" + version + '\'' + - ", title='" + title + '\'' + - ", authorName='" + authorName + '\'' + - ", providerName='" + providerName + '\'' + - ", thumbnailUrl='" + thumbnailUrl + '\'' + - ", width=" + width + - ", height=" + height + - '}'; - } - } -} diff --git a/backup/src/main/java/com/caliverse/video/global/util/UrlParser.java b/backup/src/main/java/com/caliverse/video/global/util/UrlParser.java deleted file mode 100644 index 5db802a..0000000 --- a/backup/src/main/java/com/caliverse/video/global/util/UrlParser.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.caliverse.video.global.util; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Arrays; -import java.util.List; - -/** - * URL 분석을 위한 유틸리티 클래스입니다. - */ -public class UrlParser { - - // 서명된 URL에 포함될 수 있는 파라미터 목록 - private static final List SIGNATURE_PARAMS = Arrays.asList( - "token", "signature", "sig", "expires", "exp", "auth", - "key", "access_token", "api_key", "hmac", "hash" - ); - - // 스트리밍 서비스의 URL 패턴 목록 - private static final List STREAMING_DOMAINS = Arrays.asList( - "vimeo.com", "dailymotion.com", - "netflix.com", "hulu.com", "amazon.com/video", "player.vimeo.com", - "twitch.tv", "hls.", ".m3u8" - ); - - /** - * URL에서 파일 확장자를 추출합니다. - * - * @param url 분석할 URL 문자열 - * @return 파일 확장자, 없으면 null - */ - public String getExtension(String url) { - if (url == null || url.trim().isEmpty()) { - return null; - } - - try { - URL urlObj = new URL(url); - String path = urlObj.getPath(); - - if (path != null && path.contains(".")) { - int lastDotIndex = path.lastIndexOf("."); - int queryIndex = path.indexOf("?", lastDotIndex); - - if (queryIndex > 0) { - return path.substring(lastDotIndex + 1, queryIndex); - } else { - return path.substring(lastDotIndex + 1); - } - } - } catch (MalformedURLException e) { - // URL 파싱 실패 시 null 반환 - } - - return null; - - } - - /** - * URL이 서명된 URL인지 확인합니다. - * - * @param url 분석할 URL 문자열 - * @return 서명된 URL이면 true, 아니면 false - */ - public boolean hasSignatureParameters(String url) { - if (url == null || !url.contains("?")) { - return false; - } - - String lowerUrl = url.toLowerCase(); - String queryPart = lowerUrl.substring(lowerUrl.indexOf('?') + 1); - String[] params = queryPart.split("&"); - - for (String param : params) { - for (String sigParam : SIGNATURE_PARAMS) { - if (param.startsWith(sigParam + "=") || param.contains("=" + sigParam)) { - return true; - } - } - } - - return false; - } - - /** - * URL이 스트리밍 서비스 URL인지 확인합니다. - * - * @param url 분석할 URL 문자열 - * @return 스트리밍 URL이면 true, 아니면 false - */ - public boolean isStreamingUrl(String url) { - if (url == null || url.isEmpty()) { - return false; - } - - String lowerUrl = url.toLowerCase(); - - for (String domain : STREAMING_DOMAINS) { - if (lowerUrl.contains(domain)) { - return true; - } - } - - // HLS 스트리밍 확인 - return lowerUrl.contains("m3u8") || lowerUrl.contains("mpd") || lowerUrl.contains("manifest"); - } -} diff --git a/backup/src/test/java/com/video/analyzer/OEmbedFetcherTest.java b/backup/src/test/java/com/video/analyzer/OEmbedFetcherTest.java deleted file mode 100644 index ac1698f..0000000 --- a/backup/src/test/java/com/video/analyzer/OEmbedFetcherTest.java +++ /dev/null @@ -1,349 +0,0 @@ -package com.video.analyzer; - -import com.caliverse.video.global.util.OEmbedFetcher; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * OEmbedFetcher 테스트 클래스입니다. - * - * 실제 API를 호출하여 oEmbed 데이터를 가져오는 테스트를 수행합니다. - */ -public class OEmbedFetcherTest { - - private OEmbedFetcher oembedFetcher; - - @Before - public void setUp() { - oembedFetcher = new OEmbedFetcher(); - } - - @Test - public void testFetchOEmbed_NaverTV() { - // 네이버TV URL 테스트 - String naverUrl = "https://tv.naver.com/v/40687083"; - - try { - OEmbedFetcher.OEmbedResponse response = oembedFetcher.fetchOEmbedData(naverUrl); - - assertNotNull("응답이 null이 아니어야 합니다", response); - assertNotNull("타입이 있어야 합니다", response.getType()); - assertNotNull("제목이 있어야 합니다", response.getTitle()); - assertNotNull("제공자 이름이 있어야 합니다", response.getProviderName()); - - // 결과 출력 - System.out.println("=== 네이버TV oEmbed 결과 ==="); - System.out.println("타입: " + response.getType()); - System.out.println("제목: " + response.getTitle()); - System.out.println("작성자: " + response.getAuthorName()); - System.out.println("제공자: " + response.getProviderName()); - System.out.println("썸네일 URL: " + response.getThumbnailUrl()); - System.out.println("썸네일 크기: " + response.getThumbnailWidth() + "x" + response.getThumbnailHeight()); - System.out.println("비디오 크기: " + response.getWidth() + "x" + response.getHeight()); - System.out.println("\n원본 JSON:"); - System.out.println(response.getRawJson()); - - } catch (Exception e) { - System.err.println("네이버TV oEmbed 테스트 실패: " + e.getMessage()); - e.printStackTrace(); - } - } - -// @Test -// public void testFetchOEmbed_NaverTV() { -// // 네이버TV URL 테스트 -// String naverUrl = "https://tv.naver.com/v/40687083"; -// -// try { -// OEmbedFetcher.OEmbedResponse response = oembedFetcher.fetchOEmbedData(naverUrl); -// -// assertNotNull("응답이 null이 아니어야 합니다", response); -// assertNotNull("타입이 있어야 합니다", response.getType()); -// assertNotNull("제목이 있어야 합니다", response.getTitle()); -// assertNotNull("제공자 이름이 있어야 합니다", response.getProviderName()); -// -// // 결과 출력 -// System.out.println("=== 네이버TV oEmbed 결과 ==="); -// System.out.println("타입: " + response.getType()); -// System.out.println("제목: " + response.getTitle()); -// System.out.println("작성자: " + response.getAuthorName()); -// System.out.println("제공자: " + response.getProviderName()); -// System.out.println("썸네일 URL: " + response.getThumbnailUrl()); -// System.out.println("썸네일 크기: " + response.getThumbnailWidth() + "x" + response.getThumbnailHeight()); -// System.out.println("비디오 크기: " + response.getWidth() + "x" + response.getHeight()); -// System.out.println("\n원본 JSON:"); -// System.out.println(response.getRawJson()); -// -// } catch (Exception e) { -// System.err.println("네이버TV oEmbed 테스트 실패: " + e.getMessage()); -// e.printStackTrace(); -// } -// } -// -// @Test -// public void testFetchOEmbed_NaverTVLive() { -// // 네이버TV URL 테스트 -// String naverUrl = "https://tv.naver.com/l/171651"; -// -// try { -// OEmbedFetcher.OEmbedResponse response = oembedFetcher.fetchOEmbedData(naverUrl); -// -// assertNotNull("응답이 null이 아니어야 합니다", response); -// assertNotNull("타입이 있어야 합니다", response.getType()); -// assertNotNull("제목이 있어야 합니다", response.getTitle()); -// assertNotNull("제공자 이름이 있어야 합니다", response.getProviderName()); -// -// // 결과 출력 -// System.out.println("=== 네이버TV 라이브 oEmbed 결과 ==="); -// System.out.println("타입: " + response.getType()); -// System.out.println("제목: " + response.getTitle()); -// System.out.println("작성자: " + response.getAuthorName()); -// System.out.println("제공자: " + response.getProviderName()); -// System.out.println("썸네일 URL: " + response.getThumbnailUrl()); -// System.out.println("썸네일 크기: " + response.getThumbnailWidth() + "x" + response.getThumbnailHeight()); -// System.out.println("비디오 크기: " + response.getWidth() + "x" + response.getHeight()); -// System.out.println("\n원본 JSON:"); -// System.out.println(response.getRawJson()); -// -// } catch (Exception e) { -// System.err.println("네이버TV 라이브 oEmbed 테스트 실패: " + e.getMessage()); -// e.printStackTrace(); -// } -// } -// -// @Test -// public void testFetchOEmbed_NaverTVShort() { -// // 네이버TV 쇼츠 URL 테스트 -// // oEmbed API가 빈 응답을 반환하면 fallback으로 embed URL을 생성합니다. -// String naverUrl = "https://tv.naver.com/h/87349660"; -// -// try { -// OEmbedFetcher.OEmbedResponse response = oembedFetcher.fetchOEmbedData(naverUrl); -// -// // fallback 응답이 생성되어야 함 -// assertNotNull("응답이 null이 아니어야 합니다", response); -// assertNotNull("타입이 있어야 합니다", response.getType()); -// assertNotNull("제공자 이름이 있어야 합니다", response.getProviderName()); -// assertEquals("타입은 video여야 합니다", "video", response.getType()); -// assertEquals("제공자는 NAVERTV여야 합니다", "NAVERTV", response.getProviderName()); -// -// // HTML에 embed URL이 포함되어 있어야 함 -// assertNotNull("HTML이 있어야 합니다", response.getHtml()); -// assertTrue("HTML에 embed URL이 포함되어야 합니다", -// response.getHtml().contains("https://tv.naver.com/embed/87349660")); -// -// // 결과 출력 -// System.out.println("=== 네이버TV 쇼츠 oEmbed 결과 (fallback) ==="); -// System.out.println("타입: " + response.getType()); -// System.out.println("제목: " + response.getTitle()); -// System.out.println("작성자: " + response.getAuthorName()); -// System.out.println("제공자: " + response.getProviderName()); -// System.out.println("썸네일 URL: " + response.getThumbnailUrl()); -// System.out.println("썸네일 크기: " + response.getThumbnailWidth() + "x" + response.getThumbnailHeight()); -// System.out.println("비디오 크기: " + response.getWidth() + "x" + response.getHeight()); -// System.out.println("HTML: " + response.getHtml()); -// System.out.println("\n원본 JSON:"); -// System.out.println(response.getRawJson()); -// -// } catch (Exception e) { -// System.err.println("네이버TV 쇼츠 oEmbed 테스트 실패: " + e.getMessage()); -// e.printStackTrace(); -// fail("예외가 발생하면 안됩니다: " + e.getMessage()); -// } -// } -// -// @Test -// public void testFetchOEmbed_NaverTVShare() { -// // 네이버TV 단축 URL 테스트 -// // naver.me URL은 쇼츠(/h/) 형식으로 리다이렉트되며, -// // fallback으로 embed URL을 생성합니다. -// String naverUrl = "https://naver.me/ID3ODX2j"; -// -// try { -// OEmbedFetcher.OEmbedResponse response = oembedFetcher.fetchOEmbedData(naverUrl); -// -// // fallback 응답이 생성되어야 함 -// assertNotNull("응답이 null이 아니어야 합니다", response); -// assertNotNull("타입이 있어야 합니다", response.getType()); -// assertNotNull("제공자 이름이 있어야 합니다", response.getProviderName()); -// assertEquals("타입은 video여야 합니다", "video", response.getType()); -// -// // 결과 출력 -// System.out.println("=== 네이버TV 공유 oEmbed 결과 ==="); -// System.out.println("타입: " + response.getType()); -// System.out.println("제목: " + response.getTitle()); -// System.out.println("작성자: " + response.getAuthorName()); -// System.out.println("제공자: " + response.getProviderName()); -// System.out.println("썸네일 URL: " + response.getThumbnailUrl()); -// System.out.println("썸네일 크기: " + response.getThumbnailWidth() + "x" + response.getThumbnailHeight()); -// System.out.println("비디오 크기: " + response.getWidth() + "x" + response.getHeight()); -// System.out.println("HTML: " + response.getHtml()); -// System.out.println("\n원본 JSON:"); -// System.out.println(response.getRawJson()); -// -// } catch (Exception e) { -// System.err.println("네이버TV 공유 oEmbed 테스트 실패: " + e.getMessage()); -// e.printStackTrace(); -// fail("예외가 발생하면 안됩니다: " + e.getMessage()); -// } -// } -// -// @Test -// public void testFetchOEmbed_YouTube() { -// // YouTube URL 테스트 -// String youtubeUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"; -// -// try { -// OEmbedFetcher.OEmbedResponse response = oembedFetcher.fetchOEmbedData(youtubeUrl); -// -// assertNotNull("응답이 null이 아니어야 합니다", response); -// assertNotNull("타입이 있어야 합니다", response.getType()); -// assertNotNull("제목이 있어야 합니다", response.getTitle()); -// -// // 결과 출력 -// System.out.println("\n=== YouTube oEmbed 결과 ==="); -// System.out.println("타입: " + response.getType()); -// System.out.println("제목: " + response.getTitle()); -// System.out.println("작성자: " + response.getAuthorName()); -// System.out.println("제공자: " + response.getProviderName()); -// System.out.println("썸네일 URL: " + response.getThumbnailUrl()); -// System.out.println("썸네일 크기: " + response.getThumbnailWidth() + "x" + response.getThumbnailHeight()); -// System.out.println("비디오 크기: " + response.getWidth() + "x" + response.getHeight()); -// System.out.println("\n원본 JSON:"); -// System.out.println(response.getRawJson()); -// -// } catch (Exception e) { -// System.err.println("YouTube oEmbed 테스트 실패: " + e.getMessage()); -// e.printStackTrace(); -// } -// } -// -// @Test -// public void testFetchOEmbed_YouTubeShortUrl() { -// // YouTube 단축 URL 테스트 -// String youtubeShortUrl = "https://youtu.be/dQw4w9WgXcQ"; -// -// try { -// OEmbedFetcher.OEmbedResponse response = oembedFetcher.fetchOEmbedData(youtubeShortUrl); -// -// assertNotNull("응답이 null이 아니어야 합니다", response); -// assertNotNull("타입이 있어야 합니다", response.getType()); -// -// // 결과 출력 -// System.out.println("\n=== YouTube 단축 URL oEmbed 결과 ==="); -// System.out.println(response); -// -// } catch (Exception e) { -// System.err.println("YouTube 단축 URL oEmbed 테스트 실패: " + e.getMessage()); -// e.printStackTrace(); -// } -// } -// -// @Test -// public void testFetchOEmbed_Vimeo() { -// // Vimeo URL 테스트 -// String vimeoUrl = "https://vimeo.com/76979871"; -// -// try { -// OEmbedFetcher.OEmbedResponse response = oembedFetcher.fetchOEmbedData(vimeoUrl); -// -// assertNotNull("응답이 null이 아니어야 합니다", response); -// assertNotNull("타입이 있어야 합니다", response.getType()); -// assertNotNull("제목이 있어야 합니다", response.getTitle()); -// -// // 결과 출력 -// System.out.println("\n=== Vimeo oEmbed 결과 ==="); -// System.out.println("타입: " + response.getType()); -// System.out.println("제목: " + response.getTitle()); -// System.out.println("작성자: " + response.getAuthorName()); -// System.out.println("제공자: " + response.getProviderName()); -// System.out.println("썸네일 URL: " + response.getThumbnailUrl()); -// System.out.println("썸네일 크기: " + response.getThumbnailWidth() + "x" + response.getThumbnailHeight()); -// System.out.println("비디오 크기: " + response.getWidth() + "x" + response.getHeight()); -// System.out.println("\n원본 JSON:"); -// System.out.println(response.getRawJson()); -// -// } catch (Exception e) { -// System.err.println("Vimeo oEmbed 테스트 실패: " + e.getMessage()); -// e.printStackTrace(); -// } -// } -// -// @Test -// public void testFetchOEmbed_UnsupportedPlatform() { -// // 지원하지 않는 플랫폼 테스트 -// String unsupportedUrl = "https://example.com/video"; -// -// try { -// oembedFetcher.fetchOEmbedData(unsupportedUrl); -// fail("지원하지 않는 플랫폼에 대해 예외가 발생해야 합니다"); -// } catch (IllegalArgumentException e) { -// // 예상된 예외 -// assertTrue("지원하지 않는 플랫폼 메시지를 포함해야 합니다", -// e.getMessage().contains("지원하지 않는 플랫폼")); -// System.out.println("\n지원하지 않는 플랫폼 예외 처리 확인: " + e.getMessage()); -// } catch (Exception e) { -// fail("IllegalArgumentException이 발생해야 합니다: " + e.getClass().getName()); -// } -// } -// -// @Test -// public void testFetchOEmbed_MultipleNaverTVVideos() { -// // 여러 네이버TV URL 테스트 -// String[] naverUrls = { -// "https://tv.naver.com/v/40687083", -// "https://tv.naver.com/v/84373511", -// "https://tv.naver.com/v/12345678" // 존재하지 않을 수 있는 URL -// }; -// -// System.out.println("\n=== 여러 네이버TV URL 테스트 ==="); -// for (String url : naverUrls) { -// try { -// OEmbedFetcher.OEmbedResponse response = oembedFetcher.fetchOEmbedData(url); -// System.out.println("\nURL: " + url); -// System.out.println("제목: " + response.getTitle()); -// System.out.println("작성자: " + response.getAuthorName()); -// System.out.println("썸네일: " + response.getThumbnailUrl()); -// System.out.println("성공!"); -// } catch (Exception e) { -// System.err.println("\nURL: " + url); -// System.err.println("실패: " + e.getMessage()); -// } -// } -// } - - @Test - public void testOEmbedResponse_AllFields() { - // 네이버TV URL로 모든 필드 확인 - String naverUrl = "https://tv.naver.com/v/40687083"; - - try { - OEmbedFetcher.OEmbedResponse response = oembedFetcher.fetchOEmbedData(naverUrl); - - System.out.println("\n=== oEmbed 응답 모든 필드 확인 ==="); - System.out.println("Type: " + response.getType()); - System.out.println("Version: " + response.getVersion()); - System.out.println("Title: " + response.getTitle()); - System.out.println("Author Name: " + response.getAuthorName()); - System.out.println("Author URL: " + response.getAuthorUrl()); - System.out.println("Provider Name: " + response.getProviderName()); - System.out.println("Provider URL: " + response.getProviderUrl()); - System.out.println("Thumbnail URL: " + response.getThumbnailUrl()); - System.out.println("Thumbnail Width: " + response.getThumbnailWidth()); - System.out.println("Thumbnail Height: " + response.getThumbnailHeight()); - System.out.println("Width: " + response.getWidth()); - System.out.println("Height: " + response.getHeight()); - System.out.println("HTML: " + (response.getHtml() != null ? response.getHtml().substring(0, Math.min(100, response.getHtml().length())) + "..." : "null")); - - // 필수 필드 확인 - assertNotNull("Title은 null이 아니어야 합니다", response.getTitle()); - assertNotNull("Provider Name은 null이 아니어야 합니다", response.getProviderName()); - - } catch (Exception e) { - System.err.println("테스트 실패: " + e.getMessage()); - e.printStackTrace(); - } - } -} diff --git a/backup/src/test/java/com/video/analyzer/PlatformAnalyzerTest.java b/backup/src/test/java/com/video/analyzer/PlatformAnalyzerTest.java deleted file mode 100644 index 13d90f8..0000000 --- a/backup/src/test/java/com/video/analyzer/PlatformAnalyzerTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.video.analyzer; - -import com.caliverse.video.analyzer.model.VideoAnalysisResult; -import com.caliverse.video.analyzer.platform.PlatformAnalyzer; -import com.caliverse.video.analyzer.platform.PlatformAnalyzerFactory; -import com.caliverse.video.analyzer.platform.impl.NaverTvPlatformAnalyzer; -import com.caliverse.video.analyzer.service.VideoAnalyzerService; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * PlatformAnalyzer와 관련된 테스트 클래스입니다. - */ -public class PlatformAnalyzerTest { - private VideoAnalyzerService videoAnalyzerService; - private NaverTvPlatformAnalyzer naverTvAnalyzer; - private PlatformAnalyzerFactory factory; - - @Before - public void setUp() { - videoAnalyzerService = new VideoAnalyzerService(); - naverTvAnalyzer = new NaverTvPlatformAnalyzer(videoAnalyzerService); - factory = new PlatformAnalyzerFactory(videoAnalyzerService); - } - - @Test - public void testNaverTvAnalyzer_CanHandle() { - // 네이버TV URL 처리 가능 여부 확인 - assertTrue("네이버TV URL을 처리할 수 있어야 합니다", - naverTvAnalyzer.canHandle("https://tv.naver.com/v/123")); - assertTrue("네이버 공유 URL을 처리할 수 있어야 합니다", - naverTvAnalyzer.canHandle("https://naver.me/FK09F4E7")); - assertTrue("대소문자 구분 없이 처리할 수 있어야 합니다", - naverTvAnalyzer.canHandle("https://TV.NAVER.COM/v/456")); - assertFalse("null URL은 처리할 수 없어야 합니다", - naverTvAnalyzer.canHandle(null)); - assertFalse("다른 도메인은 처리할 수 없어야 합니다", - naverTvAnalyzer.canHandle("https://youtube.com/watch?v=123")); - } - - @Test - public void testNaverTvAnalyzer_GetPlatformName() { - // 플랫폼 이름 확인 - assertEquals("플랫폼 이름은 '네이버TV'여야 합니다", - "네이버TV", naverTvAnalyzer.getPlatformName()); - } - - @Test - public void testNaverTvAnalyzer_Analyze_ReturnsResult() { - // 네이버TV URL 분석 시 VideoAnalysisResult 반환 확인 - String naverUrl = "https://tv.naver.com/v/84373511"; - - VideoAnalysisResult result = naverTvAnalyzer.analyze(naverUrl); - - assertNotNull("분석 결과는 null이 아니어야 합니다", result); - assertNotNull("success 값은 null이 아니어야 합니다", result.isSuccess()); - assertNotNull("reason은 null이 아니어야 합니다", result.getReason()); - - // 결과 출력 (디버깅용) - System.out.println("네이버TV 분석 결과: " + result); - } - - @Test - public void testFactory_FindAnalyzer_NaverTv() { - // 네이버TV URL에 대한 분석기 찾기 - PlatformAnalyzer naverAnalyzer = factory.findAnalyzer("https://tv.naver.com/v/123"); - - assertNotNull("네이버TV 분석기를 찾아야 합니다", naverAnalyzer); - assertEquals("플랫폼 이름은 '네이버TV'여야 합니다", - "네이버TV", naverAnalyzer.getPlatformName()); - } - - @Test - public void testFactory_FindAnalyzer_UnknownUrl() { - // 알 수 없는 URL에 대한 분석기 찾기 - PlatformAnalyzer unknownAnalyzer = factory.findAnalyzer("https://example.com/video.mp4"); - - assertNull("알 수 없는 URL은 null을 반환해야 합니다", unknownAnalyzer); - } - - @Test - public void testFactory_GetAllAnalyzers() { - var analyzers = factory.getAllAnalyzers(); - - assertNotNull("분석기 목록은 null이 아니어야 합니다", analyzers); - assertFalse("최소 1개 이상의 분석기가 있어야 합니다", analyzers.isEmpty()); - - // 네이버TV 분석기가 포함되어 있는지 확인 - boolean hasNaverTv = analyzers.stream() - .anyMatch(analyzer -> "네이버TV".equals(analyzer.getPlatformName())); - - assertTrue("네이버TV 분석기가 포함되어 있어야 합니다", hasNaverTv); - } - -} diff --git a/backup/src/test/java/com/video/analyzer/VideoAnalyzerTest.java b/backup/src/test/java/com/video/analyzer/VideoAnalyzerTest.java deleted file mode 100644 index 04d6030..0000000 --- a/backup/src/test/java/com/video/analyzer/VideoAnalyzerTest.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.video.analyzer; - -import com.caliverse.video.analyzer.VideoAnalyzer; -import com.caliverse.video.analyzer.model.VideoAnalysisResult; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * VideoAnalyzer의 통합 테스트 클래스입니다. - */ -public class VideoAnalyzerTest { - - private VideoAnalyzer analyzer; - - @Before - public void setUp() { - analyzer = new VideoAnalyzer(); - } - - @Test - public void testAnalyzeUrl_Success() { - // 재생 가능한 일반 비디오 URL (mp4) - String videoUrl = "https://example.com/video.mp4"; - - VideoAnalysisResult result = analyzer.analyzeUrl(videoUrl); - - assertNotNull("결과는 null이 아니어야 합니다", result); - assertNotNull("reason은 null이 아니어야 합니다", result.getReason()); - } - - @Test - public void testAnalyzeUrl_NaverTv() { - // 네이버TV URL - String naverUrl = "https://tv.naver.com/v/84373511"; - - VideoAnalysisResult result = analyzer.analyzeUrl(naverUrl); - - assertNotNull("결과는 null이 아니어야 합니다", result); - assertNotNull("reason은 null이 아니어야 합니다", result.getReason()); - - // 결과 출력 (디버깅용) - System.out.println("네이버TV 분석 결과: " + result); - } - - @Test - public void testAnalyzeUrl_Naver() { - // 네이버TV URL - String naverUrl = "https://naver.me/ID3ODX2j"; - - VideoAnalysisResult result = analyzer.analyzeUrl(naverUrl); - - assertNotNull("결과는 null이 아니어야 합니다", result); - assertNotNull("reason은 null이 아니어야 합니다", result.getReason()); - - // 결과 출력 (디버깅용) - System.out.println("네이버 공유 분석 결과: " + result); - } - - @Test - public void testAnalyzeUrl_NaverTv2() { - // 네이버TV URL - String naverUrl = "https://tv.naver.com/h/87349660"; - - VideoAnalysisResult result = analyzer.analyzeUrl(naverUrl); - - assertNotNull("결과는 null이 아니어야 합니다", result); - assertNotNull("reason은 null이 아니어야 합니다", result.getReason()); - - // 결과 출력 (디버깅용) - System.out.println("네이버 숏츠 분석 결과: " + result); - } - - @Test - public void testAnalyzeUrl_NaverTvLive() { - // 네이버TV URL - String naverUrl = "https://tv.naver.com/l/171651"; - - VideoAnalysisResult result = analyzer.analyzeUrl(naverUrl); - - assertNotNull("결과는 null이 아니어야 합니다", result); - assertNotNull("reason은 null이 아니어야 합니다", result.getReason()); - - // 결과 출력 (디버깅용) - System.out.println("네이버 라이브 분석 결과: " + result); - } - - @Test - public void testSupportedPlatforms() { - String[] platforms = analyzer.getSupportedPlatforms(); - - assertNotNull("플랫폼 목록은 null이 아니어야 합니다", platforms); - assertTrue("최소 1개 이상의 플랫폼을 지원해야 합니다", platforms.length > 0); - - // 기본 플랫폼 확인 - boolean hasNaverTv = false; - for (String platform : platforms) { - if ("네이버TV".equals(platform)) { - hasNaverTv = true; - break; - } - } - - assertTrue("네이버TV를 지원해야 합니다", hasNaverTv); - } - - @Test - public void testAnalyzeUrl_Result_HasProperties() { - // VideoAnalysisResult가 올바른 속성을 가지고 있는지 확인 - String testUrl = "https://example.com/test.mp4"; - - VideoAnalysisResult result = analyzer.analyzeUrl(testUrl); - - // result 객체가 올바른 메소드를 가지고 있는지 확인 - assertNotNull(result); - - String reason = result.getReason(); - - assertNotNull("reason은 항상 값이 있어야 합니다", reason); - assertFalse("reason은 빈 문자열이 아니어야 합니다", reason.trim().isEmpty()); - - // toString()이 정상 작동하는지 확인 - String resultString = result.toString(); - assertNotNull(resultString); - assertTrue("toString()은 'VideoAnalysisResult'를 포함해야 합니다", - resultString.contains("VideoAnalysisResult")); - } - -} diff --git a/build/classes/java/main/com/caliverse/analyzer/VideoAnalyzer.class b/build/classes/java/main/com/caliverse/analyzer/VideoAnalyzer.class deleted file mode 100644 index 9d7af22..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/VideoAnalyzer.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/entity/UrlType.class b/build/classes/java/main/com/caliverse/analyzer/entity/UrlType.class deleted file mode 100644 index 731a42d..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/entity/UrlType.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/model/VideoAnalysisResult.class b/build/classes/java/main/com/caliverse/analyzer/model/VideoAnalysisResult.class deleted file mode 100644 index b9ad8e3..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/model/VideoAnalysisResult.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/PlatformAnalyzer.class b/build/classes/java/main/com/caliverse/analyzer/platform/PlatformAnalyzer.class deleted file mode 100644 index 0189ffc..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/PlatformAnalyzer.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/PlatformAnalyzerFactory.class b/build/classes/java/main/com/caliverse/analyzer/platform/PlatformAnalyzerFactory.class deleted file mode 100644 index b7d9680..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/PlatformAnalyzerFactory.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/impl/DirectUrlPlatformAnalyzer$ValidationResult.class b/build/classes/java/main/com/caliverse/analyzer/platform/impl/DirectUrlPlatformAnalyzer$ValidationResult.class deleted file mode 100644 index 6739989..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/impl/DirectUrlPlatformAnalyzer$ValidationResult.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/impl/DirectUrlPlatformAnalyzer.class b/build/classes/java/main/com/caliverse/analyzer/platform/impl/DirectUrlPlatformAnalyzer.class deleted file mode 100644 index c70db4e..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/impl/DirectUrlPlatformAnalyzer.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/impl/HttpBasedPlatformAnalyzer.class b/build/classes/java/main/com/caliverse/analyzer/platform/impl/HttpBasedPlatformAnalyzer.class deleted file mode 100644 index 67993a2..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/impl/HttpBasedPlatformAnalyzer.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/impl/KakaoTvPlatformAnalyzer.class b/build/classes/java/main/com/caliverse/analyzer/platform/impl/KakaoTvPlatformAnalyzer.class deleted file mode 100644 index 908070f..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/impl/KakaoTvPlatformAnalyzer.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/impl/NaverTvPlatformAnalyzer.class b/build/classes/java/main/com/caliverse/analyzer/platform/impl/NaverTvPlatformAnalyzer.class deleted file mode 100644 index 17fe785..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/impl/NaverTvPlatformAnalyzer.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/impl/TikTokPlatformAnalyzer.class b/build/classes/java/main/com/caliverse/analyzer/platform/impl/TikTokPlatformAnalyzer.class deleted file mode 100644 index 4039db1..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/impl/TikTokPlatformAnalyzer.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/impl/TwitterPlatformAnalyzer.class b/build/classes/java/main/com/caliverse/analyzer/platform/impl/TwitterPlatformAnalyzer.class deleted file mode 100644 index 9ef30b3..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/impl/TwitterPlatformAnalyzer.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/impl/VimeoPlatformAnalyzer.class b/build/classes/java/main/com/caliverse/analyzer/platform/impl/VimeoPlatformAnalyzer.class deleted file mode 100644 index 093420c..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/impl/VimeoPlatformAnalyzer.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/impl/WeiboPlatformAnalyzer$VideoMetadata.class b/build/classes/java/main/com/caliverse/analyzer/platform/impl/WeiboPlatformAnalyzer$VideoMetadata.class deleted file mode 100644 index cc9ff7b..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/impl/WeiboPlatformAnalyzer$VideoMetadata.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/impl/WeiboPlatformAnalyzer.class b/build/classes/java/main/com/caliverse/analyzer/platform/impl/WeiboPlatformAnalyzer.class deleted file mode 100644 index e8ca6a8..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/impl/WeiboPlatformAnalyzer.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/platform/impl/YouTubePlatformAnalyzer.class b/build/classes/java/main/com/caliverse/analyzer/platform/impl/YouTubePlatformAnalyzer.class deleted file mode 100644 index 6772f6b..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/platform/impl/YouTubePlatformAnalyzer.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/service/VideoAnalyzerService$1.class b/build/classes/java/main/com/caliverse/analyzer/service/VideoAnalyzerService$1.class deleted file mode 100644 index 8a07d6b..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/service/VideoAnalyzerService$1.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/service/VideoAnalyzerService$CheckResult.class b/build/classes/java/main/com/caliverse/analyzer/service/VideoAnalyzerService$CheckResult.class deleted file mode 100644 index ebcaf77..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/service/VideoAnalyzerService$CheckResult.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/analyzer/service/VideoAnalyzerService.class b/build/classes/java/main/com/caliverse/analyzer/service/VideoAnalyzerService.class deleted file mode 100644 index 40aac3d..0000000 Binary files a/build/classes/java/main/com/caliverse/analyzer/service/VideoAnalyzerService.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/global/common/CommonConstants.class b/build/classes/java/main/com/caliverse/global/common/CommonConstants.class deleted file mode 100644 index f230592..0000000 Binary files a/build/classes/java/main/com/caliverse/global/common/CommonConstants.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/global/common/Messages.class b/build/classes/java/main/com/caliverse/global/common/Messages.class deleted file mode 100644 index a365ae0..0000000 Binary files a/build/classes/java/main/com/caliverse/global/common/Messages.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/global/util/HttpUtils.class b/build/classes/java/main/com/caliverse/global/util/HttpUtils.class deleted file mode 100644 index 88c9758..0000000 Binary files a/build/classes/java/main/com/caliverse/global/util/HttpUtils.class and /dev/null differ diff --git a/build/classes/java/main/com/caliverse/global/util/UrlParser.class b/build/classes/java/main/com/caliverse/global/util/UrlParser.class deleted file mode 100644 index 953a4a9..0000000 Binary files a/build/classes/java/main/com/caliverse/global/util/UrlParser.class and /dev/null differ diff --git a/build/libs/video-url-analyzer-1.0.0.jar b/build/libs/video-url-analyzer-1.0.0.jar deleted file mode 100644 index 8425594..0000000 Binary files a/build/libs/video-url-analyzer-1.0.0.jar and /dev/null differ diff --git a/build/tmp/.cache/expanded.lock b/build/tmp/.cache/expanded.lock deleted file mode 100644 index f7af5ae..0000000 Binary files a/build/tmp/.cache/expanded.lock and /dev/null differ diff --git a/build/tmp/compileJava/previous-compilation-data.bin b/build/tmp/compileJava/previous-compilation-data.bin deleted file mode 100644 index 06d9b9b..0000000 Binary files a/build/tmp/compileJava/previous-compilation-data.bin and /dev/null differ diff --git a/build/tmp/jar/MANIFEST.MF b/build/tmp/jar/MANIFEST.MF deleted file mode 100644 index de173a8..0000000 --- a/build/tmp/jar/MANIFEST.MF +++ /dev/null @@ -1,5 +0,0 @@ -Manifest-Version: 1.0 -Implementation-Title: Video URL Analyzer -Implementation-Version: 1.0.0 -Main-Class: com.caliverse.analyzer.VideoAnalyzer -