Project Icon

java-youtube-downloader

Java实现的YouTube视频元数据解析库

该库具有获取视频详情、下载视频和字幕、搜索视频等功能。它支持异步操作、自定义配置和格式过滤,便于集成到Java项目中。由于YouTube接口变化频繁,库的稳定性有限,但社区持续更新维护。使用时需注意仅限教育用途,并遵守相关法律规定。

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"
  }
}
项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

白日梦AI

白日梦AI提供专注于AI视频生成的多样化功能,包括文生视频、动态画面和形象生成等,帮助用户快速上手,创造专业级内容。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

讯飞绘镜

讯飞绘镜是一个支持从创意到完整视频创作的智能平台,用户可以快速生成视频素材并创作独特的音乐视频和故事。平台提供多样化的主题和精选作品,帮助用户探索创意灵感。

Project Cover

讯飞文书

讯飞文书依托讯飞星火大模型,为文书写作者提供从素材筹备到稿件撰写及审稿的全程支持。通过录音智记和以稿写稿等功能,满足事务性工作的高频需求,帮助撰稿人节省精力,提高效率,优化工作与生活。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号