如何使用youtube_explode_dart仅下载YouTube音频流的指定片段
基于youtube_explode_dart实现YouTube音频指定片段下载方案
实现原理
YouTube的媒体流支持 HTTP Range请求,你可以先通过音频流的总时长、总大小计算出你需要的时间段对应的字节偏移范围,直接请求这个范围的内容即可,不需要全量下载。
完整实现代码
首先将时间参数转为Duration类型,计算对应字节范围后直接请求分片:
import 'package:youtube_explode_dart/youtube_explode_dart.dart'; import 'dart:io'; void main() async { var yt = YoutubeExplode(); var id = "aboZctrHfK8"; var file = File("myCroppedSong.mp4"); // 时间参数转成Duration方便计算 var start = Duration(milliseconds: 2000); var end = Duration(milliseconds: 5000); var targetDuration = end - start; // 获取音频流信息 var manifest = await yt.videos.streamsClient.getManifest(id); var audio = manifest.audioOnly.first; // 音频总时长 var totalDuration = audio.duration!; // 音频总字节数 var totalBytes = audio.size.totalBytes; // 计算起始和结束字节偏移,预留冗余避免切到关键帧导致文件损坏 var startBytes = (start.inMicroseconds / totalDuration.inMicroseconds * totalBytes).floor(); // 多申请128KB冗余,保证帧数据完整 var endBytes = (end.inMicroseconds / totalDuration.inMicroseconds * totalBytes).ceil() + 131072; // 字节范围超过总大小则取总大小 if (endBytes > totalBytes) endBytes = totalBytes; // 直接请求指定范围的字节流 var audioStream = yt.videos.streamsClient.get(audio, start: startBytes, end: endBytes); var output = file.openWrite(mode: FileMode.writeOnlyAppend); await for (final data in audioStream) { output.add(data); } await output.close(); yt.close(); }
扩展方法封装(实现你预期的调用形式)
你可以把上面的逻辑封装为StreamClient的扩展方法,就能实现audioStream.subset(start, end)的调用效果:
extension StreamSubset on StreamClient { Stream<List<int>> subset(StreamInfo info, Duration start, Duration end) { var totalDuration = info.duration!; var totalBytes = info.size.totalBytes; var startBytes = (start.inMicroseconds / totalDuration.inMicroseconds * totalBytes).floor(); var endBytes = (end.inMicroseconds / totalDuration.inMicroseconds * totalBytes).ceil() + 131072; if (endBytes > totalBytes) endBytes = totalBytes; return get(info, start: startBytes, end: endBytes); } }
注意事项
- 直接下载的分片文件开头可能存在不完整的音频帧,建议下载完成后用ffmpeg做一次快速裁剪,保证文件可正常播放,此时文件体积已经很小,裁剪耗时可以忽略:
FlutterFFmpeg _flutterFFmpeg = FlutterFFmpeg(); await _flutterFFmpeg.executeWithArguments([ "-v", "error", "-ss", "0", "-t", targetDuration.inSeconds.toString(), "-i", file.path, "-acodec", "copy", "finalSong.mp4"]);
- 可以根据音质需求选择码率更低的音频流,不用默认取
audioOnly.first,进一步加快下载速度。
内容的提问来源于stack exchange,提问作者Jérémy
相关产品推荐
相关产品推荐

