extractor
一个强大的 Flutter 插件,使用 yt-dlp 从 1000 多个网站下载视频和音频。支持画质选择、格式转换、进度跟踪等功能。
从 YouTube、Vimeo 等视频网站提取直接视频链接。
{"sdk":"flutter"}^1.0.0^22.7.0以下为英文项目原文快照,最新内容请访问 GitHub。
A robust, production-ready Flutter plugin for downloading videos and audio from 1000+ websites using yt-dlp. Built with native Android (Kotlin) implementation.
| Platform | Status | Implementation |
|---|---|---|
| Android | ✅ Fully Supported | Native Kotlin + youtubedl-android |
| iOS | 💡 Open for Contributions | See iOS Support below |
| Main Screen - Quality Selection | Download in Progress | Downloads Page |
| Quality Selection Browse and select video quality |
Download Progress Real-time progress with logs |
Downloads Manager View completed downloads |
| Settings - Version Info | Settings - Features | |
| Version Information Check library versions |
Features List All plugin capabilities |
Add to your pubspec.yaml:
dependencies:
extractor: latest
Minimum SDK version (API 24+):
android {
defaultConfig {
minSdk = 24
}
}
Required: Set extractNativeLibs in AndroidManifest.xml:
<application
android:extractNativeLibs="true"
...>
import 'package:extractor/extractor.dart';
final youtubeDL = YoutubeDLFlutter.instance;
// Initialize with FFmpeg and Aria2c
final result = await youtubeDL.initialize(
enableFFmpeg: true,
enableAria2c: true,
);
if (result.success) {
print('Initialized successfully');
} else {
print('Error: ${result.errorMessage}');
}
try {
final info = await youtubeDL.getVideoInfo('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
print('Title: ${info.title}');
print('Duration: ${info.duration} seconds');
print('Uploader: ${info.uploader}');
print('Thumbnail: ${info.thumbnail}');
print('Available formats: ${info.formats?.length}');
// List all formats
info.formats?.forEach((format) {
print('Format: ${format?.formatId} - ${format?.resolution} - ${format?.ext}');
});
} catch (e) {
print('Error: $e');
}
import 'dart:io';
import 'package:path_provider/path_provider.dart';
// Get download directory
final dir = await getExternalStorageDirectory();
final downloadPath = '${dir!.path}/Downloads';
// Create download request
final request = DownloadRequest(
url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
outputPath: downloadPath,
outputTemplate: '%(title)s.%(ext)s',
format: 'bestvideo+bestaudio/best', // Best quality
processId: 'download_${DateTime.now().millisecondsSinceEpoch}',
embedThumbnail: true,
embedMetadata: true,
customOptions: {
'--downloader': 'libaria2c.so', // Use Aria2c for faster downloads
},
);
// Start download
try {
final result = await youtubeDL.download(request);
if (result.status == OperationStatus.success) {
print('Downloaded to: ${result.outputPath}');
} else {
print('Download failed: ${result.errorMessage}');
}
} catch (e) {
print('Error: $e');
}
final request = DownloadRequest(
url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
outputPath: downloadPath,
outputTemplate: '%(title)s.%(ext)s',
extractAudio: true,
audioFormat: 'mp3',
audioQuality: 0, // Best quality (0-9, 0 is best)
embedThumbnail: true,
embedMetadata: true,
processId: 'audio_${DateTime.now().millisecondsSinceEpoch}',
);
final result = await youtubeDL.download(request);
// Listen to progress updates
youtubeDL.onProgress.listen((progress) {
print('Process: ${progress.processId}');
print('Progress: ${progress.progress}%');
print('ETA: ${progress.eta.inSeconds} seconds');
});
// Listen to state changes
youtubeDL.onStateChanged.listen((state) {
print('Process: ${state.processId}');
print('State: ${state.state}'); // started, completed, cancelled
});
// Listen to errors
youtubeDL.onError.listen((error) {
print('Process: ${error.processId}');
print('Error: ${error.error}');
});
final cancelled = await youtubeDL.cancelDownload(processId: 'download_123');
if (cancelled) {
print('Download cancelled');
}
final result = await youtubeDL.updateYoutubeDL(channel: UpdateChannel.stable);
if (result.status == OperationStatus.success) {
print('Updated to: ${result.version}');
} else {
print('Update failed: ${result.errorMessage}');
}
final versionInfo = await youtubeDL.getVersion();
print('yt-dlp: ${versionInfo.youtubeDlVersion}');
print('FFmpeg: ${versionInfo.ffmpegVersion}');
print('Python: ${versionInfo.pythonVersion}');
Pre-configured quality presets for common use cases:
import 'package:extractor/extractor.dart';
// Best Quality (highest resolution + audio)
final bestQuality = DownloadTemplates.bestQuality(
url: videoUrl,
outputPath: downloadPath,
);
// Audio Only (best quality)
final audioOnly = DownloadTemplates.audioOnly(
url: videoUrl,
outputPath: downloadPath,
audioFormat: 'mp3',
);
// 1080p Video
final video1080p = DownloadTemplates.video1080p(
url: videoUrl,
outputPath: downloadPath,
);
// 720p Video
final video720p = DownloadTemplates.video720p(
url: videoUrl,
outputPath: downloadPath,
);
// 480p Video
final video480p = DownloadTemplates.video480p(
url: videoUrl,
outputPath: downloadPath,
);
// Small Size (best video under 100MB)
final smallSize = DownloadTemplates.smallSize(
url: videoUrl,
outputPath: downloadPath,
);
// Use template
final result = await youtubeDL.download(bestQuality);
// Download specific format by ID
final request = DownloadRequest(
url: videoUrl,
outputPath: downloadPath,
format: '137+140', // Format IDs from getVideoInfo()
);
// Download best video with height <= 720p
final request = DownloadRequest(
url: videoUrl,
outputPath: downloadPath,
format: 'bestvideo[height<=720]+bestaudio/best[height<=720]',
);
// Download best MP4 video
final request = DownloadRequest(
url: videoUrl,
outputPath: downloadPath,
format: 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]',
);
final request = DownloadRequest(
url: videoUrl,
outputPath: downloadPath,
writeSubtitles: true,
writeAutoSubtitles: true,
embedSubtitles: true,
subtitlesLang: 'en,es,fr', // Multiple languages
);
final request = DownloadRequest(
url: videoUrl,
outputPath: downloadPath,
customOptions: {
'--no-playlist': '', // Don't download playlist
'--max-downloads': '5', // Limit downloads
'--rate-limit': '1M', // Limit download speed
'--retries': '10', // Retry attempts
'--fragment-retries': '10',
'--skip-unavailable-fragments': '',
'--no-mtime': '', // Don't use Last-modified header
'--no-update': '', // Suppress update warning
},
);
// Download entire playlist
final request = DownloadRequest(
url: 'https://www.youtube.com/playlist?list=...',
outputPath: downloadPath,
noPlaylist: false, // Allow playlist
outputTemplate: '%(playlist_index)s - %(title)s.%(ext)s',
);
// Download only first video from playlist
final request = DownloadRequest(
url: 'https://www.youtube.com/playlist?list=...',
outputPath: downloadPath,
noPlaylist: true, // Skip playlist
);
The example app demonstrates all features:
Run the example:
cd example
flutter run
ExtractorPlugin (Main)
└── YoutubeDLManager (Coordinator)
├── LibraryService (Initialization & Versions)
├── UpdateService (Binary Updates)
├── InfoService (Video Information)
├── DownloadService (Downloads & Progress)
└── VideoInfoMapper (JSON Mapping)
YouTube, Vimeo, Dailymotion, Facebook, Instagram, Twitter, TikTok, Reddit, Twitch, SoundCloud, Bandcamp, and many more.
MP3, M4A, WAV, FLAC, AAC, OPUS, OGG, VORBIS
MP4, MKV, WEBM, AVI, FLV, MOV
SRT, VTT, ASS, LRC
Build Error: NDK version mismatch
// In android/app/build.gradle
android {
ndkVersion = "27.0.12077973"
}
Build Error: extractNativeLibs
android:extractNativeLibs="true" is set in AndroidManifest.xmlUpdate Failed
Download Failed
Currently, this plugin only supports Android. iOS implementation is open for contributions!
If you're interested in adding iOS support, here are some approaches to consider:
Feel free to open an issue or pull request if you'd like to contribute iOS support!
Initialize the library with configuration.
Future<InitResult> initialize({
bool enableFFmpeg = true,
bool enableAria2c = false,
})
Get video information without downloading.
Future<VideoInfo> getVideoInfo(String url)
Returns: VideoInfo with title, duration, formats, thumbnail, uploader, etc.
Download a video with specified configuration.
Future<DownloadResult> download(DownloadRequest request)
Cancel an active download by process ID.
Future<bool> cancelDownload(String processId)
Update yt-dlp binary to the latest version.
Future<UpdateResult> updateYoutubeDL({
UpdateChannel channel = UpdateChannel.stable,
})
Get version information for yt-dlp, FFmpeg, and Python.
Future<VersionInfo> getVersion()
Listen to real-time updates:
// Progress updates
Stream<DownloadProgress> get onProgress
// State changes (started, completed, cancelled)
Stream<DownloadState> get onStateChanged
// Errors
Stream<DownloadError> get onError
// Logs (yt-dlp output)
Stream<LogMessage> get onLog
DownloadRequest({
required String url,
required String outputPath,
String? outputTemplate, // e.g., '%(title)s.%(ext)s'
String? format, // e.g., 'bestvideo+bestaudio/best'
bool noPlaylist = true,
bool extractAudio = false,
String? audioFormat, // 'mp3', 'm4a', 'wav', etc.
int? audioQuality, // 0-9 (0 is best)
bool embedThumbnail = false,
bool embedMetadata = false,
bool embedSubtitles = false,
String? subtitlesLang,
bool writeSubtitles = false,
bool writeAutoSubtitles = false,
Map<String, String>? customOptions,
String? processId,
})
class VideoInfo {
String? id, title, description;
String? uploader, uploaderId, uploaderUrl;
String? channelId, channelUrl;
int? duration, viewCount, likeCount;
String? thumbnail, url;
List<VideoFormat?>? formats;
String? ext;
int? width, height, fps;
String? vcodec, acodec;
}
class VideoFormat {
String? formatId, formatNote, ext, url;
int? width, height, fps, filesize, tbr;
String? vcodec, acodec, resolution;
}
// Get best video/audio formats
FormatHelper.getBestVideo(formats)
FormatHelper.getBestAudio(formats)
// Filter by resolution
FormatHelper.getFormatsByResolution(formats, minHeight, maxHeight)
// Get specific format types
FormatHelper.getAudioFormats(formats)
FormatHelper.getVideoFormats(formats)
// Format utilities
FormatHelper.formatFileSize(bytes) // "50.00 MB"
FormatHelper.formatResolution(format) // "1920x1080"
FormatHelper.getFormatDescription(format) // "1080p • mp4 • 50.00 MB"
See CONTRIBUTING.md for contribution guidelines.
This project is licensed under the MIT License - see the LICENSE file for details.
This plugin uses open-source libraries including youtubedl-android (GPL-3.0). See LICENSE file for third-party acknowledgments.
Made with ❤️ by Ashish Pipaliya
⭐ Star this repo if you find it useful!