extractor
yt-dlp를 사용하여 1000개 이상의 웹사이트에서 비디오 및 오디오를 다운로드할 수 있는 강력한 Flutter 플러그인입니다. 품질 선택, 형식 변환, 진행률 추적 등이 포함되어 있습니다.
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!