feat: 유튜브 URL videoId 추출 유틸

watch/shorts/embed/live/youtu.be 지원, 11자 ID 검증.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
hehihoho3@gmail.com 2026-08-03 11:58:17 +09:00
parent 0142a439d3
commit 3fda9077a6
2 changed files with 48 additions and 0 deletions

View File

@ -0,0 +1,21 @@
package com.hlab.yanalyst.domain.shortform;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** 유튜브 URL에서 11자 videoId를 뽑는다. youtube.com / youtu.be 도메인만 인정. */
public final class YoutubeVideoId {
private static final Pattern PATTERN = Pattern.compile(
"^https?://(?:www\\.|m\\.)?(?:youtube\\.com/(?:watch\\?[^#]*\\bv=|shorts/|embed/|live/)|youtu\\.be/)"
+ "([A-Za-z0-9_-]{11})(?:[?&#/].*)?$");
private YoutubeVideoId() {}
public static Optional<String> from(String url) {
if (url == null || url.isBlank()) return Optional.empty();
Matcher m = PATTERN.matcher(url.trim());
return m.matches() ? Optional.of(m.group(1)) : Optional.empty();
}
}

View File

@ -0,0 +1,27 @@
package com.hlab.yanalyst.domain.shortform;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class YoutubeVideoIdTest {
@Test
void 다양한_유튜브_url_형식에서_videoId를_추출한다() {
assertEquals("JW5mwZ8RsG8", YoutubeVideoId.from("https://www.youtube.com/watch?v=JW5mwZ8RsG8").orElseThrow());
assertEquals("JW5mwZ8RsG8", YoutubeVideoId.from("https://www.youtube.com/watch?v=JW5mwZ8RsG8&t=10s").orElseThrow());
assertEquals("JW5mwZ8RsG8", YoutubeVideoId.from("https://youtu.be/JW5mwZ8RsG8").orElseThrow());
assertEquals("JW5mwZ8RsG8", YoutubeVideoId.from("https://www.youtube.com/shorts/JW5mwZ8RsG8").orElseThrow());
assertEquals("JW5mwZ8RsG8", YoutubeVideoId.from("https://www.youtube.com/embed/JW5mwZ8RsG8").orElseThrow());
assertEquals("JW5mwZ8RsG8", YoutubeVideoId.from("https://www.youtube.com/live/JW5mwZ8RsG8?feature=share").orElseThrow());
}
@Test
void 유튜브가_아니거나_id가_없으면_empty() {
assertTrue(YoutubeVideoId.from(null).isEmpty());
assertTrue(YoutubeVideoId.from("").isEmpty());
assertTrue(YoutubeVideoId.from("https://example.com/watch?v=JW5mwZ8RsG8").isEmpty());
assertTrue(YoutubeVideoId.from("https://www.youtube.com/watch").isEmpty());
assertTrue(YoutubeVideoId.from("https://www.youtube.com/watch?v=short").isEmpty());
}
}