Extractor - Flutter Video Downloader Plugin
A robust, production-ready Flutter plugin for downloading videos and audio from 1000+ websites using yt-dlp. Built with native Android (Kotlin) implementation.
Platform Support
| Platform | Status | Implementation |
|---|---|---|
| Android | β Fully Supported | Native Kotlin + youtubedl-android |
| iOS | π‘ Open for Contributions | See iOS Support below |
π± Screenshots (example app)
![]() |
![]() |
![]() |
| Quality Selection Browse and select video quality |
Download Progress Real-time progress with logs |
Downloads Manager View completed downloads |
![]() |
![]() |
|
| Version Information Check library versions |
Features List All plugin capabilities |
β¨ Features
Core Functionality
- π₯ Download videos from 1000+ websites (YouTube, Vimeo, Dailymotion, etc.)
- π΅ Extract audio with format conversion (MP3, M4A, WAV, FLAC, AAC, OPUS)
- π Get video information (title, duration, formats, thumbnails, metadata)
- π¬ Format selection - Choose quality, resolution, codec
- π Subtitle support - Download and embed subtitles in multiple languages
- πΌοΈ Thumbnail embedding - Embed video thumbnails in audio files
- π Metadata embedding - Add title, artist, album info
- β‘ Aria2c integration - Faster downloads with external downloader
- π Progress tracking - Real-time progress updates with ETA
- βΈοΈ Download management - Cancel, pause, resume downloads
- π Update yt-dlp - Update to latest stable version
Advanced Features
- π― Download templates - Pre-configured quality presets (Best, 1080p, 720p, 480p, Audio Only, Small Size)
- π¨ Custom format strings - Advanced format selection with yt-dlp syntax
- π§ Custom yt-dlp options - Pass any yt-dlp command-line option
- π¦ Playlist support - Download entire playlists or individual videos
- π Chapter support - Preserve video chapters
- π Multi-language subtitles - Download subtitles in any language
- π Cookie support - Use cookies for authentication (planned)
- π± Scoped storage - Android 10+ compliant storage handling
Architecture & Code Quality
- ποΈ Clean Architecture - SOLID principles with separation of concerns
- π Type-safe Communication - Pigeon for compile-time type safety
- π§© Service Layer Pattern - LibraryService, UpdateService, InfoService, DownloadService
- π§΅ Thread Safety - Proper threading with coroutines (Android)
- π Well Documented - Comprehensive API documentation
- β Production Ready - Battle-tested architecture
Example App Features
- π¨ Material Design 3 - Modern UI with dynamic colors
- π± Inline Format Selection - Horizontal scrollable quality cards
- π₯ Downloads Manager - View and manage downloaded files
- πΎ Save to Gallery - Export videos to public storage
- π File Management - View info, share, delete files
- βοΈ Settings Page - Version info, updates, library management
- π Dark Mode - Automatic theme switching
π¦ Installation
Add to your pubspec.yaml:
dependencies:
extractor: latest
Android Requirements
Minimum SDK version (API 24+):
android {
defaultConfig {
minSdk = 24
}
}
Required: Set extractNativeLibs in AndroidManifest.xml:
<application
android:extractNativeLibs="true"
...>
π Quick Start
Initialize
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}');
}
Get Video Information
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');
}
Download Video
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');
}
Download Audio Only
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);
Track Download Progress
// 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}');
});
Cancel Download
final cancelled = await youtubeDL.cancelDownload(processId: 'download_123');
if (cancelled) {
print('Download cancelled');
}
Update yt-dlp
final result = await youtubeDL.updateYoutubeDL(channel: UpdateChannel.stable);
if (result.status == OperationStatus.success) {
print('Updated to: ${result.version}');
} else {
print('Update failed: ${result.errorMessage}');
}
Get Version Information
final versionInfo = await youtubeDL.getVersion();
print('yt-dlp: ${versionInfo.youtubeDlVersion}');
print('FFmpeg: ${versionInfo.ffmpegVersion}');
print('Python: ${versionInfo.pythonVersion}');
π Download Templates
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);
π― Advanced Usage
Custom Format Selection
// 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]',
);
Subtitle Download
final request = DownloadRequest(
url: videoUrl,
outputPath: downloadPath,
writeSubtitles: true,
writeAutoSubtitles: true,
embedSubtitles: true,
subtitlesLang: 'en,es,fr', // Multiple languages
);
Custom yt-dlp Options
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
},
);
Playlist Handling
// 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
);
π± Example App
The example app demonstrates all features:
- Main Page: URL input, video info display, inline format selection
- Downloads Page: View downloaded files, save to gallery, file management
- Settings Page: Version info, update yt-dlp, library information
Run the example:
cd example
flutter run
ποΈ Architecture
Service Layer
ExtractorPlugin (Main)
βββ YoutubeDLManager (Coordinator)
βββ LibraryService (Initialization & Versions)
βββ UpdateService (Binary Updates)
βββ InfoService (Video Information)
βββ DownloadService (Downloads & Progress)
βββ VideoInfoMapper (JSON Mapping)
Communication
- Pigeon: Type-safe communication between Dart and native code
- Streams: Real-time progress updates via Dart streams
- Coroutines: Async operations on Android
π Supported Platforms
Video Platforms (1000+)
YouTube, Vimeo, Dailymotion, Facebook, Instagram, Twitter, TikTok, Reddit, Twitch, SoundCloud, Bandcamp, and many more.
Audio Formats
MP3, M4A, WAV, FLAC, AAC, OPUS, OGG, VORBIS
Video Formats
MP4, MKV, WEBM, AVI, FLV, MOV
Subtitle Formats
SRT, VTT, ASS, LRC
π§ Troubleshooting
Android
Build Error: NDK version mismatch
// In android/app/build.gradle
android {
ndkVersion = "27.0.12077973"
}
Build Error: extractNativeLibs
- Ensure
android:extractNativeLibs="true"is set in AndroidManifest.xml - This is required for the plugin to extract native libraries
Update Failed
- Check internet connection
- Ensure sufficient storage space
- Try again later (GitHub API rate limits)
Download Failed
- Verify the URL is supported (check supported sites)
- Check internet connection
- Try updating yt-dlp to the latest version
- Some sites may require cookies or authentication
π‘ iOS Support
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:
- Rust + FFI: Use Rust with flutter_rust_bridge for cross-platform yt-dlp integration
- Native Swift: Port the Android implementation to Swift/Objective-C
- Server-side: Implement a backend service for video processing (recommended for App Store apps)
Feel free to open an issue or pull request if you'd like to contribute iOS support!
π API Reference
Core Methods
initialize()
Initialize the library with configuration.
Future<InitResult> initialize({
bool enableFFmpeg = true,
bool enableAria2c = false,
})
getVideoInfo()
Get video information without downloading.
Future<VideoInfo> getVideoInfo(String url)
Returns: VideoInfo with title, duration, formats, thumbnail, uploader, etc.
download()
Download a video with specified configuration.
Future<DownloadResult> download(DownloadRequest request)
cancelDownload()
Cancel an active download by process ID.
Future<bool> cancelDownload(String processId)
updateYoutubeDL()
Update yt-dlp binary to the latest version.
Future<UpdateResult> updateYoutubeDL({
UpdateChannel channel = UpdateChannel.stable,
})
getVersion()
Get version information for yt-dlp, FFmpeg, and Python.
Future<VersionInfo> getVersion()
Streams
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
Key Models
DownloadRequest
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,
})
VideoInfo
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;
}
VideoFormat
class VideoFormat {
String? formatId, formatNote, ext, url;
int? width, height, fps, filesize, tbr;
String? vcodec, acodec, resolution;
}
FormatHelper Utilities
// 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"
π€ Contributing
See CONTRIBUTING.md for contribution guidelines.
π License
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.
π Credits
- yt-dlp - The amazing video downloader
- youtubedl-android - Android port
- Pigeon - Type-safe native communication
π Documentation
- README.md - Complete guide with features, installation, usage, and API reference
- CHANGELOG.md - Version history and release notes
- CONTRIBUTING.md - Contribution guidelines
π Links
β‘ Performance
- Fast Downloads: Aria2c integration for multi-connection downloads
- Efficient: Lazy extractors for faster startup
- Optimized: Native code for best performance
- Scalable: Handle multiple concurrent downloads
π Security
- Scoped Storage: Android 10+ compliant
- Permission Minimal: Only requests necessary permissions
- Type-Safe: Compile-time type checking with Pigeon
π Version Information
- Plugin Version: 1.0.0
- youtubedl-android: v0.18.1
- yt-dlp: 2025.11.12 (bundled, updatable)
- FFmpeg: 6.0 (bundled)
- Python: 3.8 (bundled)
- Minimum Android: API 24 (Android 7.0)
Made with β€οΈ by Ashish Pipaliya
β Star this repo if you find it useful!




