java-youtube-downloader
一个简单的Java解析器,用于获取YouTube视频元数据。
该库不稳定,因为YouTube经常更改其网页结构。我不经常使用这个库来发现错误。这就是为什么只有当有人发现错误并提出问题时,才会修复错误。欢迎报告错误或提交PR。
警告:YouTube API不支持视频下载。事实上,这是被禁止的 - 服务条款 - II. 禁止事项。
警告:下载视频可能会侵犯版权!
本项目仅供教育目的。我敦促不要使用本项目违反任何法律。
用法
配置
// 使用默认配置初始化下载器
YoutubeDownloader downloader = new YoutubeDownloader();
// 或者使用自定义配置
Config config = new Config.Builder()
.executorService(executorService) // 用于异步请求,默认为Executors.newCachedThreadPool()
.maxRetries(1) // 失败时重试,默认为0
.header("Accept-language", "en-US,en;") // 额外的请求头
.proxy("192.168.0.1", 2005)
.proxyCredentialsManager(proxyCredentials) // 默认为ProxyCredentialsImpl
.proxy("192.168.0.1", 2005, "login", "pass")
.build();
YoutubeDownloader downloader = new YoutubeDownloader(config);
// 或在初始化后进行配置
Config config = downloader.getConfig();
config.setMaxRetries(0);
请求
// 每个请求都接受可选参数,这些参数将覆盖全局配置
Request request = new Request(...)
.maxRetries(...)
.proxy(...)
.header(...)
.callback(...) // 添加异步处理的回调
.async(); // 使请求异步
响应
Response<T> response = downloader.get...(request)
// 获取响应状态,可能是 [downloading, completed, canceled, error] 中的一个
ResponseStatus status = response.status();
// 获取响应数据
// 注意:如果请求是异步的,将阻塞当前线程直到完成
T data = response.data();
// 或者设置超时时间获取,可能抛出TimeoutException
T data = response.data(1, TimeUnit.SECONDS);
// 如果请求是异步的,可以取消
boolean canceled = response.cancel();
// 如果请求异常结束,获取响应错误
// 注意:如果请求是异步的,将阻塞当前线程直到完成
Throwable error = response.error();
// 检查请求是否成功完成
// 注意:如果请求是异步的,将阻塞当前线程直到完成
boolean ok = response.ok();
视频信息
String videoId = "abc12345"; // 对应URL https://www.youtube.com/watch?v=abc12345
// 同步解析
RequestVideoInfo request = new RequestVideoInfo(videoId);
Response<VideoInfo> response = downloader.getVideoInfo(request);
VideoInfo video = response.data();
// 异步解析
RequestVideoInfo request = new RequestVideoInfo(videoId)
.callback(new YoutubeCallback<VideoInfo>() {
@Override
public void onFinished(VideoInfo videoInfo) {
System.out.println("解析完成");
}
@Override
public void onError(Throwable throwable) {
System.out.println("错误: " + throwable.getMessage());
}
})
.async();
Response<VideoInfo> response = downloader.getVideoInfo(request);
VideoInfo video = response.data(); // 将阻塞线程
// 视频详情
VideoDetails details = video.details();
System.out.println(details.title());
System.out.println(details.viewCount());
details.thumbnails().forEach(image -> System.out.println("缩略图: " + image));
// HLS URL仅适用于直播视频和流
if (video.details().isLive()) {
System.out.println("直播流HLS URL: " + video.details().liveUrl());
}
// 获取仅包含音频的视频格式
List<VideoWithAudioFormat> videoWithAudioFormats = video.videoWithAudioFormats();
videoWithAudioFormats.forEach(it -> {
System.out.println(it.audioQuality() + ", " + it.videoQuality() + " : " + it.url());
});
// 获取所有视频格式(可能包含更高质量但没有音频的格式)
List<VideoFormat> videoFormats = video.videoFormats();
videoFormats.forEach(it -> {
System.out.println(it.videoQuality() + " : " + it.url());
});
// 获取音频格式
List<AudioFormat> audioFormats = video.audioFormats();
audioFormats.forEach(it -> {
System.out.println(it.audioQuality() + " : " + it.url());
});
// 获取最佳格式
video.bestVideoWithAudioFormat();
video.bestVideoFormat();
video.bestAudioFormat();
// 过滤格式
List<Format> formats = video.findFormats(new Filter<Format>() {
@Override
public boolean test(Format format) {
return format.extension() == Extension.WEBM;
}
});
// itag可以在这里找到 - https://gist.github.com/sidneys/7095afe4da4ae58694d128b1034e01e2
Format formatByItag = video.findFormatByItag(18); // 如果未找到则返回null
if (formatByItag != null) {
System.out.println(formatByItag.url());
}
下载视频
File outputDir = new File("my_videos");
Format format = videoFormats.get(0);
// 同步下载
RequestVideoFileDownload request = new RequestVideoFileDownload(format)
// 可选参数
.saveTo(outputDir) // 默认为"videos"目录
.renameTo("video") // 默认文件名与YouTube上的视频标题相同
.overwriteIfExists(true); // 如果为false且同名文件已存在,将添加后缀,如video(1).mp4
Response<File> response = downloader.downloadVideoFile(request);
File data = response.data();
// 带回调的异步下载
RequestVideoFileDownload request = new RequestVideoFileDownload(format)
.callback(new YoutubeProgressCallback<File>() {
@Override
public void onDownloading(int progress) {
System.out.printf("已下载 %d%%\n", progress);
}
@Override
public void onFinished(File videoInfo) {
System.out.println("文件下载完成: " + videoInfo);
}
@Override
public void onError(Throwable throwable) {
System.out.println("错误: " + throwable.getLocalizedMessage());
}
})
.async();
Response<File> response = downloader.downloadVideoFile(request);
File data = response.data(); // 将阻塞当前线程
// 无回调的异步下载
RequestVideoFileDownload request = new RequestVideoFileDownload(format).async();
Response<File> response = downloader.downloadVideoFile(request);
File data = response.data(20, TimeUnit.SECONDS); // 将阻塞当前线程,可能抛出TimeoutExeption
// 下载到内存中的OutputStream
OutputStream os = new ByteArrayOutputStream();
RequestVideoStreamDownload request = new RequestVideoStreamDownload(format, os);
Response<Void> response = downloader.downloadVideoStream(request);
字幕
// 如果已经解析了视频信息,可以从视频字幕中获取字幕
List<SubtitlesInfo> subtitlesInfo = video.subtitles(); // 注意:包括自动生成的字幕
// 如果不需要视频信息,只需要字幕,可以使用这个请求
Response<List<SubtitlesInfo>> response = downloader.getSubtitlesInfo(new RequestSubtitlesInfo(videoId)); // 注意:不包括自动生成的字幕
List<SubtitlesInfo> subtitlesInfo = response.data();
for (SubtitlesInfo info : subtitles) {
RequestSubtitlesDownload request = new RequestSubtitlesDownload(info)
// 可选
.formatTo(Extension.JSON3)
.translateTo("uk");
// 同步下载
Response<String> response = downloader.downloadSubtitle(request);
String subtitlesString = response.data();
// 异步下载
RequestSubtitlesDownload request = new RequestSubtitlesDownload(info)
.callback(...) // 可选
.async();
Response<String> response = downloader.downloadSubtitle(request);
String subtitlesString = response.data(); // 将阻塞当前线程
// 使用外部下载管理器下载
String downloadUrl = request.getDownloadUrl();
}
播放列表
String playlistId = "abc12345"; // 对应URL https://www.youtube.com/playlist?list=abc12345
RequestPlaylistInfo request = new RequestPlaylistInfo(playlistId);
Response<PlaylistInfo> response = downloader.getPlaylistInfo(request);
PlaylistInfo playlistInfo = response.data();
// 播放列表详情
PlaylistDetails details = playlistInfo.details();
System.out.println(details.title());
System.out.println(details.videoCount());
// 获取视频详情
PlaylistVideoDetails videoDetails = playlistInfo.videos().get(0);
System.out.println(videoDetails.videoId());
System.out.println(videoDetails.title());
System.out.println(videoDetails.index());
频道上传
String channelId = "abc12345"; // 对应URL https://www.youtube.com/channel/abc12345
// 或
String channelId = "someName"; // 对应URL https://www.youtube.com/c/someName
RequestChannelUploads request = new RequestChannelUploads(channelId);
Response<PlaylistInfo> response = downloader.getChannelUploads(request);
PlaylistInfo playlistInfo = response.data();
搜索
RequestSearchResult request = new RequestSearchResult("搜索查询")
// 过滤器
.type(TypeField.VIDEO) // 仅视频
.format(FormatField._3D,
FormatField.HD) // 3D高清视频
.match(FeatureField.SUBTITLES) // 带字幕
.during(DurationField.OVER_20_MINUTES) // 20分钟以上的视频
.uploadedThis(UploadDateField.MONTH) // 本月上传的
// 其他参数
.forceExactQuery(true) // 避免自动更正
.sortBy(SortField.VIEW_COUNT); // 结果按观看次数排序
// 或者
RequestSearchResult request = new RequestSearchResult("搜索查询")
.filter(
TypeField.VIDEO,
FormatField.HD,
(...)
UploadDateField.MONTH);
SearchResult result = downloader.search(request).data();
// 获取下一个结果(每次最多20项)
if (result.hasContinuation()) {
RequestSearchContinuation nextRequest = new RequestSearchContinuation(result);
SearchResult continuation = downloader.searchContinuation(nextRequest).data();
}
// 如果有建议的查询,获取其结果
if (result.suggestion() != null) {
System.out.println(result.suggestion().query()); // 建议的查询
RequestSearchable suggestedRequest = new RequestSearchable(result.suggestion());
SearchResult suggestedResult = downloader.search(suggestedRequest).data();
}
// 查询细化
if (result.refinements() != null) {
System.out.println(result.refinements().get(0).query()); // 细化查询
RequestSearchable refinedRequest = new RequestSearchable(result.refinements().get(0));
SearchResult refinedResult = downloader.search(refinedRequest).data();
}
// 查询已被自动更正,强制使用原始查询
if (result.isAutoCorrected()) {
System.out.println(result.autoCorrectedQuery()); // 已更正的查询
SearchResult forcedResult = downloader.search(request.forceExactQuery(true)).data();
}
// 详情
System.out.println(result.estimatedResults());
// 项目,每个结果最多20项(第一个结果可能包含额外的分类)
List<SearchResultItem> items = result.items();
List<SearchResultVideoDetails> videos = result.videos();
List<SearchResultChannelDetails> channels = result.channels();
List<SearchResultPlaylistDetails> playlists = result.playlists();
List<SearchResultShelf> shelves = result.shelves();
// 项目类型转换
SearchResultItem item = items.get(0);
switch (item.type()) {
case VIDEO:
System.out.println(item.asVideo().description());
break;
case SHELF:
for (SearchResultVideoDetails video : item.asShelf().videos()) {
System.out.println(video.author());
}
break;
(...)
}
// Base64(可选):为搜索参数使用另一个Base64编码器
// 经典JDK和Android API >= 26
Base64Encoder.setInstance(bytes -> Base64.getUrlEncoder().encodeToString(bytes));
// Android API < 26
Base64Encoder.setInstance(new Base64Encoder() {
@Override
public String encodeToString(byte[] bytes) {
return Base64.encodeToString(bytes, Base64.URL_SAFE);
}
};
引入
Maven
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.sealedtx</groupId>
<artifactId>java-youtube-downloader</artifactId>
<version>3.2.4</version>
</dependency>
Gradle
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.sealedtx:java-youtube-downloader:3.2.4'
}
Android
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
// 对于Kotlin项目
kotlinOptions {
jvmTarget = "1.8"
}
}